support git 2.34.0's handling of merge conflict between annexed and non-annexed file

This version of git -- or its new default "ort" resolver -- handles such
a conflict by staging two files, one with the original name and the other
named file~ref. Use unmergedSiblingFile when the latter is detected.

(It doesn't do that when the conflict is between a directory and a file
or symlink though, so see previous commit for how that case is handled.)

The sibling file has to be deleted separately, because cleanConflictCruft
may not delete it -- that only handles files that are annex links,
but the sibling file may be the non-annexed file side of the conflict.

The graftin code had assumed that, when the other side of a conclict
is a symlink, the file in the work tree will contain the non-annexed
content that we want it to contain. But that is not the case with the new
git; the file may be the annex link and needs to be replaced with the
content, while the annex link will be written as a -variant file.

(The weird doesDirectoryExist check in graftin turns out to still be
needed, test suite failed when I tried to remove it.)

Test suite passes with new git with ort resolver default. Have not tried it
with old git or other defaults.

Sponsored-by: Noam Kremen on Patreon
This commit is contained in:
Joey Hess 2021-11-22 15:40:03 -04:00
parent c49787824c
commit 5a7f253974
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
5 changed files with 95 additions and 22 deletions

View file

@ -5,6 +5,8 @@
- Licensed under the GNU AGPL version 3 or higher.
-}
{-# LANGUAGE OverloadedStrings #-}
module Git.LsFiles (
Options(..),
inRepo,
@ -236,7 +238,14 @@ data Unmerged = Unmerged
{ unmergedFile :: RawFilePath
, unmergedTreeItemType :: Conflicting TreeItemType
, unmergedSha :: Conflicting Sha
}
, unmergedSiblingFile :: Maybe RawFilePath
-- ^ Normally this is Nothing, because a
-- merge conflict is represented as a single file with two
-- stages. However, git resolvers sometimes choose to stage
-- two files, one for each side of the merge conflict. In such a case,
-- this is used for the name of the second file, which is related
-- to the first file. (Eg, "foo" and "foo~ref")
} deriving (Show)
{- Returns a list of the files in the specified locations that have
- unresolved merge conflicts.
@ -246,7 +255,7 @@ data Unmerged = Unmerged
- 1 = old version, can be ignored
- 2 = us
- 3 = them
- If a line is omitted, that side removed the file.
- If line 2 or 3 is omitted, that side removed the file.
-}
unmerged :: [RawFilePath] -> Repo -> IO ([Unmerged], IO Bool)
unmerged l repo = guardSafeForLsFiles repo $ do
@ -265,7 +274,7 @@ data InternalUnmerged = InternalUnmerged
, ifile :: RawFilePath
, itreeitemtype :: Maybe TreeItemType
, isha :: Maybe Sha
}
} deriving (Show)
parseUnmerged :: String -> Maybe InternalUnmerged
parseUnmerged s
@ -296,16 +305,25 @@ reduceUnmerged c (i:is) = reduceUnmerged (new:c) rest
{ unmergedFile = ifile i
, unmergedTreeItemType = Conflicting treeitemtypeA treeitemtypeB
, unmergedSha = Conflicting shaA shaB
, unmergedSiblingFile = if ifile sibi == ifile i
then Nothing
else Just (ifile sibi)
}
findsib templatei [] = ([], removed templatei)
findsib templatei (l:ls)
| ifile l == ifile templatei = (ls, l)
| ifile l == ifile templatei || issibfile templatei l = (ls, l)
| otherwise = (l:ls, removed templatei)
removed templatei = templatei
{ isus = not (isus templatei)
, itreeitemtype = Nothing
, isha = Nothing
}
-- foo~<ref> are unmerged sibling files of foo
-- Some versions or resolvers of git stage the sibling files,
-- other versions or resolvers do not.
issibfile x y = (ifile x <> "~") `S.isPrefixOf` ifile y
&& isus x || isus y
&& not (isus x && isus y)
{- Gets the InodeCache equivilant information stored in the git index.
-