packages feed

git-annex 6.20170925 → 6.20171003

raw patch · 22 files changed

+248/−95 lines, 22 files

Files

Annex/Action.hs view
@@ -17,6 +17,10 @@ import Annex.Common import qualified Annex import Annex.Content+import Annex.CatFile+import Annex.CheckAttr+import Annex.HashObject+import Annex.CheckIgnore  {- Actions to perform each time ran. -} startup :: Annex ()@@ -32,4 +36,13 @@ shutdown nocommit = do 	saveState nocommit 	sequence_ =<< M.elems <$> Annex.getState Annex.cleanup+	stopCoProcesses 	liftIO reapZombies -- zombies from long-running git processes++{- Stops all long-running git query processes. -}+stopCoProcesses :: Annex ()+stopCoProcesses = do+	catFileStop+	checkAttrStop+	hashObjectStop+	checkIgnoreStop
Annex/AdjustedBranch.hs view
@@ -167,7 +167,7 @@ 	| adjustedBranchPrefix `isPrefixOf` bs = do 		let (base, as) = separate (== '(') (drop prefixlen bs) 		adj <- deserialize (takeWhile (/= ')') as)-		Just (adj, Git.Ref.underBase "refs/heads" (Ref base))+		Just (adj, Git.Ref.branchRef (Ref base)) 	| otherwise = Nothing   where 	bs = fromRef b
Annex/Concurrent.hs view
@@ -7,12 +7,9 @@  module Annex.Concurrent where -import Annex.Common import Annex-import Annex.CatFile-import Annex.CheckAttr-import Annex.HashObject-import Annex.CheckIgnore+import Annex.Common+import Annex.Action import qualified Annex.Queue  import qualified Data.Map as M@@ -61,11 +58,3 @@ 		uncurry addCleanup 	Annex.Queue.mergeFrom st' 	changeState $ \s -> s { errcounter = errcounter s + errcounter st' }--{- Stops all long-running git query processes. -}-stopCoProcesses :: Annex ()-stopCoProcesses = do-	catFileStop-	checkAttrStop-	hashObjectStop-	checkIgnoreStop
Annex/Init.hs view
@@ -146,40 +146,40 @@ probeCrippledFileSystem = do 	tmp <- fromRepo gitAnnexTmpMiscDir 	createAnnexDirectory tmp-	probeCrippledFileSystem' tmp+	(r, warnings) <- liftIO $ probeCrippledFileSystem' tmp+	mapM_ warning warnings+	return r -probeCrippledFileSystem' :: FilePath -> Annex Bool+probeCrippledFileSystem' :: FilePath -> IO (Bool, [String]) #ifdef mingw32_HOST_OS probeCrippledFileSystem' _ = return True #else probeCrippledFileSystem' tmp = do 	let f = tmp </> "gaprobe"-	liftIO $ writeFile f ""-	uncrippled <- probe f-	void $ liftIO $ tryIO $ allowWrite f-	liftIO $ removeFile f-	return $ not uncrippled+	writeFile f ""+	r <- probe f+	void $ tryIO $ allowWrite f+	removeFile f+	return r   where-	probe f = catchBoolIO $ do+	probe f = catchDefaultIO (True, []) $ do 		let f2 = f ++ "2"-		liftIO $ nukeFile f2-		liftIO $ createSymbolicLink f f2-		liftIO $ nukeFile f2-		liftIO $ preventWrite f+		nukeFile f2+		createSymbolicLink f f2+		nukeFile f2+		preventWrite f 		-- Should be unable to write to the file, unless 		-- running as root, but some crippled 		-- filesystems ignore write bit removals.-		ifM ((== 0) <$> liftIO getRealUserID)-			( return True+		ifM ((== 0) <$> getRealUserID)+			( return (False, []) 			, do-				r <- liftIO $ catchBoolIO $-					writeFile f "2" >> return True+				r <- catchBoolIO $ do+					writeFile f "2"+					return True 				if r-					then do-						warning "Filesystem allows writing to files whose write bit is not set."-						return False-					else return True-+					then return (True, ["Filesystem allows writing to files whose write bit is not set."])+					else return (False, []) 			) #endif 
Annex/MakeRepo.hs view
@@ -16,6 +16,7 @@ import qualified Annex import Annex.UUID import Annex.Direct+import Annex.Action import Types.StandardGroups import Logs.PreferredContent import qualified Annex.Branch@@ -42,7 +43,7 @@ inDir :: FilePath -> Annex a -> IO a inDir dir a = do 	state <- Annex.new =<< Git.Config.read =<< Git.Construct.fromPath dir-	Annex.eval state a+	Annex.eval state $ a `finally` stopCoProcesses  {- Creates a new repository, and returns its UUID. -} initRepo :: Bool -> Bool -> FilePath -> Maybe String -> Maybe StandardGroup -> IO UUID
Annex/MetaData.hs view
@@ -39,12 +39,20 @@  -} genMetaData :: Key -> FilePath -> FileStatus -> Annex () genMetaData key file status = do-	maybe noop (`copyMetaData` key) =<< catKeyFileHEAD file+	v <- catKeyFileHEAD file+	case v of+		Nothing -> noop+		Just oldkey -> +			whenM (copyMetaData oldkey key)+				warncopied 	whenM (annexGenMetaData <$> Annex.getGitConfig) $ do 		curr <- getCurrentMetaData key 		addMetaData key (dateMetaData mtime curr)   where 	mtime = posixSecondsToUTCTime $ realToFrac $ modificationTime status+	warncopied = warning $ +		"Copied metadata from old version of " ++ file ++ " to new version. " ++ +		"If you don't want this copied metadata, run: git annex metadata --remove-all " ++ file  {- Generates metadata for a file's date stamp.  - Does not overwrite any existing metadata values. -}
Annex/TaggedPush.hs view
@@ -30,7 +30,7 @@  - Both UUIDs and Base64 encoded data are always legal to be used in git  - refs, per git-check-ref-format.  -}-toTaggedBranch :: UUID -> Maybe String -> Git.Branch -> Git.Branch+toTaggedBranch :: UUID -> Maybe String -> Git.Branch -> Git.Ref toTaggedBranch u info b = Git.Ref $ intercalate "/" $ catMaybes 	[ Just "refs/synced" 	, Just $ fromUUID u@@ -38,13 +38,17 @@ 	, Just $ Git.fromRef $ Git.Ref.base b 	] -fromTaggedBranch :: Git.Branch -> Maybe (UUID, Maybe String)+fromTaggedBranch :: Git.Ref -> Maybe (UUID, Maybe String) fromTaggedBranch b = case splitc '/' $ Git.fromRef b of 	("refs":"synced":u:info:_base) -> 		Just (toUUID u, fromB64Maybe info) 	("refs":"synced":u:_base) -> 		Just (toUUID u, Nothing) 	_ -> Nothing++listTaggedBranches :: Annex [(Git.Sha, Git.Ref)]+listTaggedBranches = filter (isJust . fromTaggedBranch . snd)+	<$> inRepo Git.Ref.list  taggedPush :: UUID -> Maybe String -> Git.Ref -> Remote -> Git.Repo -> IO Bool taggedPush u info branch remote = Git.Command.runBool
CHANGELOG view
@@ -1,3 +1,26 @@+git-annex (6.20171003) unstable; urgency=medium++  * webdav: Improve error message for failed request to include the request+    method and path.+  * metadata: Added --remove-all.+  * Warn when metadata is inherited from a previous version of a file,+    to avoid the user being surprised in cases where that behavior is not+    desired or expected.+  * sync: Added --cleanup, which removes local and remote synced/ branches.+  * external: When the external special remote program crashed, a newline+    could be output, which messed up the expected output for --batch mode.+  * external: Avoid checking EXPORTSUPPORTED for special remotes that are+    not configured to use exports.+  * test: Fix reversion that made it only run inside a git repository.+  * copy, move: Behave same with --fast when sending to remotes located+    on a local disk as when sending to other remotes.+  * Fix process and file descriptor leak that was exposed when+    git-annex was built with ghc 8.2.1. Broke git-annex test on OSX+    due to running out of FDs, and may have also leaked in other situations.+  * info: Improve cleanup of stale transfer info files.++ -- Joey Hess <id@joeyh.name>  Tue, 03 Oct 2017 13:18:15 -0400+ git-annex (6.20170925) unstable; urgency=medium    * git-annex export: New command, can create and efficiently update
Command/MetaData.hs view
@@ -64,6 +64,10 @@ 			( long "remove"  <> short 'r' <> metavar "FIELD" 			<> help "remove all values of a field" 			)+		<|> flag' DelAllMeta+			( long "remove-all"+			<> help "remove all metadata"+			)  seek :: MetaDataOptions -> CommandSeek seek o = case batchOption o of
Command/Move.hs view
@@ -109,7 +109,7 @@ toStart' :: Remote -> Bool -> AssociatedFile -> Key -> ActionItem -> CommandStart toStart' dest move afile key ai = do 	fast <- Annex.getState Annex.fast-	if fast && not move && not (Remote.hasKeyCheap dest)+	if fast && not move 		then ifM (expectedPresent dest key) 			( stop 			, go True (pure $ Right False)
Command/Sync.hs view
@@ -59,6 +59,7 @@ import Annex.UpdateInstead import Annex.Export import Annex.LockFile+import Annex.TaggedPush import qualified Database.Export as Export import Utility.Bloom import Utility.OptParse@@ -82,6 +83,7 @@ 	, contentOption :: Bool 	, noContentOption :: Bool 	, contentOfOption :: [FilePath]+	, cleanupOption :: Bool 	, keyOptions :: Maybe KeyOptions 	, resolveMergeOverride :: ResolveMergeOverride 	}@@ -129,6 +131,10 @@ 		<> help "transfer file contents of files in a given location" 		<> metavar paramPath 		))+	<*> switch+		( long "cleanup"+		<> help "remove synced/ branches from previous sync"+		) 	<*> optional parseAllOption 	<*> (ResolveMergeOverride <$> invertableSwitch "resolvemerge" True 		( help "do not automatically resolve merge conflicts"@@ -147,6 +153,7 @@ 		<*> pure (contentOption v) 		<*> pure (noContentOption v) 		<*> liftIO (mapM absPath (contentOfOption v))+		<*> pure (cleanupOption v) 		<*> pure (keyOptions v) 		<*> pure (resolveMergeOverride v) @@ -163,32 +170,39 @@ 		. filter (\r -> Remote.uuid r /= NoUUID) 		<$> filterM (not <$$> liftIO . getDynamicConfig . remoteAnnexIgnore . Remote.gitconfig) remotes -	-- Syncing involves many actions, any of which can independently-	-- fail, without preventing the others from running.-	-- These actions cannot be run concurrently.-	mapM_ includeCommandAction $ concat-		[ [ commit o ]-		, [ withbranch (mergeLocal mergeConfig (resolveMergeOverride o)) ]-		, map (withbranch . pullRemote o mergeConfig) gitremotes-		,  [ mergeAnnex ]-		]-	whenM shouldsynccontent $ do-		syncedcontent <- seekSyncContent o dataremotes-		exportedcontent <- seekExportContent exportremotes-		-- Transferring content can take a while,-		-- and other changes can be pushed to the git-annex-		-- branch on the remotes in the meantime, so pull-		-- and merge again to avoid our push overwriting-		-- those changes.-		when (syncedcontent || exportedcontent) $ do+	if cleanupOption o+		then do+			commandAction (withbranch cleanupLocal)+			mapM_ (commandAction . withbranch . cleanupRemote) gitremotes+		else do+			-- Syncing involves many actions, any of which+			-- can independently fail, without preventing+			-- the others from running. +			-- These actions cannot be run concurrently. 			mapM_ includeCommandAction $ concat-				[ map (withbranch . pullRemote o mergeConfig) gitremotes-				, [ commitAnnex, mergeAnnex ]+				[ [ commit o ]+				, [ withbranch (mergeLocal mergeConfig (resolveMergeOverride o)) ]+				, map (withbranch . pullRemote o mergeConfig) gitremotes+				,  [ mergeAnnex ] 				]+			+			whenM shouldsynccontent $ do+				syncedcontent <- seekSyncContent o dataremotes+				exportedcontent <- seekExportContent exportremotes+				-- Transferring content can take a while,+				-- and other changes can be pushed to the+				-- git-annex branch on the remotes in the+				-- meantime, so pull and merge again to+				-- avoid our push overwriting those changes.+				when (syncedcontent || exportedcontent) $ do+					mapM_ includeCommandAction $ concat+						[ map (withbranch . pullRemote o mergeConfig) gitremotes+						, [ commitAnnex, mergeAnnex ]+						] 	-	void $ includeCommandAction $ withbranch pushLocal-	-- Pushes to remotes can run concurrently.-	mapM_ (commandAction . withbranch . pushRemote o) gitremotes+			void $ includeCommandAction $ withbranch pushLocal+			-- Pushes to remotes can run concurrently.+			mapM_ (commandAction . withbranch . pushRemote o) gitremotes   where 	shouldsynccontent = pure (contentOption o) 		<||> pure (not (null (contentOfOption o)))@@ -682,3 +696,32 @@ 			Remote.name r ++  			". Use git-annex export to resolve this conflict." 		return False++cleanupLocal :: CurrBranch -> CommandStart+cleanupLocal (Nothing, _) = stop+cleanupLocal (Just currb, _) = do+	showStart "cleanup" "local"+	next $ next $ do+		delbranch $ syncBranch currb+		delbranch $ syncBranch $ Git.Ref.base $ Annex.Branch.name+		mapM_ (\(s,r) -> inRepo $ Git.Ref.delete s r)+			=<< listTaggedBranches+		return True+  where+	delbranch b = whenM (inRepo $ Git.Ref.exists $ Git.Ref.branchRef b) $+		inRepo $ Git.Branch.delete b++cleanupRemote :: Remote -> CurrBranch -> CommandStart+cleanupRemote _ (Nothing, _) = stop+cleanupRemote remote (Just b, _) = do+	showStart "cleanup" (Remote.name remote)+	next $ next $+		inRepo $ Git.Command.runBool+			[ Param "push"+			, Param "--quiet"+			, Param "--delete"+			, Param $ Remote.name remote+			, Param $ Git.fromRef $ syncBranch b+			, Param $ Git.fromRef $ syncBranch $+				Git.Ref.base $ Annex.Branch.name+			]
Git/Ref.hs view
@@ -45,6 +45,10 @@ underBase :: String -> Ref -> Ref underBase dir r = Ref $ dir ++ "/" ++ fromRef (base r) +{- Convert a branch such as "master" into a fully qualified ref. -}+branchRef :: Branch -> Ref+branchRef = underBase "refs/heads"+ {- A Ref that can be used to refer to a file in the repository, as staged  - in the index.  -@@ -101,7 +105,7 @@ matchingWithHEAD :: [Ref] -> Repo -> IO [(Sha, Branch)] matchingWithHEAD refs repo = matching' ("--head" : map fromRef refs) repo -{- List of (shas, branches) matching a given ref or refs. -}+{- List of (shas, branches) matching a given ref spec. -} matching' :: [String] -> Repo -> IO [(Sha, Branch)] matching' ps repo = map gen . lines <$>  	pipeReadStrict (Param "show-ref" : map Param ps) repo@@ -109,12 +113,26 @@ 	gen l = let (r, b) = separate (== ' ') l 		in (Ref r, Ref b) -{- List of (shas, branches) matching a given ref spec.+{- List of (shas, branches) matching a given ref.  - Duplicate shas are filtered out. -} matchingUniq :: [Ref] -> Repo -> IO [(Sha, Branch)] matchingUniq refs repo = nubBy uniqref <$> matching refs repo   where 	uniqref (a, _) (b, _) = a == b++{- List of all refs. -}+list :: Repo -> IO [(Sha, Ref)]+list = matching' []++{- Deletes a ref. This can delete refs that are not branches, + - which git branch --delete refuses to delete. -}+delete :: Sha -> Ref -> Repo -> IO ()+delete oldvalue ref = run+	[ Param "update-ref"+	, Param "-d"+	, Param $ fromRef ref+	, Param $ fromRef oldvalue+	]  {- Gets the sha of the tree a ref uses. -} tree :: Ref -> Repo -> IO (Maybe Sha)
Logs/MetaData.hs view
@@ -55,6 +55,9 @@ 	config <- Annex.getGitConfig 	readLog $ metaDataLogFile config key +logToCurrentMetaData :: [LogEntry MetaData] -> MetaData+logToCurrentMetaData = currentMetaData . combineMetaData . map value+ {- Go through the log from oldest to newest, and combine it all  - into a single MetaData representing the current state.  -@@ -64,7 +67,7 @@ getCurrentMetaData :: Key -> Annex MetaData getCurrentMetaData k = do 	ls <- S.toAscList <$> getMetaDataLog k-	let loggedmeta = currentMetaData $ combineMetaData $ map value ls+	let loggedmeta = logToCurrentMetaData ls 	return $ currentMetaData $ unionMetaData loggedmeta 		(lastchanged ls loggedmeta)   where@@ -177,13 +180,18 @@  - repository.  -   - Any metadata already attached to the new key is not preserved.+ -+ - Returns True when metadata was copied.  -}-copyMetaData :: Key -> Key -> Annex ()+copyMetaData :: Key -> Key -> Annex Bool copyMetaData oldkey newkey-	| oldkey == newkey = noop+	| oldkey == newkey = return False 	| otherwise = do 		l <- getMetaDataLog oldkey-		unless (S.null l) $ do-			config <- Annex.getGitConfig-			Annex.Branch.change (metaDataLogFile config newkey) $-				const $ showLog l+		if logToCurrentMetaData (S.toAscList l) == emptyMetaData+			then return False+			else do+				config <- Annex.getGitConfig+				Annex.Branch.change (metaDataLogFile config newkey) $+					const $ showLog l+				return True
Logs/Transfer.hs view
@@ -93,22 +93,23 @@ checkTransfer :: Transfer -> Annex (Maybe TransferInfo) checkTransfer t = do 	tfile <- fromRepo $ transferFile t+	let lck = transferLockFile tfile 	let cleanstale = do 		void $ tryIO $ removeFile tfile-		void $ tryIO $ removeFile $ transferLockFile tfile+		void $ tryIO $ removeFile lck #ifndef mingw32_HOST_OS-	let lck = transferLockFile tfile 	v <- getLockStatus lck 	case v of 		StatusLockedBy pid -> liftIO $ catchDefaultIO Nothing $ 			readTransferInfoFile (Just pid) tfile-		StatusNoLockFile -> return Nothing-		StatusUnLocked -> do+		_ -> do 			-- Take a non-blocking lock while deleting 			-- the stale lock file. Ignore failure 			-- due to permissions problems, races, etc. 			void $ tryIO $ do-				r <- tryLockExclusive Nothing lck+				mode <- annexFileMode+				let lck = transferLockFile tfile+				r <- tryLockExclusive (Just mode) lck 				case r of 					Just lockhandle -> liftIO $ do 						cleanstale@@ -116,7 +117,7 @@ 					_ -> noop 			return Nothing #else-	v <- liftIO $ lockShared $ transferLockFile tfile+	v <- liftIO $ lockShared lck 	liftIO $ case v of 		Nothing -> catchDefaultIO Nothing $ 			readTransferInfoFile Nothing tfile
Remote/External.hs view
@@ -20,6 +20,7 @@ import Git.Env import Remote.Helper.Special import Remote.Helper.Export+import Annex.Export import Remote.Helper.ReadOnly import Remote.Helper.Messages import Utility.Metered@@ -69,7 +70,9 @@ 		Annex.addCleanup (RemoteCleanup u) $ stopExternal external 		cst <- getCost external r gc 		avail <- getAvailability external r gc-		exportsupported <- checkExportSupported' external+		exportsupported <- if exportTree c+			then checkExportSupported' external+			else return False 		let exportactions = if exportsupported 			then return $ ExportActions 				{ storeExport = storeExportM external@@ -165,8 +168,9 @@ 		=<< newExternal externaltype NoUUID c gc  checkExportSupported' :: External -> Annex Bool-checkExportSupported' external = safely $-	handleRequest external EXPORTSUPPORTED Nothing $ \resp -> case resp of+checkExportSupported' external = go `catchNonAsync` (const (return False))+  where+	go = handleRequest external EXPORTSUPPORTED Nothing $ \resp -> case resp of 		EXPORTSUPPORTED_SUCCESS -> Just $ return True 		EXPORTSUPPORTED_FAILURE -> Just $ return False 		UNSUPPORTED_REQUEST -> Just $ return False@@ -313,7 +317,7 @@   where 	go (Right r) = return r 	go (Left e) = do-		warning $ show e+		toplevelWarning False (show e) 		return False  {- Sends a Request to the external remote, and waits for it to generate
Remote/Git.hs view
@@ -59,7 +59,7 @@ import Creds import Messages.Progress import Types.NumCopies-import Annex.Concurrent+import Annex.Action  import Control.Concurrent import Control.Concurrent.MSampleVar@@ -311,11 +311,12 @@ 	 - it if allowed. However, if that fails, still return the read 	 - git config. -} 	readlocalannexconfig = do-		s <- Annex.new r-		Annex.eval s $ do+		let check = do 			Annex.BranchState.disableUpdate 			void $ tryNonAsync $ ensureInitialized 			Annex.getState Annex.repo+		s <- Annex.new r+		Annex.eval s $ check `finally` stopCoProcesses 		 	configlistfields = if autoinit 		then [(Fields.autoInit, "1")]@@ -611,7 +612,7 @@ 	Annex.eval s $ do 		Annex.BranchState.disableUpdate 		ensureInitialized-		a+		a `finally` stopCoProcesses  {- Runs an action from the perspective of a local remote.  -@@ -632,7 +633,7 @@ 	go st = do 		curro <- Annex.getState Annex.output 		(ret, st') <- liftIO $ Annex.run (st { Annex.output = curro }) $-			stopCoProcesses `after` a+			a `finally` stopCoProcesses 		cache st' 		return ret 
Remote/WebDAV.hs view
@@ -16,6 +16,7 @@ import qualified Data.ByteString.UTF8 as B8 import qualified Data.ByteString.Lazy.UTF8 as L8 import Network.HTTP.Client (HttpException(..), RequestBody)+import qualified Network.HTTP.Client as HTTP import Network.HTTP.Types import System.IO.Error import Control.Monad.Catch@@ -378,17 +379,20 @@ prettifyExceptions :: DAVT IO a -> DAVT IO a prettifyExceptions a = catchJust (matchStatusCodeException (const True)) a go   where-	go (HttpExceptionRequest _ (StatusCodeException response message)) = error $ unwords+	go (HttpExceptionRequest req (StatusCodeException response message)) = giveup $ unwords 		[ "DAV failure:" 		, show (responseStatus response) 		, show (message)+		, "HTTP request:"+		, show (HTTP.method req)+		, show (HTTP.path req) 		] 	go e = throwM e #else prettifyExceptions :: DAVT IO a -> DAVT IO a prettifyExceptions a = catchJust (matchStatusCodeException (const True)) a go   where-	go (StatusCodeException status _ _) = error $ unwords+	go (StatusCodeException status _ _) = giveup $ unwords 		[ "DAV failure:" 		, show (statusCode status) 		, show (statusMessage status)
Test.hs view
@@ -82,6 +82,7 @@ import qualified Annex.VectorClock import qualified Annex.View import qualified Annex.View.ViewedFile+import qualified Annex.Action import qualified Logs.View import qualified Utility.Path import qualified Utility.FileMode@@ -147,7 +148,7 @@ 		exitWith exitcode 	runsubprocesstests opts (Just _) = isolateGitConfig $ do 		ensuretmpdir-		crippledfilesystem <- annexeval $ Annex.Init.probeCrippledFileSystem' tmpdir+		crippledfilesystem <- fst <$> Annex.Init.probeCrippledFileSystem' tmpdir 		case tryIngredients ingredients (tastyOptionSet opts) (tests crippledfilesystem opts) of 			Nothing -> error "No tests found!?" 			Just act -> ifM act@@ -1778,7 +1779,7 @@ 	s <- Annex.new =<< Git.CurrentRepo.get 	Annex.eval s $ do 		Annex.setOutput Types.Messages.QuietOutput-		a+		a `finally` Annex.Action.stopCoProcesses  innewrepo :: Assertion -> Assertion innewrepo a = withgitrepo $ \r -> indir r a@@ -1813,7 +1814,8 @@ checkRepo :: Types.Annex a -> FilePath -> IO a checkRepo getval d = do 	s <- Annex.new =<< Git.Construct.fromPath d-	Annex.eval s getval+	Annex.eval s $+		getval `finally` Annex.Action.stopCoProcesses  isInDirect :: FilePath -> IO Bool isInDirect = checkRepo (not <$> Config.isDirect)
Types/MetaData.hs view
@@ -244,12 +244,17 @@ metaDataValues :: MetaField -> MetaData -> S.Set MetaValue metaDataValues f (MetaData m) = fromMaybe S.empty (M.lookup f m) +mapMetaData :: (S.Set MetaValue -> S.Set MetaValue) -> MetaData -> MetaData+mapMetaData f (MetaData m) = MetaData (M.map f m)+ {- Ways that existing metadata can be modified -} data ModMeta 	= AddMeta MetaField MetaValue 	| DelMeta MetaField (Maybe MetaValue) 	-- ^ delete value of a field. With Just, only that specific value-	-- is deleted; with Nothing, all current values are deleted.+	-- is deleted; with Nothing, all current values are deleted.a+	| DelAllMeta+	-- ^ delete all currently set metadata 	| SetMeta MetaField (S.Set MetaValue) 	-- ^ removes any existing values 	| MaybeSetMeta MetaField MetaValue@@ -265,6 +270,9 @@ 	updateMetaData f (unsetMetaValue oldv) emptyMetaData modMeta m (DelMeta f Nothing) = MetaData $ M.singleton f $ 	S.fromList $ map unsetMetaValue $ S.toList $ currentMetaDataValues f m+modMeta m DelAllMeta = mapMetaData+	(S.fromList . map unsetMetaValue . S.toList)+	(currentMetaData m) modMeta m (SetMeta f s) = updateMetaData' f s $ 	foldr (updateMetaData f) emptyMetaData $ 		map unsetMetaValue $ S.toList $ currentMetaDataValues f m
doc/git-annex-metadata.mdwn view
@@ -60,6 +60,15 @@    Unset a tag. +* `--remove-all`++  Remove all metadata from the specified files.++  When a file is modified and the new version added, git-annex will copy+  over the metadata from the old version of the file. In situations where+  you don't want that copied metadata, you can use this option to remove+  it.+ * `--force`    By default, `git annex metadata` refuses to recursively set metadata
doc/git-annex-sync.mdwn view
@@ -125,6 +125,19 @@   resolution. It can also be disabled by setting annex.resolvemerge   to false. +* `--cleanup`++  Removes the local and remote `synced/` branches, which were created+  and pushed by `git-annex sync`.++  This can come in handy when you've synced a change to remotes and now+  want to reset your master branch back before that change. So you+  run `git reset` and force-push the master branch to remotes, only+  to find that the next `git annex merge` or `git annex sync` brings the+  changes back. Why? Because the `synced/master` branch is hanging+  around and still has the change in it. Cleaning up the `synced/` branches+  prevents that problem.+ # SEE ALSO  [[git-annex]](1)
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 6.20170925+Version: 6.20171003 Cabal-Version: >= 1.8 License: GPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -399,6 +399,7 @@   if flag(S3)     Build-Depends: conduit, conduit-extra, aws (>= 0.9.2)     CPP-Options: -DWITH_S3+    Other-Modules: Remote.S3    if flag(WebDAV)     Build-Depends: DAV (>= 1.0)@@ -919,7 +920,6 @@     Remote.P2P     Remote.Rsync     Remote.Rsync.RsyncUrl-    Remote.S3     Remote.Tahoe     Remote.Web     Remote.WebDAV