add --dry-run: New option

This is intended for users who want to see what it would output in order to
eg, check if a file would be added to git or the annex. It is not intended
as a way for scripts to get information.

Sponsored-by: Dartmouth College's Datalad project
This commit is contained in:
Joey Hess 2022-08-03 11:16:04 -04:00
parent e005bd6f98
commit 3a513cfe73
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
10 changed files with 78 additions and 27 deletions

View file

@ -10,6 +10,7 @@ git-annex (10.20220725) UNRELEASED; urgency=medium
* Added new matching options --want-get-by and --want-drop-by. * Added new matching options --want-get-by and --want-drop-by.
* Allow find --branch to be used in a bare repository, the same as * Allow find --branch to be used in a bare repository, the same as
the deprecated findref can be. the deprecated findref can be.
* add --dry-run: New option.
-- Joey Hess <id@joeyh.name> Mon, 25 Jul 2022 15:35:45 -0400 -- Joey Hess <id@joeyh.name> Mon, 25 Jul 2022 15:35:45 -0400

View file

@ -128,6 +128,12 @@ parseUUIDOption :: String -> DeferredParse UUID
parseUUIDOption = DeferredParse parseUUIDOption = DeferredParse
. (Remote.nameToUUID) . (Remote.nameToUUID)
parseDryRunOption :: Parser DryRun
parseDryRunOption = DryRun <$> switch
( long "dry-run"
<> help "don't make changes, but show what would be done"
)
-- | From or To a remote. -- | From or To a remote.
data FromToOptions data FromToOptions
= FromRemote (DeferredParse Remote) = FromRemote (DeferredParse Remote)

View file

@ -106,6 +106,12 @@ stop = return Nothing
stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a) stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a)
stopUnless c a = ifM c ( a , stop ) stopUnless c a = ifM c ( a , stop )
{- When doing a dry run, avoid actually performing the action, but pretend
- that it succeeded. -}
skipWhenDryRun :: DryRun -> CommandPerform -> CommandPerform
skipWhenDryRun (DryRun False) a = a
skipWhenDryRun (DryRun True) _ = next $ return True
{- When acting on a failed transfer, stops unless it was in the specified {- When acting on a failed transfer, stops unless it was in the specified
- direction. -} - direction. -}
checkFailedTransferDirection :: ActionItem -> Direction -> Annex (Maybe a) -> Annex (Maybe a) checkFailedTransferDirection :: ActionItem -> Direction -> Annex (Maybe a) -> Annex (Maybe a)

View file

