]> dgit.raspbian.org Git - git-annex.git/commitdiff
whereused --historical
authorJoey Hess <joeyh@joeyh.name>
Wed, 14 Jul 2021 19:38:28 +0000 (15:38 -0400)
committerJoey Hess <joeyh@joeyh.name>
Wed, 14 Jul 2021 19:38:28 +0000 (15:38 -0400)
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
Git/DiffTree.hs

index 4534d90e1a3449b2daf8b46f4f51d05f17c9a638..7cd886f42208ac10c01493b34f1c2bba3c46a231 100644 (file)
@@ -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"
index 570f5cc35e9823181e81c3c1bb1918dbf4dd2d20..273b9427e02067d4dbb7bea38798cd8602e05e48 100644 (file)
@@ -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 ++ "\""
 
 -- :<srcmode> SP <dstmode> SP <srcsha> SP <dstsha> SP <status>
+--
+-- 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