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.
* Allow find --branch to be used in a bare repository, the same as
the deprecated findref can be.
* add --dry-run: New option.
-- 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
. (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.
data FromToOptions
= FromRemote (DeferredParse Remote)

View file

@ -106,6 +106,12 @@ stop = return Nothing
stopUnless :: Annex Bool -> Annex (Maybe a) -> Annex (Maybe a)
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
- direction. -}
checkFailedTransferDirection :: ActionItem -> Direction -> Annex (Maybe a) -> Annex (Maybe a)

View file

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

View file

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

View file

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

View file

@ -133,3 +133,6 @@ descSection SectionUtility = "Utility commands"
descSection SectionPlumbing = "Plumbing commands"
descSection SectionTesting = "Testing 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
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`
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]]
[[!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.
"""]]