correctly update keys db in merge conflict
This is quite a subtle edge case, see the bug report for full details. The second git diff is needed only when there's a merge conflict. It would be possible to speed it up marginally by using --diff-filter=Unmerged, but probably not enough to bother with. Sponsored-by: Graham Spencer on Patreon
This commit is contained in:
parent
da24034331
commit
0101363eb8
2 changed files with 87 additions and 23 deletions
|
@ -245,7 +245,8 @@ reconcileStaged qh = do
|
|||
go cur indexcache (Just newtree) = do
|
||||
oldtree <- getoldtree
|
||||
when (oldtree /= newtree) $ do
|
||||
updatetodiff (fromRef oldtree) (fromRef newtree)
|
||||
updatetodiff (Just (fromRef oldtree)) (fromRef newtree) procdiff
|
||||
>>= flushdb . fst
|
||||
liftIO $ writeFile indexcache $ showInodeCache cur
|
||||
-- Storing the tree in a ref makes sure it does not
|
||||
-- get garbage collected, and is available to diff
|
||||
|
@ -253,22 +254,34 @@ reconcileStaged qh = do
|
|||
inRepo $ update' lastindexref newtree
|
||||
-- git write-tree will fail if the index is locked or when there is
|
||||
-- a merge conflict. To get up-to-date with the current index,
|
||||
-- diff --cached with the old index tree. The current index tree
|
||||
-- diff --staged with the old index tree. The current index tree
|
||||
-- is not known, so not recorded, and the inode cache is not updated,
|
||||
-- so the next time git-annex runs, it will diff again, even
|
||||
-- if the index is unchanged.
|
||||
--
|
||||
-- When there is a merge conflict, that will not see the new local
|
||||
-- version of the files that are conflicted. So a second diff
|
||||
-- is done, with --staged but no old tree.
|
||||
go _ _ Nothing = do
|
||||
oldtree <- getoldtree
|
||||
updatetodiff (fromRef oldtree) "--cached"
|
||||
(changed, conflicted) <- updatetodiff
|
||||
(Just (fromRef oldtree)) "--staged" procdiff
|
||||
changed' <- if conflicted
|
||||
then fst <$> updatetodiff Nothing "--staged"
|
||||
procmergeconflictdiff
|
||||
else pure False
|
||||
flushdb (changed || changed')
|
||||
|
||||
updatetodiff old new = do
|
||||
updatetodiff old new processor = do
|
||||
(l, cleanup) <- inRepo $ pipeNullSplit' $ diff old new
|
||||
changed <- procdiff l False
|
||||
void $ liftIO cleanup
|
||||
-- Flush database changes immediately
|
||||
-- so other processes can see them.
|
||||
when changed $
|
||||
liftIO $ H.flushDbQueue qh
|
||||
processor l False False
|
||||
`finally` void (liftIO cleanup)
|
||||
|
||||
-- Flush database changes immediately
|
||||
-- so other processes can see them.
|
||||
flushdb changed
|
||||
| changed = liftIO $ H.flushDbQueue qh
|
||||
| otherwise = noop
|
||||
|
||||
-- Avoid running smudge clean filter, which would block trying to
|
||||
-- access the locked database. git write-tree sometimes calls it,
|
||||
|
@ -288,8 +301,8 @@ reconcileStaged qh = do
|
|||
-- (The -G option may make it be used otherwise.)
|
||||
[ Param "-c", Param "diff.external="
|
||||
, Param "diff"
|
||||
, Param old
|
||||
, Param new
|
||||
] ++ maybeToList (Param <$> old) ++
|
||||
[ Param new
|
||||
, Param "--raw"
|
||||
, Param "-z"
|
||||
, Param "--no-abbrev"
|
||||
|
@ -307,12 +320,13 @@ reconcileStaged qh = do
|
|||
, Param "--no-ext-diff"
|
||||
]
|
||||
|
||||
procdiff (info:file:rest) changed
|
||||
procdiff (info:file:rest) changed conflicted
|
||||
| ":" `S.isPrefixOf` info = case S8.words info of
|
||||
(_colonsrcmode:dstmode:srcsha:dstsha:status:[]) -> do
|
||||
let conflicted' = status == "U"
|
||||
-- avoid removing associated file when
|
||||
-- there is a merge conflict
|
||||
removed <- if status /= "U"
|
||||
removed <- if not conflicted'
|
||||
then catKey (Ref srcsha) >>= \case
|
||||
Just oldkey -> do
|
||||
liftIO $ SQL.removeAssociatedFile oldkey
|
||||
|
@ -330,9 +344,32 @@ reconcileStaged qh = do
|
|||
reconcilerace (asTopFilePath file) key
|
||||
return True
|
||||
Nothing -> return False
|
||||
procdiff rest (changed || removed || added)
|
||||
_ -> return changed -- parse failed
|
||||
procdiff _ changed = return changed
|
||||
procdiff rest
|
||||
(changed || removed || added)
|
||||
(conflicted || conflicted')
|
||||
_ -> return (changed, conflicted) -- parse failed
|
||||
procdiff _ changed conflicted = return (changed, conflicted)
|
||||
|
||||
-- Processing a diff --index when there is a merge conflict.
|
||||
-- This diff will have the new local version of a file as the
|
||||
-- first sha, and a null sha as the second sha, and we only
|
||||
-- care about files that are in conflict.
|
||||
procmergeconflictdiff (info:file:rest) changed conflicted
|
||||
| ":" `S.isPrefixOf` info = case S8.words info of
|
||||
(_colonmode:_mode:sha:_sha:status:[]) -> do
|
||||
let conflicted' = status == "U"
|
||||
added <- catKey (Ref sha) >>= \case
|
||||
Just key -> do
|
||||
liftIO $ SQL.addAssociatedFile key
|
||||
(asTopFilePath file)
|
||||
(SQL.WriteHandle qh)
|
||||
return True
|
||||
Nothing -> return False
|
||||
procmergeconflictdiff rest
|
||||
(changed || added)
|
||||
(conflicted || conflicted')
|
||||
_ -> return (changed, conflicted) -- parse failed
|
||||
procmergeconflictdiff _ changed conflicted = return (changed, conflicted)
|
||||
|
||||
reconcilerace file key = do
|
||||
caches <- liftIO $ SQL.getInodeCaches key (SQL.ReadHandle qh)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue