From e2d4c133f58d4c534f2e37e7bf940e303d9c1c21 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Tue, 5 Nov 2019 12:41:15 -0400 Subject: [PATCH] init: fix data loss bug Fix bug that lost modifications to unlocked files when init is re-ran in an already initialized repo. In retrospect needing scanUnlockedFiles False in the direct mode upgrade path was a good hint that it was unsafe when used with True. However, this bug did not affect upgrade from v5. In such an upgrade, an unlocked file that is modified is left as-is. The only place scanUnlockedFiles True did overwrite modified unlocked files is during an git-annex init of a repo that was already initialized by git-annex. (I also tried a scenario where the repo had not been initialized by git-annex yet, but was cloned from a v7 repo with an unlocked file, and the pointer file replaced with some other content, and the data loss did not occur in that situation.) Since the fixed scanUnlockedFiles avoids overwriting non-pointer files, it should be safe to run in any situation, so there's no need any longer for the parameter. --- Annex/Init.hs | 2 +- Annex/WorkTree.hs | 34 +++++++++++++++++++--------------- CHANGELOG | 2 ++ Upgrade/V5.hs | 5 +---- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Annex/Init.hs b/Annex/Init.hs index e590620cb5..1bc82710f4 100644 --- a/Annex/Init.hs +++ b/Annex/Init.hs @@ -109,7 +109,7 @@ initialize' mversion = checkCanInitialize $ do setVersion (fromMaybe defaultVersion mversion) configureSmudgeFilter showSideAction "scanning for unlocked files" - scanUnlockedFiles True + scanUnlockedFiles unlessM isBareRepo $ do hookWrite postCheckoutHook hookWrite postMergeHook diff --git a/Annex/WorkTree.hs b/Annex/WorkTree.hs index eb64e0cbb1..cf40f527bf 100644 --- a/Annex/WorkTree.hs +++ b/Annex/WorkTree.hs @@ -70,11 +70,12 @@ ifAnnexed file yes no = maybe no yes =<< lookupFile file - when initializing/upgrading a v6+ mode repository. - - Also, the content for the unlocked file may already be present as - - an annex object. If so, make the unlocked file use that content - - when replacefiles is True. + - an annex object. If so, populate the pointer file with it. + - But if worktree file does not have a pointer file's content, it is left + - as-is. -} -scanUnlockedFiles :: Bool -> Annex () -scanUnlockedFiles replacefiles = whenM (inRepo Git.Ref.headExists) $ do +scanUnlockedFiles :: Annex () +scanUnlockedFiles = whenM (inRepo Git.Ref.headExists) $ do Database.Keys.runWriter $ liftIO . Database.Keys.SQL.dropAllAssociatedFiles (l, cleanup) <- inRepo $ Git.LsTree.lsTree Git.LsTree.LsTreeRecursive Git.Ref.headRef @@ -92,15 +93,18 @@ scanUnlockedFiles replacefiles = whenM (inRepo Git.Ref.headExists) $ do let tf = Git.LsTree.file i Database.Keys.runWriter $ liftIO . Database.Keys.SQL.addAssociatedFileFast (toIKey k) tf - whenM (pure replacefiles <&&> inAnnex k) $ do + whenM (inAnnex k) $ do f <- fromRepo $ fromTopFilePath tf - destmode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus f - ic <- replaceFile f $ \tmp -> - linkFromAnnex k tmp destmode >>= \case - LinkAnnexOk -> - withTSDelta (liftIO . genInodeCache tmp) - LinkAnnexNoop -> return Nothing - LinkAnnexFailed -> liftIO $ do - writePointerFile tmp k destmode - return Nothing - maybe noop (restagePointerFile (Restage True) f) ic + liftIO (isPointerFile f) >>= \case + Just k' | k' == k -> do + destmode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus f + ic <- replaceFile f $ \tmp -> + linkFromAnnex k tmp destmode >>= \case + LinkAnnexOk -> + withTSDelta (liftIO . genInodeCache tmp) + LinkAnnexNoop -> return Nothing + LinkAnnexFailed -> liftIO $ do + writePointerFile tmp k destmode + return Nothing + maybe noop (restagePointerFile (Restage True) f) ic + _ -> noop diff --git a/CHANGELOG b/CHANGELOG index 22c69752e1..344e2ff299 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ git-annex (7.20191025) UNRELEASED; urgency=medium + * init: Fix bug that lost modifications to unlocked files when init is + re-ran in an already initialized repo. * benchmark: Add --databases to benchmark sqlite databases. -- Joey Hess Tue, 29 Oct 2019 15:13:03 -0400 diff --git a/Upgrade/V5.hs b/Upgrade/V5.hs index 5c8ed0a1bd..5d331c8787 100644 --- a/Upgrade/V5.hs +++ b/Upgrade/V5.hs @@ -42,13 +42,10 @@ upgrade automatic = flip catchNonAsync (const $ return False) $ do ( do checkGitVersionForDirectUpgrade convertDirect - -- Worktree files are already populated, so don't - -- have this try to populate them again. - scanUnlockedFiles False , do checkGitVersionForIndirectUpgrade - scanUnlockedFiles True ) + scanUnlockedFiles configureSmudgeFilter -- Inode sentinal file was only used in direct mode and when -- locking down files as they were added. In v6, it's used more