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
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"
diffFiles,
diffLog,
commitDiff,
+ parseDiffRaw,
) where
import qualified Data.ByteString as B
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