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:
		
					parent
					
						
							
								e005bd6f98
							
						
					
				
			
			
				commit
				
					
						3a513cfe73
					
				
			
		
					 10 changed files with 78 additions and 27 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,6 +203,7 @@ start si file addunlockedmatcher =
 | 
			
		|||
  where
 | 
			
		||||
	go s = ifAnnexed file (addpresent s) (add s)
 | 
			
		||||
	add s = starting "add" (ActionItemTreeFile file) si $
 | 
			
		||||
		skipWhenDryRun dr $
 | 
			
		||||
			if isSymbolicLink s
 | 
			
		||||
				then next $ addFile Small file s
 | 
			
		||||
				else perform file addunlockedmatcher
 | 
			
		||||
| 
						 | 
				
			
			@ -209,7 +213,7 @@ start si file addunlockedmatcher =
 | 
			
		|||
	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,7 +227,8 @@ start si file addunlockedmatcher =
 | 
			
		|||
						)
 | 
			
		||||
	fixuppointer s key =
 | 
			
		||||
		starting "add" (ActionItemTreeFile file) si $
 | 
			
		||||
			addingExistingLink file key $ do
 | 
			
		||||
			addingExistingLink file key $
 | 
			
		||||
				skipWhenDryRun dr $ do
 | 
			
		||||
					Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)
 | 
			
		||||
					next $ addFile Large file s
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
"""]]
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue