summaryrefslogtreecommitdiff
path: root/Git
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2013-10-23 12:21:59 -0400
committerGravatar Joey Hess <joey@kitenet.net>2013-10-23 12:21:59 -0400
commitfb2ccfd60ff09c1b1d03838d42eba3c65fd7fb27 (patch)
tree6311388390b6bd61c98bd770460a0275584aa500 /Git
parent3aca9448cf014262e4ffd42c2ebf729d4f0a2282 (diff)
add repair command
Diffstat (limited to 'Git')
-rw-r--r--Git/RecoverRepository.hs103
1 files changed, 88 insertions, 15 deletions
diff --git a/Git/RecoverRepository.hs b/Git/RecoverRepository.hs
index f591bd6b2..1ae817fbc 100644
--- a/Git/RecoverRepository.hs
+++ b/Git/RecoverRepository.hs
@@ -6,6 +6,7 @@
-}
module Git.RecoverRepository (
+ runRecovery,
cleanCorruptObjects,
retrieveMissingObjects,
resetLocalBranches,
@@ -17,23 +18,23 @@ module Git.RecoverRepository (
import Common
import Git
import Git.Command
-import Git.Fsck
import Git.Objects
import Git.Sha
import Git.Types
-import qualified Git.Config
-import qualified Git.Construct
+import Git.Fsck
+import qualified Git.Config as Config
+import qualified Git.Construct as Construct
import qualified Git.LsTree as LsTree
import qualified Git.LsFiles as LsFiles
import qualified Git.Ref as Ref
import qualified Git.RefLog as RefLog
import qualified Git.UpdateIndex as UpdateIndex
+import qualified Git.Branch as Branch
import Utility.Tmp
import Utility.Rsync
import qualified Data.Set as S
import qualified Data.ByteString.Lazy as L
-import System.Log.Logger
import Data.Tuple.Utils
{- Given a set of bad objects found by git fsck, removes all
@@ -52,7 +53,7 @@ cleanCorruptObjects :: FsckResults -> Repo -> IO MissingObjects
cleanCorruptObjects mmissing r = check mmissing
where
check Nothing = do
- notice "git fsck found a problem but no specific broken objects. Perhaps a corrupt pack file?"
+ putStrLn "git fsck found a problem but no specific broken objects. Perhaps a corrupt pack file?"
ifM (explodePacks r)
( retry S.empty
, return S.empty
@@ -60,7 +61,7 @@ cleanCorruptObjects mmissing r = check mmissing
check (Just bad)
| S.null bad = return S.empty
| otherwise = do
- notice $ unwords
+ putStrLn $ unwords
[ "git fsck found"
, show (S.size bad)
, "broken objects."
@@ -71,7 +72,7 @@ cleanCorruptObjects mmissing r = check mmissing
then retry bad
else return bad
retry oldbad = do
- notice "Re-running git fsck to see if it finds more problems."
+ putStrLn "Re-running git fsck to see if it finds more problems."
v <- findBroken False r
case v of
Nothing -> error $ unwords
@@ -92,7 +93,7 @@ removeLoose r s = do
count <- length <$> filterM doesFileExist fs
if (count > 0)
then do
- notice $ unwords
+ putStrLn $ unwords
[ "removing"
, show count
, "corrupt loose objects"
@@ -107,7 +108,7 @@ explodePacks r = do
if null packs
then return False
else do
- notice "Unpacking all pack files."
+ putStrLn "Unpacking all pack files."
mapM_ go packs
return True
where
@@ -128,7 +129,7 @@ retrieveMissingObjects missing r
| otherwise = withTmpDir "tmprepo" $ \tmpdir -> do
unlessM (boolSystem "git" [Params "init", File tmpdir]) $
error $ "failed to create temp repository in " ++ tmpdir
- tmpr <- Git.Config.read =<< Git.Construct.fromAbsPath tmpdir
+ tmpr <- Config.read =<< Construct.fromAbsPath tmpdir
stillmissing <- pullremotes tmpr (remotes r) fetchrefstags missing
if S.null stillmissing
then return stillmissing
@@ -138,14 +139,14 @@ retrieveMissingObjects missing r
pullremotes tmpr (rmt:rmts) fetchrefs s
| S.null s = return s
| otherwise = do
- notice $ "Trying to recover missing objects from remote " ++ repoDescribe rmt
+ putStrLn $ "Trying to recover missing objects from remote " ++ repoDescribe rmt
ifM (fetchsome rmt fetchrefs tmpr)
( do
void $ copyObjects tmpr r
stillmissing <- findMissing (S.toList s) r
pullremotes tmpr rmts fetchrefs stillmissing
, do
- notice $ unwords
+ putStrLn $ unwords
[ "failed to fetch from remote"
, repoDescribe rmt
, "(will continue without it, but making this remote available may improve recovery)"
@@ -360,7 +361,7 @@ rewriteIndex :: MissingObjects -> Repo -> IO [FilePath]
rewriteIndex missing r
| repoIsLocalBare r = return []
| otherwise = do
- (indexcontents, cleanup) <- LsFiles.stagedDetails [Git.repoPath r] r
+ (indexcontents, cleanup) <- LsFiles.stagedDetails [repoPath r] r
let (bad, good) = partition ismissing indexcontents
unless (null bad) $ do
nukeFile (localGitDir r </> "index")
@@ -390,5 +391,77 @@ addGoodCommits :: [Sha] -> GoodCommits -> GoodCommits
addGoodCommits shas (GoodCommits s) = GoodCommits $
S.union s (S.fromList shas)
-notice :: String -> IO ()
-notice = noticeM "RecoverRepository"
+displayList :: [String] -> String -> IO ()
+displayList items header
+ | null items = return ()
+ | otherwise = do
+ putStrLn header
+ putStr $ unlines $ map (\i -> "\t" ++ i) truncateditems
+ where
+ numitems = length items
+ truncateditems
+ | numitems > 10 = take 10 items ++ ["(and " ++ show (numitems - 10) ++ " more)"]
+ | otherwise = items
+
+{- Put it all together. -}
+runRecovery :: Bool -> Repo -> IO Bool
+runRecovery forced g = do
+ putStrLn "Running git fsck ..."
+ fsckresult <- findBroken False g
+ missing <- cleanCorruptObjects fsckresult g
+ stillmissing <- retrieveMissingObjects missing g
+ if S.null stillmissing
+ then successfulfinish
+ else do
+ putStrLn $ unwords
+ [ show (S.size stillmissing)
+ , "missing objects could not be recovered!"
+ ]
+ if forced
+ then do
+ (remotebranches, goodcommits) <- removeTrackingBranches stillmissing emptyGoodCommits g
+ unless (null remotebranches) $
+ putStrLn $ unwords
+ [ "removed"
+ , show (length remotebranches)
+ , "remote tracking branches that referred to missing objects"
+ ]
+ (resetbranches, deletedbranches, _) <- resetLocalBranches stillmissing goodcommits g
+ displayList (map show resetbranches)
+ "Reset these local branches to old versions before the missing objects were committed:"
+ displayList (map show deletedbranches)
+ "Deleted these local branches, which could not be recovered due to missing objects:"
+ deindexedfiles <- rewriteIndex stillmissing g
+ displayList deindexedfiles
+ "Removed these missing files from the index. You should look at what files are present in your working tree and git add them back to the index when appropriate."
+ if null resetbranches && null deletedbranches
+ then successfulfinish
+ else do
+ unless (repoIsLocalBare g) $ do
+ mcurr <- 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!"
+ ]
+ putStrLn "Successfully recovered repository!"
+ putStrLn "Please carefully check that the changes mentioned above are ok.."
+ return True
+ else do
+ if repoIsLocalBare g
+ then do
+ putStrLn "If you have a clone of this bare repository, you should add it as a remote of this repository, and re-run git-recover-repository."
+ putStrLn "If there are no clones of this repository, you can instead run git-recover-repository with the --force parameter to force recovery to a possibly usable state."
+ else putStrLn "To force a recovery to a usable state, run this command again with the --force parameter."
+ return False
+ where
+ successfulfinish = do
+ mapM_ putStrLn
+ [ "Successfully recovered repository!"
+ , "You should run \"git fsck\" to make sure, but it looks like"
+ , "everything was recovered ok."
+ ]
+ return True