From fcd1b93a7dd2d26f02f013440fb03696bf4d31e6 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 14 Jul 2021 15:38:28 -0400 Subject: [PATCH] whereused --historical Does not check the reflog, but otherwise works. It's possible for it to display something that is not an annexed file, if a non-annexed file somehow ends up containing something that looks like the key's name. This seems very unlikely to happen, and it would add a lot of complexity to detect it and somehow skip over that file, since the git log would need to either be run again, or not limited to 1 result and canceled once enough results have been read. Also, it kind of seems ok, if a file refers to a key, to consider that as a place the key was used, for some definition of used. So, I punted on dealing with that. May revisit later. Sponsored-by: Brock Spratlen on Patreon --- Command/WhereUsed.hs | 70 ++++++++++++++++++++++++++++++++++++++++++-- Git/DiffTree.hs | 7 ++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/Command/WhereUsed.hs b/Command/WhereUsed.hs index 4534d90e1a..7cd886f422 100644 --- a/Command/WhereUsed.hs +++ b/Command/WhereUsed.hs @@ -8,10 +8,21 @@ module Command.WhereUsed where import Command +import Git +import Git.Sha import Git.FilePath +import qualified Git.Ref +import qualified Git.Command +import qualified Git.DiffTree as DiffTree +import qualified Annex +import qualified Annex.Branch import Annex.CatFile import Database.Keys +import Data.Char +import qualified Data.ByteString as S +import qualified Data.ByteString.Lazy as L + cmd :: Command cmd = noCommit $ withGlobalOptions [annexedMatchingOptions] $ command "whereused" SectionQuery @@ -60,9 +71,62 @@ start o (_, key, _) = startingCustomOutput key $ do Just k | k == key -> return True _ -> return False -display :: Key -> FilePath -> IO () -display key f = putStrLn (serializeKey key ++ " " ++ f) +display :: Key -> String -> IO () +display key loc = putStrLn (serializeKey key ++ " " ++ loc) findHistorical :: Key -> Annex () findHistorical key = do - error "TODO" + Annex.inRepo $ \repo -> do + -- Find most recent change to the key, in all branches and + -- tags, except the git-annex branch. + (output, cleanup) <- Git.Command.pipeNullSplit + [ Param "log" + , Param "-z" + -- Don't convert pointer files. + , Param "--no-textconv" + -- Only find the most recent commit, for speed. + , Param "-n1" + -- Find commits that contain the key. + , Param ("-S" ++ fromRawFilePath (keyFile key)) + -- Skip commits where the file was deleted, + -- only find those where it was added or modified. + , Param "--diff-filter=ACMRTUX" + -- Search all local branches, except git-annex branch. + , Param ("--exclude=*/" ++ fromRef (Annex.Branch.name)) + , Param "--glob=*" + -- Also search remote branches + , Param ("--exclude=" ++ fromRef (Annex.Branch.name)) + , Param "--remotes=*" + -- And search tags. + , Param "--tags=*" + -- Output the commit hash + , Param "--pretty=%H" + -- And the raw diff. + , Param "--raw" + -- Don't abbreviate hashes. + , Param "--no-abbrev" + ] repo + found <- case output of + (h:rest) -> do + commitsha <- getSha "log" (pure (L.toStrict h)) + let diff = DiffTree.parseDiffRaw rest + forM_ (map (flip fromTopFilePath repo . DiffTree.file) diff) $ \f -> do + commitdesc <- S.takeWhile (/= fromIntegral (ord '\n')) + <$> Git.Command.pipeReadStrict + [ Param "describe" + , Param "--contains" + , Param "--all" + , Param (fromRef commitsha) + ] repo + if S.null commitdesc + then return False + else do + rf <- relPathCwdToFile f + let fref = Git.Ref.branchFileRef (Ref commitdesc) rf + display key (fromRef fref) + return True + _ -> return False + void cleanup + + unless found $ do + error "todo reflog" diff --git a/Git/DiffTree.hs b/Git/DiffTree.hs index 570f5cc35e..273b9427e0 100644 --- a/Git/DiffTree.hs +++ b/Git/DiffTree.hs @@ -15,6 +15,7 @@ module Git.DiffTree ( diffFiles, diffLog, commitDiff, + parseDiffRaw, ) where import qualified Data.ByteString as B @@ -116,9 +117,13 @@ parseDiffRaw l = go l go (s:[]) = error $ "diff-tree parse error near \"" ++ decodeBL' s ++ "\"" -- : SP SP SP SP +-- +-- May be prefixed with a newline, which git log --pretty=format +-- adds to the first line of the diff, even with -z. parserDiffRaw :: RawFilePath -> A.Parser DiffTreeItem parserDiffRaw f = DiffTreeItem - <$ A8.char ':' + <$ A.option '\n' (A8.char '\n') + <* A8.char ':' <*> octal <* A8.char ' ' <*> octal