@ -52,6 +52,7 @@ data AddOptions = AddOptions
, updateOnly :: Bool , updateOnly :: Bool
, largeFilesOverride :: Maybe Bool , largeFilesOverride :: Maybe Bool
, checkGitIgnoreOption :: CheckGitIgnore , checkGitIgnoreOption :: CheckGitIgnore
, dryRunOption :: DryRun
} }
optParser :: CmdParamsDesc -> Parser AddOptions optParser :: CmdParamsDesc -> Parser AddOptions
@ -65,6 +66,7 @@ optParser desc = AddOptions
) )
<*> (parseforcelarge <|> parseforcesmall) <*> (parseforcelarge <|> parseforcesmall)
<*> checkGitIgnoreSwitch <*> checkGitIgnoreSwitch
<*> parseDryRunOption
where where
parseforcelarge = flag Nothing (Just True) parseforcelarge = flag Nothing (Just True)
( long "force-large" ( long "force-large"
@ -91,16 +93,16 @@ seek o = startConcurrency commandStages $ do
ifM (pure (annexdotfiles || not (dotfile file)) ifM (pure (annexdotfiles || not (dotfile file))
<&&> (checkFileMatcher largematcher file <&&> (checkFileMatcher largematcher file
<||> Annex.getRead Annex.force)) <||> Annex.getRead Annex.force))
( start si file addunlockedmatcher ( start dr si file addunlockedmatcher
, if includingsmall , if includingsmall
then ifM (annexAddSmallFiles <$> Annex.getGitConfig) then ifM (annexAddSmallFiles <$> Annex.getGitConfig)
( startSmall si file s ( startSmall dr si file s
, stop , stop
) )
else stop else stop
) )
Just True -> start si file addunlockedmatcher Just True -> start dr si file addunlockedmatcher
Just False -> startSmallOverridden si file Just False -> startSmallOverridden dr si file
case batchOption o of case batchOption o of
Batch fmt Batch fmt
| updateOnly o -> | updateOnly o ->
@ -126,25 +128,26 @@ seek o = startConcurrency commandStages $ do
-- same as a modified unlocked file would get -- same as a modified unlocked file would get
-- locked when added. -- locked when added.
go False withUnmodifiedUnlockedPointers go False withUnmodifiedUnlockedPointers
where
dr = dryRunOption o
{- Pass file off to git-add. -} {- Pass file off to git-add. -}
startSmall :: SeekInput -> RawFilePath -> FileStatus -> CommandStart startSmall :: DryRun -> SeekInput -> RawFilePath -> FileStatus -> CommandStart
startSmall si file s = startSmall dr si file s =
starting "add" (ActionItemTreeFile file) si $ starting "add" (ActionItemTreeFile file) si $
next $ addSmall file s addSmall dr file s
addSmall :: RawFilePath -> FileStatus -> Annex Bool addSmall :: DryRun -> RawFilePath -> FileStatus -> CommandPerform
addSmall file s = do addSmall dr file s = do
showNote "non-large file; adding content to git repository" showNote "non-large file; adding content to git repository"
addFile Small file s skipWhenDryRun dr $ next $ addFile Small file s
startSmallOverridden :: SeekInput -> RawFilePath -> CommandStart startSmallOverridden :: DryRun -> SeekInput -> RawFilePath -> CommandStart
startSmallOverridden si file = startSmallOverridden dr si file =
liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
Just s -> starting "add" (ActionItemTreeFile file) si $ next $ do Just s -> starting "add" (ActionItemTreeFile file) si $ do
showNote "adding content to git repository" showNote "adding content to git repository"
addFile Small file s skipWhenDryRun dr $ next $ addFile Small file s
Nothing -> stop Nothing -> stop
data SmallOrLarge = Small | Large data SmallOrLarge = Small | Large
@ -188,8 +191,8 @@ addFile smallorlarge file s = do
isRegularFile a /= isRegularFile b || isRegularFile a /= isRegularFile b ||
isSymbolicLink a /= isSymbolicLink b isSymbolicLink a /= isSymbolicLink b
start :: SeekInput -> RawFilePath -> AddUnlockedMatcher -> CommandStart start :: DryRun -> SeekInput -> RawFilePath -> AddUnlockedMatcher -> CommandStart
start si file addunlockedmatcher = start dr si file addunlockedmatcher =
liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
Nothing -> stop Nothing -> stop
Just s Just s
@ -200,6 +203,7 @@ start si file addunlockedmatcher =
where where
go s = ifAnnexed file (addpresent s) (add s) go s = ifAnnexed file (addpresent s) (add s)
add s = starting "add" (ActionItemTreeFile file) si $ add s = starting "add" (ActionItemTreeFile file) si $
skipWhenDryRun dr $
if isSymbolicLink s if isSymbolicLink s
then next $ addFile Small file s then next $ addFile Small file s
else perform file addunlockedmatcher else perform file addunlockedmatcher
@ -209,7 +213,7 @@ start si file addunlockedmatcher =
fixuplink key = fixuplink key =
starting "add" (ActionItemTreeFile file) si $ starting "add" (ActionItemTreeFile file) si $
addingExistingLink file key $ addingExistingLink file key $
withOtherTmp $ \tmp -> do skipWhenDryRun dr $ withOtherTmp $ \tmp -> do
let tmpf = tmp P.</> P.takeFileName file let tmpf = tmp P.</> P.takeFileName file
liftIO $ moveFile file tmpf liftIO $ moveFile file tmpf
ifM (isSymbolicLink <$> liftIO (R.getSymbolicLinkStatus tmpf)) ifM (isSymbolicLink <$> liftIO (R.getSymbolicLinkStatus tmpf))
@ -223,7 +227,8 @@ start si file addunlockedmatcher =
) )
fixuppointer s key = fixuppointer s key =
starting "add" (ActionItemTreeFile file) si $ starting "add" (ActionItemTreeFile file) si $
addingExistingLink file key $ do addingExistingLink file key $
skipWhenDryRun dr $ do
Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file) Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)
next $ addFile Large file s next $ addFile Large file s

