index file recovery

This commit is contained in:
Joey Hess 2013-10-22 12:58:04 -04:00
parent 5673fbbd72
commit 3e61749d08
6 changed files with 69 additions and 16 deletions

View file

@ -10,6 +10,7 @@ module Git.RecoverRepository (
retrieveMissingObjects, retrieveMissingObjects,
resetLocalBranches, resetLocalBranches,
removeTrackingBranches, removeTrackingBranches,
rewriteIndex,
emptyGoodCommits, emptyGoodCommits,
) where ) where
@ -19,17 +20,21 @@ import Git.Command
import Git.Fsck import Git.Fsck
import Git.Objects import Git.Objects
import Git.Sha import Git.Sha
import Git.Types
import qualified Git.Config import qualified Git.Config
import qualified Git.Construct import qualified Git.Construct
import qualified Git.LsTree as LsTree import qualified Git.LsTree as LsTree
import qualified Git.LsFiles as LsFiles
import qualified Git.Ref as Ref import qualified Git.Ref as Ref
import qualified Git.RefLog as RefLog import qualified Git.RefLog as RefLog
import qualified Git.UpdateIndex as UpdateIndex
import Utility.Tmp import Utility.Tmp
import Utility.Rsync import Utility.Rsync
import qualified Data.Set as S import qualified Data.Set as S
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import System.Log.Logger import System.Log.Logger
import Data.Tuple.Utils
{- Finds and removes corrupt objects from the repository, returning a list {- Finds and removes corrupt objects from the repository, returning a list
- of all such objects, which need to be found elsewhere to finish - of all such objects, which need to be found elsewhere to finish
@ -349,6 +354,30 @@ verifyTree missing treesha r
-- as long as ls-tree succeeded, we're good -- as long as ls-tree succeeded, we're good
else cleanup else cleanup
{- Rewrites the index file, removing from it any files whose blobs are
- missing. Returns the list of affected files. -}
rewriteIndex :: MissingObjects -> Repo -> IO [FilePath]
rewriteIndex missing r
| repoIsLocalBare r = return []
| otherwise = do
(indexcontents, cleanup) <- LsFiles.stagedDetails [Git.repoPath r] r
let (missing, present) = partition ismissing indexcontents
unless (null missing) $ do
nukeFile (localGitDir r </> "index")
UpdateIndex.streamUpdateIndex r
=<< (catMaybes <$> mapM reinject present)
void cleanup
return $ map fst3 missing
where
getblob (_file, Just sha, Just _mode) = Just sha
getblob _ = Nothing
ismissing = maybe False (`S.member` missing) . getblob
reinject (file, Just sha, Just mode) = case toBlobType mode of
Nothing -> return Nothing
Just blobtype -> Just <$>
UpdateIndex.stageFile sha blobtype file r
reinject _ = return Nothing
newtype GoodCommits = GoodCommits (S.Set Sha) newtype GoodCommits = GoodCommits (S.Set Sha)
emptyGoodCommits :: GoodCommits emptyGoodCommits :: GoodCommits

View file

@ -9,6 +9,7 @@ module Git.Types where
import Network.URI import Network.URI
import qualified Data.Map as M import qualified Data.Map as M
import System.Posix.Types
{- Support repositories on local disk, and repositories accessed via an URL. {- Support repositories on local disk, and repositories accessed via an URL.
- -
@ -81,3 +82,9 @@ readBlobType "100644" = Just FileBlob
readBlobType "100755" = Just ExecutableBlob readBlobType "100755" = Just ExecutableBlob
readBlobType "120000" = Just SymlinkBlob readBlobType "120000" = Just SymlinkBlob
readBlobType _ = Nothing readBlobType _ = Nothing
toBlobType :: FileMode -> Maybe BlobType
toBlobType 0o100644 = Just FileBlob
toBlobType 0o100755 = Just ExecutableBlob
toBlobType 0o120000 = Just SymlinkBlob
toBlobType _ = Nothing

View file

@ -13,6 +13,7 @@ module Git.UpdateIndex (
streamUpdateIndex, streamUpdateIndex,
lsTree, lsTree,
updateIndexLine, updateIndexLine,
stageFile,
unstageFile, unstageFile,
stageSymlink stageSymlink
) where ) where
@ -61,6 +62,11 @@ updateIndexLine :: Sha -> BlobType -> TopFilePath -> String
updateIndexLine sha filetype file = updateIndexLine sha filetype file =
show filetype ++ " blob " ++ show sha ++ "\t" ++ indexPath file show filetype ++ " blob " ++ show sha ++ "\t" ++ indexPath file
stageFile :: Sha -> BlobType -> FilePath -> Repo -> IO Streamer
stageFile sha filetype file repo = do
p <- toTopFilePath file repo
return $ pureStreamer $ updateIndexLine sha filetype p
{- A streamer that removes a file from the index. -} {- A streamer that removes a file from the index. -}
unstageFile :: FilePath -> Repo -> IO Streamer unstageFile :: FilePath -> Repo -> IO Streamer
unstageFile file repo = do unstageFile file repo = do

View file

@ -148,8 +148,5 @@ that was found for it.
if none was found. **done** if none was found. **done**
* (Decided not to touch tags.) * (Decided not to touch tags.)
TODO: The index file can still refer to objects that were missing. The index file can still refer to objects that were missing.
This prevents git commit from working. And simply re-staging things doesn't Rewrite to remove them. **done**
seem to help; git sees the sha is "known" and does not re-add it,
apparently. So, need to do something to clean up the index, while ideally
not losing any staged changes.

View file

@ -15,11 +15,12 @@ It does by deleting all corrupt objects, and retreiving all missing
objects that it can from the remotes of the repository. objects that it can from the remotes of the repository.
If that is not sufficient to fully recover the repository, it can also If that is not sufficient to fully recover the repository, it can also
reset branches back to commits before the corruption happened, and delete reset branches back to commits before the corruption happened, delete
branches that are no longer available due to the lost data. It will only branches that are no longer available due to the lost data, and remove any
do this if run with the `--force` option, since that rewrites history missing files from the index. It will only do this if run with the
and throws out missing data. Note that the `--force` option never touches `--force` option, since that rewrites history and throws out missing data.
tags, even if they are no longer usable due to missing data. Note that the `--force` option never touches tags, even if they are no
longer usable due to missing data.
After running this command, you will probably want to run `git fsck` to After running this command, you will probably want to run `git fsck` to
verify it fixed the repository. Note that fsck may still complain about verify it fixed the repository. Note that fsck may still complain about

View file

@ -68,12 +68,13 @@ main = do
, "remote tracking branches that referred to missing objects" , "remote tracking branches that referred to missing objects"
] ]
(resetbranches, deletedbranches, _) <- Git.RecoverRepository.resetLocalBranches stillmissing goodcommits g (resetbranches, deletedbranches, _) <- Git.RecoverRepository.resetLocalBranches stillmissing goodcommits g
unless (null resetbranches) $ do printList (map show resetbranches)
putStrLn "Reset these local branches to old versions before the missing objects were committed:" "Reset these local branches to old versions before the missing objects were committed:"
putStr $ unlines $ map show resetbranches printList (map show deletedbranches)
unless (null deletedbranches) $ do "Deleted these local branches, which could not be recovered due to missing objects:"
putStrLn "Deleted these local branches, which could not be recovered due to missing objects:" deindexedfiles <- Git.RecoverRepository.rewriteIndex stillmissing g
putStr $ unlines $ map show deletedbranches printList 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."
mcurr <- Git.Branch.currentUnsafe g mcurr <- Git.Branch.currentUnsafe g
case mcurr of case mcurr of
Nothing -> return () Nothing -> return ()
@ -84,3 +85,15 @@ main = do
, "checked out. You may have staged changes in the index that can be committed to recover the lost state of this branch!" , "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." else putStrLn "To force a recovery to a usable state, run this command again with the --force parameter."
printList :: [String] -> String -> IO ()
printList 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