fixed reconcileStaged crash when index is locked or in conflict

Eg, when git commit runs the smudge filter.

Commit 428c91606b introduced the crash,
as write-tree fails in those situations. Now it will work, and git-annex
always gets up-to-date information even in those situations. It does
need to do a bit more work, each time git-annex is run with the index
locked. Although if the index is unmodified from the last time
write-tree succeeded, that work is avoided.
This commit is contained in:
Joey Hess 2021-05-24 11:33:23 -04:00
parent 3698e804d4
commit efae085272
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
3 changed files with 47 additions and 26 deletions

View file

@ -45,7 +45,7 @@ import Git.Command
import Git.Types import Git.Types
import Git.Index import Git.Index
import Git.Sha import Git.Sha
import Git.Branch (writeTree, update') import Git.Branch (writeTreeQuiet, update')
import qualified Git.Ref import qualified Git.Ref
import Config.Smudge import Config.Smudge
import qualified Utility.RawFilePath as R import qualified Utility.RawFilePath as R
@ -226,35 +226,48 @@ reconcileStaged qh = do
withTSDelta (liftIO . genInodeCache gitindex) >>= \case withTSDelta (liftIO . genInodeCache gitindex) >>= \case
Just cur -> Just cur ->
liftIO (maybe Nothing readInodeCache <$> catchMaybeIO (readFile indexcache)) >>= \case liftIO (maybe Nothing readInodeCache <$> catchMaybeIO (readFile indexcache)) >>= \case
Nothing -> go cur indexcache Nothing -> go cur indexcache =<< getindextree
Just prev -> ifM (compareInodeCaches prev cur) Just prev -> ifM (compareInodeCaches prev cur)
( noop ( noop
, go cur indexcache , go cur indexcache =<< getindextree
) )
Nothing -> noop Nothing -> noop
where where
lastindexref = Ref "refs/annex/last-index" lastindexref = Ref "refs/annex/last-index"
go cur indexcache = do getindextree = inRepo writeTreeQuiet
oldtree <- fromMaybe emptyTree
<$> inRepo (Git.Ref.sha lastindexref) getoldtree = fromMaybe emptyTree <$> inRepo (Git.Ref.sha lastindexref)
newtree <- inRepo writeTree
go cur indexcache (Just newtree) = do
oldtree <- getoldtree
when (oldtree /= newtree) $ do when (oldtree /= newtree) $ do
(l, cleanup) <- inRepo $ pipeNullSplit' $ updatetodiff (fromRef oldtree) (fromRef newtree)
diff oldtree newtree
changed <- procdiff l False
void $ liftIO cleanup
-- Flush database changes immediately
-- so other processes can see them.
when changed $
liftIO $ H.flushDbQueue qh
liftIO $ writeFile indexcache $ showInodeCache cur liftIO $ writeFile indexcache $ showInodeCache cur
-- Storing the tree in a ref makes sure it does not -- Storing the tree in a ref makes sure it does not
-- get garbage collected, and is available to diff -- get garbage collected, and is available to diff
-- against next time. -- against next time.
inRepo $ update' lastindexref newtree 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
-- 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.
go _ _ Nothing = do
oldtree <- getoldtree
updatetodiff (fromRef oldtree) "--cached"
updatetodiff old new = 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
diff oldtree newtree = diff old new =
-- Avoid running smudge or clean filters, since we want the -- Avoid running smudge or clean filters, since we want the
-- raw output, and they would block trying to access the -- raw output, and they would block trying to access the
-- locked database. The --raw normally avoids git diff -- locked database. The --raw normally avoids git diff
@ -264,6 +277,8 @@ reconcileStaged qh = do
-- (The -G option may make it be used otherwise.) -- (The -G option may make it be used otherwise.)
[ Param "-c", Param "diff.external=" [ Param "-c", Param "diff.external="
, Param "diff" , Param "diff"
, Param old
, Param new
, Param "--raw" , Param "--raw"
, Param "-z" , Param "-z"
, Param "--no-abbrev" , Param "--no-abbrev"
@ -279,8 +294,6 @@ reconcileStaged qh = do
-- Avoid other complications. -- Avoid other complications.
, Param "--ignore-submodules=all" , Param "--ignore-submodules=all"
, Param "--no-ext-diff" , Param "--no-ext-diff"
, Param (fromRef oldtree)
, Param (fromRef newtree)
] ]
procdiff (info:file:rest) changed procdiff (info:file:rest) changed

View file

@ -185,8 +185,18 @@ commitAlways :: CommitMode -> String -> Branch -> [Ref] -> Repo -> IO Sha
commitAlways commitmode message branch parentrefs repo = fromJust commitAlways commitmode message branch parentrefs repo = fromJust
<$> commit commitmode True message branch parentrefs repo <$> commit commitmode True message branch parentrefs repo
-- Throws exception if the index is locked, with an error message output by
-- git on stderr.
writeTree :: Repo -> IO Sha writeTree :: Repo -> IO Sha
writeTree repo = getSha "write-tree" $ pipeReadStrict [Param "write-tree"] repo writeTree repo = getSha "write-tree" $
pipeReadStrict [Param "write-tree"] repo
-- Avoids error output if the command fails due to eg, the index being locked.
writeTreeQuiet :: Repo -> IO (Maybe Sha)
writeTreeQuiet repo = extractSha <$> withNullHandle go
where
go nullh = pipeReadStrict' (\p -> p { std_err = UseHandle nullh })
[Param "write-tree"] repo
commitTree :: CommitMode -> String -> [Ref] -> Ref -> Repo -> IO Sha commitTree :: CommitMode -> String -> [Ref] -> Ref -> Repo -> IO Sha
commitTree commitmode message parentrefs tree repo = commitTree commitmode message parentrefs tree repo =

View file

@ -70,17 +70,15 @@ pipeReadLazy params repo = assertLocal repo $ do
- Nonzero exit status is ignored. - Nonzero exit status is ignored.
-} -}
pipeReadStrict :: [CommandParam] -> Repo -> IO S.ByteString pipeReadStrict :: [CommandParam] -> Repo -> IO S.ByteString
pipeReadStrict = pipeReadStrict' S.hGetContents pipeReadStrict = pipeReadStrict' id
{- The reader action must be strict. -} pipeReadStrict' :: (CreateProcess -> CreateProcess) -> [CommandParam] -> Repo -> IO S.ByteString
pipeReadStrict' :: (Handle -> IO a) -> [CommandParam] -> Repo -> IO a pipeReadStrict' fp params repo = assertLocal repo $ withCreateProcess p go
pipeReadStrict' reader params repo = assertLocal repo $ withCreateProcess p go
where where
p = (gitCreateProcess params repo) p = fp (gitCreateProcess params repo) { std_out = CreatePipe }
{ std_out = CreatePipe }
go _ (Just outh) _ pid = do go _ (Just outh) _ pid = do
output <- reader outh output <- S.hGetContents outh
hClose outh hClose outh
void $ waitForProcess pid void $ waitForProcess pid
return output return output