View file

@ -476,7 +476,7 @@ addWorkTree _ addunlockedmatcher u url file key mtmp = case mtmp of
(fromRawFilePath file) (fromRawFilePath file)
(fromRawFilePath tmp) (fromRawFilePath tmp)
go go
else void $ Command.Add.addSmall file s else void $ Command.Add.addSmall (DryRun False) file s
where where
go = do go = do
maybeShowJSON $ JSONChunk [("key", serializeKey key)] maybeShowJSON $ JSONChunk [("key", serializeKey key)]

View file

@ -248,7 +248,7 @@ startLocal o addunlockedmatcher largematcher mode (srcfile, destfile) =
>>= maybe >>= maybe
stop stop
(\addedk -> next $ Command.Add.cleanup addedk True) (\addedk -> next $ Command.Add.cleanup addedk True)
, next $ Command.Add.addSmall destfile s , Command.Add.addSmall (DryRun False) destfile s
) )
notoverwriting why = do notoverwriting why = do
warning $ "not overwriting existing " ++ fromRawFilePath destfile ++ " " ++ why warning $ "not overwriting existing " ++ fromRawFilePath destfile ++ " " ++ why

View file

@ -133,3 +133,6 @@ descSection SectionUtility = "Utility commands"
descSection SectionPlumbing = "Plumbing commands" descSection SectionPlumbing = "Plumbing commands"
descSection SectionTesting = "Testing commands" descSection SectionTesting = "Testing commands"
descSection SectionAddOn = "Addon commands" descSection SectionAddOn = "Addon commands"
newtype DryRun = DryRun Bool
deriving (Show)

View file

@ -77,6 +77,10 @@ annexed content, and other symlinks.
Like `git add --update`, this does not add new files, but any updates Like `git add --update`, this does not add new files, but any updates
to tracked files will be added to the index. to tracked files will be added to the index.
* `--dry-run`
Output what would be done for each file, but avoid making any changes.
* `--json` * `--json`
Enable JSON output. This is intended to be parsed by programs that use Enable JSON output. This is intended to be parsed by programs that use

View file

@ -22,3 +22,4 @@ well -- unless there was a version staged already you don't want to loose etc.
[[!meta author=yoh]] [[!meta author=yoh]]
[[!tag projects/datalad]] [[!tag projects/datalad]]
> [[done]] joey

View file

@ -0,0 +1,25 @@
[[!comment format=mdwn
username="joey"
subject="""comment 2"""
date="2022-08-03T14:53:35Z"
content="""
Implemented `git-annex add --dry-run`.
As noted it does not let you tell if a file would be added locked or unlocked
since `git-annex add` output is the same either way.
Also when JSON output is enabled, `git-annex add` usually outputs the key,
but with `--dry-run`, it does not. Generating the key would involve locking
down the file, and by that point the command is making changes to the
filesystem. Use `git-annex calckey` instead.
Implementation was not as simple as making CommandPerform a no-op. Some parts
of CommandPerform actions needed to be run when doing a dry-run, in order for
addingExistingLink to display its warning, and for showNote to display the
very information that the user is interested in seeing from --dry-run.
A cleaner implementation would probably involve moving those actions out of
CommandPerform, perhaps by splitting CommandPerform into two parts.
Which would be a lot of work and still failure prone. That is why I don't
think it's a good idea to add --dry-run to very many commands.
"""]]