improve repair of bad branches

The repair code assumed that if fsck found no broken objects, after
removing bad objects and possibly pulling replacements from remote, all was
well.. but this is not really true. Removing bad objects could leave some
branches broken. fsck doesn't report any missing objects in this case,
and its messages about broken branches are ignored by the fsck output
parser.

To deal with this, added a separate scan of all refs to find broken ones
and remove them when --forced. This will also let anyone who ran into this
bug run repair again to fix up the incomplete repair done before.

This commit was sponsored by Aaron Whitehouse.
This commit is contained in:
Joey Hess 2014-07-21 18:42:58 -04:00
parent 7a847b1d40
commit 000dd42ac4
2 changed files with 34 additions and 7 deletions

View file

@ -8,6 +8,7 @@
module Git.Repair ( module Git.Repair (
runRepair, runRepair,
runRepairOf, runRepairOf,
removeBadBranches,
successfulRepair, successfulRepair,
cleanCorruptObjects, cleanCorruptObjects,
retrieveMissingObjects, retrieveMissingObjects,
@ -191,8 +192,11 @@ isTrackingBranch b = "refs/remotes/" `isPrefixOf` fromRef b
- any branches (filtered by a predicate) that reference them - any branches (filtered by a predicate) that reference them
- Returns a list of all removed branches. - Returns a list of all removed branches.
-} -}
removeBadBranches :: (Ref -> Bool) -> MissingObjects -> GoodCommits -> Repo -> IO ([Branch], GoodCommits) removeBadBranches :: (Ref -> Bool) -> Repo -> IO [Branch]
removeBadBranches removablebranch missing goodcommits r = removeBadBranches removablebranch r = fst <$> removeBadBranches' removablebranch S.empty emptyGoodCommits r
removeBadBranches' :: (Ref -> Bool) -> MissingObjects -> GoodCommits -> Repo -> IO ([Branch], GoodCommits)
removeBadBranches' removablebranch missing goodcommits r =
go [] goodcommits =<< filter removablebranch <$> getAllRefs r go [] goodcommits =<< filter removablebranch <$> getAllRefs r
where where
go removed gcs [] = return (removed, gcs) go removed gcs [] = return (removed, gcs)
@ -204,6 +208,11 @@ removeBadBranches removablebranch missing goodcommits r =
nukeBranchRef b r nukeBranchRef b r
go (b:removed) gcs' bs go (b:removed) gcs' bs
badBranches :: MissingObjects -> Repo -> IO [Branch]
badBranches missing r = filterM isbad =<< getAllRefs r
where
isbad b = not . fst <$> verifyCommit missing emptyGoodCommits b r
{- Gets all refs, including ones that are corrupt. {- Gets all refs, including ones that are corrupt.
- git show-ref does not output refs to commits that are directly - git show-ref does not output refs to commits that are directly
- corrupted, so it is not used. - corrupted, so it is not used.
@ -439,8 +448,12 @@ runRepair removablebranch forced g = do
if foundBroken fsckresult if foundBroken fsckresult
then runRepair' removablebranch fsckresult forced Nothing g then runRepair' removablebranch fsckresult forced Nothing g
else do else do
putStrLn "No problems found." bad <- badBranches S.empty g
return (True, []) if null bad
then do
putStrLn "No problems found."
return (True, [])
else runRepair' removablebranch fsckresult forced Nothing g
runRepairOf :: FsckResults -> (Ref -> Bool) -> Bool -> Maybe FilePath -> Repo -> IO (Bool, [Branch]) runRepairOf :: FsckResults -> (Ref -> Bool) -> Bool -> Maybe FilePath -> Repo -> IO (Bool, [Branch])
runRepairOf fsckresult removablebranch forced referencerepo g = do runRepairOf fsckresult removablebranch forced referencerepo g = do
@ -455,9 +468,9 @@ runRepair' removablebranch fsckresult forced referencerepo g = do
case stillmissing of case stillmissing of
FsckFoundMissing s t FsckFoundMissing s t
| S.null s -> if repoIsLocalBare g | S.null s -> if repoIsLocalBare g
then successfulfinish [] then checkbadbranches s
else ifM (checkIndex g) else ifM (checkIndex g)
( successfulfinish [] ( checkbadbranches s
, do , do
putStrLn "No missing objects found, but the index file is corrupt!" putStrLn "No missing objects found, but the index file is corrupt!"
if forced if forced
@ -488,7 +501,7 @@ runRepair' removablebranch fsckresult forced referencerepo g = do
| otherwise -> unsuccessfulfinish | otherwise -> unsuccessfulfinish
where where
repairbranches missing = do repairbranches missing = do
(removedbranches, goodcommits) <- removeBadBranches removablebranch missing emptyGoodCommits g (removedbranches, goodcommits) <- removeBadBranches' removablebranch missing emptyGoodCommits g
let remotebranches = filter isTrackingBranch removedbranches let remotebranches = filter isTrackingBranch removedbranches
unless (null remotebranches) $ unless (null remotebranches) $
putStrLn $ unwords putStrLn $ unwords
@ -503,6 +516,16 @@ runRepair' removablebranch fsckresult forced referencerepo g = do
"Deleted these local branches, which could not be recovered due to missing objects:" "Deleted these local branches, which could not be recovered due to missing objects:"
return (resetbranches ++ deletedbranches) return (resetbranches ++ deletedbranches)
checkbadbranches missing = do
bad <- badBranches missing g
case (null bad, forced) of
(True, _) -> successfulfinish []
(False, False) -> do
displayList (map fromRef bad)
"Some git branches refer to missing objects:"
unsuccessfulfinish
(False, True) -> successfulfinish =<< repairbranches missing
forcerepair missing fscktruncated = do forcerepair missing fscktruncated = do
modifiedbranches <- repairbranches missing modifiedbranches <- repairbranches missing
deindexedfiles <- rewriteIndex g deindexedfiles <- rewriteIndex g

4
debian/changelog vendored
View file

@ -2,6 +2,10 @@ git-annex (5.20140718) UNRELEASED; urgency=medium
* webapp: Automatically install Konqueror integration scripts * webapp: Automatically install Konqueror integration scripts
to get and drop files. to get and drop files.
* repair: Removing bad objects could leave fsck finding no more
unreachable objects, but some branches no longer accessible.
Fix this, including support for fixing up repositories that
were incompletely repaired before.
-- Joey Hess <joeyh@debian.org> Mon, 21 Jul 2014 14:41:26 -0400 -- Joey Hess <joeyh@debian.org> Mon, 21 Jul 2014 14:41:26 -0400