prospective fix for bad_merge_commit_deleting_all_files

Assuming my analysis of a race is correct. In any case, this certianly closes a
race..
This commit is contained in:
Joey Hess 2014-07-09 15:07:53 -04:00
parent 4915f9ad03
commit 58acaf8026
4 changed files with 44 additions and 8 deletions

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE CPP #-}
module Annex.Direct where
import Common.Annex
@ -150,13 +152,16 @@ addDirect file cache = do
- directory, and the merge is staged into a copy of the index.
- Then the work tree is updated to reflect the merge, and
- finally, the merge is committed and the real index updated.
-
- A lock file is used to avoid races with any other caller of mergeDirect.
-
- To avoid other git processes from making change to the index while our
- merge is in progress, the index lock file is used as the temp index
- file. This is the same as what git does when updating the index
- normally.
-}
mergeDirect :: Maybe Git.Ref -> Maybe Git.Ref -> Git.Branch -> Annex Bool -> Git.Branch.CommitMode -> Annex Bool
mergeDirect startbranch oldref branch resolvemerge commitmode = do
-- Use the index lock file as the temp index file.
-- This is actually what git does when updating the index,
-- and so it will prevent other git processes from making
-- any changes to the index while our merge is in progress.
mergeDirect startbranch oldref branch resolvemerge commitmode = lockMerge $ do
reali <- fromRepo indexFile
tmpi <- fromRepo indexFileLock
liftIO $ copyFile reali tmpi
@ -174,9 +179,29 @@ mergeDirect startbranch oldref branch resolvemerge commitmode = do
else resolvemerge
mergeDirectCleanup d (fromMaybe Git.Sha.emptyTree oldref)
mergeDirectCommit merged startbranch branch commitmode
liftIO $ rename tmpi reali
return r
lockMerge :: Annex a -> Annex a
lockMerge a = do
lockfile <- fromRepo gitAnnexMergeLock
createAnnexDirectory $ takeDirectory lockfile
mode <- annexFileMode
bracketIO (lock lockfile mode) unlock (const a)
where
#ifndef mingw32_HOST_OS
lock lockfile mode = do
l <- noUmask mode $ createFile lockfile mode
waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0)
return l
unlock = closeFd
#else
lock lockfile _mode = waitToLock $ lockExclusive lockfile
unlock = dropLock
#endif
{- Stage a merge into the index, avoiding changing HEAD or the current
- branch. -}
stageMerge :: FilePath -> Git.Branch -> Git.Branch.CommitMode -> Annex Bool

View file

@ -42,6 +42,7 @@ module Locations (
gitAnnexJournalDir,
gitAnnexJournalLock,
gitAnnexPreCommitLock,
gitAnnexMergeLock,
gitAnnexIndex,
gitAnnexIndexStatus,
gitAnnexViewIndex,
@ -262,6 +263,10 @@ gitAnnexJournalLock r = gitAnnexDir r </> "journal.lck"
gitAnnexPreCommitLock :: Git.Repo -> FilePath
gitAnnexPreCommitLock r = gitAnnexDir r </> "precommit.lck"
{- Lock file for direct mode merge. -}
gitAnnexMergeLock :: Git.Repo -> FilePath
gitAnnexMergeLock r = gitAnnexDir r </> "merge.lck"
{- .git/annex/index is used to stage changes to the git-annex branch -}
gitAnnexIndex :: Git.Repo -> FilePath
gitAnnexIndex r = gitAnnexDir r </> "index"

4
debian/changelog vendored
View file

@ -3,6 +3,10 @@ git-annex (5.20140708) UNRELEASED; urgency=medium
* Fix git version that supported --no-gpg-sign.
* Fix bug in automatic merge conflict resolution, when one side is an
annexed symlink, and the other side is a non-annexed symlink.
* Fix race in direct mode merge code that could cause all files in the
repository to be removed. It should be able to recover repositories
experiencing this bug without data loss. See:
http://git-annex.branchable.com/bugs/bad_merge_commit_deleting_all_files/
-- Joey Hess <joeyh@debian.org> Tue, 08 Jul 2014 12:44:42 -0400

View file

@ -27,9 +27,11 @@ redo, passing `-m 2` instead.)
Once the revert is done, all your files should be back. You can switch
the repository back to direct mode if desired (`git annex direct`)
and can `git annex sync` to push the fix out to all other clones.
Everywhere the fix lands should restore the deleted files. (Although
it's possible that some repositories may have pruned the deleted
files as unused.)
Everywhere the fix lands should restore the deleted files.
Note that it's possible that some repositories may have pruned the deleted
files as unused. This is most likely to happen in the repository that made
the bad merge commit in the first place.
[[!tag confirmed urgent]]