git-annex 8.20201116 → 8.20201127
raw patch · 90 files changed
+1540/−1113 lines, 90 files
Files
- Annex.hs +4/−2
- Annex/AdjustedBranch.hs +185/−197
- Annex/AdjustedBranch/Merge.hs +164/−0
- Annex/AdjustedBranch/Name.hs +12/−1
- Annex/AutoMerge.hs +3/−3
- Annex/Branch.hs +1/−1
- Annex/Content.hs +27/−232
- Annex/Content/LowLevel.hs +3/−3
- Annex/Content/PointerFile.hs +2/−7
- Annex/Content/Presence.hs +246/−0
- Annex/ExternalAddonProcess.hs +1/−1
- Annex/Import.hs +2/−2
- Annex/Ingest.hs +12/−5
- Annex/Init.hs +6/−4
- Annex/PidLock.hs +2/−2
- Annex/Ssh.hs +1/−1
- Annex/Tmp.hs +2/−1
- Annex/View/ViewedFile.hs +1/−1
- Annex/YoutubeDl.hs +7/−4
- Assistant.hs +2/−2
- Assistant/Repair.hs +2/−1
- Assistant/Threads/Upgrader.hs +1/−1
- Assistant/Upgrade.hs +16/−3
- Assistant/WebApp/Configurators/Preferences.hs +2/−2
- CHANGELOG +29/−0
- Command/Adjust.hs +8/−0
- Command/ExamineKey.hs +2/−2
- Command/Find.hs +5/−3
- Command/FuzzTest.hs +2/−1
- Command/Get.hs +1/−1
- Command/Move.hs +1/−1
- Command/Multicast.hs +4/−3
- Command/P2P.hs +2/−1
- Command/ReKey.hs +1/−1
- Command/RecvKey.hs +1/−1
- Command/Reinject.hs +1/−1
- Command/SetKey.hs +1/−1
- Command/Sync.hs +7/−6
- Command/TestRemote.hs +4/−4
- Command/TransferKey.hs +1/−1
- Command/TransferKeys.hs +1/−1
- Command/Vicfg.hs +2/−1
- Command/Whereis.hs +2/−4
- Common.hs +1/−1
- Creds.hs +4/−4
- Git/Config.hs +13/−13
- Git/Fsck.hs +9/−5
- Git/Hook.hs +3/−4
- Git/Queue.hs +2/−0
- Git/Repair.hs +11/−8
- Logs/Transfer.hs +1/−1
- Messages/Progress.hs +2/−2
- P2P/Annex.hs +1/−1
- P2P/IO.hs +2/−1
- Remote/BitTorrent.hs +1/−1
- Remote/Directory.hs +4/−4
- Remote/Directory/LegacyChunked.hs +6/−5
- Remote/GCrypt.hs +1/−1
- Remote/Git.hs +1/−1
- Remote/Helper/Special.hs +3/−2
- Remote/Helper/Ssh.hs +12/−21
- Test.hs +368/−386
- Test/Framework.hs +57/−36
- Types/AdjustedBranch.hs +13/−3
- Types/CleanupActions.hs +1/−0
- Types/GitConfig.hs +5/−0
- Upgrade/V0.hs +2/−1
- Upgrade/V1.hs +1/−1
- Upgrade/V5.hs +9/−3
- Utility/Batch.hs +1/−3
- Utility/Daemon.hs +4/−4
- Utility/Directory.hs +1/−1
- Utility/FileMode.hs +1/−1
- Utility/FileSize.hs +1/−1
- Utility/FileSystemEncoding.hs +6/−3
- Utility/Gpg.hs +1/−1
- Utility/LockFile/Windows.hs +0/−1
- Utility/MagicWormhole.hs +11/−6
- Utility/Metered.hs +23/−35
- Utility/MoveFile.hs +2/−2
- Utility/Process.hs +91/−1
- Utility/Process/Transcript.hs +10/−9
- Utility/RawFilePath.hs +3/−2
- Utility/Tmp.hs +1/−1
- Utility/Url.hs +25/−18
- Utility/WebApp.hs +1/−3
- doc/git-annex-adjust.mdwn +24/−5
- doc/git-annex-examinekey.mdwn +2/−1
- doc/git-annex.mdwn +17/−0
- git-annex.cabal +3/−1
Annex.hs view
@@ -143,6 +143,7 @@ , sentinalstatus :: Maybe SentinalStatus , useragent :: Maybe String , errcounter :: Integer+ , adjustedbranchrefreshcounter :: Integer , unusedkeys :: Maybe (S.Set Key) , tempurls :: M.Map Key URLString , existinghooks :: M.Map Git.Hook.Hook Bool@@ -203,6 +204,7 @@ , sentinalstatus = Nothing , useragent = Nothing , errcounter = 0+ , adjustedbranchrefreshcounter = 0 , unusedkeys = Nothing , tempurls = M.empty , existinghooks = M.empty@@ -399,8 +401,8 @@ incError :: Annex () incError = changeState $ \s -> - let ! c = errcounter s + 1 - ! s' = s { errcounter = c }+ let !c = errcounter s + 1 + !s' = s { errcounter = c } in s' getGitRemotes :: Annex [Git.Repo]
Annex/AdjustedBranch.hs view
@@ -11,7 +11,9 @@ Adjustment(..), LinkAdjustment(..), PresenceAdjustment(..),+ LinkPresentAdjustment(..), adjustmentHidesFiles,+ adjustmentIsStable, OrigBranch, AdjBranch(..), originalToAdjusted,@@ -19,11 +21,20 @@ fromAdjustedBranch, getAdjustment, enterAdjustedBranch,- updateAdjustedBranch,+ adjustedBranchRefresh,+ adjustedBranchRefreshFull, adjustBranch,+ adjustTree, adjustToCrippledFileSystem,- mergeToAdjustedBranch,+ commitForAdjustedBranch, propigateAdjustedCommits,+ propigateAdjustedCommits',+ commitAdjustedTree,+ commitAdjustedTree',+ BasisBranch(..),+ basisBranch,+ setBasisBranch,+ preventCommits, AdjustedClone(..), checkAdjustedClone, checkVersionSupported,@@ -34,6 +45,7 @@ import Types.AdjustedBranch import Annex.AdjustedBranch.Name import qualified Annex+import qualified Annex.Queue import Git import Git.Types import qualified Git.Branch@@ -41,7 +53,6 @@ import qualified Git.Command import qualified Git.Tree import qualified Git.DiffTree-import qualified Git.Merge import Git.Tree (TreeItem(..)) import Git.Sha import Git.Env@@ -51,23 +62,19 @@ import qualified Git.Version import Annex.CatFile import Annex.Link-import Annex.AutoMerge-import Annex.Content-import Annex.Tmp-import Annex.GitOverlay-import Utility.Tmp.Dir-import Utility.CopyFile-import Utility.Directory.Create+import Annex.Content.Presence+import Annex.CurrentBranch+import Types.CleanupActions import qualified Database.Keys import Config import qualified Data.Map as M-import qualified Data.ByteString as S-import qualified System.FilePath.ByteString as P --- How to perform various adjustments to a TreeItem. class AdjustTreeItem t where+ -- How to perform various adjustments to a TreeItem. adjustTreeItem :: t -> TreeItem -> Annex (Maybe TreeItem)+ -- Will adjusting a given tree always yield the same adjusted tree?+ adjustmentIsStable :: t -> Bool instance AdjustTreeItem Adjustment where adjustTreeItem (LinkAdjustment l) t = adjustTreeItem l t@@ -76,31 +83,72 @@ adjustTreeItem p t >>= \case Nothing -> return Nothing Just t' -> adjustTreeItem l t'+ adjustTreeItem (LinkPresentAdjustment l) t = adjustTreeItem l t + adjustmentIsStable (LinkAdjustment l) = adjustmentIsStable l+ adjustmentIsStable (PresenceAdjustment p _) = adjustmentIsStable p+ adjustmentIsStable (LinkPresentAdjustment l) = adjustmentIsStable l+ instance AdjustTreeItem LinkAdjustment where- adjustTreeItem UnlockAdjustment = ifSymlink adjustToPointer noAdjust- adjustTreeItem LockAdjustment = ifSymlink noAdjust adjustToSymlink- adjustTreeItem FixAdjustment = ifSymlink adjustToSymlink noAdjust- adjustTreeItem UnFixAdjustment = ifSymlink (adjustToSymlink' gitAnnexLinkCanonical) noAdjust+ adjustTreeItem UnlockAdjustment =+ ifSymlink adjustToPointer noAdjust+ adjustTreeItem LockAdjustment =+ ifSymlink noAdjust adjustToSymlink+ adjustTreeItem FixAdjustment =+ ifSymlink adjustToSymlink noAdjust+ adjustTreeItem UnFixAdjustment =+ ifSymlink (adjustToSymlink' gitAnnexLinkCanonical) noAdjust+ + adjustmentIsStable _ = True instance AdjustTreeItem PresenceAdjustment where- adjustTreeItem HideMissingAdjustment = \ti@(TreeItem _ _ s) ->- catKey s >>= \case- Just k -> ifM (inAnnex k)- ( return (Just ti)- , return Nothing- )- Nothing -> return (Just ti)- adjustTreeItem ShowMissingAdjustment = noAdjust+ adjustTreeItem HideMissingAdjustment = + ifPresent noAdjust hideAdjust+ adjustTreeItem ShowMissingAdjustment =+ noAdjust -ifSymlink :: (TreeItem -> Annex a) -> (TreeItem -> Annex a) -> TreeItem -> Annex a+ adjustmentIsStable HideMissingAdjustment = False+ adjustmentIsStable ShowMissingAdjustment = True++instance AdjustTreeItem LinkPresentAdjustment where+ adjustTreeItem UnlockPresentAdjustment = + ifPresent adjustToPointer adjustToSymlink+ adjustTreeItem LockPresentAdjustment =+ -- Turn all pointers back to symlinks, whether the content+ -- is present or not. This is done because the content+ -- availability may have changed and the branch not been+ -- re-adjusted to keep up, so there may be pointers whose+ -- content is not present.+ ifSymlink noAdjust adjustToSymlink++ adjustmentIsStable UnlockPresentAdjustment = False+ adjustmentIsStable LockPresentAdjustment = True++ifSymlink+ :: (TreeItem -> Annex a)+ -> (TreeItem -> Annex a)+ -> TreeItem+ -> Annex a ifSymlink issymlink notsymlink ti@(TreeItem _f m _s) | toTreeItemType m == Just TreeSymlink = issymlink ti | otherwise = notsymlink ti +ifPresent+ :: (TreeItem -> Annex (Maybe TreeItem))+ -> (TreeItem -> Annex (Maybe TreeItem))+ -> TreeItem+ -> Annex (Maybe TreeItem)+ifPresent ispresent notpresent ti@(TreeItem _ _ s) =+ catKey s >>= \case+ Just k -> ifM (inAnnex k) (ispresent ti, notpresent ti)+ Nothing -> return (Just ti)+ noAdjust :: TreeItem -> Annex (Maybe TreeItem) noAdjust = return . Just +hideAdjust :: TreeItem -> Annex (Maybe TreeItem)+hideAdjust _ = return Nothing+ adjustToPointer :: TreeItem -> Annex (Maybe TreeItem) adjustToPointer ti@(TreeItem f _m s) = catKey s >>= \case Just k -> do@@ -177,70 +225,125 @@ , do b <- preventCommits $ const $ adjustBranch adj origbranch- checkoutAdjustedBranch b []+ checkoutAdjustedBranch b False ) -checkoutAdjustedBranch :: AdjBranch -> [CommandParam] -> Annex Bool-checkoutAdjustedBranch (AdjBranch b) checkoutparams = do- showOutput -- checkout can have output in large repos+checkoutAdjustedBranch :: AdjBranch -> Bool -> Annex Bool+checkoutAdjustedBranch (AdjBranch b) quietcheckout = do+ -- checkout can have output in large repos+ unless quietcheckout+ showOutput inRepo $ Git.Command.runBool $ [ Param "checkout" , Param $ fromRef $ Git.Ref.base b- -- always show checkout progress, even if --quiet is used- -- to suppress other messages- , Param "--progress"- ] ++ checkoutparams+ , if quietcheckout then Param "--quiet" else Param "--progress"+ ] {- Already in a branch with this adjustment, but the user asked to enter it- - again. This should have the same result as checking out the original branch,- - deleting and rebuilding the adjusted branch, and then checking it out.+ - again. This should have the same result as propagating any commits+ - back to the original branch, checking out the original branch, deleting+ - and rebuilding the adjusted branch, and then checking it out. - But, it can be implemented more efficiently than that. -} updateAdjustedBranch :: Adjustment -> AdjBranch -> OrigBranch -> Annex Bool-updateAdjustedBranch adj@(PresenceAdjustment _ _) (AdjBranch currbranch) origbranch = do- b <- preventCommits $ \commitlck -> do- -- Avoid losing any commits that the adjusted branch has that- -- have not yet been propigated back to the origbranch.- _ <- propigateAdjustedCommits' origbranch adj commitlck+updateAdjustedBranch adj (AdjBranch currbranch) origbranch+ | not (adjustmentIsStable adj) = do+ b <- preventCommits $ \commitlck -> do+ -- Avoid losing any commits that the adjusted branch+ -- has that have not yet been propigated back to the+ -- origbranch.+ _ <- propigateAdjustedCommits' origbranch adj commitlck - -- Git normally won't do anything when asked to check out the- -- currently checked out branch, even when its ref has- -- changed. Work around this by writing a raw sha to .git/HEAD.- inRepo (Git.Ref.sha currbranch) >>= \case- Just headsha -> inRepo $ \r ->- writeFile (Git.Ref.headFile r) (fromRef headsha)- _ -> noop+ -- Git normally won't do anything when asked to check+ -- out the currently checked out branch, even when its+ -- ref has changed. Work around this by writing a raw+ -- sha to .git/HEAD.+ inRepo (Git.Ref.sha currbranch) >>= \case+ Just headsha -> inRepo $ \r ->+ writeFile (Git.Ref.headFile r) (fromRef headsha)+ _ -> noop - adjustBranch adj origbranch+ adjustBranch adj origbranch - -- Make git checkout quiet to avoid warnings about disconnected- -- branch tips being lost.- checkoutAdjustedBranch b [Param "--quiet"]-updateAdjustedBranch adj@(LinkAdjustment _) _ origbranch = preventCommits $ \commitlck -> do- -- Not really needed here, but done for consistency.- _ <- propigateAdjustedCommits' origbranch adj commitlck- -- No need to do anything else, because link adjustments are stable.- return True+ -- Make git checkout quiet to avoid warnings about+ -- disconnected branch tips being lost.+ checkoutAdjustedBranch b True+ | otherwise = preventCommits $ \commitlck -> do+ -- Done for consistency.+ _ <- propigateAdjustedCommits' origbranch adj commitlck+ -- No need to actually update the branch because the+ -- adjustment is stable.+ return True +{- Passed an action that, if it succeeds may get or drop the Key associated+ - with the file. When the adjusted branch needs to be refreshed to reflect+ - those changes, it's handled here.+ -+ - Note that the AssociatedFile must be verified by this to point to the+ - Key. In some cases, the value was provided by the user and might not+ - really be an associated file.+ -}+adjustedBranchRefresh :: AssociatedFile -> Annex a -> Annex a+adjustedBranchRefresh _af a = do+ r <- a+ annexAdjustedBranchRefresh <$> Annex.getGitConfig >>= \case+ 0 -> return ()+ n -> go n+ return r+ where+ go n = getCurrentBranch >>= \case+ (Just origbranch, Just adj) ->+ unless (adjustmentIsStable adj) $+ ifM (checkcounter n)+ ( update adj origbranch+ , Annex.addCleanup AdjustedBranchUpdate $+ adjustedBranchRefreshFull adj origbranch+ )+ _ -> return ()+ + checkcounter n+ -- Special case, 1 (or true) refreshes only at shutdown.+ | n == 1 = pure False+ | otherwise = Annex.withState $ \s -> + let !c = Annex.adjustedbranchrefreshcounter s + 1+ !enough = c >= pred n+ !c' = if enough then 0 else c+ !s' = s { Annex.adjustedbranchrefreshcounter = c' }+ in pure (s', enough)++ update adj origbranch = do+ -- Flush the queue, to make any pending changes be written+ -- out to disk. But mostly so any pointer files+ -- restagePointerFile was called on get updated so git+ -- checkout won't fall over.+ Annex.Queue.flush+ -- This is slow, it would be better to incrementally+ -- adjust the AssociatedFile, and only call this once+ -- at shutdown to handle cases where not all+ -- AssociatedFiles are known.+ adjustedBranchRefreshFull adj origbranch++{- Slow, but more dependable version of adjustedBranchRefresh that+ - does not rely on all AssociatedFiles being known. -}+adjustedBranchRefreshFull :: Adjustment -> OrigBranch -> Annex ()+adjustedBranchRefreshFull adj origbranch = do+ let adjbranch = originalToAdjusted origbranch adj+ unlessM (updateAdjustedBranch adj adjbranch origbranch) $+ warning $ unwords [ "Updating adjusted branch failed." ]+ adjustToCrippledFileSystem :: Annex () adjustToCrippledFileSystem = do warning "Entering an adjusted branch where files are unlocked as this filesystem does not support locked files." checkVersionSupported- whenM (isNothing <$> inRepo Git.Branch.current) $ do- cmode <- annexCommitMode <$> Annex.getGitConfig- void $ inRepo $ Git.Branch.commitCommand cmode- [ Param "--quiet"- , Param "--allow-empty"- , Param "-m"- , Param "commit before entering adjusted unlocked branch"- ]+ whenM (isNothing <$> inRepo Git.Branch.current) $+ commitForAdjustedBranch [] inRepo Git.Branch.current >>= \case Just currbranch -> case getAdjustment currbranch of Just curradj | curradj == adj -> return () _ -> do let adjbranch = originalToAdjusted currbranch adj ifM (inRepo (Git.Ref.exists $ adjBranch adjbranch))- ( unlessM (checkoutAdjustedBranch adjbranch []) $+ ( unlessM (checkoutAdjustedBranch adjbranch False) $ failedenter , unlessM (enterAdjustedBranch adj) $ failedenter@@ -250,6 +353,22 @@ adj = LinkAdjustment UnlockAdjustment failedenter = warning "Failed to enter adjusted branch!" +{- Commit before entering adjusted branch. Only needs to be done+ - when the current branch does not have any commits yet.+ -+ - If something is already staged, it will be committed, but otherwise+ - an empty commit will be made.+ -}+commitForAdjustedBranch :: [CommandParam] -> Annex ()+commitForAdjustedBranch ps = do+ cmode <- annexCommitMode <$> Annex.getGitConfig+ void $ inRepo $ Git.Branch.commitCommand cmode $+ [ Param "--quiet"+ , Param "--allow-empty"+ , Param "-m"+ , Param "commit before entering adjusted branch"+ ] ++ ps+ setBasisBranch :: BasisBranch -> Ref -> Annex () setBasisBranch (BasisBranch basis) new = inRepo $ Git.Branch.update' basis new@@ -338,137 +457,6 @@ | otherwise = case commitParent c of [p] -> go =<< catCommit p _ -> return Nothing--{- Update the currently checked out adjusted branch, merging the provided- - branch into it. Note that the provided branch should be a non-adjusted- - branch. -}-mergeToAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Bool -> Git.Branch.CommitMode -> Annex Bool-mergeToAdjustedBranch tomerge (origbranch, adj) mergeconfig canresolvemerge commitmode = catchBoolIO $- join $ preventCommits go- where- adjbranch@(AdjBranch currbranch) = originalToAdjusted origbranch adj- basis = basisBranch adjbranch-- go commitsprevented =- ifM (inRepo $ Git.Branch.changed currbranch tomerge)- ( do- (updatedorig, _) <- propigateAdjustedCommits'- origbranch adj commitsprevented- changestomerge updatedorig- , nochangestomerge- )-- nochangestomerge = return $ return True-- {- Since the adjusted branch changes files, merging tomerge- - directly into it would likely result in unncessary merge- - conflicts. To avoid those conflicts, instead merge tomerge into- - updatedorig. The result of the merge can the be- - adjusted to yield the final adjusted branch.- -- - In order to do a merge into a ref that is not checked out,- - set the work tree to a temp directory, and set GIT_DIR- - to another temp directory, in which HEAD contains the- - updatedorig sha. GIT_COMMON_DIR is set to point to the real- - git directory, and so git can read and write objects from there,- - but will use GIT_DIR for HEAD and index.- -- - (Doing the merge this way also lets it run even though the main- - index file is currently locked.)- -}- changestomerge (Just updatedorig) = withOtherTmp $ \othertmpdir -> do- git_dir <- fromRepo Git.localGitDir- let git_dir' = fromRawFilePath git_dir- tmpwt <- fromRepo gitAnnexMergeDir- withTmpDirIn (fromRawFilePath othertmpdir) "git" $ \tmpgit -> withWorkTreeRelated tmpgit $- withemptydir git_dir tmpwt $ withWorkTree tmpwt $ do- liftIO $ writeFile (tmpgit </> "HEAD") (fromRef updatedorig)- -- Copy in refs and packed-refs, to work- -- around bug in git 2.13.0, which- -- causes it not to look in GIT_DIR for refs.- refs <- liftIO $ dirContentsRecursive $- git_dir' </> "refs"- let refs' = (git_dir' </> "packed-refs") : refs- liftIO $ forM_ refs' $ \src ->- whenM (doesFileExist src) $ do- dest <- relPathDirToFile git_dir- (toRawFilePath src)- let dest' = toRawFilePath tmpgit P.</> dest- createDirectoryUnder git_dir- (P.takeDirectory dest')- void $ createLinkOrCopy src- (fromRawFilePath dest')- -- This reset makes git merge not care- -- that the work tree is empty; otherwise- -- it will think that all the files have- -- been staged for deletion, and sometimes- -- the merge includes these deletions- -- (for an unknown reason).- -- http://thread.gmane.org/gmane.comp.version-control.git/297237- inRepo $ Git.Command.run [Param "reset", Param "HEAD", Param "--quiet"]- showAction $ "Merging into " ++ fromRef (Git.Ref.base origbranch)- merged <- autoMergeFrom' tomerge Nothing mergeconfig commitmode True- (const $ resolveMerge (Just updatedorig) tomerge True)- if merged- then do- !mergecommit <- liftIO $ extractSha- <$> S.readFile (tmpgit </> "HEAD")- -- This is run after the commit lock is dropped.- return $ postmerge mergecommit- else return $ return False- changestomerge Nothing = return $ return False- - withemptydir git_dir d a = bracketIO setup cleanup (const a)- where- setup = do- whenM (doesDirectoryExist d) $- removeDirectoryRecursive d- createDirectoryUnder git_dir (toRawFilePath d)- cleanup _ = removeDirectoryRecursive d-- {- A merge commit has been made between the basisbranch and - - tomerge. Update the basisbranch and origbranch to point- - to that commit, adjust it to get the new adjusted branch,- - and check it out.- -- - But, there may be unstaged work tree changes that conflict, - - so the check out is done by making a normal merge of- - the new adjusted branch.- -}- postmerge (Just mergecommit) = do- setBasisBranch basis mergecommit- inRepo $ Git.Branch.update' origbranch mergecommit- adjtree <- adjustTree adj (BasisBranch mergecommit)- adjmergecommit <- commitAdjustedTree adjtree (BasisBranch mergecommit)- -- Make currbranch be the parent, so that merging- -- this commit will be a fast-forward.- adjmergecommitff <- commitAdjustedTree' adjtree (BasisBranch mergecommit) [currbranch]- showAction "Merging into adjusted branch"- ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig commitmode canresolvemerge)- ( reparent adjtree adjmergecommit =<< getcurrentcommit- , return False- )- postmerge Nothing = return False-- -- Now that the merge into the adjusted branch is complete,- -- take the tree from that merge, and attach it on top of the- -- adjmergecommit, if it's different.- reparent adjtree adjmergecommit (Just currentcommit) = do- if (commitTree currentcommit /= adjtree)- then do- cmode <- annexCommitMode <$> Annex.getGitConfig- c <- inRepo $ Git.Branch.commitTree cmode- ("Merged " ++ fromRef tomerge) [adjmergecommit]- (commitTree currentcommit)- inRepo $ Git.Branch.update "updating adjusted branch" currbranch c- propigateAdjustedCommits origbranch adj- else inRepo $ Git.Branch.update "updating adjusted branch" currbranch adjmergecommit- return True- reparent _ _ Nothing = return False-- getcurrentcommit = inRepo Git.Branch.currentUnsafe >>= \case- Nothing -> return Nothing- Just c -> catCommit c {- Check for any commits present on the adjusted branch that have not yet - been propigated to the basis branch, and propigate them to the basis
+ Annex/AdjustedBranch/Merge.hs view
@@ -0,0 +1,164 @@+{- adjusted branch merging+ -+ - Copyright 2016-2020 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE BangPatterns, OverloadedStrings #-}++module Annex.AdjustedBranch.Merge (+ mergeToAdjustedBranch,+) where++import Annex.Common+import Annex.AdjustedBranch+import qualified Annex+import Git+import Git.Types+import qualified Git.Branch+import qualified Git.Ref+import qualified Git.Command+import qualified Git.Merge+import Git.Sha+import Annex.CatFile+import Annex.AutoMerge+import Annex.Tmp+import Annex.GitOverlay+import Utility.Tmp.Dir+import Utility.CopyFile+import Utility.Directory.Create++import qualified Data.ByteString as S+import qualified System.FilePath.ByteString as P++{- Update the currently checked out adjusted branch, merging the provided+ - branch into it. Note that the provided branch should be a non-adjusted+ - branch. -}+mergeToAdjustedBranch :: Branch -> (OrigBranch, Adjustment) -> [Git.Merge.MergeConfig] -> Bool -> Git.Branch.CommitMode -> Annex Bool+mergeToAdjustedBranch tomerge (origbranch, adj) mergeconfig canresolvemerge commitmode = catchBoolIO $+ join $ preventCommits go+ where+ adjbranch@(AdjBranch currbranch) = originalToAdjusted origbranch adj+ basis = basisBranch adjbranch++ go commitsprevented =+ ifM (inRepo $ Git.Branch.changed currbranch tomerge)+ ( do+ (updatedorig, _) <- propigateAdjustedCommits'+ origbranch adj commitsprevented+ changestomerge updatedorig+ , nochangestomerge+ )++ nochangestomerge = return $ return True++ {- Since the adjusted branch changes files, merging tomerge+ - directly into it would likely result in unncessary merge+ - conflicts. To avoid those conflicts, instead merge tomerge into+ - updatedorig. The result of the merge can the be+ - adjusted to yield the final adjusted branch.+ -+ - In order to do a merge into a ref that is not checked out,+ - set the work tree to a temp directory, and set GIT_DIR+ - to another temp directory, in which HEAD contains the+ - updatedorig sha. GIT_COMMON_DIR is set to point to the real+ - git directory, and so git can read and write objects from there,+ - but will use GIT_DIR for HEAD and index.+ -+ - (Doing the merge this way also lets it run even though the main+ - index file is currently locked.)+ -}+ changestomerge (Just updatedorig) = withOtherTmp $ \othertmpdir -> do+ git_dir <- fromRepo Git.localGitDir+ let git_dir' = fromRawFilePath git_dir+ tmpwt <- fromRepo gitAnnexMergeDir+ withTmpDirIn (fromRawFilePath othertmpdir) "git" $ \tmpgit -> withWorkTreeRelated tmpgit $+ withemptydir git_dir tmpwt $ withWorkTree tmpwt $ do+ liftIO $ writeFile (tmpgit </> "HEAD") (fromRef updatedorig)+ -- Copy in refs and packed-refs, to work+ -- around bug in git 2.13.0, which+ -- causes it not to look in GIT_DIR for refs.+ refs <- liftIO $ dirContentsRecursive $+ git_dir' </> "refs"+ let refs' = (git_dir' </> "packed-refs") : refs+ liftIO $ forM_ refs' $ \src ->+ whenM (doesFileExist src) $ do+ dest <- relPathDirToFile git_dir+ (toRawFilePath src)+ let dest' = toRawFilePath tmpgit P.</> dest+ createDirectoryUnder git_dir+ (P.takeDirectory dest')+ void $ createLinkOrCopy src+ (fromRawFilePath dest')+ -- This reset makes git merge not care+ -- that the work tree is empty; otherwise+ -- it will think that all the files have+ -- been staged for deletion, and sometimes+ -- the merge includes these deletions+ -- (for an unknown reason).+ -- http://thread.gmane.org/gmane.comp.version-control.git/297237+ inRepo $ Git.Command.run [Param "reset", Param "HEAD", Param "--quiet"]+ showAction $ "Merging into " ++ fromRef (Git.Ref.base origbranch)+ merged <- autoMergeFrom' tomerge Nothing mergeconfig commitmode True+ (const $ resolveMerge (Just updatedorig) tomerge True)+ if merged+ then do+ !mergecommit <- liftIO $ extractSha+ <$> S.readFile (tmpgit </> "HEAD")+ -- This is run after the commit lock is dropped.+ return $ postmerge mergecommit+ else return $ return False+ changestomerge Nothing = return $ return False+ + withemptydir git_dir d a = bracketIO setup cleanup (const a)+ where+ setup = do+ whenM (doesDirectoryExist d) $+ removeDirectoryRecursive d+ createDirectoryUnder git_dir (toRawFilePath d)+ cleanup _ = removeDirectoryRecursive d++ {- A merge commit has been made between the basisbranch and + - tomerge. Update the basisbranch and origbranch to point+ - to that commit, adjust it to get the new adjusted branch,+ - and check it out.+ -+ - But, there may be unstaged work tree changes that conflict, + - so the check out is done by making a normal merge of+ - the new adjusted branch.+ -}+ postmerge (Just mergecommit) = do+ setBasisBranch basis mergecommit+ inRepo $ Git.Branch.update' origbranch mergecommit+ adjtree <- adjustTree adj (BasisBranch mergecommit)+ adjmergecommit <- commitAdjustedTree adjtree (BasisBranch mergecommit)+ -- Make currbranch be the parent, so that merging+ -- this commit will be a fast-forward.+ adjmergecommitff <- commitAdjustedTree' adjtree (BasisBranch mergecommit) [currbranch]+ showAction "Merging into adjusted branch"+ ifM (autoMergeFrom adjmergecommitff (Just currbranch) mergeconfig commitmode canresolvemerge)+ ( reparent adjtree adjmergecommit =<< getcurrentcommit+ , return False+ )+ postmerge Nothing = return False++ -- Now that the merge into the adjusted branch is complete,+ -- take the tree from that merge, and attach it on top of the+ -- adjmergecommit, if it's different.+ reparent adjtree adjmergecommit (Just currentcommit) = do+ if (commitTree currentcommit /= adjtree)+ then do+ cmode <- annexCommitMode <$> Annex.getGitConfig+ c <- inRepo $ Git.Branch.commitTree cmode+ ("Merged " ++ fromRef tomerge) [adjmergecommit]+ (commitTree currentcommit)+ inRepo $ Git.Branch.update "updating adjusted branch" currbranch c+ propigateAdjustedCommits origbranch adj+ else inRepo $ Git.Branch.update "updating adjusted branch" currbranch adjmergecommit+ return True+ reparent _ _ Nothing = return False++ getcurrentcommit = inRepo Git.Branch.currentUnsafe >>= \case+ Nothing -> return Nothing+ Just c -> catCommit c
Annex/AdjustedBranch/Name.hs view
@@ -1,6 +1,6 @@ {- adjusted branch names -- - Copyright 2016-2018 Joey Hess <id@joeyh.name>+ - Copyright 2016-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -37,12 +37,16 @@ serializeAdjustment p serializeAdjustment (PresenceAdjustment p (Just l)) = serializeAdjustment p <> "-" <> serializeAdjustment l+ serializeAdjustment (LinkPresentAdjustment l) =+ serializeAdjustment l deserializeAdjustment s = (LinkAdjustment <$> deserializeAdjustment s) <|> (PresenceAdjustment <$> deserializeAdjustment s1 <*> pure (deserializeAdjustment s2)) <|> (PresenceAdjustment <$> deserializeAdjustment s <*> pure Nothing)+ <|>+ (LinkPresentAdjustment <$> deserializeAdjustment s) where (s1, s2) = separate' (== (fromIntegral (ord '-'))) s @@ -62,6 +66,13 @@ serializeAdjustment ShowMissingAdjustment = "showmissing" deserializeAdjustment "hidemissing" = Just HideMissingAdjustment deserializeAdjustment "showmissing" = Just ShowMissingAdjustment+ deserializeAdjustment _ = Nothing++instance SerializeAdjustment LinkPresentAdjustment where+ serializeAdjustment UnlockPresentAdjustment = "unlockpresent"+ serializeAdjustment LockPresentAdjustment = "lockpresent"+ deserializeAdjustment "unlockpresent" = Just UnlockPresentAdjustment+ deserializeAdjustment "lockpresent" = Just LockPresentAdjustment deserializeAdjustment _ = Nothing newtype AdjBranch = AdjBranch { adjBranch :: Branch }
Annex/AutoMerge.hs view
@@ -34,11 +34,11 @@ import Annex.InodeSentinal import Utility.InodeCache import Utility.FileMode+import qualified Utility.RawFilePath as R import qualified Data.Set as S import qualified Data.Map as M import qualified Data.ByteString.Lazy as L-import qualified Utility.RawFilePath as R {- Merges from a branch into the current branch (which may not exist yet), - with automatic merge conflict resolution.@@ -176,7 +176,7 @@ -- files, so delete here. unless inoverlay $ unless (islocked LsFiles.valUs) $- liftIO $ removeWhenExistsWith removeLink file+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath file) | otherwise -> do -- Only resolve using symlink when both -- were locked, otherwise use unlocked@@ -309,7 +309,7 @@ <$> mapM Database.Keys.getInodeCaches resolvedks forM_ (M.toList unstagedmap) $ \(i, f) -> whenM (matchesresolved is i f) $- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) where fs = S.fromList resolvedfs ks = S.fromList resolvedks
Annex/Branch.hs view
@@ -535,7 +535,7 @@ stagedfs <- lines <$> hGetContents jlogh mapM_ (removeFile . (dir </>)) stagedfs hClose jlogh- removeWhenExistsWith removeLink jlogf+ removeWhenExistsWith (R.removeLink) (toRawFilePath jlogf) openjlog tmpdir = liftIO $ openTempFile tmpdir "jlog" {- This is run after the refs have been merged into the index,
Annex/Content.hs view
@@ -60,132 +60,37 @@ import qualified Data.Set as S import Annex.Common-import Logs.Location-import Types.Transfer-import Logs.Transfer+import Annex.Content.Presence+import Annex.Content.LowLevel+import Annex.Content.PointerFile import qualified Git import qualified Annex import qualified Annex.Queue import qualified Annex.Branch-import Utility.FileMode import qualified Annex.Url as Url-import Utility.CopyFile-import Utility.Metered+import qualified Backend+import qualified Database.Keys import Git.FilePath import Annex.Perms import Annex.Link import Annex.LockPool-import Annex.WorkerPool+import Annex.UUID+import Annex.InodeSentinal+import Annex.AdjustedBranch (adjustedBranchRefresh) import Messages.Progress-import Types.Remote (unVerified, Verification(..), RetrievalSecurityPolicy(..))-import qualified Types.Remote-import qualified Types.Backend-import qualified Backend-import qualified Database.Keys+import Types.Remote (RetrievalSecurityPolicy(..)) import Types.NumCopies import Types.Key-import Annex.UUID-import Annex.InodeSentinal+import Types.Transfer+import Logs.Transfer+import Logs.Location import Utility.InodeCache-import Annex.Content.LowLevel-import Annex.Content.PointerFile-import Types.WorkerPool+import Utility.CopyFile+import Utility.Metered import qualified Utility.RawFilePath as R import qualified System.FilePath.ByteString as P -{- Checks if a given key's content is currently present. -}-inAnnex :: Key -> Annex Bool-inAnnex key = inAnnexCheck key $ liftIO . R.doesPathExist--{- Runs an arbitrary check on a key's content. -}-inAnnexCheck :: Key -> (RawFilePath -> Annex Bool) -> Annex Bool-inAnnexCheck key check = inAnnex' id False check key--{- inAnnex that performs an arbitrary check of the key's content. -}-inAnnex' :: (a -> Bool) -> a -> (RawFilePath -> Annex a) -> Key -> Annex a-inAnnex' isgood bad check key = withObjectLoc key $ \loc -> do- r <- check loc- if isgood r- then ifM (annexThin <$> Annex.getGitConfig)- -- When annex.thin is set, the object file- -- could be modified; make sure it's not.- -- (Suppress any messages about- -- checksumming, to avoid them cluttering- -- the display.)- ( ifM (doQuietAction $ isUnmodified key loc)- ( return r- , return bad- )- , return r- )- else return bad--{- Like inAnnex, checks if the object file for a key exists,- - but there are no guarantees it has the right content. -}-objectFileExists :: Key -> Annex Bool-objectFileExists key =- calcRepo (gitAnnexLocation key)- >>= liftIO . R.doesPathExist--{- A safer check; the key's content must not only be present, but- - is not in the process of being removed. -}-inAnnexSafe :: Key -> Annex (Maybe Bool)-inAnnexSafe key = inAnnex' (fromMaybe True) (Just False) go key- where- is_locked = Nothing- is_unlocked = Just True- is_missing = Just False-- go contentfile = flip checklock contentfile =<< contentLockFile key--#ifndef mingw32_HOST_OS- checklock Nothing contentfile = checkOr is_missing contentfile- {- The content file must exist, but the lock file generally- - won't exist unless a removal is in process. -}- checklock (Just lockfile) contentfile =- ifM (liftIO $ doesFileExist (fromRawFilePath contentfile))- ( checkOr is_unlocked lockfile- , return is_missing- )- checkOr d lockfile = checkLocked lockfile >>= return . \case- Nothing -> d- Just True -> is_locked- Just False -> is_unlocked-#else- checklock Nothing contentfile = liftIO $ ifM (doesFileExist (fromRawFilePath contentfile))- ( lockShared contentfile >>= \case- Nothing -> return is_locked- Just lockhandle -> do- dropLock lockhandle- return is_unlocked- , return is_missing- )- {- In Windows, see if we can take a shared lock. If so, - - remove the lock file to clean up after ourselves. -}- checklock (Just lockfile) contentfile =- ifM (liftIO $ doesFileExist (fromRawFilePath contentfile))- ( modifyContent lockfile $ liftIO $- lockShared lockfile >>= \case- Nothing -> return is_locked- Just lockhandle -> do- dropLock lockhandle- void $ tryIO $ removeWhenExistsWith removeLink lockfile- return is_unlocked- , return is_missing- )-#endif--{- Windows has to use a separate lock file from the content, since- - locking the actual content file would interfere with the user's- - use of it. -}-contentLockFile :: Key -> Annex (Maybe RawFilePath)-#ifndef mingw32_HOST_OS-contentLockFile _ = pure Nothing-#else-contentLockFile key = Just <$> calcRepo (gitAnnexContentLock key)-#endif- {- Prevents the content from being removed while the action is running. - Uses a shared lock. -@@ -249,7 +154,7 @@ winLocker takelock _ (Just lockfile) = do modifyContent lockfile $ void $ liftIO $ tryIO $- writeFile lockfile ""+ writeFile (fromRawFilePath lockfile) "" liftIO $ takelock lockfile -- never reached; windows always uses a separate lock file winLocker _ _ Nothing = return Nothing@@ -299,15 +204,15 @@ {- Runs an action, passing it the temp file to get, - and if the action succeeds, verifies the file matches - the key and moves the file into the annex as a key's content. -}-getViaTmp :: RetrievalSecurityPolicy -> VerifyConfig -> Key -> (RawFilePath -> Annex (Bool, Verification)) -> Annex Bool-getViaTmp rsp v key action = checkDiskSpaceToGet key False $- getViaTmpFromDisk rsp v key action+getViaTmp :: RetrievalSecurityPolicy -> VerifyConfig -> Key -> AssociatedFile -> (RawFilePath -> Annex (Bool, Verification)) -> Annex Bool+getViaTmp rsp v key af action = checkDiskSpaceToGet key False $+ getViaTmpFromDisk rsp v key af action {- Like getViaTmp, but does not check that there is enough disk space - for the incoming key. For use when the key content is already on disk - and not being copied into place. -}-getViaTmpFromDisk :: RetrievalSecurityPolicy -> VerifyConfig -> Key -> (RawFilePath -> Annex (Bool, Verification)) -> Annex Bool-getViaTmpFromDisk rsp v key action = checkallowed $ do+getViaTmpFromDisk :: RetrievalSecurityPolicy -> VerifyConfig -> Key -> AssociatedFile -> (RawFilePath -> Annex (Bool, Verification)) -> Annex Bool+getViaTmpFromDisk rsp v key af action = checkallowed $ do tmpfile <- prepTmp key resuming <- liftIO $ R.doesPathExist tmpfile (ok, verification) <- action tmpfile@@ -322,7 +227,7 @@ else verification if ok then ifM (verifyKeyContent rsp v verification' key tmpfile)- ( ifM (pruneTmpWorkDirBefore tmpfile (moveAnnex key))+ ( ifM (pruneTmpWorkDirBefore tmpfile (moveAnnex key af)) ( do logStatus key InfoPresent return True@@ -357,71 +262,6 @@ ) ) -{- Verifies that a file is the expected content of a key.- -- - Configuration can prevent verification, for either a- - particular remote or always, unless the RetrievalSecurityPolicy- - requires verification.- -- - Most keys have a known size, and if so, the file size is checked.- -- - When the key's backend allows verifying the content (via checksum),- - it is checked. - -- - If the RetrievalSecurityPolicy requires verification and the key's- - backend doesn't support it, the verification will fail.- -}-verifyKeyContent :: RetrievalSecurityPolicy -> VerifyConfig -> Verification -> Key -> RawFilePath -> Annex Bool-verifyKeyContent rsp v verification k f = case (rsp, verification) of- (_, Verified) -> return True- (RetrievalVerifiableKeysSecure, _) -> ifM (Backend.isVerifiable k)- ( verify- , ifM (annexAllowUnverifiedDownloads <$> Annex.getGitConfig)- ( verify- , warnUnverifiableInsecure k >> return False- )- )- (_, UnVerified) -> ifM (shouldVerify v)- ( verify- , return True- )- (_, MustVerify) -> verify- where- verify = enteringStage VerifyStage $ verifysize <&&> verifycontent- verifysize = case fromKey keySize k of- Nothing -> return True- Just size -> do- size' <- liftIO $ catchDefaultIO 0 $ getFileSize f- return (size' == size)- verifycontent = Backend.maybeLookupBackendVariety (fromKey keyVariety k) >>= \case- Nothing -> return True- Just b -> case Types.Backend.verifyKeyContent b of- Nothing -> return True- Just verifier -> verifier k f--warnUnverifiableInsecure :: Key -> Annex ()-warnUnverifiableInsecure k = warning $ unwords- [ "Getting " ++ kv ++ " keys with this remote is not secure;"- , "the content cannot be verified to be correct."- , "(Use annex.security.allow-unverified-downloads to bypass"- , "this safety check.)"- ]- where- kv = decodeBS (formatKeyVariety (fromKey keyVariety k))--data VerifyConfig = AlwaysVerify | NoVerify | RemoteVerify Remote | DefaultVerify--shouldVerify :: VerifyConfig -> Annex Bool-shouldVerify AlwaysVerify = return True-shouldVerify NoVerify = return False-shouldVerify DefaultVerify = annexVerify <$> Annex.getGitConfig-shouldVerify (RemoteVerify r) = - (shouldVerify DefaultVerify- <&&> pure (remoteAnnexVerify (Types.Remote.gitconfig r)))- -- Export remotes are not key/value stores, so always verify- -- content from them even when verification is disabled.- <||> Types.Remote.isExportSupported r- {- Checks if there is enough free disk space to download a key - to its temp file. -@@ -490,8 +330,8 @@ - accepted into the repository. Will display a warning message in this - case. May also throw exceptions in some cases. -}-moveAnnex :: Key -> RawFilePath -> Annex Bool-moveAnnex key src = ifM (checkSecureHashes' key)+moveAnnex :: Key -> AssociatedFile -> RawFilePath -> Annex Bool+moveAnnex key af src = ifM (checkSecureHashes' key) ( do withObjectLoc key storeobject return True@@ -500,7 +340,7 @@ where storeobject dest = ifM (liftIO $ R.doesPathExist dest) ( alreadyhave- , modifyContent dest $ do+ , adjustedBranchRefresh af $ modifyContent dest $ do freezeContent src liftIO $ moveFile (fromRawFilePath src)@@ -648,10 +488,6 @@ then Nothing else Just (fromRawFilePath f, sameInodeCache f cache') -{- Performs an action, passing it the location to use for a key's content. -}-withObjectLoc :: Key -> (RawFilePath -> Annex a) -> Annex a-withObjectLoc key a = a =<< calcRepo (gitAnnexLocation key)- cleanObjectLoc :: Key -> Annex () -> Annex () cleanObjectLoc key cleaner = do file <- calcRepo (gitAnnexLocation key)@@ -665,8 +501,7 @@ maybe noop (const $ removeparents dir (n-1)) <=< catchMaybeIO $ removeDirectory (fromRawFilePath dir) -{- Removes a key's file from .git/annex/objects/- -}+{- Removes a key's file from .git/annex/objects/ -} removeAnnex :: ContentRemovalLock -> Annex () removeAnnex (ContentRemovalLock key) = withObjectLoc key $ \file -> cleanObjectLoc key $ do@@ -680,54 +515,14 @@ -- Check associated pointer file for modifications, and reset if -- it's unmodified. resetpointer file = ifM (isUnmodified key file)- ( depopulatePointerFile key file+ ( adjustedBranchRefresh (AssociatedFile (Just file)) $+ depopulatePointerFile key file -- Modified file, so leave it alone. -- If it was a hard link to the annex object, -- that object might have been frozen as part of the -- removal process, so thaw it. , void $ tryIO $ thawContent file )--{- Check if a file contains the unmodified content of the key.- -- - The expensive way to tell is to do a verification of its content.- - The cheaper way is to see if the InodeCache for the key matches the- - file. -}-isUnmodified :: Key -> RawFilePath -> Annex Bool-isUnmodified key f = go =<< geti- where- go Nothing = return False- go (Just fc) = isUnmodifiedCheap' key fc <||> expensivecheck fc- expensivecheck fc = ifM (verifyKeyContent RetrievalAllKeysSecure AlwaysVerify UnVerified key f)- ( do- -- The file could have been modified while it was- -- being verified. Detect that.- ifM (geti >>= maybe (return False) (compareInodeCaches fc))- ( do- -- Update the InodeCache to avoid- -- performing this expensive check again.- Database.Keys.addInodeCaches key [fc]- return True- , return False- )- , return False- )- geti = withTSDelta (liftIO . genInodeCache f)--{- Cheap check if a file contains the unmodified content of the key,- - only checking the InodeCache of the key.- -- - Note that, on systems not supporting high-resolution mtimes,- - this may report a false positive when repeated edits are made to a file- - within a small time window (eg 1 second).- -}-isUnmodifiedCheap :: Key -> RawFilePath -> Annex Bool-isUnmodifiedCheap key f = maybe (return False) (isUnmodifiedCheap' key) - =<< withTSDelta (liftIO . genInodeCache f)--isUnmodifiedCheap' :: Key -> InodeCache -> Annex Bool-isUnmodifiedCheap' key fc = - anyM (compareInodeCaches fc) =<< Database.Keys.getInodeCaches key {- Moves a key out of .git/annex/objects/ into .git/annex/bad, and - returns the file it was moved to. -}
Annex/Content/LowLevel.hs view
@@ -18,7 +18,6 @@ import Utility.CopyFile import qualified Utility.RawFilePath as R -import System.PosixCompat.Files import qualified System.FilePath.ByteString as P {- Runs the secure erase command if set, otherwise does nothing.@@ -75,8 +74,9 @@ =<< liftIO (R.getFileStatus src) checkedCopyFile' :: Key -> RawFilePath -> RawFilePath -> Maybe FileMode -> FileStatus -> Annex Bool-checkedCopyFile' key src dest destmode s = catchBoolIO $- ifM (checkDiskSpace' (fromIntegral $ fileSize s) (Just $ P.takeDirectory dest) key 0 True)+checkedCopyFile' key src dest destmode s = catchBoolIO $ do+ sz <- liftIO $ getFileSize' src s+ ifM (checkDiskSpace' sz (Just $ P.takeDirectory dest) key 0 True) ( liftIO $ copyFileExternal CopyAllMetaData (fromRawFilePath src) (fromRawFilePath dest) <&&> preserveGitMode dest destmode
Annex/Content/PointerFile.hs view
@@ -9,12 +9,6 @@ module Annex.Content.PointerFile where -#if ! defined(mingw32_HOST_OS)-import System.Posix.Files-#else-import System.PosixCompat.Files-#endif- import Annex.Common import Annex.Perms import Annex.Link@@ -22,10 +16,11 @@ import Annex.InodeSentinal import Annex.Content.LowLevel import Utility.InodeCache+import qualified Utility.RawFilePath as R #if ! defined(mingw32_HOST_OS) import Utility.Touch+import System.Posix.Files (modificationTimeHiRes) #endif-import qualified Utility.RawFilePath as R {- Populates a pointer file with the content of a key. -
+ Annex/Content/Presence.hs view
@@ -0,0 +1,246 @@+{- git-annex object content presence+ -+ - Copyright 2010-2020 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP #-}++module Annex.Content.Presence (+ inAnnex,+ inAnnex',+ inAnnexSafe,+ inAnnexCheck,+ objectFileExists,+ withObjectLoc,+ isUnmodified,+ isUnmodifiedCheap,+ verifyKeyContent,+ VerifyConfig(..),+ Verification(..),+ unVerified,+ warnUnverifiableInsecure,+ contentLockFile,+) where++import Annex.Common+import qualified Annex+import Annex.LockPool+import Annex.WorkerPool+import Types.Remote (unVerified, Verification(..), RetrievalSecurityPolicy(..))+import qualified Types.Remote+import qualified Types.Backend+import qualified Backend+import qualified Database.Keys+import Types.Key+import Annex.InodeSentinal+import Utility.InodeCache+import Types.WorkerPool+import qualified Utility.RawFilePath as R++#ifdef mingw32_HOST_OS+import Annex.Perms+#endif++{- Checks if a given key's content is currently present. -}+inAnnex :: Key -> Annex Bool+inAnnex key = inAnnexCheck key $ liftIO . R.doesPathExist++{- Runs an arbitrary check on a key's content. -}+inAnnexCheck :: Key -> (RawFilePath -> Annex Bool) -> Annex Bool+inAnnexCheck key check = inAnnex' id False check key++{- inAnnex that performs an arbitrary check of the key's content. -}+inAnnex' :: (a -> Bool) -> a -> (RawFilePath -> Annex a) -> Key -> Annex a+inAnnex' isgood bad check key = withObjectLoc key $ \loc -> do+ r <- check loc+ if isgood r+ then ifM (annexThin <$> Annex.getGitConfig)+ -- When annex.thin is set, the object file+ -- could be modified; make sure it's not.+ -- (Suppress any messages about+ -- checksumming, to avoid them cluttering+ -- the display.)+ ( ifM (doQuietAction $ isUnmodified key loc)+ ( return r+ , return bad+ )+ , return r+ )+ else return bad++{- Like inAnnex, checks if the object file for a key exists,+ - but there are no guarantees it has the right content. -}+objectFileExists :: Key -> Annex Bool+objectFileExists key =+ calcRepo (gitAnnexLocation key)+ >>= liftIO . R.doesPathExist++{- A safer check; the key's content must not only be present, but+ - is not in the process of being removed. -}+inAnnexSafe :: Key -> Annex (Maybe Bool)+inAnnexSafe key = inAnnex' (fromMaybe True) (Just False) go key+ where+ is_locked = Nothing+ is_unlocked = Just True+ is_missing = Just False++ go contentfile = flip checklock contentfile =<< contentLockFile key++#ifndef mingw32_HOST_OS+ checklock Nothing contentfile = checkOr is_missing contentfile+ {- The content file must exist, but the lock file generally+ - won't exist unless a removal is in process. -}+ checklock (Just lockfile) contentfile =+ ifM (liftIO $ doesFileExist (fromRawFilePath contentfile))+ ( checkOr is_unlocked lockfile+ , return is_missing+ )+ checkOr d lockfile = checkLocked lockfile >>= return . \case+ Nothing -> d+ Just True -> is_locked+ Just False -> is_unlocked+#else+ checklock Nothing contentfile = liftIO $ ifM (doesFileExist (fromRawFilePath contentfile))+ ( lockShared contentfile >>= \case+ Nothing -> return is_locked+ Just lockhandle -> do+ dropLock lockhandle+ return is_unlocked+ , return is_missing+ )+ {- In Windows, see if we can take a shared lock. If so, + - remove the lock file to clean up after ourselves. -}+ checklock (Just lockfile) contentfile =+ ifM (liftIO $ doesFileExist (fromRawFilePath contentfile))+ ( modifyContent lockfile $ liftIO $+ lockShared lockfile >>= \case+ Nothing -> return is_locked+ Just lockhandle -> do+ dropLock lockhandle+ void $ tryIO $ removeWhenExistsWith R.removeLink lockfile+ return is_unlocked+ , return is_missing+ )+#endif++{- Windows has to use a separate lock file from the content, since+ - locking the actual content file would interfere with the user's+ - use of it. -}+contentLockFile :: Key -> Annex (Maybe RawFilePath)+#ifndef mingw32_HOST_OS+contentLockFile _ = pure Nothing+#else+contentLockFile key = Just <$> calcRepo (gitAnnexContentLock key)+#endif++{- Performs an action, passing it the location to use for a key's content. -}+withObjectLoc :: Key -> (RawFilePath -> Annex a) -> Annex a+withObjectLoc key a = a =<< calcRepo (gitAnnexLocation key)++{- Check if a file contains the unmodified content of the key.+ -+ - The expensive way to tell is to do a verification of its content.+ - The cheaper way is to see if the InodeCache for the key matches the+ - file. -}+isUnmodified :: Key -> RawFilePath -> Annex Bool+isUnmodified key f = go =<< geti+ where+ go Nothing = return False+ go (Just fc) = isUnmodifiedCheap' key fc <||> expensivecheck fc+ expensivecheck fc = ifM (verifyKeyContent RetrievalAllKeysSecure AlwaysVerify UnVerified key f)+ ( do+ -- The file could have been modified while it was+ -- being verified. Detect that.+ ifM (geti >>= maybe (return False) (compareInodeCaches fc))+ ( do+ -- Update the InodeCache to avoid+ -- performing this expensive check again.+ Database.Keys.addInodeCaches key [fc]+ return True+ , return False+ )+ , return False+ )+ geti = withTSDelta (liftIO . genInodeCache f)++{- Cheap check if a file contains the unmodified content of the key,+ - only checking the InodeCache of the key.+ -+ - Note that, on systems not supporting high-resolution mtimes,+ - this may report a false positive when repeated edits are made to a file+ - within a small time window (eg 1 second).+ -}+isUnmodifiedCheap :: Key -> RawFilePath -> Annex Bool+isUnmodifiedCheap key f = maybe (return False) (isUnmodifiedCheap' key) + =<< withTSDelta (liftIO . genInodeCache f)++isUnmodifiedCheap' :: Key -> InodeCache -> Annex Bool+isUnmodifiedCheap' key fc = + anyM (compareInodeCaches fc) =<< Database.Keys.getInodeCaches key++{- Verifies that a file is the expected content of a key.+ -+ - Configuration can prevent verification, for either a+ - particular remote or always, unless the RetrievalSecurityPolicy+ - requires verification.+ -+ - Most keys have a known size, and if so, the file size is checked.+ -+ - When the key's backend allows verifying the content (via checksum),+ - it is checked. + -+ - If the RetrievalSecurityPolicy requires verification and the key's+ - backend doesn't support it, the verification will fail.+ -}+verifyKeyContent :: RetrievalSecurityPolicy -> VerifyConfig -> Verification -> Key -> RawFilePath -> Annex Bool+verifyKeyContent rsp v verification k f = case (rsp, verification) of+ (_, Verified) -> return True+ (RetrievalVerifiableKeysSecure, _) -> ifM (Backend.isVerifiable k)+ ( verify+ , ifM (annexAllowUnverifiedDownloads <$> Annex.getGitConfig)+ ( verify+ , warnUnverifiableInsecure k >> return False+ )+ )+ (_, UnVerified) -> ifM (shouldVerify v)+ ( verify+ , return True+ )+ (_, MustVerify) -> verify+ where+ verify = enteringStage VerifyStage $ verifysize <&&> verifycontent+ verifysize = case fromKey keySize k of+ Nothing -> return True+ Just size -> do+ size' <- liftIO $ catchDefaultIO 0 $ getFileSize f+ return (size' == size)+ verifycontent = Backend.maybeLookupBackendVariety (fromKey keyVariety k) >>= \case+ Nothing -> return True+ Just b -> case Types.Backend.verifyKeyContent b of+ Nothing -> return True+ Just verifier -> verifier k f++warnUnverifiableInsecure :: Key -> Annex ()+warnUnverifiableInsecure k = warning $ unwords+ [ "Getting " ++ kv ++ " keys with this remote is not secure;"+ , "the content cannot be verified to be correct."+ , "(Use annex.security.allow-unverified-downloads to bypass"+ , "this safety check.)"+ ]+ where+ kv = decodeBS (formatKeyVariety (fromKey keyVariety k))++data VerifyConfig = AlwaysVerify | NoVerify | RemoteVerify Remote | DefaultVerify++shouldVerify :: VerifyConfig -> Annex Bool+shouldVerify AlwaysVerify = return True+shouldVerify NoVerify = return False+shouldVerify DefaultVerify = annexVerify <$> Annex.getGitConfig+shouldVerify (RemoteVerify r) = + (shouldVerify DefaultVerify+ <&&> pure (remoteAnnexVerify (Types.Remote.gitconfig r)))+ -- Export remotes are not key/value stores, so always verify+ -- content from them even when verification is disabled.+ <||> Types.Remote.isExportSupported r
Annex/ExternalAddonProcess.hs view
@@ -53,7 +53,7 @@ Left _ -> runerr cmdpath started cmd errrelayer pall@(Just hin, Just hout, Just herr, ph) = do- stderrelay <- async $ errrelayer herr+ stderrelay <- async $ errrelayer ph herr let shutdown forcestop = do -- Close the process's stdin, to let it know there -- are no more requests, so it will exit.
Annex/Import.hs view
@@ -460,7 +460,7 @@ ia loc cid (fromRawFilePath tmpfile) (pure k) (combineMeterUpdate p' p)- ok <- moveAnnex k' tmpfile+ ok <- moveAnnex k' af tmpfile when ok $ logStatus k InfoPresent return (Just (k', ok))@@ -503,7 +503,7 @@ p case keyGitSha k of Nothing -> do- ok <- moveAnnex k tmpfile+ ok <- moveAnnex k af tmpfile when ok $ do recordcidkey cidmap db cid k logStatus k InfoPresent
Annex/Ingest.hs view
@@ -49,9 +49,9 @@ import Annex.InodeSentinal import Annex.AdjustedBranch import Annex.FileMatcher+import qualified Utility.RawFilePath as R import Control.Exception (IOException)-import qualified Utility.RawFilePath as R data LockedDown = LockedDown { lockDownConfig :: LockDownConfig@@ -113,7 +113,7 @@ (tmpfile, h) <- openTempFile (fromRawFilePath tmpdir) $ relatedTemplate $ "ingest-" ++ takeFileName file hClose h- removeWhenExistsWith removeLink tmpfile+ removeWhenExistsWith R.removeLink (toRawFilePath tmpfile) withhardlink' delta tmpfile `catchIO` const (nohardlink' delta) @@ -177,13 +177,19 @@ go _ _ Nothing = failure "failed to generate a key" golocked key mcache s =- tryNonAsync (moveAnnex key $ contentLocation source) >>= \case+ tryNonAsync (moveAnnex key naf (contentLocation source)) >>= \case Right True -> do populateAssociatedFiles key source restage success key mcache s Right False -> giveup "failed to add content to annex" Left e -> restoreFile (keyFilename source) key e + -- moveAnnex uses the AssociatedFile provided to it to unlock+ -- locked files when getting a file in an adjusted branch.+ -- That case does not apply here, where we're adding an unlocked+ -- file, so provide it nothing.+ naf = AssociatedFile Nothing+ gounlocked key (Just cache) s = do -- Remove temp directory hard link first because -- linkToAnnex falls back to copying if a file@@ -352,7 +358,7 @@ stagePointerFile file mode =<< hashPointerFile key Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file) case mtmp of- Just tmp -> ifM (moveAnnex key tmp)+ Just tmp -> ifM (moveAnnex key af tmp) ( linkunlocked mode >> return True , writepointer mode >> return False )@@ -363,10 +369,11 @@ , do addLink ci file key Nothing case mtmp of- Just tmp -> moveAnnex key tmp+ Just tmp -> moveAnnex key af tmp Nothing -> return True ) where+ af = AssociatedFile (Just file) mi = case mtmp of Just tmp -> MatchingFile $ FileInfo { contentFile = Just tmp
Annex/Init.hs view
@@ -49,19 +49,21 @@ import Upgrade import Annex.Tmp import Utility.UserInfo-import Utility.ThreadScheduler import qualified Utility.RawFilePath as R #ifndef mingw32_HOST_OS+import Utility.ThreadScheduler import Annex.Perms import Utility.FileMode import System.Posix.User import qualified Utility.LockFile.Posix as Posix-import Data.Either #endif import qualified Data.Map as M+#ifndef mingw32_HOST_OS+import Data.Either import qualified System.FilePath.ByteString as P import Control.Concurrent.Async+#endif checkCanInitialize :: Annex a -> Annex a checkCanInitialize a = canInitialize' >>= \case@@ -210,9 +212,9 @@ where probe f = catchDefaultIO (True, []) $ do let f2 = f ++ "2"- removeWhenExistsWith removeLink f2+ removeWhenExistsWith R.removeLink (toRawFilePath f2) createSymbolicLink f f2- removeWhenExistsWith removeLink f2+ removeWhenExistsWith R.removeLink (toRawFilePath f2) preventWrite (toRawFilePath f) -- Should be unable to write to the file, unless -- running as root, but some crippled
Annex/PidLock.hs view
@@ -10,10 +10,10 @@ module Annex.PidLock where import Annex.Common-import Annex.GitOverlay import Git-import Git.Env #ifndef mingw32_HOST_OS+import Git.Env+import Annex.GitOverlay import qualified Utility.LockFile.PidLock as PidF import qualified Utility.LockPool.PidLock as PidP import Utility.LockPool (dropLock)
Annex/Ssh.hs view
@@ -337,7 +337,7 @@ } void $ liftIO $ catchMaybeIO $ withCreateProcess p $ \_ _ _ pid -> forceSuccessProcess p pid- liftIO $ removeWhenExistsWith removeLink socketfile+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath socketfile) {- This needs to be as short as possible, due to limitations on the length - of the path to a socket file. At the same time, it needs to be unique
Annex/Tmp.hs view
@@ -12,6 +12,7 @@ import Annex.LockFile import Annex.Perms import Types.CleanupActions+import qualified Utility.RawFilePath as R import Data.Time.Clock.POSIX @@ -67,5 +68,5 @@ let oldenough = now - (60 * 60 * 24 * 7) catchMaybeIO (modificationTime <$> getSymbolicLinkStatus f) >>= \case Just mtime | realToFrac mtime <= oldenough -> - void $ tryIO $ removeWhenExistsWith removeLink f+ void $ tryIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) _ -> return ()
Annex/View/ViewedFile.hs view
@@ -83,7 +83,7 @@ prop_viewedFile_roundtrips tf -- Relative filenames wanted, not directories. | any (isPathSeparator) (end f ++ beginning f) = True- | isAbsolute f = True+ | isAbsolute f || isDrive f = True | otherwise = dir == dirFromViewedFile (viewedFileFromReference f) where f = fromTestableFilePath tf
Annex/YoutubeDl.hs view
@@ -225,15 +225,18 @@ liftIO $ withCreateProcess p waitproc waitproc Nothing (Just o) (Just e) pid = do- output <- fmap fst $ - hGetContentsStrict o- `concurrently`- hGetContentsStrict e+ errt <- async $ discardstderr pid e+ output <- hGetContentsStrict o ok <- liftIO $ checkSuccessProcess pid+ wait errt return $ case (ok, lines output) of (True, (f:_)) | not (null f) -> Right f _ -> nomedia waitproc _ _ _ _ = error "internal"++ discardstderr pid e = hGetLineUntilExitOrEOF pid e >>= \case+ Nothing -> return ()+ Just _ -> discardstderr pid e nomedia = Left "no media in url"
Assistant.hs view
@@ -102,7 +102,7 @@ createAnnexDirectory (parentDir logfile) ifM (liftIO $ isNothing <$> getEnv flag) ( liftIO $ withNullHandle $ \nullh -> do- loghandle <- openLog logfile+ loghandle <- openLog (fromRawFilePath logfile) e <- getEnvironment cmd <- programPath ps <- getArgs@@ -115,7 +115,7 @@ exitcode <- withCreateProcess p $ \_ _ _ pid -> waitForProcess pid exitWith exitcode- , start (Utility.Daemon.foreground (Just pidfile)) $+ , start (Utility.Daemon.foreground (Just (fromRawFilePath pidfile))) $ case startbrowser of Nothing -> Nothing Just a -> Just $ a Nothing Nothing
Assistant/Repair.hs view
@@ -30,6 +30,7 @@ #endif import qualified Utility.Lsof as Lsof import Utility.ThreadScheduler+import qualified Utility.RawFilePath as R import Control.Concurrent.Async @@ -149,7 +150,7 @@ waitforit "to check stale git lock file" l' <- getsizes if l' == l- then liftIO $ mapM_ (removeWhenExistsWith removeLink . fst) l+ then liftIO $ mapM_ (removeWhenExistsWith R.removeLink . toRawFilePath . fst) l else go l' , do waitforit "for git lock file writer"
Assistant/Threads/Upgrader.hs view
@@ -31,7 +31,7 @@ upgraderThread :: UrlRenderer -> NamedThread upgraderThread urlrenderer = namedThread "Upgrader" $- when (isJust BuildInfo.upgradelocation) $ do+ when upgradeSupported $ do {- Check for upgrade on startup, unless it was just - upgraded. -} unlessM (liftIO checkSuccessfulUpgrade) $
Assistant/Upgrade.hs view
@@ -41,6 +41,7 @@ import qualified Utility.Url as Url import qualified Annex.Url as Url hiding (download) import Utility.Tuple+import qualified Utility.RawFilePath as R import Data.Either import qualified Data.Map as M@@ -220,7 +221,7 @@ error $ "did not find " ++ dir ++ " in " ++ distributionfile makeorigsymlink olddir = do let origdir = fromRawFilePath (parentDir (toRawFilePath olddir)) </> installBase- removeWhenExistsWith removeLink origdir+ removeWhenExistsWith R.removeLink (toRawFilePath origdir) createSymbolicLink newdir origdir {- Finds where the old version was installed. -}@@ -278,8 +279,8 @@ deleteFromManifest :: FilePath -> IO () deleteFromManifest dir = do fs <- map (dir </>) . lines <$> catchDefaultIO "" (readFile manifest)- mapM_ (removeWhenExistsWith removeLink) fs- removeWhenExistsWith removeLink manifest+ mapM_ (removeWhenExistsWith R.removeLink . toRawFilePath) fs+ removeWhenExistsWith R.removeLink (toRawFilePath manifest) removeEmptyRecursive dir where manifest = dir </> "git-annex.MANIFEST"@@ -337,6 +338,18 @@ distributionInfoSigUrl :: String distributionInfoSigUrl = distributionInfoUrl ++ ".sig"++{- Upgrade only supported on linux and OSX. -}+upgradeSupported :: Bool+#ifdef linux_HOST_OS+upgradeSupported = isJust BuildInfo.upgradelocation+#else+#ifdef darwin_HOST_OS+upgradeSupported = isJust BuildInfo.upgradelocation+#else+upgradeSupported = False+#endif+#endif {- Verifies that a file from the git-annex distribution has a valid - signature. Pass the detached .sig file; the file to be verified should
Assistant/WebApp/Configurators/Preferences.hs view
@@ -21,7 +21,7 @@ import Utility.DataUnits import Git.Config import Types.Distribution-import qualified BuildInfo+import Assistant.Upgrade import qualified Data.Text as T import qualified System.FilePath.ByteString as P@@ -59,7 +59,7 @@ , ("disabled", NoAutoUpgrade) ] autoUpgradeLabel- | isJust BuildInfo.upgradelocation = "Auto upgrade"+ | upgradeSupported = "Auto upgrade" | otherwise = "Auto restart on upgrade" positiveIntField = check isPositive intField
CHANGELOG view
@@ -1,3 +1,32 @@+git-annex (8.20201127) upstream; urgency=medium++ * adjust: New --unlock-present mode which locks files whose content is not+ present (so the broken symlink is visible), while unlocking files whose+ content is present.+ * Added annex.adjustedbranchrefresh git config to update adjusted+ branches set up by git-annex adjust --unlock-present/--hide-missing.+ * Fix hang when an external special remote program exited but+ the stderr pipe to it was left open, due to a daemon having inherited+ the file descriptor.+ * Fix a bug that could make resuming a download from the web fail+ when the entire content of the file is actually already present+ locally.+ * examinekey: Added a "file" format variable for consistency with find,+ and for easier scripting.+ * init: When writing hook scripts, set all execute bits, not only+ the user execute bit.+ * upgrade: Support an edge case upgrading a v5 direct mode repo+ where nothing had ever been committed to the head branch.+ * Made the test suite significantly less noisy, only displaying command+ output when something failed.+ * Fix building without the torrent library.+ Thanks, Kyle Meyer.+ * Fix build on Windows.+ * Prevent windows assistant from trying (and failing) to upgrade+ itself, which has never been supported on windows.++ -- Joey Hess <id@joeyh.name> Fri, 27 Nov 2020 12:54:11 -0400+ git-annex (8.20201116) upstream; urgency=medium * move: Fix a regression in the last release that made move --to not
Command/Adjust.hs view
@@ -19,6 +19,7 @@ optParser _ = (LinkAdjustment <$> linkAdjustmentParser) <|> (PresenceAdjustment <$> presenceAdjustmentParser <*> maybeLinkAdjustmentParser)+ <|> (LinkPresentAdjustment <$> linkPresentAdjustmentParser) linkAdjustmentParser :: Parser LinkAdjustment linkAdjustmentParser =@@ -43,6 +44,13 @@ flag' HideMissingAdjustment ( long "hide-missing" <> help "hide annexed files whose content is not present"+ )++linkPresentAdjustmentParser :: Parser LinkPresentAdjustment+linkPresentAdjustmentParser =+ flag' UnlockPresentAdjustment+ ( long "unlock-present"+ <> help "unlock files whose content is present; lock rest" ) seek :: Adjustment -> CommandSeek
Command/ExamineKey.hs view
@@ -9,7 +9,7 @@ import Command import qualified Utility.Format-import Command.Find (parseFormatOption, showFormatted, keyVars)+import Command.Find (parseFormatOption, showFormatted, formatVars) import Annex.Link import Backend import Types.Backend@@ -57,7 +57,7 @@ showFormatted (format o) (serializeKey' k) $ [ ("objectpath", fromRawFilePath objectpath) , ("objectpointer", fromRawFilePath objectpointer)- ] ++ keyVars k+ ] ++ formatVars k af return True where -- Parse the input, which is either a key, or in batch mode
Command/Find.hs view
@@ -75,7 +75,8 @@ start :: FindOptions -> SeekInput -> RawFilePath -> Key -> CommandStart start o _ file key = startingCustomOutput key $ do- showFormatted (formatOption o) file $ ("file", fromRawFilePath file) : keyVars key+ showFormatted (formatOption o) file + (formatVars key (AssociatedFile (Just file))) next $ return True startKeys :: FindOptions -> (SeekInput, Key, ActionItem) -> CommandStart@@ -92,8 +93,9 @@ Utility.Format.format formatter $ M.fromList vars -keyVars :: Key -> [(String, String)]-keyVars key =+formatVars :: Key -> AssociatedFile -> [(String, String)]+formatVars key (AssociatedFile af) =+ (maybe id (\f l -> (("file", fromRawFilePath f) : l)) af) [ ("key", serializeKey key) , ("backend", decodeBS $ formatKeyVariety $ fromKey keyVariety key) , ("bytesize", size show)
Command/FuzzTest.hs view
@@ -17,6 +17,7 @@ import Utility.ThreadScheduler import Utility.DiskFree import Git.Types (fromConfigKey)+import qualified Utility.RawFilePath as R import Data.Time.Clock import System.Random (getStdRandom, random, randomR)@@ -178,7 +179,7 @@ n <- liftIO (getStdRandom random :: IO Int) liftIO $ writeFile f $ show n ++ "\n" runFuzzAction (FuzzDelete (FuzzFile f)) = liftIO $- removeWhenExistsWith removeLink f+ removeWhenExistsWith R.removeLink (toRawFilePath f) runFuzzAction (FuzzMove (FuzzFile src) (FuzzFile dest)) = liftIO $ rename src dest runFuzzAction (FuzzDeleteDir (FuzzDir d)) = liftIO $
Command/Get.hs view
@@ -114,7 +114,7 @@ | Remote.hasKeyCheap r = either (const False) id <$> Remote.hasKey r key | otherwise = return True- docopy r witness = getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) key $ \dest ->+ docopy r witness = getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) key afile $ \dest -> download (Remote.uuid r) key afile stdRetry (\p -> do showAction $ "from " ++ Remote.name r
Command/Move.hs view
@@ -225,7 +225,7 @@ where get = notifyTransfer Download afile $ download (Remote.uuid src) key afile stdRetry $ \p ->- getViaTmp (Remote.retrievalSecurityPolicy src) (RemoteVerify src) key $ \t ->+ getViaTmp (Remote.retrievalSecurityPolicy src) (RemoteVerify src) key afile $ \t -> Remote.verifiedAction $ Remote.retrieveKeyFile src key afile (fromRawFilePath t) p dispatch _ _ False = stop -- failed
Command/Multicast.hs view
@@ -27,6 +27,7 @@ import Utility.Tmp import Utility.Tmp.Dir import Utility.Process.Transcript+import qualified Utility.RawFilePath as R import Data.Char import qualified Data.ByteString.Lazy.UTF8 as B8@@ -84,7 +85,7 @@ KeyContainer s -> liftIO $ genkey (Param s) KeyFile f -> do createAnnexDirectory (toRawFilePath (takeDirectory f))- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) liftIO $ protectedOutput $ genkey (File f) case (ok, parseFingerprint s) of (False, _) -> giveup $ "uftp_keymgt failed: " ++ s@@ -210,9 +211,9 @@ case deserializeKey (takeFileName f) of Nothing -> do warning $ "Received a file " ++ f ++ " that is not a git-annex key. Deleting this file."- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) Just k -> void $- getViaTmpFromDisk RetrievalVerifiableKeysSecure AlwaysVerify k $ \dest -> unVerified $+ getViaTmpFromDisk RetrievalVerifiableKeysSecure AlwaysVerify k (AssociatedFile Nothing) $ \dest -> unVerified $ liftIO $ catchBoolIO $ do rename f (fromRawFilePath dest) return True
Command/P2P.hs view
@@ -24,6 +24,7 @@ import Utility.Tmp.Dir import Utility.FileMode import Utility.ThreadScheduler+import qualified Utility.RawFilePath as R import qualified Utility.MagicWormhole as Wormhole import Control.Concurrent.Async@@ -256,7 +257,7 @@ Wormhole.sendFile sendf observer wormholeparams `concurrently` Wormhole.receiveFile recvf producer wormholeparams- liftIO $ removeWhenExistsWith removeLink sendf+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath sendf) if sendres /= True then return SendFailed else if recvres /= True
Command/ReKey.hs view
@@ -90,7 +90,7 @@ - This avoids hard linking to content linked to an - unlocked file, which would leave the new key unlocked - and vulnerable to corruption. -}- ( getViaTmpFromDisk RetrievalAllKeysSecure DefaultVerify newkey $ \tmp -> unVerified $ do+ ( getViaTmpFromDisk RetrievalAllKeysSecure DefaultVerify newkey (AssociatedFile Nothing) $ \tmp -> unVerified $ do oldobj <- calcRepo (gitAnnexLocation oldkey) isJust <$> linkOrCopy' (return True) newkey oldobj tmp Nothing , do
Command/RecvKey.hs view
@@ -33,7 +33,7 @@ let verify = if fromunlocked then AlwaysVerify else DefaultVerify -- This matches the retrievalSecurityPolicy of Remote.Git let rsp = RetrievalAllKeysSecure- ifM (getViaTmp rsp verify key go)+ ifM (getViaTmp rsp verify key (AssociatedFile Nothing) go) ( do -- forcibly quit after receiving one key, -- and shutdown cleanly
Command/Reinject.hs view
@@ -88,7 +88,7 @@ ) where move = checkDiskSpaceToGet key False $- moveAnnex key src+ moveAnnex key (AssociatedFile Nothing) src cleanup :: Key -> CommandCleanup cleanup key = do
Command/SetKey.hs view
@@ -35,7 +35,7 @@ -- the file might be on a different filesystem, so moveFile is used -- rather than simply calling moveAnnex; disk space is also -- checked this way.- ok <- getViaTmp RetrievalAllKeysSecure DefaultVerify key $ \dest -> unVerified $+ ok <- getViaTmp RetrievalAllKeysSecure DefaultVerify key (AssociatedFile Nothing) $ \dest -> unVerified $ if dest /= file then liftIO $ catchBoolIO $ do moveFile (fromRawFilePath file) (fromRawFilePath dest)
Command/Sync.hs view
@@ -60,6 +60,7 @@ import Logs.PreferredContent import Annex.AutoMerge import Annex.AdjustedBranch+import Annex.AdjustedBranch.Merge import Annex.Ssh import Annex.BloomFilter import Annex.UpdateInstead@@ -406,17 +407,17 @@ updateBranches (Just branch, madj) = do -- When in an adjusted branch, propigate any changes made to it -- back to the original branch. The adjusted branch may also need- -- to be updated to hide/expose files.+ -- to be updated, if the adjustment is not stable, and the usual+ -- configuration does not update it. case madj of Nothing -> noop Just adj -> do let origbranch = branch propigateAdjustedCommits origbranch adj- when (adjustmentHidesFiles adj) $ do- showSideAction "updating adjusted branch"- let adjbranch = originalToAdjusted origbranch adj- unlessM (updateAdjustedBranch adj adjbranch origbranch) $- warning $ unwords [ "Updating adjusted branch failed." ]+ unless (adjustmentIsStable adj) $+ annexAdjustedBranchRefresh <$> Annex.getGitConfig >>= \case+ 0 -> adjustedBranchRefreshFull adj origbranch+ _ -> return () -- Update the sync branch to match the new state of the branch inRepo $ updateBranch (syncBranch branch) branch
Command/TestRemote.hs view
@@ -294,7 +294,7 @@ Just b -> case Types.Backend.verifyKeyContent b of Nothing -> return True Just verifier -> verifier k (serializeKey' k)- get r k = getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k $ \dest ->+ get r k = getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k (AssociatedFile Nothing) $ \dest -> tryNonAsync (Remote.retrieveKeyFile r k (AssociatedFile Nothing) (fromRawFilePath dest) nullMeterUpdate) >>= \case Right v -> return (True, v) Left _ -> return (False, UnVerified)@@ -368,13 +368,13 @@ , check (`notElem` [Right True, Right False]) "checkPresent" $ \r k -> Remote.checkPresent r k , check (== Right False) "retrieveKeyFile" $ \r k ->- getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k $ \dest ->+ getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k (AssociatedFile Nothing) $ \dest -> tryNonAsync (Remote.retrieveKeyFile r k (AssociatedFile Nothing) (fromRawFilePath dest) nullMeterUpdate) >>= \case Right v -> return (True, v) Left _ -> return (False, UnVerified) , check (== Right False) "retrieveKeyFileCheap" $ \r k -> case Remote.retrieveKeyFileCheap r of Nothing -> return False- Just a -> getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k $ \dest -> + Just a -> getViaTmp (Remote.retrievalSecurityPolicy r) (RemoteVerify r) k (AssociatedFile Nothing) $ \dest -> unVerified $ isRight <$> tryNonAsync (a k (AssociatedFile Nothing) (fromRawFilePath dest)) ]@@ -436,7 +436,7 @@ k <- case Types.Backend.genKey Backend.Hash.testKeyBackend of Just a -> a ks nullMeterUpdate Nothing -> giveup "failed to generate random key (backend problem)"- _ <- moveAnnex k (toRawFilePath f)+ _ <- moveAnnex k (AssociatedFile Nothing) (toRawFilePath f) return k getReadonlyKey :: Remote -> FilePath -> Annex Key
Command/TransferKey.hs view
@@ -63,7 +63,7 @@ fromPerform :: Key -> AssociatedFile -> Remote -> CommandPerform fromPerform key file remote = go Upload file $ download (uuid remote) key file stdRetry $ \p ->- getViaTmp (retrievalSecurityPolicy remote) (RemoteVerify remote) key $ \t ->+ getViaTmp (retrievalSecurityPolicy remote) (RemoteVerify remote) key file $ \t -> tryNonAsync (Remote.retrieveKeyFile remote key file (fromRawFilePath t) p) >>= \case Right v -> return (True, v) Left e -> do
Command/TransferKeys.hs view
@@ -47,7 +47,7 @@ return True | otherwise = notifyTransfer direction file $ download (Remote.uuid remote) key file stdRetry $ \p ->- getViaTmp (Remote.retrievalSecurityPolicy remote) (RemoteVerify remote) key $ \t -> do+ getViaTmp (Remote.retrievalSecurityPolicy remote) (RemoteVerify remote) key file $ \t -> do r <- tryNonAsync (Remote.retrieveKeyFile remote key file (fromRawFilePath t) p) >>= \case Left e -> do warning (show e)
Command/Vicfg.hs view
@@ -32,6 +32,7 @@ import Types.NumCopies import Remote import Git.Types (fromConfigKey, fromConfigValue)+import qualified Utility.RawFilePath as R cmd :: Command cmd = command "vicfg" SectionSetup "edit configuration in git-annex branch"@@ -58,7 +59,7 @@ unlessM (liftIO $ boolSystem "sh" [Param "-c", Param $ unwords [vi, shellEscape f]]) $ giveup $ vi ++ " exited nonzero; aborting" r <- parseCfg (defCfg curcfg) <$> liftIO (readFileStrict f)- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) case r of Left s -> do liftIO $ writeFile f s
Command/Whereis.hs view
@@ -95,10 +95,8 @@ mapM_ (showRemoteUrls remotemap) urls Just formatter -> liftIO $ do- let vs = catMaybes- [ fmap (("file",) . fromRawFilePath)- (actionItemWorkTreeFile ai)- ] ++ Command.Find.keyVars key+ let vs = Command.Find.formatVars key+ (AssociatedFile (actionItemWorkTreeFile ai)) let showformatted muuid murl = putStr $ Utility.Format.format formatter $ M.fromList $ vs ++ catMaybes
Common.hs view
@@ -13,7 +13,7 @@ import System.FilePath as X import System.IO as X hiding (FilePath) import System.Exit as X-import System.PosixCompat.Files as X hiding (fileSize)+import System.PosixCompat.Files as X hiding (fileSize, removeLink) import Utility.Misc as X import Utility.Exception as X
Creds.hs view
@@ -32,12 +32,13 @@ import Types.ProposedAccepted import Remote.Helper.Encryptable (remoteCipher, remoteCipher', embedCreds, EncryptionIsSetup, extractCipher) import Utility.Env (getEnv)+import Utility.Base64+import qualified Utility.RawFilePath as R import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.ByteString.Char8 as S import qualified Data.Map as M import qualified System.FilePath.ByteString as P-import Utility.Base64 {- A CredPair can be stored in a file, or in the environment, or - in a remote's configuration. -}@@ -211,9 +212,8 @@ removeCreds :: FilePath -> Annex () removeCreds file = do- d <- fromRawFilePath <$> fromRepo gitAnnexCredsDir- let f = d </> file- liftIO $ removeWhenExistsWith removeLink f+ d <- fromRepo gitAnnexCredsDir+ liftIO $ removeWhenExistsWith R.removeLink (d P.</> toRawFilePath file) includeCredsInfo :: ParsedRemoteConfig -> CredPairStorage -> [(String, String)] -> Annex [(String, String)] includeCredsInfo pc@(ParsedRemoteConfig cm _) storage info = do
Git/Config.hs view
@@ -22,7 +22,6 @@ import qualified Git.Command import qualified Git.Construct import Utility.UserInfo-import Utility.ThreadScheduler {- Returns a single git config setting, or a fallback value if not set. -} get :: ConfigKey -> ConfigValue -> Repo -> ConfigValue@@ -186,6 +185,10 @@ | s' == "0" = Just False | s' == "" = Just False + -- Git treats any number other than 0 as true,+ -- including negative numbers.+ | S8.all (\c -> isDigit c || c == '-') s' = Just True+ | otherwise = Nothing where s' = S8.map toLower s@@ -207,8 +210,8 @@ {- Runs a command to get the configuration of a repo, - and returns a repo populated with the configuration, as well as the raw- - output and standard error of the command. -}-fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Either SomeException (Repo, S.ByteString, S.ByteString))+ - output and the standard error of the command. -}+fromPipe :: Repo -> String -> [CommandParam] -> ConfigStyle -> IO (Either SomeException (Repo, S.ByteString, String)) fromPipe r cmd params st = tryNonAsync $ withCreateProcess p go where p = (proc cmd $ toCommand params)@@ -216,24 +219,21 @@ , std_err = CreatePipe } go _ (Just hout) (Just herr) pid =- withAsync (S.hGetContents herr) $ \errreader -> do+ withAsync (getstderr pid herr []) $ \errreader -> do val <- S.hGetContents hout- -- In case the process exits while something else,- -- like a background process, keeps the stderr handle- -- open, don't block forever waiting for stderr.- -- A few seconds after finishing reading stdout- -- should get any error message.- err <- either id id <$> - wait errreader- `race` (threadDelaySeconds (Seconds 2) >> return mempty)+ err <- wait errreader forceSuccessProcess p pid r' <- store val st r return (r', val, err) go _ _ _ _ = error "internal" + getstderr pid herr c = hGetLineUntilExitOrEOF pid herr >>= \case+ Just l -> getstderr pid herr (l:c)+ Nothing -> return (unlines (reverse c))+ {- Reads git config from a specified file and returns the repo populated - with the configuration. -}-fromFile :: Repo -> FilePath -> IO (Either SomeException (Repo, S.ByteString, S.ByteString))+fromFile :: Repo -> FilePath -> IO (Either SomeException (Repo, S.ByteString, String)) fromFile r f = fromPipe r "git" [ Param "config" , Param "--file"
Git/Fsck.hs view
@@ -85,8 +85,8 @@ where go _ (Just outh) (Just errh) pid = do (o1, o2) <- concurrently- (parseFsckOutput maxobjs r outh)- (parseFsckOutput maxobjs r errh)+ (parseFsckOutput maxobjs r outh pid)+ (parseFsckOutput maxobjs r errh pid) fsckok <- checkSuccessProcess pid case mappend o1 o2 of FsckOutput badobjs truncated@@ -121,9 +121,9 @@ findMissing :: [Sha] -> Repo -> IO MissingObjects findMissing objs r = S.fromList <$> filterM (`isMissing` r) objs -parseFsckOutput :: Int -> Repo -> Handle -> IO FsckOutput-parseFsckOutput maxobjs r h = do- ls <- lines <$> hGetContents h+parseFsckOutput :: Int -> Repo -> Handle -> ProcessHandle -> IO FsckOutput+parseFsckOutput maxobjs r h pid = do+ ls <- getlines [] if null ls then return NoFsckOutput else if all ("duplicateEntries" `isInfixOf`) ls@@ -133,6 +133,10 @@ let !truncated = length shas > maxobjs missingobjs <- findMissing (take maxobjs shas) r return $ FsckOutput missingobjs truncated+ where+ getlines c = hGetLineUntilExitOrEOF pid h >>= \case+ Nothing -> return (reverse c)+ Just l -> getlines (l:c) isMissing :: Sha -> Repo -> IO Bool isMissing s r = either (const True) (const False) <$> tryIO dump
Git/Hook.hs view
@@ -13,9 +13,7 @@ import Git import Utility.Tmp import Utility.Shell-#ifndef mingw32_HOST_OS import Utility.FileMode-#endif data Hook = Hook { hookName :: FilePath@@ -55,8 +53,9 @@ f = hookFile h r go = do viaTmp writeFile f (hookScript h)- p <- getPermissions f- void $ tryIO $ setPermissions f $ p {executable = True}+ void $ tryIO $ modifyFileMode+ (toRawFilePath f)+ (addModes executeModes) return True {- Removes a hook. Returns False if the hook contained something else, and
Git/Queue.hs view
@@ -207,11 +207,13 @@ where gitparams = gitCommandLine (Param (getSubcommand action):getParams action) repo+#ifndef mingw32_HOST_OS go p (Just h) _ _ pid = do hPutStr h $ intercalate "\0" $ toCommand $ getFiles action hClose h forceSuccessProcess p pid go _ _ _ _ _ = error "internal"+#endif runAction repo action@(InternalAction {}) = let InternalActionRunner _ runner = getRunner action in runner repo (getInternalFiles action)
Git/Repair.hs view
@@ -5,6 +5,8 @@ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE OverloadedStrings #-}+ module Git.Repair ( runRepair, runRepairOf,@@ -243,11 +245,12 @@ explodePackedRefsFile :: Repo -> IO () explodePackedRefsFile r = do let f = packedRefsFile r+ let f' = toRawFilePath f whenM (doesFileExist f) $ do rs <- mapMaybe parsePacked . lines- <$> catchDefaultIO "" (safeReadFile f)+ <$> catchDefaultIO "" (safeReadFile f') forM_ rs makeref- removeWhenExistsWith removeLink f+ removeWhenExistsWith R.removeLink f' where makeref (sha, ref) = do let gitd = localGitDir r@@ -444,13 +447,13 @@ preRepair :: Repo -> IO () preRepair g = do unlessM (validhead <$> catchDefaultIO "" (safeReadFile headfile)) $ do- removeWhenExistsWith removeLink headfile- writeFile headfile "ref: refs/heads/master"+ removeWhenExistsWith R.removeLink headfile+ writeFile (fromRawFilePath headfile) "ref: refs/heads/master" explodePackedRefsFile g unless (repoIsLocalBare g) $ void $ tryIO $ allowWrite $ indexFile g where- headfile = fromRawFilePath (localGitDir g) </> "HEAD"+ headfile = localGitDir g P.</> "HEAD" validhead s = "ref: refs/" `isPrefixOf` s || isJust (extractSha (encodeBS' s)) @@ -616,7 +619,7 @@ successfulRepair :: (Bool, [Branch]) -> Bool successfulRepair = fst -safeReadFile :: FilePath -> IO String+safeReadFile :: RawFilePath -> IO String safeReadFile f = do- allowRead (toRawFilePath f)- readFileStrict f+ allowRead f+ readFileStrict (fromRawFilePath f)
Logs/Transfer.hs view
@@ -131,7 +131,7 @@ v <- liftIO $ lockShared lck liftIO $ case v of Nothing -> catchDefaultIO Nothing $- readTransferInfoFile Nothing tfile+ readTransferInfoFile Nothing (fromRawFilePath tfile) Just lockhandle -> do dropLock lockhandle cleanstale
Messages/Progress.hs view
@@ -140,11 +140,11 @@ <$> pure True <*> mkStderrEmitter -mkStderrRelayer :: Annex (Handle -> IO ())+mkStderrRelayer :: Annex (ProcessHandle -> Handle -> IO ()) mkStderrRelayer = do quiet <- commandProgressDisabled emitter <- mkStderrEmitter- return $ \h -> avoidProgress quiet h emitter+ return $ \ph h -> avoidProgress quiet ph h emitter {- Generates an IO action that can be used to emit stderr. -
P2P/Annex.hs view
@@ -76,7 +76,7 @@ v <- tryNonAsync $ do let runtransfer ti = Right <$> transfer download k af (\p ->- getViaTmp rsp DefaultVerify k $ \tmp ->+ getViaTmp rsp DefaultVerify k af $ \tmp -> storefile (fromRawFilePath tmp) o l getb validitycheck p ti) let fallback = return $ Left $ ProtoFailureMessage "transfer already in progress, or unable to take transfer lock"
P2P/IO.hs view
@@ -37,6 +37,7 @@ import Utility.FileMode import Types.UUID import Annex.ChangedRefs+import qualified Utility.RawFilePath as R import Control.Monad.Free import Control.Monad.IO.Class@@ -124,7 +125,7 @@ -- the callback. serveUnixSocket :: FilePath -> (Handle -> IO ()) -> IO () serveUnixSocket unixsocket serveconn = do- removeWhenExistsWith removeLink unixsocket+ removeWhenExistsWith R.removeLink (toRawFilePath unixsocket) soc <- S.socket S.AF_UNIX S.Stream S.defaultProtocol S.bind soc (S.SockAddrUnix unixsocket) -- Allow everyone to read and write to the socket,
Remote/BitTorrent.hs view
@@ -382,7 +382,7 @@ (d:[]) -> return $ map (splitsize d) files _ -> parsefailed (show v) where- getfield = btshowmetainfo torrent+ getfield = btshowmetainfo (fromRawFilePath torrent) parsefailed s = giveup $ "failed to parse btshowmetainfo output for torrent file: " ++ show s -- btshowmetainfo outputs a list of "filename (size)"
Remote/Directory.hs view
@@ -258,7 +258,7 @@ #ifdef mingw32_HOST_OS {- Windows needs the files inside the directory to be writable - before it can delete them. -}- void $ tryIO $ mapM_ allowWrite =<< dirContents dir+ void $ tryIO $ mapM_ (allowWrite . toRawFilePath) =<< dirContents dir #endif tryNonAsync (removeDirectoryRecursive dir) >>= \case Right () -> return ()@@ -301,10 +301,10 @@ removeExportM :: RawFilePath -> Key -> ExportLocation -> Annex () removeExportM d _k loc = liftIO $ do- removeWhenExistsWith removeLink src+ removeWhenExistsWith R.removeLink src removeExportLocation d loc where- src = fromRawFilePath $ exportPath d loc+ src = exportPath d loc checkPresentExportM :: RawFilePath -> Key -> ExportLocation -> Annex Bool checkPresentExportM d _k loc =@@ -446,7 +446,7 @@ #ifndef mingw32_HOST_OS =<< getFdStatus fd #else- =<< getFileStatus f+ =<< R.getFileStatus f #endif guardSameContentIdentifiers cont cid currcid
Remote/Directory/LegacyChunked.hs view
@@ -22,6 +22,7 @@ import Annex.Tmp import Utility.Metered import Utility.Directory.Create+import qualified Utility.RawFilePath as R withCheckedFiles :: (FilePath -> IO Bool) -> FilePath -> (FilePath -> Key -> [FilePath]) -> Key -> ([FilePath] -> IO Bool) -> IO Bool withCheckedFiles _ [] _locations _ _ = return False@@ -98,15 +99,15 @@ retrieve :: (RawFilePath -> Key -> [RawFilePath]) -> RawFilePath -> Retriever retrieve locations d basek p c = withOtherTmp $ \tmpdir -> do showLongNote "This remote uses the deprecated chunksize setting. So this will be quite slow."- let tmp = fromRawFilePath $ - tmpdir P.</> keyFile basek <> ".directorylegacy.tmp"+ let tmp = tmpdir P.</> keyFile basek <> ".directorylegacy.tmp"+ let tmp' = fromRawFilePath tmp let go = \k sink -> do liftIO $ void $ withStoredFiles (fromRawFilePath d) (legacyLocations locations) k $ \fs -> do forM_ fs $- S.appendFile tmp <=< S.readFile+ S.appendFile tmp' <=< S.readFile return True- b <- liftIO $ L.readFile tmp- liftIO $ removeWhenExistsWith removeLink tmp+ b <- liftIO $ L.readFile tmp'+ liftIO $ removeWhenExistsWith R.removeLink tmp sink b byteRetriever go basek p c
Remote/GCrypt.hs view
@@ -487,7 +487,7 @@ extract Nothing = (Nothing, r) extract (Just r') = (fromConfigValue <$> Git.Config.getMaybe coreGCryptId r', r') -getConfigViaRsync :: Git.Repo -> RemoteGitConfig -> Annex (Either SomeException (Git.Repo, S.ByteString, S.ByteString))+getConfigViaRsync :: Git.Repo -> RemoteGitConfig -> Annex (Either SomeException (Git.Repo, S.ByteString, String)) getConfigViaRsync r gc = do (rsynctransport, rsyncurl, _) <- rsyncTransport r gc opts <- rsynctransport
Remote/Git.hs view
@@ -690,7 +690,7 @@ copier <- mkCopier hardlink st params let verify = Annex.Content.RemoteVerify r let rsp = RetrievalAllKeysSecure- res <- Annex.Content.getViaTmp rsp verify key $ \dest ->+ res <- Annex.Content.getViaTmp rsp verify key file $ \dest -> metered (Just (combineMeterUpdate meterupdate p)) key $ \_ p' -> copier object (fromRawFilePath dest) p' (liftIO checksuccessio) Annex.Content.saveState True
Remote/Helper/Special.hs view
@@ -51,6 +51,7 @@ import qualified Git import qualified Git.Construct import Git.Types+import qualified Utility.RawFilePath as R import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L@@ -284,10 +285,10 @@ withBytes content $ \b -> decrypt cmd c cipher (feedBytes b) $ readBytes write- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) (Nothing, _, FileContent f) -> do withBytes content write- liftIO $ removeWhenExistsWith removeLink f+ liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f) (Nothing, _, ByteContent b) -> write b where write b = case mh of
Remote/Helper/Ssh.hs view
@@ -28,7 +28,6 @@ import Control.Concurrent.STM import Control.Concurrent.Async-import qualified Data.ByteString as B toRepo :: ConsumeStdin -> Git.Repo -> RemoteGitConfig -> SshCommand -> Annex (FilePath, [CommandParam]) toRepo cs r gc remotecmd = do@@ -272,7 +271,7 @@ , P2P.connIdent = P2P.ConnIdent $ Just $ "ssh connection " ++ show pidnum }- stderrhandlerst <- newStderrHandler err+ stderrhandlerst <- newStderrHandler err pid runst <- P2P.mkRunState P2P.Client let c = P2P.OpenConnection (runst, conn, pid, stderrhandlerst) -- When the connection is successful, the remote@@ -301,32 +300,24 @@ modifyTVar' connpool $ maybe (Just P2PSshUnsupported) Just -newStderrHandler :: Handle -> IO (TVar StderrHandlerState)-newStderrHandler errh = do+newStderrHandler :: Handle -> ProcessHandle -> IO (TVar StderrHandlerState)+newStderrHandler errh ph = do -- stderr from git-annex-shell p2pstdio is initially discarded -- because old versions don't support the command. Once it's known -- to be running, this is changed to DisplayStderr. v <- newTVarIO DiscardStderr- p <- async $ go v- void $ async $ ender p v+ void $ async $ go v return v where go v = do- l <- B.hGetLine errh- atomically (readTVar v) >>= \case- DiscardStderr -> go v- DisplayStderr -> do- B.hPut stderr l- go v- EndStderrHandler -> return ()- - ender p v = do- atomically $ do- readTVar v >>= \case- EndStderrHandler -> return ()- _ -> retry- hClose errh- cancel p+ hGetLineUntilExitOrEOF ph errh >>= \case+ Nothing -> hClose errh+ Just l -> atomically (readTVar v) >>= \case+ DiscardStderr -> go v+ DisplayStderr -> do+ hPutStrLn stderr l+ go v+ EndStderrHandler -> hClose errh -- Runs a P2P Proto action on a remote when it supports that, -- otherwise the fallback action.
Test.hs view
@@ -247,15 +247,14 @@ d <- newmainrepodir setmainrepodir d innewrepo $ do- git_annex "init" [reponame, "--quiet"]- @? "init failed"+ git_annex "init" [reponame, "--quiet"] "init" preinitremote git_annex "initremote" ([ remotename , "type=" ++ remotetype , "--quiet" ] ++ config)- @? "init failed"+ "init" r <- annexeval $ either error return =<< Remote.byName' remotename cache <- Command.TestRemote.newRemoteVariantCache@@ -368,7 +367,7 @@ test_init :: Assertion test_init = innewrepo $ do ver <- annexVersion <$> getTestMode- git_annex "init" [reponame, "--version", show (fromRepoVersion ver)] @? "init failed"+ git_annex "init" [reponame, "--version", show (fromRepoVersion ver)] "init" setupTestMode where reponame = "test repo"@@ -378,62 +377,62 @@ test_add :: Assertion test_add = inmainrepo $ do writecontent annexedfile $ content annexedfile- add_annex annexedfile @? "add failed"+ add_annex annexedfile "add" annexed_present annexedfile writecontent sha1annexedfile $ content sha1annexedfile- git_annex "add" [sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"+ git_annex "add" [sha1annexedfile, "--backend=SHA1"] "add with SHA1" whenM (unlockedFiles <$> getTestMode) $- git_annex "unlock" [sha1annexedfile] @? "unlock failed"+ git_annex "unlock" [sha1annexedfile] "unlock" annexed_present sha1annexedfile checkbackend sha1annexedfile backendSHA1 writecontent ingitfile $ content ingitfile- boolSystem "git" [Param "add", File ingitfile] @? "git add failed"- boolSystem "git" [Param "commit", Param "-q", Param "-m", Param "commit"] @? "git commit failed"- git_annex "add" [ingitfile] @? "add ingitfile should be no-op"+ git "add" [ingitfile] "git add"+ git "commit" ["-q", "-m", "commit"] "git commit"+ git_annex "add" [ingitfile] "add ingitfile should be no-op" unannexed ingitfile test_add_dup :: Assertion test_add_dup = intmpclonerepo $ do writecontent annexedfiledup $ content annexedfiledup- add_annex annexedfiledup @? "add of second file with same content failed"+ add_annex annexedfiledup "add of second file with same content failed" annexed_present annexedfiledup annexed_present annexedfile test_add_extras :: Assertion test_add_extras = intmpclonerepo $ do writecontent wormannexedfile $ content wormannexedfile- git_annex "add" [wormannexedfile, "--backend=WORM"] @? "add with WORM failed"+ git_annex "add" [wormannexedfile, "--backend=WORM"] "add with WORM" whenM (unlockedFiles <$> getTestMode) $- git_annex "unlock" [wormannexedfile] @? "unlock failed"+ git_annex "unlock" [wormannexedfile] "unlock" annexed_present wormannexedfile checkbackend wormannexedfile backendWORM test_ignore_deleted_files :: Assertion test_ignore_deleted_files = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get failed"+ git_annex "get" [annexedfile] "get" git_annex_expectoutput "find" [] [annexedfile]- removeWhenExistsWith removeLink annexedfile+ removeWhenExistsWith R.removeLink (toRawFilePath annexedfile) -- A file that has been deleted, but the deletion not staged, -- is a special case; make sure git-annex skips these. git_annex_expectoutput "find" [] [] test_metadata :: Assertion test_metadata = intmpclonerepo $ do- git_annex "metadata" ["-s", "foo=bar", annexedfile] @? "set metadata"+ git_annex "metadata" ["-s", "foo=bar", annexedfile] "set metadata" git_annex_expectoutput "find" ["--metadata", "foo=bar"] [annexedfile] git_annex_expectoutput "find" ["--metadata", "foo=other"] [] writecontent annexedfiledup $ content annexedfiledup- add_annex annexedfiledup @? "add of second file with same content failed"+ add_annex annexedfiledup "add of second file with same content failed" annexed_present annexedfiledup git_annex_expectoutput "find" ["--metadata", "foo=bar"] [annexedfile, annexedfiledup] git_annex_shouldfail "metadata" ["--remove", "foo", "."]- @? "removing metadata from dir with multiple files failed to fail"+ "removing metadata from dir with multiple files not allowed" git_annex "metadata" ["--remove", "foo", annexedfile]- @? "removing metadata failed"+ "removing metadata" git_annex_expectoutput "find" ["--metadata", "foo=bar"] [] git_annex "metadata" ["--force", "-s", "foo=bar", "."]- @? "recursively set metadata force"+ "recursively set metadata force" test_shared_clone :: Assertion test_shared_clone = intmpsharedclonerepo $ do@@ -448,30 +447,27 @@ test_log :: Assertion test_log = intmpclonerepo $ do- git_annex "log" [annexedfile] @? "log failed"+ git_annex "log" [annexedfile] "log" test_view :: Assertion test_view = intmpclonerepo $ do- git_annex "metadata" ["-s", "test=test1", annexedfile] @? "metadata failed"- git_annex "metadata" ["-s", "test=test2", sha1annexedfile] @? "metadata failed"- git_annex "view" ["test=test1"] @? "entering view failed"+ git_annex "metadata" ["-s", "test=test1", annexedfile] "metadata"+ git_annex "metadata" ["-s", "test=test2", sha1annexedfile] "metadata"+ git_annex "view" ["test=test1"] "entering view" checkexists annexedfile checkdoesnotexist sha1annexedfile test_magic :: Assertion test_magic = intmpclonerepo $ do #ifdef WITH_MAGICMIME- boolSystem "git"- [ Param "config"- , Param "annex.largefiles"- , Param "mimeencoding=binary"- ] @? "git config annex.largefiles failed"+ git "config" ["annex.largefiles", "mimeencoding=binary"]+ "git config annex.largefiles" writeFile "binary" "\127" writeFile "text" "test\n" git_annex "add" ["binary", "text"]- @? "git-annex add failed with mimeencoding in largefiles"+ "git-annex add with mimeencoding in largefiles" git_annex "sync" []- @? "git-annex sync failed"+ "git-annex sync" (isJust <$> annexeval (Annex.CatFile.catKeyFile (encodeBS "binary"))) @? "binary file not added to annex despite mimeencoding config" (isNothing <$> annexeval (Annex.CatFile.catKeyFile (encodeBS "text")))@@ -483,40 +479,40 @@ test_import :: Assertion test_import = intmpclonerepo $ Utility.Tmp.Dir.withTmpDir "importtest" $ \importdir -> do (toimport1, importf1, imported1) <- mktoimport importdir "import1"- git_annex "import" [toimport1] @? "import failed"+ git_annex "import" [toimport1] "import" annexed_present_imported imported1 checkdoesnotexist importf1 (toimport2, importf2, imported2) <- mktoimport importdir "import2"- git_annex "import" [toimport2] @? "import of duplicate failed"+ git_annex "import" [toimport2] "import of duplicate" annexed_present_imported imported2 checkdoesnotexist importf2 (toimport3, importf3, imported3) <- mktoimport importdir "import3" git_annex "import" ["--skip-duplicates", toimport3]- @? "import of duplicate with --skip-duplicates failed"+ "import of duplicate with --skip-duplicates" checkdoesnotexist imported3 checkexists importf3 git_annex "import" ["--clean-duplicates", toimport3]- @? "import of duplicate with --clean-duplicates failed"+ "import of duplicate with --clean-duplicates" checkdoesnotexist imported3 checkdoesnotexist importf3 (toimport4, importf4, imported4) <- mktoimport importdir "import4"- git_annex "import" ["--deduplicate", toimport4] @? "import --deduplicate failed"+ git_annex "import" ["--deduplicate", toimport4] "import --deduplicate" checkdoesnotexist imported4 checkdoesnotexist importf4 (toimport5, importf5, imported5) <- mktoimport importdir "import5"- git_annex "import" ["--duplicate", toimport5] @? "import --duplicate failed"+ git_annex "import" ["--duplicate", toimport5] "import --duplicate" annexed_present_imported imported5 checkexists importf5 - git_annex "drop" ["--force", imported1, imported2, imported5] @? "drop failed"+ git_annex "drop" ["--force", imported1, imported2, imported5] "drop" annexed_notpresent_imported imported2 (toimportdup, importfdup, importeddup) <- mktoimport importdir "importdup" git_annex_shouldfail "import" ["--clean-duplicates", toimportdup] - @? "import of missing duplicate with --clean-duplicates failed to fail"+ "import of missing duplicate with --clean-duplicates not allowed" checkdoesnotexist importeddup checkexists importfdup where@@ -528,16 +524,16 @@ test_reinject :: Assertion test_reinject = intmpclonerepo $ do- git_annex "drop" ["--force", sha1annexedfile] @? "drop failed"+ git_annex "drop" ["--force", sha1annexedfile] "drop" annexed_notpresent sha1annexedfile writecontent tmp $ content sha1annexedfile key <- Key.serializeKey <$> getKey backendSHA1 tmp- git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed"+ git_annex "reinject" [tmp, sha1annexedfile] "reinject" annexed_present sha1annexedfile -- fromkey can't be used on a crippled filesystem, since it makes a -- symlink unlessM (annexeval Config.crippledFileSystem) $ do- git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed for dup"+ git_annex "fromkey" [key, sha1annexedfiledup] "fromkey for dup" annexed_present_locked sha1annexedfiledup where tmp = "tmpfile"@@ -545,41 +541,40 @@ test_unannex_nocopy :: Assertion test_unannex_nocopy = intmpclonerepo $ do annexed_notpresent annexedfile- git_annex "unannex" [annexedfile] @? "unannex failed with no copy"+ git_annex "unannex" [annexedfile] "unannex with no copy" annexed_notpresent annexedfile test_unannex_withcopy :: Assertion test_unannex_withcopy = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get failed"+ git_annex "get" [annexedfile] "get" annexed_present annexedfile- git_annex "unannex" [annexedfile, sha1annexedfile] @? "unannex failed"+ git_annex "unannex" [annexedfile, sha1annexedfile] "unannex" unannexed annexedfile- git_annex "unannex" [annexedfile] @? "unannex failed on non-annexed file"+ git_annex "unannex" [annexedfile] "unannex on non-annexed file" unannexed annexedfile- git_annex "unannex" [ingitfile] @? "unannex ingitfile should be no-op"+ git_annex "unannex" [ingitfile] "unannex ingitfile should be no-op" unannexed ingitfile test_drop_noremote :: Assertion test_drop_noremote = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get failed"- boolSystem "git" [Param "remote", Param "rm", Param "origin"]- @? "git remote rm origin failed"- git_annex_shouldfail "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of file"+ git_annex "get" [annexedfile] "get"+ git "remote" ["rm", "origin"] "git remote rm origin"+ git_annex_shouldfail "drop" [annexedfile] "drop with no known copy of file not allowed" annexed_present annexedfile- git_annex "drop" ["--force", annexedfile] @? "drop --force failed"+ git_annex "drop" ["--force", annexedfile] "drop --force" annexed_notpresent annexedfile- git_annex "drop" [annexedfile] @? "drop of dropped file failed"- git_annex "drop" [ingitfile] @? "drop ingitfile should be no-op"+ git_annex "drop" [annexedfile] "drop of dropped file"+ git_annex "drop" [ingitfile] "drop ingitfile should be no-op" unannexed ingitfile test_drop_withremote :: Assertion test_drop_withremote = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get failed"+ git_annex "get" [annexedfile] "get" annexed_present annexedfile- git_annex "numcopies" ["2"] @? "numcopies config failed"- git_annex_shouldfail "drop" [annexedfile] @? "drop succeeded although numcopies is not satisfied"- git_annex "numcopies" ["1"] @? "numcopies config failed"- git_annex "drop" [annexedfile] @? "drop failed though origin has copy"+ git_annex "numcopies" ["2"] "numcopies config"+ git_annex_shouldfail "drop" [annexedfile] "drop with numcopies not satisfied is not allowed"+ git_annex "numcopies" ["1"] "numcopies config"+ git_annex "drop" [annexedfile] "drop when origin has copy" annexed_notpresent annexedfile -- make sure that the correct symlink is staged for the file -- after drop@@ -588,10 +583,10 @@ test_drop_untrustedremote :: Assertion test_drop_untrustedremote = intmpclonerepo $ do- git_annex "untrust" ["origin"] @? "untrust of origin failed"- git_annex "get" [annexedfile] @? "get failed"+ git_annex "untrust" ["origin"] "untrust of origin"+ git_annex "get" [annexedfile] "get" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile] @? "drop wrongly succeeded with only an untrusted copy of the file"+ git_annex_shouldfail "drop" [annexedfile] "drop with only an untrusted copy of the file should fail" annexed_present annexedfile inmainrepo $ annexed_present annexedfile @@ -610,15 +605,15 @@ test_get' setup = setup $ do inmainrepo $ annexed_present annexedfile annexed_notpresent annexedfile- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" inmainrepo $ annexed_present annexedfile annexed_present annexedfile- git_annex "get" [annexedfile] @? "get of file already here failed"+ git_annex "get" [annexedfile] "get of file already here" inmainrepo $ annexed_present annexedfile annexed_present annexedfile inmainrepo $ unannexed ingitfile unannexed ingitfile- git_annex "get" [ingitfile] @? "get ingitfile should be no-op"+ git_annex "get" [ingitfile] "get ingitfile should be no-op" inmainrepo $ unannexed ingitfile unannexed ingitfile @@ -637,63 +632,63 @@ test_move' setup = setup $ do annexed_notpresent annexedfile inmainrepo $ annexed_present annexedfile- git_annex "move" ["--from", "origin", annexedfile] @? "move --from of file failed"+ git_annex "move" ["--from", "origin", annexedfile] "move --from of file" annexed_present annexedfile inmainrepo $ annexed_notpresent annexedfile- git_annex "move" ["--from", "origin", annexedfile] @? "move --from of file already here failed"+ git_annex "move" ["--from", "origin", annexedfile] "move --from of file already here" annexed_present annexedfile inmainrepo $ annexed_notpresent annexedfile- git_annex "move" ["--to", "origin", annexedfile] @? "move --to of file failed"+ git_annex "move" ["--to", "origin", annexedfile] "move --to of file" inmainrepo $ annexed_present annexedfile annexed_notpresent annexedfile- git_annex "move" ["--to", "origin", annexedfile] @? "move --to of file already there failed"+ git_annex "move" ["--to", "origin", annexedfile] "move --to of file already there" inmainrepo $ annexed_present annexedfile annexed_notpresent annexedfile unannexed ingitfile inmainrepo $ unannexed ingitfile- git_annex "move" ["--to", "origin", ingitfile] @? "move of ingitfile should be no-op"+ git_annex "move" ["--to", "origin", ingitfile] "move of ingitfile should be no-op" unannexed ingitfile inmainrepo $ unannexed ingitfile- git_annex "move" ["--from", "origin", ingitfile] @? "move of ingitfile should be no-op"+ git_annex "move" ["--from", "origin", ingitfile] "move of ingitfile should be no-op" unannexed ingitfile inmainrepo $ unannexed ingitfile test_move_numcopies :: Assertion test_move_numcopies = intmpclonerepo $ do inmainrepo $ annexed_present annexedfile- git_annex "numcopies" ["2"] @? "setting numcopies failed"- git_annex "move" ["--from", "origin", annexedfile] @? "move of file --from remote with numcopies unsatisfied but not made worse failed unexpectedly"+ git_annex "numcopies" ["2"] "setting numcopies"+ git_annex "move" ["--from", "origin", annexedfile] "move of file --from remote with numcopies unsatisfied but not made worse" annexed_present annexedfile inmainrepo $ annexed_notpresent annexedfile- git_annex "move" ["--to", "origin", annexedfile] @? "move of file --to remote with numcopies unsatisfied but not made worse failed unexpectedly"+ git_annex "move" ["--to", "origin", annexedfile] "move of file --to remote with numcopies unsatisfied but not made worse" annexed_notpresent annexedfile inmainrepo $ annexed_present annexedfile- git_annex "get" [annexedfile] @? "get of file failed"- git_annex_shouldfail "move" ["--from", "origin", annexedfile] @? "move of file --from remote violated numcopies setting"- git_annex_shouldfail "move" ["--to", "origin", annexedfile] @? "move of file --to remote violated numcopies setting"+ git_annex "get" [annexedfile] "get of file"+ git_annex_shouldfail "move" ["--from", "origin", annexedfile] "move of file --from remote that violates numcopies setting not allowd"+ git_annex_shouldfail "move" ["--to", "origin", annexedfile] "move of file --to remote that violates numcopies setting not allowed" test_copy :: Assertion test_copy = intmpclonerepo $ do annexed_notpresent annexedfile inmainrepo $ annexed_present annexedfile- git_annex "copy" ["--from", "origin", annexedfile] @? "copy --from of file failed"+ git_annex "copy" ["--from", "origin", annexedfile] "copy --from of file" annexed_present annexedfile inmainrepo $ annexed_present annexedfile- git_annex "copy" ["--from", "origin", annexedfile] @? "copy --from of file already here failed"+ git_annex "copy" ["--from", "origin", annexedfile] "copy --from of file already here" annexed_present annexedfile inmainrepo $ annexed_present annexedfile- git_annex "copy" ["--to", "origin", annexedfile] @? "copy --to of file already there failed"+ git_annex "copy" ["--to", "origin", annexedfile] "copy --to of file already there" annexed_present annexedfile inmainrepo $ annexed_present annexedfile- git_annex "move" ["--to", "origin", annexedfile] @? "move --to of file already there failed"+ git_annex "move" ["--to", "origin", annexedfile] "move --to of file already there" annexed_notpresent annexedfile inmainrepo $ annexed_present annexedfile unannexed ingitfile inmainrepo $ unannexed ingitfile- git_annex "copy" ["--to", "origin", ingitfile] @? "copy of ingitfile should be no-op"+ git_annex "copy" ["--to", "origin", ingitfile] "copy of ingitfile should be no-op" unannexed ingitfile inmainrepo $ unannexed ingitfile- git_annex "copy" ["--from", "origin", ingitfile] @? "copy of ingitfile should be no-op"+ git_annex "copy" ["--from", "origin", ingitfile] "copy of ingitfile should be no-op" checkregularfile ingitfile checkcontent ingitfile @@ -702,40 +697,40 @@ annexed_notpresent annexedfile -- get/copy --auto looks only at numcopies when preferred content is not -- set, and with 1 copy existing, does not get the file.- git_annex "get" ["--auto", annexedfile] @? "get --auto of file failed with default preferred content"+ git_annex "get" ["--auto", annexedfile] "get --auto of file with default preferred content" annexed_notpresent annexedfile- git_annex "copy" ["--from", "origin", "--auto", annexedfile] @? "copy --auto --from of file failed with default preferred content"+ git_annex "copy" ["--from", "origin", "--auto", annexedfile] "copy --auto --from of file with default preferred content" annexed_notpresent annexedfile - git_annex "wanted" [".", "standard"] @? "set expression to standard failed"- git_annex "group" [".", "client"] @? "set group to standard failed"- git_annex "get" ["--auto", annexedfile] @? "get --auto of file failed for client"+ git_annex "wanted" [".", "standard"] "set expression to standard"+ git_annex "group" [".", "client"] "set group to standard"+ git_annex "get" ["--auto", annexedfile] "get --auto of file for client" annexed_present annexedfile- git_annex "drop" [annexedfile] @? "drop of file failed"- git_annex "copy" ["--from", "origin", "--auto", annexedfile] @? "copy --auto --from of file failed for client"+ git_annex "drop" [annexedfile] "drop of file"+ git_annex "copy" ["--from", "origin", "--auto", annexedfile] "copy --auto --from of file for client" annexed_present annexedfile- git_annex "ungroup" [".", "client"] @? "ungroup failed"+ git_annex "ungroup" [".", "client"] "ungroup" - git_annex "wanted" [".", "standard"] @? "set expression to standard failed"- git_annex "group" [".", "manual"] @? "set group to manual failed"+ git_annex "wanted" [".", "standard"] "set expression to standard"+ git_annex "group" [".", "manual"] "set group to manual" -- drop --auto with manual leaves the file where it is- git_annex "drop" ["--auto", annexedfile] @? "drop --auto of file failed with manual preferred content"+ git_annex "drop" ["--auto", annexedfile] "drop --auto of file with manual preferred content" annexed_present annexedfile- git_annex "drop" [annexedfile] @? "drop of file failed"+ git_annex "drop" [annexedfile] "drop of file" annexed_notpresent annexedfile -- copy/get --auto with manual does not get the file- git_annex "get" ["--auto", annexedfile] @? "get --auto of file failed with manual preferred content"+ git_annex "get" ["--auto", annexedfile] "get --auto of file with manual preferred content" annexed_notpresent annexedfile- git_annex "copy" ["--from", "origin", "--auto", annexedfile] @? "copy --auto --from of file failed with manual preferred content"+ git_annex "copy" ["--from", "origin", "--auto", annexedfile] "copy --auto --from of file with manual preferred content" annexed_notpresent annexedfile- git_annex "ungroup" [".", "client"] @? "ungroup failed"+ git_annex "ungroup" [".", "client"] "ungroup" - git_annex "wanted" [".", "exclude=*"] @? "set expression to exclude=* failed"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "wanted" [".", "exclude=*"] "set expression to exclude=*"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "drop" ["--auto", annexedfile] @? "drop --auto of file failed with exclude=*"+ git_annex "drop" ["--auto", annexedfile] "drop --auto of file with exclude=*" annexed_notpresent annexedfile- git_annex "get" ["--auto", annexedfile] @? "get --auto of file failed with exclude=*"+ git_annex "get" ["--auto", annexedfile] "get --auto of file with exclude=*" annexed_notpresent annexedfile test_lock :: Assertion@@ -745,41 +740,41 @@ -- regression test: unlock of newly added, not committed file -- should not fail. writecontent "newfile" "foo"- git_annex "add" ["newfile"] @? "add new file failed"- git_annex "unlock" ["newfile"] @? "unlock failed on newly added, never committed file"+ git_annex "add" ["newfile"] "add new file"+ git_annex "unlock" ["newfile"] "unlock on newly added, never committed file" - git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "unlock" [annexedfile] @? "unlock failed" + git_annex "unlock" [annexedfile] "unlock" unannexed annexedfile -- write different content, to verify that lock -- throws it away changecontent annexedfile writecontent annexedfile $ content annexedfile ++ "foo"- git_annex_shouldfail "lock" [annexedfile] @? "lock failed to fail without --force"- git_annex "lock" ["--force", annexedfile] @? "lock --force failed"+ git_annex_shouldfail "lock" [annexedfile] "lock without --force should not be allowed"+ git_annex "lock" ["--force", annexedfile] "lock --force" -- The original content of an unlocked file is not always -- preserved after modification, so re-get it.- git_annex "get" [annexedfile] @? "get of file failed after lock --force"+ git_annex "get" [annexedfile] "get of file after lock --force" annexed_present_locked annexedfile- git_annex "unlock" [annexedfile] @? "unlock failed" + git_annex "unlock" [annexedfile] "unlock" unannexed annexedfile changecontent annexedfile- boolSystem "git" [Param "add", Param annexedfile] @? "add of modified file failed"+ git "add" [annexedfile] "add of modified file" runchecks [checkregularfile, checkwritable] annexedfile c <- readFile annexedfile assertEqual "content of modified file" c (changedcontent annexedfile)- r' <- git_annex "drop" [annexedfile]- not r' @? "drop wrongly succeeded with no known copy of modified file"+ git_annex_shouldfail "drop" [annexedfile]+ "drop with no known copy of modified file should not be allowed" -- Regression test: lock --force when work tree file -- was modified lost the (unmodified) annex object. -- (Only occurred when the keys database was out of sync.) test_lock_force :: Assertion test_lock_force = intmpclonerepo $ do- git_annex "upgrade" [] @? "upgrade failed"- git_annex "get" [annexedfile] @? "get of file failed"- git_annex "unlock" [annexedfile] @? "unlock failed"+ git_annex "upgrade" [] "upgrade"+ git_annex "get" [annexedfile] "get of file"+ git_annex "unlock" [annexedfile] "unlock" annexeval $ do Just k <- Annex.WorkTree.lookupKey (toRawFilePath annexedfile) Database.Keys.removeInodeCaches k@@ -787,8 +782,8 @@ liftIO . removeWhenExistsWith R.removeLink =<< Annex.fromRepo Annex.Locations.gitAnnexKeysDbIndexCache writecontent annexedfile "test_lock_force content"- git_annex_shouldfail "lock" [annexedfile] @? "lock of modified file failed to fail"- git_annex "lock" ["--force", annexedfile] @? "lock --force of modified file failed"+ git_annex_shouldfail "lock" [annexedfile] "lock of modified file should not be allowed"+ git_annex "lock" ["--force", annexedfile] "lock --force of modified file" annexed_present_locked annexedfile test_edit :: Assertion@@ -799,45 +794,41 @@ test_edit' :: Bool -> Assertion test_edit' precommit = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "edit" [annexedfile] @? "edit failed"+ git_annex "edit" [annexedfile] "edit" unannexed annexedfile changecontent annexedfile- boolSystem "git" [Param "add", File annexedfile]- @? "git add of edited file failed"+ git "add" [annexedfile] "git add of edited file" if precommit- then git_annex "pre-commit" []- @? "pre-commit failed"- else boolSystem "git" [Param "commit", Param "-q", Param "-m", Param "contentchanged"]- @? "git commit of edited file failed"+ then git_annex "pre-commit" [] "pre-commit"+ else git "commit" ["-q", "-m", "contentchanged"] "git commit of edited file" runchecks [checkregularfile, checkwritable] annexedfile c <- readFile annexedfile assertEqual "content of modified file" c (changedcontent annexedfile)- git_annex_shouldfail "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file"+ git_annex_shouldfail "drop" [annexedfile] "drop no known copy of modified file should not be allowed" test_partial_commit :: Assertion test_partial_commit = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "unlock" [annexedfile] @? "unlock failed"+ git_annex "unlock" [annexedfile] "unlock" changecontent annexedfile- boolSystem "git" [Param "commit", Param "-q", Param "-m", Param "test", File annexedfile]- @? "partial commit of unlocked file should be allowed"+ git "commit" ["-q", "-m", "test", annexedfile]+ "partial commit of unlocked file should be allowed" test_fix :: Assertion test_fix = intmpclonerepo $ unlessM (hasUnlockedFiles <$> getTestMode) $ do annexed_notpresent annexedfile- git_annex "fix" [annexedfile] @? "fix of not present failed"+ git_annex "fix" [annexedfile] "fix of not present" annexed_notpresent annexedfile- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "fix" [annexedfile] @? "fix of present file failed"+ git_annex "fix" [annexedfile] "fix of present file" annexed_present annexedfile createDirectory subdir- boolSystem "git" [Param "mv", File annexedfile, File subdir]- @? "git mv failed"- git_annex "fix" [newfile] @? "fix of moved file failed"+ git "mv" [annexedfile, subdir] "git mv"+ git_annex "fix" [newfile] "fix of moved file" runchecks [checklink, checkunwritable] newfile c <- readFile newfile assertEqual "content of moved file" c (content annexedfile)@@ -847,21 +838,21 @@ test_trust :: Assertion test_trust = intmpclonerepo $ do- git_annex "trust" [repo] @? "trust failed"+ git_annex "trust" [repo] "trust" trustcheck Logs.Trust.Trusted "trusted 1"- git_annex "trust" [repo] @? "trust of trusted failed"+ git_annex "trust" [repo] "trust of trusted" trustcheck Logs.Trust.Trusted "trusted 2"- git_annex "untrust" [repo] @? "untrust failed"+ git_annex "untrust" [repo] "untrust" trustcheck Logs.Trust.UnTrusted "untrusted 1"- git_annex "untrust" [repo] @? "untrust of untrusted failed"+ git_annex "untrust" [repo] "untrust of untrusted" trustcheck Logs.Trust.UnTrusted "untrusted 2"- git_annex "dead" [repo] @? "dead failed"+ git_annex "dead" [repo] "dead" trustcheck Logs.Trust.DeadTrusted "deadtrusted 1"- git_annex "dead" [repo] @? "dead of dead failed"+ git_annex "dead" [repo] "dead of dead" trustcheck Logs.Trust.DeadTrusted "deadtrusted 2"- git_annex "semitrust" [repo] @? "semitrust failed"+ git_annex "semitrust" [repo] "semitrust" trustcheck Logs.Trust.SemiTrusted "semitrusted 1"- git_annex "semitrust" [repo] @? "semitrust of semitrusted failed"+ git_annex "semitrust" [repo] "semitrust of semitrusted" trustcheck Logs.Trust.SemiTrusted "semitrusted 2" where repo = "origin"@@ -874,52 +865,52 @@ test_fsck_basic :: Assertion test_fsck_basic = intmpclonerepo $ do- git_annex "fsck" [] @? "fsck failed"- git_annex "numcopies" ["2"] @? "numcopies config failed"+ git_annex "fsck" [] "fsck"+ git_annex "numcopies" ["2"] "numcopies config" fsck_should_fail "numcopies unsatisfied"- git_annex "numcopies" ["1"] @? "numcopies config failed"+ git_annex "numcopies" ["1"] "numcopies config" corrupt annexedfile corrupt sha1annexedfile where corrupt f = do- git_annex "get" [f] @? "get of file failed"+ git_annex "get" [f] "get of file" Utility.FileMode.allowWrite (toRawFilePath f) writecontent f (changedcontent f) ifM (hasUnlockedFiles <$> getTestMode)- ( git_annex "fsck" [] @? "fsck failed on unlocked file with changed file content"- , git_annex_shouldfail "fsck" [] @? "fsck failed to fail with corrupted file content"+ ( git_annex "fsck" []"fsck on unlocked file with changed file content"+ , git_annex_shouldfail "fsck" [] "fsck with corrupted file content should error" )- git_annex "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f+ git_annex "fsck" [] "second fsck, after first fsck should have fixed problem" test_fsck_bare :: Assertion test_fsck_bare = intmpbareclonerepo $- git_annex "fsck" [] @? "fsck failed"+ git_annex "fsck" [] "fsck" test_fsck_localuntrusted :: Assertion test_fsck_localuntrusted = intmpclonerepo $ do- git_annex "get" [annexedfile] @? "get failed"- git_annex "untrust" ["origin"] @? "untrust of origin repo failed"- git_annex "untrust" ["."] @? "untrust of current repo failed"+ git_annex "get" [annexedfile] "get"+ git_annex "untrust" ["origin"] "untrust of origin repo"+ git_annex "untrust" ["."] "untrust of current repo" fsck_should_fail "content only available in untrusted (current) repository"- git_annex "trust" ["."] @? "trust of current repo failed"- git_annex "fsck" [annexedfile] @? "fsck failed on file present in trusted repo"+ git_annex "trust" ["."] "trust of current repo"+ git_annex "fsck" [annexedfile] "fsck on file present in trusted repo" test_fsck_remoteuntrusted :: Assertion test_fsck_remoteuntrusted = intmpclonerepo $ do- git_annex "numcopies" ["2"] @? "numcopies config failed"- git_annex "get" [annexedfile] @? "get failed"- git_annex "get" [sha1annexedfile] @? "get failed"- git_annex "fsck" [] @? "fsck failed with numcopies=2 and 2 copies"- git_annex "untrust" ["origin"] @? "untrust of origin failed"+ git_annex "numcopies" ["2"] "numcopies config"+ git_annex "get" [annexedfile] "get"+ git_annex "get" [sha1annexedfile] "get"+ git_annex "fsck" [] "fsck with numcopies=2 and 2 copies"+ git_annex "untrust" ["origin"] "untrust of origin" fsck_should_fail "content not replicated to enough non-untrusted repositories" test_fsck_fromremote :: Assertion test_fsck_fromremote = intmpclonerepo $ do- git_annex "fsck" ["--from", "origin"] @? "fsck --from origin failed"+ git_annex "fsck" ["--from", "origin"] "fsck --from origin" fsck_should_fail :: String -> Assertion fsck_should_fail m = git_annex_shouldfail "fsck" []- @? "fsck failed to fail with " ++ m+ ("fsck should not succeed with " ++ m) test_migrate :: Assertion test_migrate = test_migrate' False@@ -931,24 +922,24 @@ test_migrate' usegitattributes = intmpclonerepo $ do annexed_notpresent annexedfile annexed_notpresent sha1annexedfile- git_annex "migrate" [annexedfile] @? "migrate of not present failed"- git_annex "migrate" [sha1annexedfile] @? "migrate of not present failed"- git_annex "get" [annexedfile] @? "get of file failed"- git_annex "get" [sha1annexedfile] @? "get of file failed"+ git_annex "migrate" [annexedfile] "migrate of not present"+ git_annex "migrate" [sha1annexedfile] "migrate of not present"+ git_annex "get" [annexedfile] "get of file"+ git_annex "get" [sha1annexedfile] "get of file" annexed_present annexedfile annexed_present sha1annexedfile if usegitattributes then do writeFile ".gitattributes" "* annex.backend=SHA1" git_annex "migrate" [sha1annexedfile]- @? "migrate sha1annexedfile failed"+ "migrate sha1annexedfile" git_annex "migrate" [annexedfile]- @? "migrate annexedfile failed"+ "migrate annexedfile" else do git_annex "migrate" [sha1annexedfile, "--backend", "SHA1"]- @? "migrate sha1annexedfile failed"+ "migrate sha1annexedfile" git_annex "migrate" [annexedfile, "--backend", "SHA1"]- @? "migrate annexedfile failed"+ "migrate annexedfile" annexed_present annexedfile annexed_present sha1annexedfile checkbackend annexedfile backendSHA1@@ -956,10 +947,8 @@ -- check that reversing a migration works writeFile ".gitattributes" "* annex.backend=SHA256"- git_annex "migrate" [sha1annexedfile]- @? "migrate sha1annexedfile failed"- git_annex "migrate" [annexedfile]- @? "migrate annexedfile failed"+ git_annex "migrate" [sha1annexedfile] "migrate sha1annexedfile"+ git_annex "migrate" [annexedfile] "migrate annexedfile" annexed_present annexedfile annexed_present sha1annexedfile checkbackend annexedfile backendSHA256@@ -968,33 +957,32 @@ test_unused :: Assertion test_unused = intmpclonerepo $ do checkunused [] "in new clone"- git_annex "get" [annexedfile] @? "get of file failed"- git_annex "get" [sha1annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file"+ git_annex "get" [sha1annexedfile] "get of file" annexedfilekey <- getKey backendSHA256E annexedfile sha1annexedfilekey <- getKey backendSHA1 sha1annexedfile checkunused [] "after get"- boolSystem "git" [Param "rm", Param "-fq", File annexedfile] @? "git rm failed"+ git "rm" ["-fq", annexedfile] "git rm" checkunused [] "after rm" -- commit the rm, and when on an adjusted branch, sync it back to -- the master branch- git_annex "sync" ["--no-push", "--no-pull"] @? "git-annex sync failed"+ git_annex "sync" ["--no-push", "--no-pull"] "git-annex sync" checkunused [] "after commit" -- unused checks origin/master; once it's gone it is really unused- boolSystem "git" [Param "remote", Param "rm", Param "origin"] @? "git remote rm origin failed"+ git "remote" ["rm", "origin"] "git remote rm origin" checkunused [annexedfilekey] "after origin branches are gone"- boolSystem "git" [Param "rm", Param "-fq", File sha1annexedfile] @? "git rm failed"- git_annex "sync" ["--no-push", "--no-pull"] @? "git-annex sync failed"+ git "rm" ["-fq", sha1annexedfile] "git rm"+ git_annex "sync" ["--no-push", "--no-pull"] "git-annex sync" checkunused [annexedfilekey, sha1annexedfilekey] "after rm sha1annexedfile" -- good opportunity to test dropkey also- git_annex "dropkey" ["--force", Key.serializeKey annexedfilekey]- @? "dropkey failed"+ git_annex "dropkey" ["--force", Key.serializeKey annexedfilekey] "dropkey" checkunused [sha1annexedfilekey] ("after dropkey --force " ++ Key.serializeKey annexedfilekey) - git_annex_shouldfail "dropunused" ["1"] @? "dropunused failed to fail without --force"- git_annex "dropunused" ["--force", "1"] @? "dropunused failed"+ git_annex_shouldfail "dropunused" ["1"] "dropunused should not be allowed without --force"+ git_annex "dropunused" ["--force", "1"] "dropunused" checkunused [] "after dropunused"- git_annex_shouldfail "dropunused" ["--force", "10", "501"] @? "dropunused failed to fail on bogus numbers"+ git_annex_shouldfail "dropunused" ["--force", "10", "501"] "dropunused on bogus numbers" -- Unused used to miss renamed symlinks that were not staged -- and pointed at annexed content, and think that content was unused.@@ -1003,10 +991,10 @@ -- to associate it with the key. unlessM (hasUnlockedFiles <$> getTestMode) $ do writecontent "unusedfile" "unusedcontent"- git_annex "add" ["unusedfile"] @? "add of unusedfile failed"+ git_annex "add" ["unusedfile"] "add of unusedfile" unusedfilekey <- getKey backendSHA256E "unusedfile" renameFile "unusedfile" "unusedunstagedfile"- boolSystem "git" [Param "rm", Param "-qf", File "unusedfile"] @? "git rm failed"+ git "rm" ["-qf", "unusedfile"] "git rm" checkunused [] "with unstaged link" removeFile "unusedunstagedfile" checkunused [unusedfilekey] "with renamed link deleted"@@ -1014,18 +1002,18 @@ -- unused used to miss symlinks that were deleted or modified -- manually writecontent "unusedfile" "unusedcontent"- git_annex "add" ["unusedfile"] @? "add of unusedfile failed"- boolSystem "git" [Param "add", File "unusedfile"] @? "git add failed"+ git_annex "add" ["unusedfile"] "add of unusedfile"+ git "add" ["unusedfile"] "git add" unusedfilekey' <- getKey backendSHA256E "unusedfile" checkunused [] "with staged deleted link"- boolSystem "git" [Param "rm", Param "-qf", File "unusedfile"] @? "git rm failed"+ git "rm" ["-qf", "unusedfile"] "git rm" checkunused [unusedfilekey'] "with staged link deleted" -- unused used to false positive on symlinks that were -- deleted or modified manually, but not staged as such writecontent "unusedfile" "unusedcontent"- git_annex "add" ["unusedfile"] @? "add of unusedfile failed"- boolSystem "git" [Param "add", File "unusedfile"] @? "git add failed"+ git_annex "add" ["unusedfile"] "add of unusedfile"+ git "add" ["unusedfile"] "git add" checkunused [] "with staged file" removeFile "unusedfile" checkunused [] "with staged deleted file"@@ -1036,17 +1024,17 @@ whenM (hasUnlockedFiles <$> getTestMode) $ do let f = "unlockedfile" writecontent f "unlockedcontent1"- boolSystem "git" [Param "add", File "unlockedfile"] @? "git add failed"+ git "add" ["unlockedfile"] "git add" checkunused [] "with unlocked file before modification" writecontent f "unlockedcontent2" checkunused [] "with unlocked file after modification"- not <$> boolSystem "git" [Param "diff", Param "--quiet", File f] @? "git diff did not show changes to unlocked file"+ git_shouldfail "diff" ["--quiet", f] "git diff should exit nonzero when unlocked file is modified" -- still nothing unused because one version is in the index -- and the other is in the work tree checkunused [] "with unlocked file after git diff" where checkunused expectedkeys desc = do- git_annex "unused" [] @? "unused failed"+ git_annex "unused" [] "unused" unusedmap <- annexeval $ Logs.Unused.readUnusedMap mempty let unusedkeys = M.elems unusedmap assertEqual ("unused keys differ " ++ desc)@@ -1054,14 +1042,14 @@ test_describe :: Assertion test_describe = intmpclonerepo $ do- git_annex "describe" [".", "this repo"] @? "describe 1 failed"- git_annex "describe" ["origin", "origin repo"] @? "describe 2 failed"+ git_annex "describe" [".", "this repo"] "describe 1"+ git_annex "describe" ["origin", "origin repo"] "describe 2" test_find :: Assertion test_find = intmpclonerepo $ do annexed_notpresent annexedfile git_annex_expectoutput "find" [] []- git_annex "get" [annexedfile] @? "get failed"+ git_annex "get" [annexedfile] "get" annexed_present annexedfile annexed_notpresent sha1annexedfile git_annex_expectoutput "find" [] [annexedfile]@@ -1076,13 +1064,13 @@ - and --exclude=* should exclude them. -} createDirectory "dir" writecontent "dir/subfile" "subfile"- git_annex "add" ["dir"] @? "add of subdir failed"+ git_annex "add" ["dir"] "add of subdir" git_annex_expectoutput "find" ["--include", "*", "--exclude", annexedfile, "--exclude", sha1annexedfile] ["dir/subfile"] git_annex_expectoutput "find" ["--exclude", "*"] [] test_merge :: Assertion test_merge = intmpclonerepo $- git_annex "merge" [] @? "merge failed"+ git_annex "merge" [] "merge" test_info :: Assertion test_info = intmpclonerepo $ do@@ -1093,22 +1081,22 @@ test_version :: Assertion test_version = intmpclonerepo $- git_annex "version" [] @? "version failed"+ git_annex "version" [] "version" test_sync :: Assertion test_sync = intmpclonerepo $ do- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" {- Regression test for bug fixed in - 039e83ed5d1a11fd562cce55b8429c840d72443e, where a present - wanted file was dropped. -}- git_annex "get" [annexedfile] @? "get failed"+ git_annex "get" [annexedfile] "get" git_annex_expectoutput "find" ["--in", "."] [annexedfile]- git_annex "wanted" [".", "present"] @? "wanted failed"- git_annex "sync" ["--content"] @? "sync failed"+ git_annex "wanted" [".", "present"] "wanted"+ git_annex "sync" ["--content"] "sync" git_annex_expectoutput "find" ["--in", "."] [annexedfile]- git_annex "drop" [annexedfile] @? "drop failed"+ git_annex "drop" [annexedfile] "drop" git_annex_expectoutput "find" ["--in", "."] []- git_annex "sync" ["--content"] @? "sync failed"+ git_annex "sync" ["--content"] "sync" git_annex_expectoutput "find" ["--in", "."] [] {- Regression test for the concurrency bug fixed in@@ -1118,23 +1106,22 @@ makedup dupfile -- This was sufficient currency to trigger the bug. git_annex "get" ["-J1", annexedfile, dupfile]- @? "concurrent get -J1 with dup failed"+ "concurrent get -J1 with dup" git_annex "drop" ["-J1"]- @? "drop with dup failed"+ "drop with dup" -- With -J2, one more dup file was needed to trigger the bug. makedup dupfile2 git_annex "get" ["-J2", annexedfile, dupfile, dupfile2]- @? "concurrent get -J2 with dup failed"+ "concurrent get -J2 with dup" git_annex "drop" ["-J2"]- @? "drop with dup failed"+ "drop with dup" where dupfile = annexedfile ++ "2" dupfile2 = annexedfile ++ "3" makedup f = do Utility.CopyFile.copyFileExternal Utility.CopyFile.CopyAllMetaData annexedfile f @? "copying annexed file failed"- boolSystem "git" [Param "add", File f]- @? "git add failed" + git "add" [f] "git add" {- Regression test for union merge bug fixed in - 0214e0fb175a608a49b812d81b4632c081f63027 -}@@ -1146,19 +1133,19 @@ withtmpclonerepo $ \r3 -> do forM_ [r1, r2, r3] $ \r -> indir r $ do when (r /= r1) $- boolSystem "git" [Param "remote", Param "add", Param "r1", File ("../../" ++ r1)] @? "remote add"+ git "remote" ["add", "r1", "../../" ++ r1] "remote add" when (r /= r2) $- boolSystem "git" [Param "remote", Param "add", Param "r2", File ("../../" ++ r2)] @? "remote add"+ git "remote" ["add", "r2", "../../" ++ r2] "remote add" when (r /= r3) $- boolSystem "git" [Param "remote", Param "add", Param "r3", File ("../../" ++ r3)] @? "remote add"- git_annex "get" [annexedfile] @? "get failed"- boolSystem "git" [Param "remote", Param "rm", Param "origin"] @? "remote rm"+ git "remote" ["add", "r3", "../../" ++ r3] "remote add"+ git_annex "get" [annexedfile] "get"+ git "remote" ["rm", "origin"] "remote rm" forM_ [r3, r2, r1] $ \r -> indir r $- git_annex "sync" [] @? ("sync failed in " ++ r)+ git_annex "sync" [] ("sync in " ++ r) forM_ [r3, r2] $ \r -> indir r $- git_annex "drop" ["--force", annexedfile] @? ("drop failed in " ++ r)+ git_annex "drop" ["--force", annexedfile] ("drop in " ++ r) indir r1 $ do- git_annex "sync" [] @? "sync failed in r1"+ git_annex "sync" [] "sync in r1" git_annex_expectoutput "find" ["--in", "r3"] [] {- This was the bug. The sync - mangled location log data and it@@ -1173,18 +1160,18 @@ let rname r = if r == r1 then "r1" else "r2" forM_ [r1, r2] $ \r -> indir r $ do {- Get all files, see check below. -}- git_annex "get" [] @? "get failed"+ git_annex "get" [] "get" disconnectOrigin pair r1 r2 forM_ [r1, r2] $ \r -> indir r $ do {- Set up a conflict. -} let newcontent = content annexedfile ++ rname r- git_annex "unlock" [annexedfile] @? "unlock failed" + git_annex "unlock" [annexedfile] "unlock" writecontent annexedfile newcontent {- Sync twice in r1 so it gets the conflict resolution - update from r2 -} forM_ [r1, r2, r1] $ \r -> indir r $- git_annex "sync" ["--force"] @? "sync failed in " ++ rname r+ git_annex "sync" ["--force"] ("sync in " ++ rname r) {- After the sync, it should be possible to get all - files. This includes both sides of the conflict, - although the filenames are not easily predictable.@@ -1192,7 +1179,7 @@ - The bug caused one repo to be missing the content - of the file that had been put in it. -} forM_ [r1, r2] $ \r -> indir r $ do- git_annex "get" [] @? "unable to get all files after merge conflict resolution in " ++ rname r+ git_annex "get" [] ("get all files after merge conflict resolution in " ++ rname r) {- Simple case of conflict resolution; 2 different versions of annexed - file. -}@@ -1203,16 +1190,16 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor1"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin writecontent conflictor "conflictor2"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r2"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r2" pair r1 r2 forM_ [r1,r2,r1] $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 where@@ -1225,7 +1212,7 @@ @? (what ++ " not exactly 2 variant files in: " ++ show l) conflictor `notElem` l @? ("conflictor still present after conflict resolution") indir d $ do- git_annex "get" v @? "get failed"+ git_annex "get" v "get" git_annex_expectoutput "find" v v {- Conflict resolution while in an adjusted branch. -}@@ -1236,20 +1223,20 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor1"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin writecontent conflictor "conflictor2"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r2"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r2" -- We might be in an adjusted branch -- already, when eg on a crippled -- filesystem. So, --force it.- git_annex "adjust" ["--unlock", "--force"] @? "adjust failed"+ git_annex "adjust" ["--unlock", "--force"] "adjust" pair r1 r2 forM_ [r1,r2,r1] $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 where@@ -1262,7 +1249,7 @@ @? (what ++ " not exactly 2 variant files in: " ++ show l) conflictor `notElem` l @? ("conflictor still present after conflict resolution") indir d $ do- git_annex "get" v @? "get failed"+ git_annex "get" v "get" git_annex_expectoutput "find" v v {- Check merge conflict resolution when one side is an annexed@@ -1277,25 +1264,26 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin createDirectory conflictor writecontent subfile "subfile"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r2"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r2" pair r1 r2 let l = if inr1 then [r1, r2] else [r2, r1] forM_ l $ \r -> indir r $- git_annex "sync" [] @? "sync failed in mixed conflict"+ git_annex "sync" [] "sync in mixed conflict" checkmerge "r1" r1 checkmerge "r2" r2 conflictor = "conflictor" subfile = conflictor </> "subfile" variantprefix = conflictor ++ ".variant" checkmerge what d = do- doesDirectoryExist (d </> conflictor) @? (d ++ " conflictor directory missing")+ doesDirectoryExist (d </> conflictor) + @? (d ++ " conflictor directory missing") l <- getDirectoryContents d let v = filter (variantprefix `isPrefixOf`) l not (null v)@@ -1303,7 +1291,7 @@ length v == 1 @? (what ++ " too many variant files in: " ++ show v) indir d $ do- git_annex "get" (conflictor:v) @? ("get failed in " ++ what)+ git_annex "get" (conflictor:v) ("get in " ++ what) git_annex_expectoutput "find" [conflictor] [fromRawFilePath (Git.FilePath.toInternalGitPath (toRawFilePath subfile))] git_annex_expectoutput "find" v v @@ -1319,23 +1307,21 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ disconnectOrigin pair r1 r2 indir r2 $ do- git_annex "sync" [] @? "sync failed in r2"- git_annex "get" [conflictor]- @? "get conflictor failed"- git_annex "unlock" [conflictor]- @? "unlock conflictor failed"+ git_annex "sync" [] "sync in r2"+ git_annex "get" [conflictor] "get conflictor"+ git_annex "unlock" [conflictor] "unlock conflictor" writecontent conflictor "newconflictor" indir r1 $- removeWhenExistsWith removeLink conflictor+ removeWhenExistsWith R.removeLink (toRawFilePath conflictor) let l = if inr1 then [r1, r2, r1] else [r2, r1, r2] forM_ l $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 conflictor = "conflictor"@@ -1361,22 +1347,21 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin writecontent conflictor nonannexed_content- boolSystem "git"- [ Param "config"- , Param "annex.largefiles"- , Param ("exclude=" ++ ingitfile ++ " and exclude=" ++ conflictor)- ] @? "git config annex.largefiles failed"- boolSystem "git" [Param "add", File conflictor] @? "git add conflictor failed"- git_annex "sync" [] @? "sync failed in r2"+ git "config"+ [ "annex.largefiles"+ , "exclude=" ++ ingitfile ++ " and exclude=" ++ conflictor+ ] "git config annex.largefiles"+ git "add" [conflictor] "git add conflictor"+ git_annex "sync" [] "sync in r2" pair r1 r2 let l = if inr1 then [r1, r2] else [r2, r1] forM_ l $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 conflictor = "conflictor"@@ -1412,17 +1397,17 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor"- add_annex conflictor @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin createSymbolicLink symlinktarget "conflictor"- boolSystem "git" [Param "add", File conflictor] @? "git add conflictor failed"- git_annex "sync" [] @? "sync failed in r2"+ git "add" [conflictor] "git add conflictor"+ git_annex "sync" [] "sync in r2" pair r1 r2 let l = if inr1 then [r1, r2] else [r2, r1] forM_ l $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 conflictor = "conflictor"@@ -1460,16 +1445,15 @@ disconnectOrigin createDirectoryIfMissing True (fromRawFilePath (parentDir (toRawFilePath remoteconflictor))) writecontent remoteconflictor annexedcontent- add_annex conflictor @? "add remoteconflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ add_annex conflictor "add remoteconflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin writecontent conflictor localcontent pair r1 r2 -- this case is intentionally not handled -- since the user can recover on their own easily- indir r2 $ git_annex_shouldfail "sync" []- @? "sync failed to fail"+ indir r2 $ git_annex_shouldfail "sync" [] "sync should not succeed" conflictor = "conflictor" localcontent = "local" annexedcontent = "annexed"@@ -1484,19 +1468,19 @@ withtmpclonerepo $ \r3 -> do indir r1 $ do writecontent conflictor "conflictor"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "sync" [] "sync in r1" check_is_link conflictor "r1" indir r2 $ do createDirectory conflictor writecontent (conflictor </> "subfile") "subfile"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r2"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "sync" [] "sync in r2" check_is_link (conflictor </> "subfile") "r2" indir r3 $ do writecontent conflictor "conflictor"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "sync" [] "sync in r1" check_is_link (conflictor </> "subfile") "r3" where conflictor = "conflictor"@@ -1516,17 +1500,17 @@ indir r1 $ do disconnectOrigin writecontent conflictor "conflictor"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "sync" [] @? "sync failed in r1"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "sync" [] "sync in r1" indir r2 $ do disconnectOrigin writecontent conflictor "conflictor"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "unlock" [conflictor] @? "unlock conflicter failed"- git_annex "sync" [] @? "sync failed in r2"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "unlock" [conflictor] "unlock conflicter"+ git_annex "sync" [] "sync in r2" pair r1 r2 forM_ [r1,r2,r1] $ \r -> indir r $- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" checkmerge "r1" r1 checkmerge "r2" r2 where@@ -1538,7 +1522,7 @@ length v == 0 @? (what ++ " not exactly 0 variant files in: " ++ show l) conflictor `elem` l @? ("conflictor not present after conflict resolution")- git_annex "get" [conflictor] @? "get failed"+ git_annex "get" [conflictor] "get" git_annex_expectoutput "find" [conflictor] [conflictor] -- regular file because it's unlocked checkregularfile conflictor@@ -1559,13 +1543,13 @@ conflictor = "conflictor" setup r = indir r $ whensupported $ do disconnectOrigin- git_annex "upgrade" [] @? "upgrade failed"- git_annex "adjust" ["--unlock", "--force"] @? "adjust failed"+ git_annex "upgrade" [] "upgrade"+ git_annex "adjust" ["--unlock", "--force"] "adjust" writecontent conflictor "conflictor"- git_annex "add" [conflictor] @? "add conflicter failed"- git_annex "sync" [] @? "sync failed"+ git_annex "add" [conflictor] "add conflicter"+ git_annex "sync" [] "sync" checkmerge what d = indir d $ whensupported $ do- git_annex "sync" [] @? ("sync failed in " ++ what)+ git_annex "sync" [] ("sync should not work in " ++ what) l <- getDirectoryContents "." conflictor `elem` l @? ("conflictor not present after merge in " ++ what)@@ -1581,66 +1565,67 @@ indir r $ do disconnectOrigin origbranch <- annexeval origBranch- git_annex "upgrade" [] @? "upgrade failed"- git_annex "adjust" ["--unlock", "--force"] @? "adjust failed"+ git_annex "upgrade" [] "upgrade"+ git_annex "adjust" ["--unlock", "--force"] "adjust" createDirectoryIfMissing True "a/b/c" writecontent "a/b/c/d" "foo"- git_annex "add" ["a/b/c"] @? "add a/b/c failed"- git_annex "sync" [] @? "sync failed"+ git_annex "add" ["a/b/c"] "add a/b/c"+ git_annex "sync" [] "sync" createDirectoryIfMissing True "a/b/x" writecontent "a/b/x/y" "foo"- git_annex "add" ["a/b/x"] @? "add a/b/x failed"- git_annex "sync" [] @? "sync failed"- boolSystem "git" [Param "checkout", Param origbranch] @? "git checkout failed"+ git_annex "add" ["a/b/x"] "add a/b/x"+ git_annex "sync" [] "sync"+ git "checkout" [origbranch] "git checkout" doesFileExist "a/b/x/y" @? ("a/b/x/y missing from master after adjusted branch sync") {- Set up repos as remotes of each other. -} pair :: FilePath -> FilePath -> Assertion pair r1 r2 = forM_ [r1, r2] $ \r -> indir r $ do when (r /= r1) $- boolSystem "git" [Param "remote", Param "add", Param "r1", File ("../../" ++ r1)] @? "remote add"+ git "remote" ["add", "r1", "../../" ++ r1] "remote add" when (r /= r2) $- boolSystem "git" [Param "remote", Param "add", Param "r2", File ("../../" ++ r2)] @? "remote add"+ git "remote" ["add", "r2", "../../" ++ r2] "remote add" test_map :: Assertion test_map = intmpclonerepo $ do -- set descriptions, that will be looked for in the map- git_annex "describe" [".", "this repo"] @? "describe 1 failed"- git_annex "describe" ["origin", "origin repo"] @? "describe 2 failed"+ git_annex "describe" [".", "this repo"] "describe 1"+ git_annex "describe" ["origin", "origin repo"] "describe 2" -- --fast avoids it running graphviz, not a build dependency- git_annex "map" ["--fast"] @? "map failed"+ git_annex "map" ["--fast"] "map" test_uninit :: Assertion test_uninit = intmpclonerepo $ do- git_annex "get" [] @? "get failed"+ git_annex "get" [] "get" annexed_present annexedfile- _ <- git_annex "uninit" [] -- exit status not checked; does abnormal exit+ -- any exit status is accepted; does abnormal exit+ git_annex' (const True) "uninit" [] "uninit" checkregularfile annexedfile doesDirectoryExist ".git" @? ".git vanished in uninit" test_uninit_inbranch :: Assertion test_uninit_inbranch = intmpclonerepo $ do- boolSystem "git" [Param "checkout", Param "git-annex"] @? "git checkout git-annex"- git_annex_shouldfail "uninit" [] @? "uninit failed to fail when git-annex branch was checked out"+ git "checkout" ["git-annex"] "git checkout git-annex"+ git_annex_shouldfail "uninit" [] "uninit should not succeed when git-annex branch is checked out" test_upgrade :: Assertion test_upgrade = intmpclonerepo $- git_annex "upgrade" [] @? "upgrade failed"+ git_annex "upgrade" [] "upgrade" test_whereis :: Assertion test_whereis = intmpclonerepo $ do annexed_notpresent annexedfile- git_annex "whereis" [annexedfile] @? "whereis on non-present file failed"- git_annex "untrust" ["origin"] @? "untrust failed"- git_annex_shouldfail "whereis" [annexedfile] @? "whereis on non-present file only present in untrusted repo failed to fail"- git_annex "get" [annexedfile] @? "get failed"+ git_annex "whereis" [annexedfile] "whereis on non-present file"+ git_annex "untrust" ["origin"] "untrust"+ git_annex_shouldfail "whereis" [annexedfile] "whereis should exit nonzero on non-present file only present in untrusted repo"+ git_annex "get" [annexedfile] "get" annexed_present annexedfile- git_annex "whereis" [annexedfile] @? "whereis on present file failed"+ git_annex "whereis" [annexedfile] "whereis on present file" test_hook_remote :: Assertion test_hook_remote = intmpclonerepo $ do #ifndef mingw32_HOST_OS- git_annex "initremote" (words "foo type=hook encryption=none hooktype=foo") @? "initremote failed"+ git_annex "initremote" (words "foo type=hook encryption=none hooktype=foo") "initremote" createDirectory dir git_config "annex.foo-store-hook" $ "cp $ANNEX_FILE " ++ loc@@ -1650,21 +1635,20 @@ "rm -f " ++ loc git_config "annex.foo-checkpresent-hook" $ "if [ -e " ++ loc ++ " ]; then echo $ANNEX_KEY; fi"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to hook remote failed"+ git_annex "copy" [annexedfile, "--to", "foo"] "copy --to hook remote" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed"+ git_annex "drop" [annexedfile, "--numcopies=2"] "drop" annexed_notpresent annexedfile- git_annex "move" [annexedfile, "--from", "foo"] @? "move --from hook remote failed"+ git_annex "move" [annexedfile, "--from", "foo"] "move --from hook remote" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"+ git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] "drop should not be allowed with numcopies=2" annexed_present annexedfile where dir = "dir" loc = dir ++ "/$ANNEX_KEY"- git_config k v = boolSystem "git" [Param "config", Param k, Param v]- @? "git config failed"+ git_config k v = git "config" [k, v] "git config" #else -- this test doesn't work in Windows TODO noop@@ -1673,32 +1657,32 @@ test_directory_remote :: Assertion test_directory_remote = intmpclonerepo $ do createDirectory "dir"- git_annex "initremote" (words "foo type=directory encryption=none directory=dir") @? "initremote failed"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "initremote" (words "foo type=directory encryption=none directory=dir") "initremote"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to directory remote failed"+ git_annex "copy" [annexedfile, "--to", "foo"] "copy --to directory remote" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed"+ git_annex "drop" [annexedfile, "--numcopies=2"] "drop" annexed_notpresent annexedfile- git_annex "move" [annexedfile, "--from", "foo"] @? "move --from directory remote failed"+ git_annex "move" [annexedfile, "--from", "foo"] "move --from directory remote" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"+ git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] "drop should not be allowed with numcopies=2" annexed_present annexedfile test_rsync_remote :: Assertion test_rsync_remote = intmpclonerepo $ do #ifndef mingw32_HOST_OS createDirectory "dir"- git_annex "initremote" (words "foo type=rsync encryption=none rsyncurl=dir") @? "initremote failed"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "initremote" (words "foo type=rsync encryption=none rsyncurl=dir") "initremote"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to rsync remote failed"+ git_annex "copy" [annexedfile, "--to", "foo"] "copy --to rsync remote" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed"+ git_annex "drop" [annexedfile, "--numcopies=2"] "drop" annexed_notpresent annexedfile- git_annex "move" [annexedfile, "--from", "foo"] @? "move --from rsync remote failed"+ git_annex "move" [annexedfile, "--from", "foo"] "move --from rsync remote" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"+ git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] "drop should not be allowed with numcopies=2" annexed_present annexedfile #else noop@@ -1709,16 +1693,16 @@ -- bup special remote needs an absolute path dir <- fromRawFilePath <$> absPath (toRawFilePath "dir") createDirectory dir- git_annex "initremote" (words $ "foo type=bup encryption=none buprepo="++dir) @? "initremote failed"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "initremote" (words $ "foo type=bup encryption=none buprepo="++dir) "initremote"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to bup remote failed"+ git_annex "copy" [annexedfile, "--to", "foo"] "copy --to bup remote" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed"+ git_annex "drop" [annexedfile, "--numcopies=2"] "drop" annexed_notpresent annexedfile- git_annex "copy" [annexedfile, "--from", "foo"] @? "copy --from bup remote failed"+ git_annex "copy" [annexedfile, "--from", "foo"] "copy --from bup remote" annexed_present annexedfile- git_annex "move" [annexedfile, "--from", "foo"] @? "move --from bup remote failed"+ git_annex "move" [annexedfile, "--from", "foo"] "move --from bup remote" annexed_present annexedfile -- gpg is not a build dependency, so only test when it's available@@ -1741,7 +1725,7 @@ @? "test harness self-test failed" void $ Utility.Gpg.testHarness gpgtmp gpgcmd $ do createDirectory "dir"- let a cmd = git_annex cmd $+ let initps = [ "foo" , "type=directory" , "encryption=" ++ scheme@@ -1750,13 +1734,13 @@ ] ++ if scheme `elem` ["hybrid","pubkey"] then ["keyid=" ++ Utility.Gpg.testKeyId] else []- a "initremote" @? "initremote failed"- not <$> a "initremote" @? "initremote failed to fail when run twice in a row"- a "enableremote" @? "enableremote failed"- a "enableremote" @? "enableremote failed when run twice in a row"- git_annex "get" [annexedfile] @? "get of file failed"+ git_annex "initremote" initps "initremote"+ git_annex_shouldfail "initremote" initps "initremote should not work when run twice in a row"+ git_annex "enableremote" initps "enableremote"+ git_annex "enableremote" initps "enableremote when run twice in a row"+ git_annex "get" [annexedfile] "get of file" annexed_present annexedfile- git_annex "copy" [annexedfile, "--to", "foo"] @? "copy --to encrypted remote failed"+ git_annex "copy" [annexedfile, "--to", "foo"] "copy --to encrypted remote" (c,k) <- annexeval $ do uuid <- Remote.nameToUUID "foo" rs <- Logs.Remote.readRemoteLog@@ -1768,11 +1752,11 @@ testEncryptedRemote scheme key c [k] @? "invalid crypto setup" annexed_present annexedfile- git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed"+ git_annex "drop" [annexedfile, "--numcopies=2"] "drop" annexed_notpresent annexedfile- git_annex "move" [annexedfile, "--from", "foo"] @? "move --from encrypted remote failed"+ git_annex "move" [annexedfile, "--from", "foo"] "move --from encrypted remote" annexed_present annexedfile- git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"+ git_annex_shouldfail "drop" [annexedfile, "--numcopies=2"] "drop should not be allowed with numcopies=2" annexed_present annexedfile {- Ensure the configuration complies with the encryption scheme, and - that all keys are encrypted properly for the given directory remote. -}@@ -1813,12 +1797,12 @@ test_add_subdirs = intmpclonerepo $ do createDirectory "dir" writecontent ("dir" </> "foo") $ "dir/" ++ content annexedfile- git_annex "add" ["dir"] @? "add of subdir failed"+ git_annex "add" ["dir"] "add of subdir" {- Regression test for Windows bug where symlinks were not - calculated correctly for files in subdirs. -} unlessM (hasUnlockedFiles <$> getTestMode) $ do- git_annex "sync" [] @? "sync failed"+ git_annex "sync" [] "sync" l <- annexeval $ Utility.FileSystemEncoding.decodeBL <$> Annex.CatFile.catObject (Git.Types.Ref (encodeBS "HEAD:dir/foo")) "../.git/annex/" `isPrefixOf` l @? ("symlink from subdir to .git/annex is wrong: " ++ l)@@ -1826,7 +1810,7 @@ createDirectory "dir2" writecontent ("dir2" </> "foo") $ content annexedfile setCurrentDirectory "dir"- git_annex "add" [".." </> "dir2"] @? "add of ../subdir failed"+ git_annex "add" [".." </> "dir2"] "add of ../subdir" test_addurl :: Assertion test_addurl = intmpclonerepo $ do@@ -1835,17 +1819,17 @@ f <- fromRawFilePath <$> absPath (toRawFilePath "myurl") let url = replace "\\" "/" ("file:///" ++ dropDrive f) writecontent f "foo"- git_annex_shouldfail "addurl" [url] @? "addurl failed to fail on file url"- filecmd "addurl" [url] @? ("addurl failed on " ++ url)+ git_annex_shouldfail "addurl" [url] "addurl should not work on file url"+ filecmd "addurl" [url] ("addurl on " ++ url) let dest = "addurlurldest"- filecmd "addurl" ["--file", dest, url] @? ("addurl failed on " ++ url ++ " with --file")+ filecmd "addurl" ["--file", dest, url] ("addurl on " ++ url ++ " with --file") doesFileExist dest @? (dest ++ " missing after addurl --file") test_export_import :: Assertion test_export_import = intmpclonerepo $ do createDirectory "dir"- git_annex "initremote" (words "foo type=directory encryption=none directory=dir exporttree=yes importtree=yes") @? "initremote failed"- git_annex "get" [] @? "get of files failed"+ git_annex "initremote" (words "foo type=directory encryption=none directory=dir exporttree=yes importtree=yes") "initremote"+ git_annex "get" [] "get of files" annexed_present annexedfile -- Nothing to commit, but this makes sure the master branch@@ -1853,38 +1837,38 @@ -- depending on how the repository was set up. commitchanges origbranch <- annexeval origBranch- git_annex "export" [origbranch, "--to", "foo"] @? "export to dir failed"+ git_annex "export" [origbranch, "--to", "foo"] "export to dir" dircontains annexedfile (content annexedfile) writedir "import" (content "import")- git_annex "import" [origbranch, "--from", "foo"] @? "import from dir failed"- git_annex "merge" ["foo/" ++ origbranch] @? "git annex merge failed"+ git_annex "import" [origbranch, "--from", "foo"] "import from dir"+ git_annex "merge" ["foo/" ++ origbranch] "git annex merge" annexed_present_imported "import" - removeWhenExistsWith removeLink "import"+ removeWhenExistsWith R.removeLink (toRawFilePath "import") writecontent "import" (content "newimport1")- git_annex "add" ["import"] @? "add of import failed"+ git_annex "add" ["import"] "add of import" commitchanges- git_annex "export" [origbranch, "--to", "foo"] @? "export modified file to dir failed"+ git_annex "export" [origbranch, "--to", "foo"] "export modified file to dir" dircontains "import" (content "newimport1") -- verify that export refuses to overwrite modified file writedir "import" (content "newimport2")- removeWhenExistsWith removeLink "import"+ removeWhenExistsWith R.removeLink (toRawFilePath "import") writecontent "import" (content "newimport3")- git_annex "add" ["import"] @? "add of import failed"+ git_annex "add" ["import"] "add of import" commitchanges- git_annex_shouldfail "export" [origbranch, "--to", "foo"] @? "export failed to fail in conflict"+ git_annex_shouldfail "export" [origbranch, "--to", "foo"] "export should not work in conflict" dircontains "import" (content "newimport2") -- resolving import conflict- git_annex "import" [origbranch, "--from", "foo"] @? "import from dir failed"- not <$> boolSystem "git" [Param "merge", Param "foo/master", Param "-mmerge"] @? "git merge of conflict failed to exit nonzero"- removeWhenExistsWith removeLink "import"+ git_annex "import" [origbranch, "--from", "foo"] "import from dir"+ git_shouldfail "merge" ["foo/master", "-mmerge"] "git merge of conflict should exit nonzero"+ removeWhenExistsWith R.removeLink (toRawFilePath "import") writecontent "import" (content "newimport3")- git_annex "add" ["import"] @? "add of import failed"+ git_annex "add" ["import"] "add of import" commitchanges- git_annex "export" [origbranch, "--to", "foo"] @? "export failed after import conflict"+ git_annex "export" [origbranch, "--to", "foo"] "export after import conflict" dircontains "import" (content "newimport3") where dircontains f v = @@ -1894,25 +1878,23 @@ -- When on an adjusted branch, this updates the master branch -- to match it, which is necessary since the master branch is going -- to be exported.- commitchanges = git_annex "sync" ["--no-pull", "--no-push"] @? "sync failed"+ commitchanges = git_annex "sync" ["--no-pull", "--no-push"] "sync" test_export_import_subdir :: Assertion test_export_import_subdir = intmpclonerepo $ do createDirectory "dir"- git_annex "initremote" (words "foo type=directory encryption=none directory=dir exporttree=yes importtree=yes") @? "initremote failed"- git_annex "get" [] @? "get of files failed"+ git_annex "initremote" (words "foo type=directory encryption=none directory=dir exporttree=yes importtree=yes") "initremote"+ git_annex "get" [] "get of files" annexed_present annexedfile createDirectory subdir- boolSystem "git" [Param "mv", File annexedfile, File subannexedfile]- @? "git mv failed"- boolSystem "git" [Param "commit", Param "-m", Param "moved"]- @? "git commit failed"+ git "mv" [annexedfile, subannexedfile] "git mv"+ git "commit" ["-m", "moved"] "git commit" -- When on an adjusted branch, this updates the master branch -- to match it, which is necessary since the master branch is going -- to be exported.- git_annex "sync" ["--no-pull", "--no-push"] @? "sync failed"+ git_annex "sync" ["--no-pull", "--no-push"] "sync" -- Run three times because there was a bug that took a couple -- of runs to lead to the wrong tree being written to the remote@@ -1933,13 +1915,13 @@ testexport = do origbranch <- annexeval origBranch- git_annex "export" [origbranch++":"++subdir, "--to", "foo"] @? "export of subdir failed"+ git_annex "export" [origbranch++":"++subdir, "--to", "foo"] "export of subdir" dircontains annexedfile (content annexedfile) testimport = do origbranch <- annexeval origBranch- git_annex "import" [origbranch++":"++subdir, "--from", "foo"] @? "import of subdir failed"- git_annex "merge" ["foo/master"] @? "git annex merge foo/master failed"+ git_annex "import" [origbranch++":"++subdir, "--from", "foo"] "import of subdir"+ git_annex "merge" ["foo/master"] "git annex merge foo/master" -- Make sure that import did not import the file to the top -- of the repo.
Test/Framework.hs view
@@ -40,34 +40,51 @@ import qualified Annex.Action import qualified Annex.AdjustedBranch import qualified Utility.Process+import qualified Utility.Process.Transcript import qualified Utility.Env import qualified Utility.Env.Set import qualified Utility.Exception import qualified Utility.ThreadScheduler import qualified Utility.Tmp.Dir import qualified Utility.Metered-import qualified Utility.SafeCommand import qualified Command.Uninit +-- Run a process. The output and stderr is captured, and is only+-- displayed if the process does not return the expected value.+testProcess :: String -> [String] -> (Bool -> Bool) -> String -> Assertion+testProcess command params expectedret faildesc = do+ (transcript, ret) <- Utility.Process.Transcript.processTranscript command params Nothing+ (expectedret ret) @? (faildesc ++ " failed (transcript follows)\n" ++ transcript)++-- Run git. (Do not use to run git-annex as the one being tested+-- may not be in path.)+git :: String -> [String] -> String -> Assertion+git command params = testProcess "git" (command:params) (== True)++-- For when git is expected to fail.+git_shouldfail :: String -> [String] -> String -> Assertion+git_shouldfail command params = testProcess "git" (command:params) (== False)+ -- Run git-annex.-git_annex :: String -> [String] -> IO Bool-git_annex command params = do- pp <- Annex.Path.programPath- Utility.SafeCommand.boolSystem pp $- map Utility.SafeCommand.Param (command:params)+git_annex :: String -> [String] -> String -> Assertion+git_annex = git_annex' (== True) -- For when git-annex is expected to fail.--- Run with -q to squelch error.-git_annex_shouldfail :: String -> [String] -> IO Bool-git_annex_shouldfail command params = not <$> git_annex command ("-q":params)+git_annex_shouldfail :: String -> [String] -> String -> Assertion+git_annex_shouldfail = git_annex' (== False) -{- Runs git-annex and returns its output. -}+git_annex' :: (Bool -> Bool) -> String -> [String] -> String -> Assertion+git_annex' expectedret command params faildesc = do+ pp <- Annex.Path.programPath+ testProcess pp (command:params) expectedret faildesc++{- Runs git-annex and returns its standard output. -} git_annex_output :: String -> [String] -> IO String git_annex_output command params = do pp <- Annex.Path.programPath Utility.Process.readProcess pp (command:params) -git_annex_expectoutput :: String -> [String] -> [String] -> IO ()+git_annex_expectoutput :: String -> [String] -> [String] -> Assertion git_annex_expectoutput command params expected = do got <- lines <$> git_annex_output command params got == expected @? ("unexpected value running " ++ command ++ " " ++ show params ++ " -- got: " ++ show got ++ " expected: " ++ show expected)@@ -96,7 +113,7 @@ origindir <- absPath . Git.Types.fromConfigValue =<< annexeval (Config.getConfig k v) let originurl = "localhost:" ++ fromRawFilePath origindir- boolSystem "git" [Param "config", Param config, Param originurl] @? "git config failed"+ git "config" [config, originurl] "git config failed" a where config = "remote.origin.url"@@ -135,7 +152,7 @@ throwM e disconnectOrigin :: Assertion-disconnectOrigin = boolSystem "git" [Param "remote", Param "rm", Param "origin"] @? "remote rm"+disconnectOrigin = git "remote" ["rm", "origin"] "remote rm" withgitrepo :: (FilePath -> Assertion) -> Assertion withgitrepo a = do@@ -163,7 +180,7 @@ setuprepo dir = do cleanup dir ensuretmpdir- boolSystem "git" [Param "init", Param "-q", File dir] @? "git init failed"+ git "init" ["-q", dir] "git init" configrepo dir return dir @@ -184,18 +201,22 @@ cleanup new ensuretmpdir let cloneparams = catMaybes- [ Just $ Param "clone"- , Just $ Param "-q"- , if bareClone cfg then Just (Param "--bare") else Nothing- , if sharedClone cfg then Just (Param "--shared") else Nothing- , Just $ File old- , Just $ File new+ [ Just "-q"+ , if bareClone cfg then Just "--bare" else Nothing+ , if sharedClone cfg then Just "--shared" else Nothing+ , Just old+ , Just new ]- boolSystem "git" cloneparams @? "git clone failed"+ git "clone" cloneparams "git clone" configrepo new indir new $ do ver <- annexVersion <$> getTestMode- git_annex "init" ["-q", new, "--version", show (Types.RepoVersion.fromRepoVersion ver)] @? "git annex init failed"+ git_annex "init" + [ "-q"+ , new, "--version"+ , show (Types.RepoVersion.fromRepoVersion ver)+ ]+ "git annex init" unless (bareClone cfg) $ indir new $ setupTestMode@@ -204,16 +225,16 @@ configrepo :: FilePath -> IO () configrepo dir = indir dir $ do -- ensure git is set up to let commits happen- boolSystem "git" [Param "config", Param "user.name", Param "Test User"] @? "git config failed"- boolSystem "git" [Param "config", Param "user.email", Param "test@example.com"] @? "git config failed"+ git "config" ["user.name", "Test User"]+ "git config"+ git "config" ["user.email", "test@example.com"]+ "git config" -- avoid signed commits by test suite- boolSystem "git" [Param "config", Param "commit.gpgsign", Param "false"] @? "git config failed"+ git "config" ["commit.gpgsign", "false"]+ "git config" -- tell git-annex to not annex the ingitfile- boolSystem "git"- [ Param "config"- , Param "annex.largefiles"- , Param ("exclude=" ++ ingitfile)- ] @? "git config annex.largefiles failed"+ git "config" ["annex.largefiles", "exclude=" ++ ingitfile]+ "git config annex.largefiles" ensuretmpdir :: IO () ensuretmpdir = do@@ -394,10 +415,10 @@ unannexed :: FilePath -> Assertion unannexed = runchecks [checkregularfile, checkcontent, checkwritable] -add_annex :: FilePath -> IO Bool-add_annex f = ifM (unlockedFiles <$> getTestMode)- ( boolSystem "git" [Param "add", File f]- , git_annex "add" [f]+add_annex :: FilePath -> String -> Assertion+add_annex f faildesc = ifM (unlockedFiles <$> getTestMode)+ ( git "add" [f] faildesc+ , git_annex "add" [f] faildesc ) data TestMode = TestMode@@ -471,8 +492,8 @@ setupTestMode = do testmode <- getTestMode when (adjustedUnlockedBranch testmode) $ do- boolSystem "git" [Param "commit", Param "--allow-empty", Param "-m", Param "empty"] @? "git commit failed"- git_annex "adjust" ["--unlock"] @? "git annex adjust failed"+ git "commit" ["--allow-empty", "-m", "empty"] "git commit failed"+ git_annex "adjust" ["--unlock"] "git annex adjust failed" changeToTmpDir :: FilePath -> IO () changeToTmpDir t = do
Types/AdjustedBranch.hs view
@@ -1,6 +1,6 @@ {- adjusted branch types -- - Copyright 2016-2018 Joey Hess <id@joeyh.name>+ - Copyright 2016-2020 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher. -}@@ -10,9 +10,9 @@ data Adjustment = LinkAdjustment LinkAdjustment | PresenceAdjustment PresenceAdjustment (Maybe LinkAdjustment)+ | LinkPresentAdjustment LinkPresentAdjustment deriving (Show, Eq) --- Doesn't make sense to combine unlock with fix. data LinkAdjustment = UnlockAdjustment | LockAdjustment@@ -25,6 +25,11 @@ | ShowMissingAdjustment deriving (Show, Eq) +data LinkPresentAdjustment+ = UnlockPresentAdjustment+ | LockPresentAdjustment+ deriving (Show, Eq)+ -- Adjustments have to be able to be reversed, so that commits made to the -- adjusted branch can be reversed to the commit that would have been made -- without the adjustment and applied to the original branch.@@ -36,6 +41,8 @@ LinkAdjustment (reverseAdjustment l) reverseAdjustment (PresenceAdjustment p ml) = PresenceAdjustment (reverseAdjustment p) (fmap reverseAdjustment ml)+ reverseAdjustment (LinkPresentAdjustment l) =+ LinkPresentAdjustment (reverseAdjustment l) instance ReversableAdjustment LinkAdjustment where reverseAdjustment UnlockAdjustment = LockAdjustment@@ -48,7 +55,10 @@ reverseAdjustment HideMissingAdjustment = ShowMissingAdjustment reverseAdjustment ShowMissingAdjustment = HideMissingAdjustment +instance ReversableAdjustment LinkPresentAdjustment where+ reverseAdjustment UnlockPresentAdjustment = LockPresentAdjustment+ reverseAdjustment LockPresentAdjustment = UnlockPresentAdjustment+ adjustmentHidesFiles :: Adjustment -> Bool adjustmentHidesFiles (PresenceAdjustment HideMissingAdjustment _) = True adjustmentHidesFiles _ = False-
Types/CleanupActions.hs view
@@ -16,6 +16,7 @@ | StopHook UUID | FsckCleanup | SshCachingCleanup+ | AdjustedBranchUpdate | TorrentCleanup URLString | OtherTmpCleanup deriving (Eq, Ord)
Types/GitConfig.hs view
@@ -125,6 +125,7 @@ , annexAutoUpgradeRepository :: Bool , annexCommitMode :: CommitMode , annexSkipUnknown :: Bool+ , annexAdjustedBranchRefresh :: Integer , coreSymlinks :: Bool , coreSharedRepository :: SharedRepository , receiveDenyCurrentBranch :: DenyCurrentBranch@@ -219,6 +220,10 @@ then ManualCommit else AutomaticCommit , annexSkipUnknown = getbool (annexConfig "skipunknown") True+ , annexAdjustedBranchRefresh = fromMaybe+ -- parse as bool if it's not a number+ (if getbool "adjustedbranchrefresh" False then 1 else 0)+ (getmayberead (annexConfig "adjustedbranchrefresh")) , coreSymlinks = getbool "core.symlinks" True , coreSharedRepository = getSharedRepository r , receiveDenyCurrentBranch = getDenyCurrentBranch r
Upgrade/V0.hs view
@@ -19,7 +19,8 @@ olddir <- fromRawFilePath <$> fromRepo gitAnnexDir keys <- getKeysPresent0 olddir forM_ keys $ \k ->- moveAnnex k $ toRawFilePath $ olddir </> keyFile0 k+ moveAnnex k (AssociatedFile Nothing)+ (toRawFilePath $ olddir </> keyFile0 k) -- update the symlinks to the key files -- No longer needed here; V1.upgrade does the same thing
Upgrade/V1.hs view
@@ -80,7 +80,7 @@ let d = parentDir f' liftIO $ allowWrite d liftIO $ allowWrite f'- _ <- moveAnnex k f'+ _ <- moveAnnex k (AssociatedFile Nothing) f' liftIO $ removeDirectory (fromRawFilePath d) updateSymlinks :: Annex ()
Upgrade/V5.hs view
@@ -32,6 +32,7 @@ import Utility.InodeCache import Utility.DottedVersion import Annex.AdjustedBranch+import qualified Utility.RawFilePath as R import qualified Data.ByteString as S @@ -88,8 +89,13 @@ - as does annex.thin. -} setConfig (annexConfig "thin") (boolConfig True) Direct.setIndirect- cur <- fromMaybe (error "Somehow no branch is checked out")- <$> inRepo Git.Branch.current+ cur <- inRepo Git.Branch.current >>= \case+ Just cur -> return cur+ Nothing -> do+ -- Avoid running pre-commit hook.+ commitForAdjustedBranch [Param "--no-verify"]+ fromMaybe (giveup "Nothing is committed, and a commit failed; unable to proceed.") + <$> inRepo Git.Branch.current upgradeDirectWorkTree removeDirectCruft {- Create adjusted branch where all files are unlocked.@@ -151,7 +157,7 @@ ) writepointer f k = liftIO $ do- removeWhenExistsWith removeLink f+ removeWhenExistsWith R.removeLink (toRawFilePath f) S.writeFile f (formatPointer k) {- Remove all direct mode bookkeeping files. -}
Utility/Batch.hs view
@@ -41,12 +41,10 @@ batchthread = asyncBound $ do setProcessPriority 0 maxNice a+ maxNice = 19 #else batch a = a #endif--maxNice :: Int-maxNice = 19 {- Makes a command be run by whichever of nice, ionice, and nocache - are available in the path. -}
Utility/Daemon.hs view
@@ -112,7 +112,7 @@ pid <- getPID writeFile pidfile (show pid) lckfile <- winLockFile pid pidfile- writeFile lckfile ""+ writeFile (fromRawFilePath lckfile) "" void $ lockExclusive lckfile #endif @@ -176,14 +176,14 @@ - when eg, restarting the daemon. -} #ifdef mingw32_HOST_OS-winLockFile :: PID -> FilePath -> IO FilePath+winLockFile :: PID -> FilePath -> IO RawFilePath winLockFile pid pidfile = do cleanstale- return $ prefix ++ show pid ++ suffix+ return $ toRawFilePath $ prefix ++ show pid ++ suffix where prefix = pidfile ++ "." suffix = ".lck" cleanstale = mapM_ (void . tryIO . removeFile) =<<- (filter iswinlockfile <$> dirContents (parentDir pidfile))+ (filter iswinlockfile <$> dirContents (fromRawFilePath (parentDir (toRawFilePath pidfile)))) iswinlockfile f = suffix `isSuffixOf` f && prefix `isPrefixOf` f #endif
Utility/Directory.hs view
@@ -16,7 +16,7 @@ import Control.Monad import System.FilePath-import System.PosixCompat.Files+import System.PosixCompat.Files hiding (removeLink) import Control.Applicative import System.IO.Unsafe (unsafeInterleaveIO) import Data.Maybe
Utility/FileMode.hs view
@@ -16,7 +16,7 @@ import System.IO import Control.Monad import System.PosixCompat.Types-import System.PosixCompat.Files+import System.PosixCompat.Files hiding (removeLink) import Control.Monad.IO.Class import Foreign (complement) import Control.Monad.Catch
Utility/FileSize.hs view
@@ -14,7 +14,7 @@ getFileSize', ) where -import System.PosixCompat.Files+import System.PosixCompat.Files hiding (removeLink) import qualified Utility.RawFilePath as R #ifdef mingw32_HOST_OS import Control.Exception (bracket)
Utility/FileSystemEncoding.hs view
@@ -36,17 +36,18 @@ import System.IO import System.IO.Unsafe import Data.Word-import Data.List+import System.FilePath.ByteString (RawFilePath, encodeFilePath, decodeFilePath) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L #ifdef mingw32_HOST_OS import qualified Data.ByteString.UTF8 as S8 import qualified Data.ByteString.Lazy.UTF8 as L8+#else+import Data.List+import Utility.Split #endif-import System.FilePath.ByteString (RawFilePath, encodeFilePath, decodeFilePath) import Utility.Exception-import Utility.Split {- Makes all subsequent Handles that are opened, as well as stdio Handles, - use the filesystem encoding, instead of the encoding of the current@@ -178,6 +179,7 @@ toRawFilePath :: FilePath -> RawFilePath toRawFilePath = encodeFilePath +#ifndef mingw32_HOST_OS {- Converts a [Word8] to a FilePath, encoding using the filesystem encoding. - - w82s produces a String, which may contain Chars that are invalid@@ -206,6 +208,7 @@ decodeW8NUL = intercalate [c2w8 nul] . map decodeW8 . splitc nul where nul = '\NUL'+#endif c2w8 :: Char -> Word8 c2w8 = fromIntegral . fromEnum
Utility/Gpg.hs view
@@ -332,6 +332,7 @@ testKeyId :: String testKeyId = "129D6E0AC537B9C7" +#ifndef mingw32_HOST_OS testKey :: String testKey = keyBlock True [ "mI0ETvFAZgEEAKnqwWgZqznMhi1RQExem2H8t3OyKDxaNN3rBN8T6LWGGqAYV4wT"@@ -400,7 +401,6 @@ | public = "PUBLIC" | otherwise = "PRIVATE" -#ifndef mingw32_HOST_OS {- Runs an action using gpg in a test harness, in which gpg does - not use ~/.gpg/, but sets up the test key in a subdirectory of - the passed directory and uses it.
Utility/LockFile/Windows.hs view
@@ -16,7 +16,6 @@ import System.Win32.Types import System.Win32.File import Control.Concurrent-import System.FilePath.ByteString (RawFilePath) import Utility.FileSystemEncoding
Utility/MagicWormhole.hs view
@@ -113,23 +113,28 @@ -- Work around stupid stdout buffering behavior of python. -- See https://github.com/warner/magic-wormhole/issues/108 environ <- addEntry "PYTHONUNBUFFERED" "1" <$> getEnvironment- runWormHoleProcess p { env = Just environ} $ \_hin hout herr -> do- (inout, inerr) <- findcode hout `concurrently` findcode herr+ runWormHoleProcess p { env = Just environ} $ \_hin hout herr ph -> do+ (inout, inerr) <- concurrently+ (findcode ph hout)+ (findcode ph herr) return (inout || inerr) where p = wormHoleProcess (Param "send" : ps ++ [File f])- findcode h = findcode' =<< words <$> hGetContents h+ findcode ph h = findcode' =<< getwords ph h [] findcode' [] = return False findcode' (w:ws) = case mkCode w of Just code -> do _ <- tryPutMVar observer code return True Nothing -> findcode' ws+ getwords ph h c = hGetLineUntilExitOrEOF ph h >>= \case+ Nothing -> return $ concatMap words $ reverse c+ Just l -> getwords ph h (l:c) -- | Receives a file. Once the receive is under way, the Code will be -- read from the CodeProducer, and fed to wormhole on stdin. receiveFile :: FilePath -> CodeProducer -> WormHoleParams -> IO Bool-receiveFile f (CodeProducer producer) ps = runWormHoleProcess p $ \hin _hout _herr -> do+receiveFile f (CodeProducer producer) ps = runWormHoleProcess p $ \hin _hout _herr _ph -> do Code c <- readMVar producer hPutStrLn hin c hFlush hin@@ -145,7 +150,7 @@ wormHoleProcess :: WormHoleParams -> CreateProcess wormHoleProcess = proc "wormhole" . toCommand -runWormHoleProcess :: CreateProcess -> (Handle -> Handle -> Handle -> IO Bool) -> IO Bool+runWormHoleProcess :: CreateProcess -> (Handle -> Handle -> Handle -> ProcessHandle -> IO Bool) -> IO Bool runWormHoleProcess p consumer = withCreateProcess p' go `catchNonAsync` const (return False) where@@ -155,7 +160,7 @@ , std_err = CreatePipe } go (Just hin) (Just hout) (Just herr) pid =- consumer hin hout herr <&&> waitbool pid+ consumer hin hout herr pid <&&> waitbool pid go _ _ _ _ = error "internal" waitbool pid = do r <- waitForProcess pid
Utility/Metered.hs view
@@ -49,7 +49,6 @@ import Utility.Percentage import Utility.DataUnits import Utility.HumanTime-import Utility.ThreadScheduler import Utility.SimpleProtocol as Proto import qualified Data.ByteString.Lazy as L@@ -266,7 +265,7 @@ commandMeterExitCode' :: ProgressParser -> OutputHandler -> Maybe Meter -> MeterUpdate -> FilePath -> [CommandParam] -> (CreateProcess -> CreateProcess) -> IO (Maybe ExitCode) commandMeterExitCode' progressparser oh mmeter meterupdate cmd params mkprocess = outputFilter cmd params mkprocess Nothing- (feedprogress mmeter zeroBytesProcessed [])+ (const $ feedprogress mmeter zeroBytesProcessed []) handlestderr where feedprogress sendtotalsize prev buf h = do@@ -291,9 +290,11 @@ meterupdate bytes feedprogress sendtotalsize' bytes buf' h - handlestderr h = unlessM (hIsEOF h) $ do- stderrHandler oh =<< hGetLine h- handlestderr h+ handlestderr ph h = hGetLineUntilExitOrEOF ph h >>= \case+ Just l -> do+ stderrHandler oh l+ handlestderr ph h+ Nothing -> return () {- Runs a command, that may display one or more progress meters on - either stdout or stderr, and prevents the meters from being displayed.@@ -306,8 +307,8 @@ demeterCommandEnv :: OutputHandler -> FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bool demeterCommandEnv oh cmd params environ = do ret <- outputFilter cmd params id environ- (\outh -> avoidProgress True outh stdouthandler)- (\errh -> avoidProgress True errh $ stderrHandler oh)+ (\ph outh -> avoidProgress True ph outh stdouthandler)+ (\ph errh -> avoidProgress True ph errh $ stderrHandler oh) return $ case ret of Just ExitSuccess -> True _ -> False@@ -320,44 +321,31 @@ - filter out lines that contain \r (typically used to reset to the - beginning of the line when updating a progress display). -}-avoidProgress :: Bool -> Handle -> (String -> IO ()) -> IO ()-avoidProgress doavoid h emitter = unlessM (hIsEOF h) $ do- s <- hGetLine h- unless (doavoid && '\r' `elem` s) $- emitter s- avoidProgress doavoid h emitter+avoidProgress :: Bool -> ProcessHandle -> Handle -> (String -> IO ()) -> IO ()+avoidProgress doavoid ph h emitter = hGetLineUntilExitOrEOF ph h >>= \case+ Just s -> do+ unless (doavoid && '\r' `elem` s) $+ emitter s+ avoidProgress doavoid ph h emitter+ Nothing -> return () outputFilter :: FilePath -> [CommandParam] -> (CreateProcess -> CreateProcess) -> Maybe [(String, String)]- -> (Handle -> IO ())- -> (Handle -> IO ())+ -> (ProcessHandle -> Handle -> IO ())+ -> (ProcessHandle -> Handle -> IO ()) -> IO (Maybe ExitCode) outputFilter cmd params mkprocess environ outfilter errfilter = catchMaybeIO $ withCreateProcess p go where- go _ (Just outh) (Just errh) pid = do- outt <- async $ tryIO (outfilter outh) >> hClose outh- errt <- async $ tryIO (errfilter errh) >> hClose errh- ret <- waitForProcess pid-- -- Normally, now that the process has exited, the threads- -- will finish processing its output and terminate.- -- But, just in case the process did something evil like- -- forking to the background while inheriting stderr,- -- it's possible that the threads will not finish, which- -- would result in a deadlock. So, wait a few seconds- -- maximum for them to finish and then cancel them.- -- (One program that has behaved this way in the past is- -- openssh.)- void $ tryNonAsync $ race_- (wait outt >> wait errt)- (threadDelaySeconds (Seconds 2))- cancel outt- cancel errt-+ go _ (Just outh) (Just errh) ph = do+ outt <- async $ tryIO (outfilter ph outh) >> hClose outh+ errt <- async $ tryIO (errfilter ph errh) >> hClose errh+ ret <- waitForProcess ph+ wait outt+ wait errt return ret go _ _ _ _ = error "internal"
Utility/MoveFile.hs view
@@ -15,12 +15,12 @@ import Control.Monad import System.FilePath-import System.PosixCompat.Files-import Control.Monad.IfElse+import System.PosixCompat.Files hiding (removeLink) import System.IO.Error import Prelude #ifndef mingw32_HOST_OS+import Control.Monad.IfElse import Utility.SafeCommand #endif
Utility/Process.hs view
@@ -6,7 +6,7 @@ - License: BSD-2-clause -} -{-# LANGUAGE CPP, Rank2Types #-}+{-# LANGUAGE CPP, Rank2Types, LambdaCase #-} {-# OPTIONS_GHC -fno-warn-tabs #-} module Utility.Process (@@ -24,6 +24,7 @@ withCreateProcess, waitForProcess, cleanupProcess,+ hGetLineUntilExitOrEOF, startInteractiveProcess, stdinHandle, stdoutHandle,@@ -229,3 +230,92 @@ maybe (return ()) hClose mb_stderr void $ waitForProcess pid #endif++{- | Like hGetLine, reads a line from the Handle. Returns Nothing if end of+ - file is reached, or the handle is closed, or if the process has exited+ - and there is nothing more buffered to read from the handle.+ -+ - This is useful to protect against situations where the process might+ - have transferred the handle being read to another process, and so+ - the handle could remain open after the process has exited. That is a rare+ - situation, but can happen. Consider a the process that started up a+ - daemon, and the daemon inherited stderr from it, rather than the more+ - usual behavior of closing the file descriptor. Reading from stderr+ - would block past the exit of the process.+ -+ - In that situation, this will detect when the process has exited,+ - and avoid blocking forever. But will still return anything the process+ - buffered to the handle before exiting.+ -+ - Note on newline mode: This ignores whatever newline mode is configured+ - for the handle, because there is no way to query that. On Windows,+ - it will remove any \r coming before the \n. On other platforms,+ - it does not treat \r specially.+ -}+hGetLineUntilExitOrEOF :: ProcessHandle -> Handle -> IO (Maybe String)+hGetLineUntilExitOrEOF ph h = go []+ where+ go buf = do+ ready <- waitforinputorerror smalldelay+ if ready+ then getloop buf go+ else getProcessExitCode ph >>= \case+ -- Process still running, wait longer.+ Nothing -> go buf+ -- Process is done. It's possible+ -- that it output something and exited+ -- since the prior hWaitForInput,+ -- so check one more time for any buffered+ -- output.+ Just _ -> finalcheck buf++ finalcheck buf = do+ ready <- waitforinputorerror 0+ if ready+ then getloop buf finalcheck+ -- No remaining buffered input, though the handle+ -- may not be EOF if something else is keeping it+ -- open. Treated the same as EOF.+ else eofwithnolineend buf++ -- On exception, proceed as if there was input;+ -- EOF and any encoding issues are dealt with+ -- when reading from the handle.+ waitforinputorerror t = hWaitForInput h t+ `catchNonAsync` const (pure True)++ getchar = + catcherr EOF $+ -- If the handle is closed, reading from it is+ -- an IllegalOperation.+ catcherr IllegalOperation $+ Just <$> hGetChar h+ where+ catcherr t = catchIOErrorType t (const (pure Nothing))++ getloop buf cont =+ getchar >>= \case+ Just c+ | c == '\n' -> return (Just (gotline buf))+ | otherwise -> cont (c:buf)+ Nothing -> eofwithnolineend buf++#ifndef mingw32_HOST_OS+ gotline buf = reverse buf+#else+ gotline ('\r':buf) = reverse buf+ gotline buf = reverse buf+#endif++ eofwithnolineend buf = return $+ if null buf + then Nothing -- no line read+ else Just (reverse buf)++ -- Tenth of a second delay. If the process exits with the FD being+ -- held open, will wait up to twice this long before returning.+ -- This delay could be made smaller. However, that is an unusual+ -- case, and making it too small would cause lots of wakeups while+ -- waiting for output. Bearing in mind that this could be run on+ -- many processes at the same time.+ smalldelay = 100 -- milliseconds
Utility/Process/Transcript.hs view
@@ -15,14 +15,13 @@ ) where import Utility.Process-import Utility.Misc import System.IO import System.Exit import Control.Concurrent.Async import Control.Monad-import Control.Exception #ifndef mingw32_HOST_OS+import Control.Exception import qualified System.Posix.IO #else import Control.Applicative@@ -63,10 +62,10 @@ , std_err = UseHandle writeh } withCreateProcess cp' $ \hin hout herr pid -> do- get <- asyncreader readh+ get <- asyncreader pid readh writeinput input (hin, hout, herr, pid)- transcript <- wait get code <- waitForProcess pid+ transcript <- wait get return (transcript, code) #else {- This implementation for Windows puts stderr after stdout. -}@@ -77,16 +76,18 @@ } withCreateProcess cp' $ \hin hout herr pid -> do let p = (hin, hout, herr, pid)- getout <- asyncreader (stdoutHandle p)- geterr <- asyncreader (stderrHandle p)+ getout <- asyncreader pid (stdoutHandle p)+ geterr <- asyncreader pid (stderrHandle p) writeinput input p- transcript <- (++) <$> wait getout <*> wait geterr code <- waitForProcess pid+ transcript <- (++) <$> wait getout <*> wait geterr return (transcript, code) #endif where- asyncreader = async . hGetContentsStrict-+ asyncreader pid h = async $ reader pid h []+ reader pid h c = hGetLineUntilExitOrEOF pid h >>= \case+ Nothing -> return (concat (reverse c))+ Just l -> reader pid h (l:c) writeinput (Just s) p = do let inh = stdinHandle p unless (null s) $ do
Utility/RawFilePath.hs view
@@ -46,7 +46,6 @@ createDirectory p = D.createDirectory p 0o777 #else-import qualified Data.ByteString as B import System.PosixCompat (FileStatus, FileMode) import qualified System.PosixCompat as P import qualified System.PosixCompat.Files as F@@ -66,8 +65,10 @@ (fromRawFilePath a) (fromRawFilePath b) +{- On windows, removeLink is not available, so only remove files,+ - not symbolic links. -} removeLink :: RawFilePath -> IO ()-removeLink = P.removeLink . fromRawFilePath+removeLink = D.removeFile . fromRawFilePath getFileStatus :: RawFilePath -> IO FileStatus getFileStatus = P.getFileStatus . fromRawFilePath
Utility/Tmp.hs view
@@ -20,7 +20,7 @@ import System.FilePath import System.Directory import Control.Monad.IO.Class-import System.PosixCompat.Files+import System.PosixCompat.Files hiding (removeLink) import Utility.Exception import Utility.FileSystemEncoding
Utility/Url.hs view
@@ -452,7 +452,12 @@ {- Download a perhaps large file using conduit, with auto-resume - of incomplete downloads. -- - Does not catch exceptions.+ - A Request can be configured to throw exceptions for non-2xx http+ - status codes, or not. That configuration is overridden by this,+ - and if it is unable to download, it throws an exception containing+ - a user-visible explanation of the problem. (However, exceptions+ - thrown for reasons other than http status codes will still be thrown+ - as usual.) -} downloadConduit :: MeterUpdate -> Request -> FilePath -> UrlOptions -> IO () downloadConduit meterupdate req file uo =@@ -480,28 +485,29 @@ filter ((/= hAcceptEncoding) . fst) (requestHeaders req) , decompress = const False+ -- Avoid throwing exceptions non-2xx http status codes,+ -- since we rely on parsing the Response to handle+ -- several such codes.+ , checkResponse = \_ _ -> return () } -- Resume download from where a previous download was interrupted, -- when supported by the http server. The server may also opt to -- send the whole file rather than resuming.- resumedownload sz = catchJust- (matchStatusCodeHeadersException (alreadydownloaded sz))- dl- (const noop)- where- dl = join $ runResourceT $ do- let req'' = req' { requestHeaders = resumeFromHeader sz : requestHeaders req }- liftIO $ debugM "url" (show req'')- resp <- http req'' (httpManager uo)- if responseStatus resp == partialContent206+ resumedownload sz = join $ runResourceT $ do+ let req'' = req' { requestHeaders = resumeFromHeader sz : requestHeaders req' }+ liftIO $ debugM "url" (show req'')+ resp <- http req'' (httpManager uo)+ if responseStatus resp == partialContent206+ then do+ store (toBytesProcessed sz) AppendMode resp+ return (return ())+ else if responseStatus resp == ok200 then do- store (toBytesProcessed sz) AppendMode resp+ store zeroBytesProcessed WriteMode resp return (return ())- else if responseStatus resp == ok200- then do- store zeroBytesProcessed WriteMode resp- return (return ())+ else if alreadydownloaded sz resp+ then return (return ()) else do rf <- extractFromResourceT (respfailure resp) if responseStatus resp == unauthorized401@@ -510,8 +516,9 @@ Just ba -> retryauthed ba else return $ giveup rf - alreadydownloaded sz s h = s == requestedRangeNotSatisfiable416 - && case lookup hContentRange h of+ alreadydownloaded sz resp+ | responseStatus resp /= requestedRangeNotSatisfiable416 = False+ | otherwise = case lookup hContentRange (responseHeaders resp) of -- This could be improved by fixing -- https://github.com/aristidb/http-types/issues/87 Just crh -> crh == B8.fromString ("bytes */" ++ show sz)
Utility/WebApp.hs view
@@ -36,9 +36,6 @@ import Control.Arrow ((***)) import Control.Concurrent -localhost :: HostName-localhost = "localhost"- {- Builds a command to use to start or open a web browser showing an url. -} browserProc :: String -> CreateProcess #ifdef darwin_HOST_OS@@ -105,6 +102,7 @@ _ -> error "unable to bind to a local socket" where hostname = fromMaybe localhost h+ localhost = "localhost" hints = defaultHints { addrSocketType = Stream } {- Repeated attempts because bind sometimes fails for an - unknown reason on OSX. -}
doc/git-annex-adjust.mdwn view
@@ -4,7 +4,7 @@ # SYNOPSIS -git annex adjust `--unlock|--lock|--fix|--hide-missing [--unlock|--lock|--fix]`+git annex adjust `--unlock|--lock|--fix|--hide-missing [--unlock|--lock|--fix]|--unlock-present` # DESCRIPTION @@ -31,8 +31,8 @@ Re-running this command with the same options while inside the adjusted branch will update the adjusted branch-as necessary (eg for `--hide-missing`), and will also propagate commits-back to the original branch.+as necessary (eg for `--hide-missing` and `--unlock-present`), +and will also propagate commits back to the original branch. # OPTIONS @@ -82,13 +82,32 @@ branch. To update the adjusted branch to reflect changes to content availability, - run `git annex adjust --hide-missing` again.+ run `git annex adjust --hide-missing` again. Or, to automate updates,+ set the `annex.adjustedbranchrefresh` config. Despite missing files being hidden, `git annex sync --content` will still operate on them, and can be used to download missing- files from remotes.+ files from remotes. It also updates the adjusted branch after+ transferring content. This option can be combined with --unlock, --lock, or --fix.++* `--unlock-present`++ Unlock files whose content is present, and lock files whose content is+ missing. This provides the benefits of working with unlocked files,+ but makes it easier to see when the content of a file is not missing,+ since it will be a broken symlink.+ + The adjusted branch is not immediately changed when content availability+ changes, so when you `git annex get` files, they will remain locked.+ And when you `git annex drop` files, they will remain locked and so will+ not be broken symlinks.+ + To update the adjusted branch to reflect changes to content availability, + run `git annex adjust --unlock-present` again. Or, to automate updates,+ set the `annex.adjustedbranchrefresh` config. Or use `git-annex sync+ --content`, which updates the branch after transferring content. # SEE ALSO
doc/git-annex-examinekey.mdwn view
@@ -28,7 +28,8 @@ These variables are also available for use in formats: ${key}, ${backend}, ${bytesize}, ${humansize}, ${keyname}, ${hashdirlower}, ${hashdirmixed},- ${mtime} (for the mtime field of a WORM key).+ ${mtime} (for the mtime field of a WORM key), ${file} (when a filename is+ provided to examinekey). Also, '\\n' is a newline, '\\000' is a NULL, etc.
doc/git-annex.mdwn view
@@ -1016,6 +1016,23 @@ When the `--batch` option is used, this configuration is ignored. +* `annex.adjustedbranchrefresh`++ When [[git-annex-adjust]](1) is used to set up an adjusted branch+ that needs to be refreshed after getting or dropping files, this config+ controls how frequently the branch is refreshed. ++ Refreshing the branch takes some time, so doing it after every file+ can be too slow. (It also can generate a lot of dangling git objects.)+ The default value is 0 (or false), which does not+ refresh the branch. Setting 1 (or true) will refresh only once, + after git-annex has made other changes. Setting 2 refreshes after every+ file, 3 after every other file, and so on; setting 100 refreshes after+ every 99 files.++ (If git-annex gets faster in the future, refresh rates will increase+ proportional to the speed improvements.)+ * `annex.queuesize` git-annex builds a queue of git commands, in order to combine similar
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 8.20201116+Version: 8.20201127 Cabal-Version: >= 1.10 License: AGPL-3 Maintainer: Joey Hess <id@joeyh.name>@@ -609,6 +609,7 @@ Annex Annex.Action Annex.AdjustedBranch+ Annex.AdjustedBranch.Merge Annex.AdjustedBranch.Name Annex.AutoMerge Annex.BloomFilter@@ -623,6 +624,7 @@ Annex.Concurrent Annex.Concurrent.Utility Annex.Content+ Annex.Content.Presence Annex.Content.LowLevel Annex.Content.PointerFile Annex.CurrentBranch