fix auto merge conflict resolution when doing out of tree merge for adjusted branch
This commit is contained in:
parent
b9e4e2ba84
commit
60bdffe43e
6 changed files with 60 additions and 65 deletions
|
@ -272,7 +272,7 @@ updateAdjustedBranch tomerge (origbranch, adj) commitmode = catchBoolIO $
|
|||
withemptydir tmpwt $ withWorkTree tmpwt $ do
|
||||
liftIO $ writeFile (tmpgit </> "HEAD") (fromRef updatedorig)
|
||||
showAction $ "Merging into " ++ fromRef (Git.Ref.base origbranch)
|
||||
ifM (autoMergeFrom tomerge (Just updatedorig) commitmode)
|
||||
ifM (autoMergeFrom tomerge (Just origbranch) True commitmode)
|
||||
( do
|
||||
!mergecommit <- liftIO $ extractSha <$> readFile (tmpgit </> "HEAD")
|
||||
-- This is run after the commit lock is dropped.
|
||||
|
@ -305,7 +305,7 @@ updateAdjustedBranch tomerge (origbranch, adj) commitmode = catchBoolIO $
|
|||
adjmergecommit <- commitAdjustedTree' adjtree mergecommit
|
||||
[mergecommit, currbranch]
|
||||
showAction "Merging into adjusted branch"
|
||||
ifM (autoMergeFrom adjmergecommit (Just currbranch) commitmode)
|
||||
ifM (autoMergeFrom adjmergecommit (Just currbranch) False commitmode)
|
||||
-- The adjusted branch has a merge commit on top;
|
||||
-- clean that up and propigate any changes made
|
||||
-- in that merge to the origbranch.
|
||||
|
|
|
@ -42,17 +42,17 @@ import qualified Data.ByteString.Lazy as L
|
|||
- Callers should use Git.Branch.changed first, to make sure that
|
||||
- there are changes from the current branch to the branch being merged in.
|
||||
-}
|
||||
autoMergeFrom :: Git.Ref -> Maybe Git.Ref -> Git.Branch.CommitMode -> Annex Bool
|
||||
autoMergeFrom branch currbranch commitmode = do
|
||||
autoMergeFrom :: Git.Ref -> Maybe Git.Ref -> Bool -> Git.Branch.CommitMode -> Annex Bool
|
||||
autoMergeFrom branch currbranch inoverlay commitmode = do
|
||||
showOutput
|
||||
case currbranch of
|
||||
Nothing -> go Nothing
|
||||
Just b -> go =<< inRepo (Git.Ref.sha b)
|
||||
where
|
||||
go old = ifM isDirect
|
||||
( mergeDirect currbranch old branch (resolveMerge old branch) commitmode
|
||||
( mergeDirect currbranch old branch (resolveMerge old branch False) commitmode
|
||||
, inRepo (Git.Merge.mergeNonInteractive branch commitmode)
|
||||
<||> (resolveMerge old branch <&&> commitResolvedMerge commitmode)
|
||||
<||> (resolveMerge old branch inoverlay <&&> commitResolvedMerge commitmode)
|
||||
)
|
||||
|
||||
{- Resolves a conflicted merge. It's important that any conflicts be
|
||||
|
@ -77,11 +77,16 @@ autoMergeFrom branch currbranch commitmode = do
|
|||
-
|
||||
- In indirect mode, the merge is resolved in the work tree and files
|
||||
- staged, to clean up from a conflicted merge that was run in the work
|
||||
- tree.
|
||||
- tree.
|
||||
-
|
||||
- In direct mode, the work tree is not touched here; files are staged to
|
||||
- the index, and written to the gitAnnexMergeDir, for later handling by
|
||||
- the direct mode merge code.
|
||||
-
|
||||
- This is complicated by needing to support merges run in an overlay
|
||||
- work tree, in which case the CWD won't be within the work tree.
|
||||
- In this mode, there is no need to update the work tree at all,
|
||||
- as the overlay work tree will get deleted.
|
||||
-
|
||||
- Unlocked files remain unlocked after merging, and locked files
|
||||
- remain locked. When the merge conflict is between a locked and unlocked
|
||||
|
@ -93,12 +98,16 @@ autoMergeFrom branch currbranch commitmode = do
|
|||
- A git merge can fail for other reasons, and this allows detecting
|
||||
- such failures.
|
||||
-}
|
||||
resolveMerge :: Maybe Git.Ref -> Git.Ref -> Annex Bool
|
||||
resolveMerge us them = do
|
||||
top <- fromRepo Git.repoPath
|
||||
resolveMerge :: Maybe Git.Ref -> Git.Ref -> Bool -> Annex Bool
|
||||
resolveMerge us them inoverlay = do
|
||||
top <- if inoverlay
|
||||
then pure "."
|
||||
else fromRepo Git.repoPath
|
||||
(fs, cleanup) <- inRepo (LsFiles.unmerged [top])
|
||||
srcmap <- inodeMap $ pure (map LsFiles.unmergedFile fs, return True)
|
||||
(mergedks, mergedfs) <- unzip <$> mapM (resolveMerge' srcmap us them) fs
|
||||
srcmap <- if inoverlay
|
||||
then pure M.empty
|
||||
else inodeMap $ pure (map LsFiles.unmergedFile fs, return True)
|
||||
(mergedks, mergedfs) <- unzip <$> mapM (resolveMerge' srcmap us them inoverlay) fs
|
||||
let mergedks' = concat mergedks
|
||||
let mergedfs' = catMaybes mergedfs
|
||||
let merged = not (null mergedfs')
|
||||
|
@ -114,15 +123,15 @@ resolveMerge us them = do
|
|||
|
||||
when merged $ do
|
||||
Annex.Queue.flush
|
||||
unlessM isDirect $ do
|
||||
unlessM (pure inoverlay <||> isDirect) $ do
|
||||
unstagedmap <- inodeMap $ inRepo $ LsFiles.notInRepo False [top]
|
||||
cleanConflictCruft mergedks' mergedfs' unstagedmap
|
||||
showLongNote "Merge conflict was automatically resolved; you may want to examine the result."
|
||||
return merged
|
||||
|
||||
resolveMerge' :: InodeMap -> Maybe Git.Ref -> Git.Ref -> LsFiles.Unmerged -> Annex ([Key], Maybe FilePath)
|
||||
resolveMerge' _ Nothing _ _ = return ([], Nothing)
|
||||
resolveMerge' unstagedmap (Just us) them u = do
|
||||
resolveMerge' :: InodeMap -> Maybe Git.Ref -> Git.Ref -> Bool -> LsFiles.Unmerged -> Annex ([Key], Maybe FilePath)
|
||||
resolveMerge' _ Nothing _ _ _ = return ([], Nothing)
|
||||
resolveMerge' unstagedmap (Just us) them inoverlay u = do
|
||||
kus <- getkey LsFiles.valUs
|
||||
kthem <- getkey LsFiles.valThem
|
||||
case (kus, kthem) of
|
||||
|
@ -133,8 +142,9 @@ resolveMerge' unstagedmap (Just us) them u = do
|
|||
makeannexlink keyThem LsFiles.valThem
|
||||
-- cleanConflictCruft can't handle unlocked
|
||||
-- files, so delete here.
|
||||
unless (islocked LsFiles.valUs) $
|
||||
liftIO $ nukeFile file
|
||||
unless inoverlay $
|
||||
unless (islocked LsFiles.valUs) $
|
||||
liftIO $ nukeFile file
|
||||
| otherwise -> do
|
||||
-- Only resolve using symlink when both
|
||||
-- were locked, otherwise use unlocked
|
||||
|
@ -170,23 +180,33 @@ resolveMerge' unstagedmap (Just us) them u = do
|
|||
where
|
||||
dest = variantFile file key
|
||||
|
||||
stagefile :: FilePath -> Annex FilePath
|
||||
stagefile f
|
||||
| inoverlay = (</> f) <$> fromRepo Git.repoPath
|
||||
| otherwise = pure f
|
||||
|
||||
makesymlink key dest = do
|
||||
l <- calcRepo $ gitAnnexLink dest key
|
||||
replacewithsymlink dest l
|
||||
stageSymlink dest =<< hashSymlink l
|
||||
unless inoverlay $ replacewithsymlink dest l
|
||||
dest' <- stagefile dest
|
||||
stageSymlink dest' =<< hashSymlink l
|
||||
|
||||
replacewithsymlink dest link = withworktree dest $ \f ->
|
||||
replaceFile f $ makeGitLink link
|
||||
|
||||
makepointer key dest = do
|
||||
unlessM (reuseOldFile unstagedmap key file dest) $ do
|
||||
r <- linkFromAnnex key dest
|
||||
case r of
|
||||
LinkAnnexFailed -> liftIO $
|
||||
writeFile dest (formatPointer key)
|
||||
_ -> noop
|
||||
stagePointerFile dest =<< hashPointerFile key
|
||||
Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath dest)
|
||||
unless inoverlay $
|
||||
unlessM (reuseOldFile unstagedmap key file dest) $ do
|
||||
r <- linkFromAnnex key dest
|
||||
case r of
|
||||
LinkAnnexFailed -> liftIO $
|
||||
writeFile dest (formatPointer key)
|
||||
_ -> noop
|
||||
dest' <- stagefile dest
|
||||
stagePointerFile dest' =<< hashPointerFile key
|
||||
unless inoverlay $
|
||||
Database.Keys.addAssociatedFile key
|
||||
=<< inRepo (toTopFilePath dest)
|
||||
|
||||
withworktree f a = ifM isDirect
|
||||
( do
|
||||
|
@ -202,7 +222,7 @@ resolveMerge' unstagedmap (Just us) them u = do
|
|||
=<< fromRepo (UpdateIndex.lsSubTree b item)
|
||||
|
||||
-- Update the work tree to reflect the graft.
|
||||
case (selectwant (LsFiles.unmergedBlobType u), selectunwant (LsFiles.unmergedBlobType u)) of
|
||||
unless inoverlay $ case (selectwant (LsFiles.unmergedBlobType u), selectunwant (LsFiles.unmergedBlobType u)) of
|
||||
-- Symlinks are never left in work tree when
|
||||
-- there's a conflict with anything else.
|
||||
-- So, when grafting in a symlink, we must create it:
|
||||
|
|
|
@ -21,14 +21,20 @@ withIndexFile f = withAltRepo
|
|||
(\g -> addGitEnv g "GIT_INDEX_FILE" f)
|
||||
(\g g' -> g' { gitEnv = gitEnv g })
|
||||
|
||||
{- Runs an action using a different git work tree. -}
|
||||
{- Runs an action using a different git work tree.
|
||||
-
|
||||
- Smudge and clean filters are disabled in this work tree. -}
|
||||
withWorkTree :: FilePath -> Annex a -> Annex a
|
||||
withWorkTree d = withAltRepo
|
||||
(\g -> return $ g { location = modlocation (location g) })
|
||||
(\g g' -> g' { location = location g })
|
||||
(\g -> return $ g { location = modlocation (location g), gitGlobalOpts = gitGlobalOpts g ++ disableSmudgeConfig })
|
||||
(\g g' -> g' { location = location g, gitGlobalOpts = gitGlobalOpts g })
|
||||
where
|
||||
modlocation l@(Local {}) = l { worktree = Just d }
|
||||
modlocation _ = error "withWorkTree of non-local git repo"
|
||||
disableSmudgeConfig = map Param
|
||||
[ "-c", "filter.annex.smudge="
|
||||
, "-c", "filter.annex.clean="
|
||||
]
|
||||
|
||||
{- Runs an action with the git index file and HEAD, and a few other
|
||||
- files that are related to the work tree coming from an overlay
|
||||
|
|
|
@ -29,7 +29,7 @@ start = do
|
|||
let merge_head = d </> "MERGE_HEAD"
|
||||
them <- fromMaybe (error nomergehead) . extractSha
|
||||
<$> liftIO (readFile merge_head)
|
||||
ifM (resolveMerge (Just us) them)
|
||||
ifM (resolveMerge (Just us) them False)
|
||||
( do
|
||||
void $ commitResolvedMerge Git.Branch.ManualCommit
|
||||
next $ next $ return True
|
||||
|
|
|
@ -170,7 +170,7 @@ merge :: CurrBranch -> Git.Branch.CommitMode -> Git.Branch -> Annex Bool
|
|||
merge (Just b, Just adj) commitmode tomerge =
|
||||
updateAdjustedBranch tomerge (b, adj) commitmode
|
||||
merge (b, _) commitmode tomerge =
|
||||
autoMergeFrom tomerge b commitmode
|
||||
autoMergeFrom tomerge b False commitmode
|
||||
|
||||
syncBranch :: Git.Branch -> Git.Branch
|
||||
syncBranch = Git.Ref.under "refs/heads/synced" . fromDirectBranch . fromAdjustedBranch
|
||||
|
|
|
@ -279,37 +279,6 @@ into adjusted view worktrees.]
|
|||
will make copies of the content of annexed files, so this would need
|
||||
to checkout the adjusted branch some other way. Maybe generalize so this
|
||||
more efficient checkout is available as a git-annex command?
|
||||
* sync in adjusted branch runs merge in overlay worktree,
|
||||
but the merge conflict resolution code does not know to use that
|
||||
worktree.
|
||||
* sync in adjusted branch can trigger merge conflict detection where
|
||||
there should be no conflict.
|
||||
|
||||
git init a
|
||||
cd a
|
||||
git annex init --version=6
|
||||
echo hi > f
|
||||
git annex add f
|
||||
git annex sync
|
||||
cd ..
|
||||
|
||||
git clone a b
|
||||
cd b
|
||||
git annex init --version=6
|
||||
git annex get
|
||||
git annex adjust --unlock
|
||||
cd ..
|
||||
|
||||
cd a
|
||||
git mv f f2
|
||||
git annex sync
|
||||
cd ..
|
||||
|
||||
cd b
|
||||
git annex sync
|
||||
|
||||
To fix, implement "avoiding conflicted merge" above.
|
||||
|
||||
* There are potentially races in code that assumes a branch like
|
||||
master is not being changed by someone else. In particular,
|
||||
propigateAdjustedCommits rebases the adjusted branch on top of master.
|
||||
|
|
Loading…
Reference in a new issue