1{- git-annex command
2 -
3 - Copyright 2013 Joey Hess <id@joeyh.name>
4 -
5 - Licensed under the GNU AGPL version 3 or higher.
6 -}
7
8module Command.Repair where
9
10import Command
11import qualified Annex
12import qualified Git.Repair
13import qualified Annex.Branch
14import qualified Git.Ref
15import Git.Types
16import Annex.Version
17import qualified Utility.RawFilePath as R
18
19cmd :: Command
20cmd = noCommit $ dontCheck repoExists $
21	command "repair" SectionMaintenance
22		"recover broken git repository"
23		paramNothing (withParams seek)
24
25seek :: CmdParams -> CommandSeek
26seek = withNothing (commandAction start)
27
28start :: CommandStart
29start = starting "repair" (ActionItemOther Nothing) (SeekInput []) $
30	next $ runRepair =<< Annex.getState Annex.force
31
32runRepair :: Bool -> Annex Bool
33runRepair forced = do
34	(ok, modifiedbranches) <- inRepo $
35		Git.Repair.runRepair isAnnexSyncBranch forced
36	-- This command can be run in git repos not using git-annex,
37	-- so avoid git annex branch stuff in that case.
38	whenM (isJust <$> getVersion) $
39		repairAnnexBranch modifiedbranches
40	return ok
41
42{- After git repository repair, the .git/annex/index file could
43 - still be broken, by pointing to bad objects, or might just be corrupt on
44 - its own. Since this index file is not used to stage things
45 - for long durations of time, it can safely be deleted if it is broken.
46 -
47 - Otherwise, if the git-annex branch was modified by the repair,
48 - commit the index file to the git-annex branch.
49 - This way, if the git-annex branch got rewound to an old version by
50 - the repository repair, or was completely deleted, this will get it back
51 - to a good state. Note that in the unlikely case where the git-annex
52 - branch was rewound to a state that, had new changes from elsewhere not
53 - yet reflected in the index, this does properly merge those into the
54 - index before committing.
55 -}
56repairAnnexBranch :: [Branch] -> Annex ()
57repairAnnexBranch modifiedbranches
58	| Annex.Branch.fullname `elem` modifiedbranches = ifM okindex
59		( commitindex
60		, do
61			nukeindex
62			missingbranch
63		)
64	| otherwise = ifM okindex
65		( noop
66		, do
67			nukeindex
68			ifM (null <$> inRepo (Git.Ref.matching [Annex.Branch.fullname]))
69				( missingbranch
70				, liftIO $ putStrLn "No data was lost."
71				)
72		)
73  where
74	okindex = Annex.Branch.withIndex $ inRepo Git.Repair.checkIndex
75	commitindex = do
76		Annex.Branch.forceCommit "committing index after git repository repair"
77		liftIO $ putStrLn "Successfully recovered the git-annex branch using .git/annex/index"
78	nukeindex = do
79		inRepo $ removeWhenExistsWith R.removeLink . gitAnnexIndex
80		liftIO $ putStrLn "Had to delete the .git/annex/index file as it was corrupt."
81	missingbranch = liftIO $ putStrLn "Since the git-annex branch is not up-to-date anymore. It would be a very good idea to run: git annex fsck --fast"
82
83trackingOrSyncBranch :: Ref -> Bool
84trackingOrSyncBranch b = Git.Repair.isTrackingBranch b || isAnnexSyncBranch b
85
86isAnnexSyncBranch :: Ref -> Bool
87isAnnexSyncBranch b = "refs/synced/" `isPrefixOf` fromRef b
88