git-annex 6.20170925 → 6.20171003
raw patch · 22 files changed
+248/−95 lines, 22 files
Files
- Annex/Action.hs +13/−0
- Annex/AdjustedBranch.hs +1/−1
- Annex/Concurrent.hs +2/−13
- Annex/Init.hs +21/−21
- Annex/MakeRepo.hs +2/−1
- Annex/MetaData.hs +9/−1
- Annex/TaggedPush.hs +6/−2
- CHANGELOG +23/−0
- Command/MetaData.hs +4/−0
- Command/Move.hs +1/−1
- Command/Sync.hs +66/−23
- Git/Ref.hs +20/−2
- Logs/MetaData.hs +15/−7
- Logs/Transfer.hs +7/−6
- Remote/External.hs +8/−4
- Remote/Git.hs +6/−5
- Remote/WebDAV.hs +6/−2
- Test.hs +5/−3
- Types/MetaData.hs +9/−1
- doc/git-annex-metadata.mdwn +9/−0
- doc/git-annex-sync.mdwn +13/−0
- git-annex.cabal +2/−2
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