2021-07-14 18:25:52 +00:00
|
|
|
{- git-annex command
|
|
|
|
-
|
|
|
|
- Copyright 2021 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Command.WhereUsed where
|
|
|
|
|
|
|
|
import Command
|
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
2021-07-14 19:38:28 +00:00
|
|
|
import Git
|
|
|
|
import Git.Sha
|
2021-07-14 18:25:52 +00:00
|
|
|
import Git.FilePath
|
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
2021-07-14 19:38:28 +00:00
|
|
|
import qualified Git.Ref
|
|
|
|
import qualified Git.Command
|
|
|
|
import qualified Git.DiffTree as DiffTree
|
|
|
|
import qualified Annex
|
|
|
|
import qualified Annex.Branch
|
2021-07-14 18:25:52 +00:00
|
|
|
import Annex.CatFile
|
|
|
|
import Database.Keys
|
|
|
|
|
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
2021-07-14 19:38:28 +00:00
|
|
|
import Data.Char
|
|
|
|
import qualified Data.ByteString as S
|
|
|
|
import qualified Data.ByteString.Lazy as L
|
|
|
|
|
2021-07-14 18:25:52 +00:00
|
|
|
cmd :: Command
|
|
|
|
cmd = noCommit $ withGlobalOptions [annexedMatchingOptions] $
|
|
|
|
command "whereused" SectionQuery
|
|
|
|
"lists repositories that have file content"
|
|
|
|
paramNothing (seek <$$> optParser)
|
|
|
|
|
|
|
|
data WhereUsedOptions = WhereUsedOptions
|
|
|
|
{ keyOptions :: KeyOptions
|
|
|
|
, historicalOption :: Bool
|
|
|
|
}
|
|
|
|
|
|
|
|
optParser :: CmdParamsDesc -> Parser WhereUsedOptions
|
|
|
|
optParser _desc = WhereUsedOptions
|
|
|
|
<$> (parseUnusedKeysOption <|> parseSpecificKeyOption)
|
|
|
|
<*> switch
|
|
|
|
( long "historical"
|
|
|
|
<> help "find historical uses"
|
|
|
|
)
|
|
|
|
|
|
|
|
seek :: WhereUsedOptions -> CommandSeek
|
|
|
|
seek o = withKeyOptions (Just (keyOptions o)) False dummyfileseeker
|
|
|
|
(commandAction . start o) dummyfilecommandseek (WorkTreeItems [])
|
|
|
|
where
|
|
|
|
dummyfileseeker = AnnexedFileSeeker
|
|
|
|
{ startAction = \_ _ _ -> return Nothing
|
|
|
|
, checkContentPresent = Nothing
|
|
|
|
, usesLocationLog = False
|
|
|
|
}
|
|
|
|
dummyfilecommandseek = const noop
|
|
|
|
|
|
|
|
start :: WhereUsedOptions -> (SeekInput, Key, ActionItem) -> CommandStart
|
|
|
|
start o (_, key, _) = startingCustomOutput key $ do
|
|
|
|
fs <- filterM stillassociated
|
|
|
|
=<< mapM (fromRepo . fromTopFilePath)
|
|
|
|
=<< getAssociatedFiles key
|
|
|
|
liftIO $ forM_ fs $ display key . fromRawFilePath
|
|
|
|
|
|
|
|
when (historicalOption o && null fs) $
|
|
|
|
findHistorical key
|
|
|
|
|
|
|
|
next $ return True
|
|
|
|
where
|
|
|
|
-- Some associated files that are in the keys database may no
|
|
|
|
-- longer correspond to files in the repository.
|
|
|
|
stillassociated f = catKeyFile f >>= \case
|
|
|
|
Just k | k == key -> return True
|
|
|
|
_ -> return False
|
|
|
|
|
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
2021-07-14 19:38:28 +00:00
|
|
|
display :: Key -> String -> IO ()
|
|
|
|
display key loc = putStrLn (serializeKey key ++ " " ++ loc)
|
2021-07-14 18:25:52 +00:00
|
|
|
|
|
|
|
findHistorical :: Key -> Annex ()
|
|
|
|
findHistorical key = do
|
2021-07-14 20:05:20 +00:00
|
|
|
-- Find most recent change to the key, in all branches and
|
|
|
|
-- tags, except the git-annex branch.
|
|
|
|
found <- searchLog key
|
|
|
|
-- 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"
|
|
|
|
] $ \h fs repo -> do
|
|
|
|
commitsha <- getSha "log" (pure h)
|
|
|
|
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 process fs $
|
|
|
|
displayreffile (Ref commitdesc)
|
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
2021-07-14 19:38:28 +00:00
|
|
|
|
2021-07-14 20:05:20 +00:00
|
|
|
unless found $
|
|
|
|
void $ searchLog key
|
|
|
|
[ Param "--walk-reflogs"
|
|
|
|
-- Output the reflog selector
|
|
|
|
, Param "--pretty=%gd"
|
|
|
|
] $ \h fs _ -> process fs $
|
|
|
|
displayreffile (Ref h)
|
|
|
|
where
|
|
|
|
process fs a = or <$> forM fs a
|
|
|
|
|
|
|
|
displayreffile r f = do
|
|
|
|
let fref = Git.Ref.branchFileRef r f
|
|
|
|
display key (fromRef fref)
|
|
|
|
return True
|
|
|
|
|
|
|
|
searchLog :: Key -> [CommandParam] -> (S.ByteString -> [RawFilePath] -> Repo -> IO Bool) -> Annex Bool
|
|
|
|
searchLog key ps a = Annex.inRepo $ \repo -> do
|
|
|
|
(output, cleanup) <- Git.Command.pipeNullSplit ps' repo
|
|
|
|
found <- case output of
|
|
|
|
(h:rest) -> do
|
|
|
|
let diff = DiffTree.parseDiffRaw rest
|
|
|
|
let fs = map (flip fromTopFilePath repo . DiffTree.file) diff
|
|
|
|
rfs <- mapM relPathCwdToFile fs
|
|
|
|
a (L.toStrict h) rfs repo
|
|
|
|
_ -> return False
|
|
|
|
void cleanup
|
|
|
|
return found
|
|
|
|
where
|
|
|
|
ps' =
|
|
|
|
[ Param "log"
|
|
|
|
, Param "-z"
|
|
|
|
-- Don't convert pointer files.
|
|
|
|
, Param "--no-textconv"
|
|
|
|
-- Don't abbreviate hashes.
|
|
|
|
, Param "--no-abbrev"
|
|
|
|
-- Only find the most recent commit, for speed.
|
|
|
|
, Param "-n1"
|
2021-07-14 20:28:07 +00:00
|
|
|
-- Be sure to treat -G as a regexp.
|
|
|
|
, Param "--basic-regexp"
|
|
|
|
-- Find commits that contain the key. The object has to
|
|
|
|
-- end with the key to avoid confusion with longer keys,
|
|
|
|
-- so a regexp is used. Since annex pointer files
|
|
|
|
-- may contain a newline followed by perhaps something
|
|
|
|
-- else, that is also matched.
|
|
|
|
, Param ("-G" ++ escapeRegexp (fromRawFilePath (keyFile key)) ++ "($|\n)")
|
2021-07-14 20:05:20 +00:00
|
|
|
-- Skip commits where the file was deleted,
|
|
|
|
-- only find those where it was added or modified.
|
|
|
|
, Param "--diff-filter=ACMRTUX"
|
|
|
|
-- Output the raw diff.
|
|
|
|
, Param "--raw"
|
|
|
|
] ++ ps
|
2021-07-14 20:28:07 +00:00
|
|
|
|
|
|
|
escapeRegexp :: String -> String
|
|
|
|
escapeRegexp = concatMap esc
|
|
|
|
where
|
|
|
|
esc c
|
|
|
|
| isAscii c && isAlphaNum c = [c]
|
|
|
|
| otherwise = ['[', c, ']']
|