summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2013-10-21 16:19:00 -0400
committerGravatar Joey Hess <joey@kitenet.net>2013-10-21 16:20:54 -0400
commitafe0cc0bb4fbe3876c35462dece4670617263877 (patch)
treef23797ee89c453d7c82f35e47bb5fc197662a5f4
parent727566ad23efc6c36a4a4473b3299af913f6ce03 (diff)
corrupt branch resetting (but not yet reflog walking)
-rw-r--r--Git/RecoverRepository.hs43
-rw-r--r--doc/design/assistant/disaster_recovery.mdwn9
-rw-r--r--git-recover-repository.hs21
3 files changed, 58 insertions, 15 deletions
diff --git a/Git/RecoverRepository.hs b/Git/RecoverRepository.hs
index f40450f46..330063abe 100644
--- a/Git/RecoverRepository.hs
+++ b/Git/RecoverRepository.hs
@@ -169,11 +169,40 @@ copyObjects srcr destr = rsync
{- To deal with missing objects that cannot be recovered, resets any
- local branches to point to an old commit before the missing
- - objects.
+ - objects. Returns all branches that were changed, and deleted.
-}
-resetLocalBranches :: MissingObjects -> GoodCommits -> Repo -> IO [Branch]
-resetLocalBranches missing goodcommits r = do
- error "TODO"
+resetLocalBranches :: MissingObjects -> GoodCommits -> Repo -> IO ([Branch], [Branch], GoodCommits)
+resetLocalBranches missing goodcommits r =
+ go [] [] goodcommits =<< filter islocalbranch <$> getAllRefs r
+ where
+ islocalbranch b = "refs/heads/" `isPrefixOf` show b
+ go changed deleted gcs [] = return (changed, deleted, gcs)
+ go changed deleted gcs (b:bs) = do
+ (mc, gcs') <- findUncorruptedCommit missing gcs b r
+ case mc of
+ Just c
+ | c == b -> go changed deleted gcs' bs
+ | otherwise -> do
+ reset b c
+ go (b:changed) deleted gcs' bs
+ Nothing -> do
+ (mc', gcs'') <- findOldBranch missing gcs' b r
+ case mc' of
+ Just c
+ | c == b -> go changed deleted gcs' bs
+ | otherwise -> do
+ reset b c
+ go (b:changed) deleted gcs'' bs
+ Nothing -> do
+ nukeBranchRef b r
+ go changed (b:deleted) gcs'' bs
+ reset b c = do
+ nukeBranchRef b r
+ void $ runBool
+ [ Param "branch"
+ , Param (show $ Ref.base b)
+ , Param (show c)
+ ] r
{- To deal with missing objects that cannot be recovered, removes
- any remote tracking branches that reference them. Returns a list of
@@ -255,6 +284,7 @@ findUncorruptedCommit missing goodcommits branch r = do
else do
(ls, cleanup) <- pipeNullSplit
[ Param "log"
+ , Param "-z"
, Param "--format=%H"
, Param (show branch)
] r
@@ -284,11 +314,12 @@ verifyCommit missing goodcommits commit r
| otherwise = do
(ls, cleanup) <- pipeNullSplit
[ Param "log"
+ , Param "-z"
, Param "--format=%H %T"
, Param (show commit)
] r
let committrees = map parse ls
- if any isNothing committrees
+ if any isNothing committrees || null committrees
then do
void cleanup
return (False, goodcommits)
@@ -304,7 +335,7 @@ verifyCommit missing goodcommits commit r
<$> extractSha commitsha
<*> extractSha treesha
_ -> Nothing
- check [] = return False
+ check [] = return True
check ((commit, tree):rest)
| checkGoodCommit commit goodcommits = return True
| otherwise = verifyTree missing tree r <&&> check rest
diff --git a/doc/design/assistant/disaster_recovery.mdwn b/doc/design/assistant/disaster_recovery.mdwn
index 1a2b55477..770c8f43a 100644
--- a/doc/design/assistant/disaster_recovery.mdwn
+++ b/doc/design/assistant/disaster_recovery.mdwn
@@ -143,8 +143,7 @@ that was found for it.
`git annex fsck --fast` to fix up any object location info.
* Remote tracking branches can just be removed, and then `git fetch`
from the remote, which will re-download missing objects from it and
- reinstate the tracking branch.
-* For other branches (or tags), it's best to not rewrite them, because
- that could get really confusing. Instead, delete the old broken branch,
- and make a "recovered/$branch" that holds the last good commit (if one
- was found).
+ reinstate the tracking branch. **done**
+* For other branches, reset them to last good commit, or delete
+ if none was found.
+* (Decided not to touch tags.)
diff --git a/git-recover-repository.hs b/git-recover-repository.hs
index 66b1708f8..3a002e72c 100644
--- a/git-recover-repository.hs
+++ b/git-recover-repository.hs
@@ -16,6 +16,7 @@ import Common
import qualified Git.CurrentRepo
import qualified Git.RecoverRepository
import qualified Git.Config
+import qualified Git.Branch
header :: String
header = "Usage: git-recover-repository"
@@ -34,7 +35,7 @@ parseArgs = do
enableDebugOutput :: IO ()
enableDebugOutput = do
s <- setFormatter
- <$> streamHandler stderr NOTICE
+ <$> streamHandler stderr DEBUG -- NOTICE
<*> pure (simpleLogFormatter "$msg")
updateGlobalLogger rootLoggerName (setLevel DEBUG . setHandlers [s])
@@ -66,8 +67,20 @@ main = do
, show (length remotebranches)
, "remote tracking branches that referred to missing objects"
]
- localbranches <- Git.RecoverRepository.resetLocalBranches stillmissing goodcommits g
- unless (null localbranches) $ do
+ (resetbranches, deletedbranches, _) <- Git.RecoverRepository.resetLocalBranches stillmissing goodcommits g
+ unless (null resetbranches) $ do
putStrLn "Reset these local branches to old versions before the missing objects were committed:"
- putStr $ unlines $ map show localbranches
+ putStr $ unlines $ map show resetbranches
+ unless (null deletedbranches) $ do
+ putStrLn "Deleted these local branches, which could not be recovered due to missing objects:"
+ putStr $ unlines $ map show deletedbranches
+ mcurr <- Git.Branch.currentUnsafe g
+ case mcurr of
+ Nothing -> return ()
+ Just curr -> when (any (== curr) (resetbranches ++ deletedbranches)) $ do
+ putStrLn $ unwords
+ [ "You currently have"
+ , show curr
+ , "checked out. You may have staged changes in the index that can be committed to recover the lost state of this branch!"
+ ]
else putStrLn "To force a recovery to a usable state, run this command again with the --force parameter."