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.
 | 
					  * 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,16 +203,17 @@ 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 $
 | 
				
			||||||
		if isSymbolicLink s
 | 
							skipWhenDryRun dr $
 | 
				
			||||||
			then next $ addFile Small file s
 | 
								if isSymbolicLink s
 | 
				
			||||||
			else perform file addunlockedmatcher
 | 
									then next $ addFile Small file s
 | 
				
			||||||
 | 
									else perform file addunlockedmatcher
 | 
				
			||||||
	addpresent s key
 | 
						addpresent s key
 | 
				
			||||||
		| isSymbolicLink s = fixuplink key
 | 
							| isSymbolicLink s = fixuplink key
 | 
				
			||||||
		| otherwise = add s
 | 
							| otherwise = add s
 | 
				
			||||||
	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,9 +227,10 @@ 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 $
 | 
				
			||||||
				Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)
 | 
									skipWhenDryRun dr $ do
 | 
				
			||||||
				next $ addFile Large file s
 | 
										Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)
 | 
				
			||||||
 | 
										next $ addFile Large file s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
perform :: RawFilePath -> AddUnlockedMatcher -> CommandPerform
 | 
					perform :: RawFilePath -> AddUnlockedMatcher -> CommandPerform
 | 
				
			||||||
perform file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 | 
					perform file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,4 +21,5 @@ 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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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