diff --git a/Build/make-sdist.sh b/Build/make-sdist.sh
--- a/Build/make-sdist.sh
+++ b/Build/make-sdist.sh
@@ -13,7 +13,7 @@
 | xargs cp --parents --target-directory dist/$sdist_dir
 
 cd dist
-tar -caf $sdist_dir.tar.gz $sdist_dir
+tar --format=ustar -caf $sdist_dir.tar.gz $sdist_dir
 
 # Check that tarball can be unpacked by cabal.
 # It's picky about tar longlinks etc.
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,74 @@
+git-repair (1.20140914) unstable; urgency=medium
+
+  * Update to build with optparse-applicative 0.10. Closes: #761552
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 14 Sep 2014 12:48:27 -0400
+
+git-repair (1.20140815) unstable; urgency=medium
+
+  * 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.
+  * Merge from git-annex.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 15 Aug 2014 13:49:09 -0400
+
+git-repair (1.20140423) unstable; urgency=medium
+
+  * Improve memory usage when git fsck finds a great many broken objects.
+  * Merge from git-annex.
+
+ -- Joey Hess <joeyh@debian.org>  Wed, 23 Apr 2014 14:01:30 -0400
+
+git-repair (1.20140227) unstable; urgency=medium
+
+  * Optimise unpacking of pack files, and avoid repeated error
+    messages about corrupt pack files.
+  * Add swapping 2 files test case.
+
+ -- Joey Hess <joeyh@debian.org>  Thu, 27 Feb 2014 11:56:27 -0400
+
+git-repair (1.20140115) unstable; urgency=medium
+
+  * Support old git versions from before git fsck --no-dangling was
+    implemented.
+  * Fix bug in packed refs file exploding code that caused a .gitrefs
+    directory to be created instead of .git/refs
+  * Check git version at run time.
+
+ -- Joey Hess <joeyh@debian.org>  Wed, 15 Jan 2014 16:53:30 -0400
+
+git-repair (1.20131213) unstable; urgency=low
+
+  * Improve repair of index files in some situations.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 13 Dec 2013 14:51:51 -0400
+
+git-repair (1.20131203) unstable; urgency=low
+
+  * Fix build deps. Closes: #731179
+
+ -- Joey Hess <joeyh@debian.org>  Tue, 03 Dec 2013 15:02:21 -0400
+
+git-repair (1.20131122) unstable; urgency=low
+
+  * Added test mode, which can be used to randomly corrupt test 
+    repositories, in reproducible ways, which allows easy
+    corruption-driven-development.
+  * Improve repair code in the case where the index file is corrupt,
+    and this hides other problems.
+  * Write a dummy .git/HEAD if the file is missing or corrupt, as
+    git otherwise will not treat the repository as a git repo.
+  * Improve fsck code to find badly corrupted objects that crash git fsck
+    before it can complain about them.
+  * Fixed crashes on bad file encodings.
+  * Can now run 10000 tests (git-repair --test -n 10000 --force)
+    with 0 failures.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 22 Nov 2013 11:16:03 -0400
+
+git-repair (1.20131118) unstable; urgency=low
+
+  * First release
+
+ -- Joey Hess <joeyh@debian.org>  Mon, 18 Nov 2013 13:38:12 -0400
diff --git a/Git/Branch.hs b/Git/Branch.hs
--- a/Git/Branch.hs
+++ b/Git/Branch.hs
@@ -14,6 +14,7 @@
 import Git.Sha
 import Git.Command
 import qualified Git.Ref
+import qualified Git.BuildVersion
 
 {- The currently checked out branch.
  -
@@ -52,9 +53,24 @@
 	diffs = pipeReadStrict
 		[ Param "log"
 		, Param (fromRef origbranch ++ ".." ++ fromRef newbranch)
-		, Params "--oneline -n1"
+		, Param "-n1"
+		, Param "--pretty=%H"
 		] repo
 
+{- Check if it's possible to fast-forward from the old
+ - ref to the new ref.
+ -
+ - This requires there to be a path from the old to the new. -}
+fastForwardable :: Ref -> Ref -> Repo -> IO Bool
+fastForwardable old new repo = not . null <$>
+	pipeReadStrict
+		[ Param "log"
+		, Param $ fromRef old ++ ".." ++ fromRef new
+		, Param "-n1"
+		, Param "--pretty=%H"
+		, Param "--ancestry-path"
+		] repo
+
 {- Given a set of refs that are all known to have commits not
  - on the branch, tries to update the branch by a fast-forward.
  -
@@ -74,7 +90,7 @@
   where
 	no_ff = return False
 	do_ff to = do
-		run [Param "update-ref", Param $ fromRef branch, Param $ fromRef to] repo
+		update branch to repo
 		return True
 	findbest c [] = return $ Just c
 	findbest c (r:rs)
@@ -88,6 +104,31 @@
 			(False, True) -> findbest c rs -- worse
 			(False, False) -> findbest c rs -- same
 
+{- The user may have set commit.gpgsign, indending all their manual
+ - commits to be signed. But signing automatic/background commits could
+ - easily lead to unwanted gpg prompts or failures.
+ -}
+data CommitMode = ManualCommit | AutomaticCommit
+	deriving (Eq)
+
+applyCommitMode :: CommitMode -> [CommandParam] -> [CommandParam]
+applyCommitMode commitmode ps
+	| commitmode == AutomaticCommit && not (Git.BuildVersion.older "2.0.0") =
+		Param "--no-gpg-sign" : ps
+	| otherwise = ps
+
+{- Commit via the usual git command. -}
+commitCommand :: CommitMode -> [CommandParam] -> Repo -> IO Bool
+commitCommand = commitCommand' runBool
+
+{- Commit will fail when the tree is clean. This suppresses that error. -}
+commitQuiet :: CommitMode -> [CommandParam] -> Repo -> IO ()
+commitQuiet commitmode ps = void . tryIO . commitCommand' runQuiet commitmode ps
+
+commitCommand' :: ([CommandParam] -> Repo -> IO a) -> CommitMode -> [CommandParam] -> Repo -> IO a
+commitCommand' runner commitmode ps = runner $
+	Param "commit" : applyCommitMode commitmode ps
+
 {- Commits the index into the specified branch (or other ref), 
  - with the specified parent refs, and returns the committed sha.
  -
@@ -97,30 +138,31 @@
  - Unlike git-commit, does not run any hooks, or examine the work tree
  - in any way.
  -}
-commit :: Bool -> String -> Branch -> [Ref] -> Repo -> IO (Maybe Sha)
-commit allowempty message branch parentrefs repo = do
+commit :: CommitMode -> Bool -> String -> Branch -> [Ref] -> Repo -> IO (Maybe Sha)
+commit commitmode allowempty message branch parentrefs repo = do
 	tree <- getSha "write-tree" $
 		pipeReadStrict [Param "write-tree"] repo
 	ifM (cancommit tree)
 		( do
-			sha <- getSha "commit-tree" $ pipeWriteRead
-				(map Param $ ["commit-tree", fromRef tree] ++ ps)
-				(Just $ flip hPutStr message) repo
+			sha <- getSha "commit-tree" $
+				pipeWriteRead ([Param "commit-tree", Param (fromRef tree)] ++ ps) sendmsg repo
 			update branch sha repo
 			return $ Just sha
 		, return Nothing
 		)
   where
-	ps = concatMap (\r -> ["-p", fromRef r]) parentrefs
+	ps = applyCommitMode commitmode $
+		map Param $ concatMap (\r -> ["-p", fromRef r]) parentrefs
 	cancommit tree
 		| allowempty = return True
 		| otherwise = case parentrefs of
 			[p] -> maybe False (tree /=) <$> Git.Ref.tree p repo
 			_ -> return True
+	sendmsg = Just $ flip hPutStr message
 
-commitAlways :: String -> Branch -> [Ref] -> Repo -> IO Sha
-commitAlways message branch parentrefs repo = fromJust
-	<$> commit True message branch parentrefs repo
+commitAlways :: CommitMode -> String -> Branch -> [Ref] -> Repo -> IO Sha
+commitAlways commitmode message branch parentrefs repo = fromJust
+	<$> commit commitmode True message branch parentrefs repo
 
 {- A leading + makes git-push force pushing a branch. -}
 forcePush :: String -> String
diff --git a/Git/CatFile.hs b/Git/CatFile.hs
--- a/Git/CatFile.hs
+++ b/Git/CatFile.hs
@@ -11,6 +11,7 @@
 	catFileStart',
 	catFileStop,
 	catFile,
+	catFileDetails,
 	catTree,
 	catObject,
 	catObjectDetails,
@@ -52,6 +53,10 @@
 catFile h branch file = catObject h $ Ref $
 	fromRef branch ++ ":" ++ toInternalGitPath file
 
+catFileDetails :: CatFileHandle -> Branch -> FilePath -> IO (Maybe (L.ByteString, Sha, ObjectType))
+catFileDetails h branch file = catObjectDetails h $ Ref $
+	fromRef branch ++ ":" ++ toInternalGitPath file
+
 {- Uses a running git cat-file read the content of an object.
  - Objects that do not exist will have "" returned. -}
 catObject :: CatFileHandle -> Ref -> IO L.ByteString
@@ -103,6 +108,6 @@
 	dropsha = L.drop 21
 
 	parsemodefile b = 
-		let (modestr, file) = separate (== ' ') (encodeW8 $ L.unpack b)
+		let (modestr, file) = separate (== ' ') (decodeBS b)
 		in (file, readmode modestr)
 	readmode = fst . fromMaybe (0, undefined) . headMaybe . readOct
diff --git a/Git/Command.hs b/Git/Command.hs
--- a/Git/Command.hs
+++ b/Git/Command.hs
@@ -9,16 +9,10 @@
 
 module Git.Command where
 
-import System.Process (std_out, env)
-
 import Common
 import Git
 import Git.Types
 import qualified Utility.CoProcess as CoProcess
-#ifdef mingw32_HOST_OS
-import Git.FilePath
-#endif
-import Utility.Batch
 
 {- Constructs a git command line operating on the specified repo. -}
 gitCommandLine :: [CommandParam] -> Repo -> [CommandParam]
@@ -35,12 +29,6 @@
 runBool :: [CommandParam] -> Repo -> IO Bool
 runBool params repo = assertLocal repo $
 	boolSystemEnv "git" (gitCommandLine params repo) (gitEnv repo)
-
-{- Runs git in batch mode. -}
-runBatch :: BatchCommandMaker -> [CommandParam] -> Repo -> IO Bool
-runBatch batchmaker params repo = assertLocal repo $ do
-	let (cmd, params') = batchmaker ("git", gitCommandLine params repo)
-	boolSystemEnv cmd params' (gitEnv repo)
 
 {- Runs git in the specified repo, throwing an error if it fails. -}
 run :: [CommandParam] -> Repo -> IO ()
diff --git a/Git/Config.hs b/Git/Config.hs
--- a/Git/Config.hs
+++ b/Git/Config.hs
@@ -9,7 +9,6 @@
 
 import qualified Data.Map as M
 import Data.Char
-import System.Process (cwd, env)
 import Control.Exception.Extensible
 
 import Common
diff --git a/Git/CurrentRepo.hs b/Git/CurrentRepo.hs
--- a/Git/CurrentRepo.hs
+++ b/Git/CurrentRepo.hs
@@ -37,8 +37,8 @@
 	case wt of
 		Nothing -> return r
 		Just d -> do
-			cwd <- getCurrentDirectory
-			unless (d `dirContains` cwd) $
+			curr <- getCurrentDirectory
+			unless (d `dirContains` curr) $
 				setCurrentDirectory d
 			return $ addworktree wt r
   where
@@ -57,8 +57,8 @@
 	configure Nothing (Just r) = Git.Config.read r
 	configure (Just d) _ = do
 		absd <- absPath d
-		cwd <- getCurrentDirectory
-		r <- newFrom $ Local { gitdir = absd, worktree = Just cwd }
+		curr <- getCurrentDirectory
+		r <- newFrom $ Local { gitdir = absd, worktree = Just curr }
 		Git.Config.read r
 	configure Nothing Nothing = error "Not in a git repository."
 
diff --git a/Git/Fsck.hs b/Git/Fsck.hs
--- a/Git/Fsck.hs
+++ b/Git/Fsck.hs
@@ -23,10 +23,16 @@
 import qualified Git.Version
 
 import qualified Data.Set as S
+import Control.Concurrent.Async
 
 type MissingObjects = S.Set Sha
 
-data FsckResults = FsckFoundMissing MissingObjects | FsckFailed
+data FsckResults 
+	= FsckFoundMissing
+		{ missingObjects :: MissingObjects
+		, missingObjectsTruncated :: Bool
+		}
+	| FsckFailed
 	deriving (Show)
 
 {- Runs fsck to find some of the broken objects in the repository.
@@ -46,20 +52,32 @@
 	(command', params') <- if batchmode
 		then toBatchCommand (command, params)
 		else return (command, params)
-	(output, fsckok) <- processTranscript command' (toCommand params') Nothing
-	let objs = findShas supportsNoDangling output
-	badobjs <- findMissing objs r
+	
+	p@(_, _, _, pid) <- createProcess $
+		(proc command' (toCommand params'))
+			{ std_out = CreatePipe
+			, std_err = CreatePipe
+			}
+	(bad1, bad2) <- concurrently
+		(readMissingObjs maxobjs r supportsNoDangling (stdoutHandle p))
+		(readMissingObjs maxobjs r supportsNoDangling (stderrHandle p))
+	fsckok <- checkSuccessProcess pid
+	let truncated = S.size bad1 == maxobjs || S.size bad1 == maxobjs
+	let badobjs = S.union bad1 bad2
+
 	if S.null badobjs && not fsckok
 		then return FsckFailed
-		else return $ FsckFoundMissing badobjs
+		else return $ FsckFoundMissing badobjs truncated
+  where
+	maxobjs = 10000
 
 foundBroken :: FsckResults -> Bool
 foundBroken FsckFailed = True
-foundBroken (FsckFoundMissing s) = not (S.null s)
+foundBroken (FsckFoundMissing s _) = not (S.null s)
 
 knownMissing :: FsckResults -> MissingObjects
 knownMissing FsckFailed = S.empty
-knownMissing (FsckFoundMissing s) = s
+knownMissing (FsckFoundMissing s _) = s
 
 {- Finds objects that are missing from the git repsitory, or are corrupt.
  -
@@ -68,6 +86,11 @@
  -}
 findMissing :: [Sha] -> Repo -> IO MissingObjects
 findMissing objs r = S.fromList <$> filterM (`isMissing` r) objs
+
+readMissingObjs :: Int -> Repo -> Bool -> Handle -> IO MissingObjects
+readMissingObjs maxobjs r supportsNoDangling h = do
+	objs <- take maxobjs . findShas supportsNoDangling <$> hGetContents h
+	findMissing objs r
 
 isMissing :: Sha -> Repo -> IO Bool
 isMissing s r = either (const True) (const False) <$> tryIO dump
diff --git a/Git/Index.hs b/Git/Index.hs
--- a/Git/Index.hs
+++ b/Git/Index.hs
@@ -30,3 +30,7 @@
 
 indexFile :: Repo -> FilePath
 indexFile r = localGitDir r </> "index"
+
+{- Git locks the index by creating this file. -}
+indexFileLock :: Repo -> FilePath
+indexFileLock r = indexFile r ++ ".lock"
diff --git a/Git/LsFiles.hs b/Git/LsFiles.hs
--- a/Git/LsFiles.hs
+++ b/Git/LsFiles.hs
@@ -132,8 +132,8 @@
 	-- git diff returns filenames relative to the top of the git repo;
 	-- convert to filenames relative to the cwd, like git ls-files.
 	let top = repoPath repo
-	cwd <- getCurrentDirectory
-	return (map (\f -> relPathDirToFile cwd $ top </> f) fs, cleanup)
+	currdir <- getCurrentDirectory
+	return (map (\f -> relPathDirToFile currdir $ top </> f) fs, cleanup)
   where
 	prefix = [Params "diff --name-only --diff-filter=T -z"]
 	suffix = Param "--" : (if null l then [File "."] else map File l)
diff --git a/Git/Repair.hs b/Git/Repair.hs
--- a/Git/Repair.hs
+++ b/Git/Repair.hs
@@ -1,7 +1,6 @@
 {- git repository recovery
-import qualified Data.Set as S
  -
- - Copyright 2013 Joey Hess <joey@kitenet.net>
+ - Copyright 2013-2014 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -9,6 +8,7 @@
 module Git.Repair (
 	runRepair,
 	runRepairOf,
+	removeBadBranches,
 	successfulRepair,
 	cleanCorruptObjects,
 	retrieveMissingObjects,
@@ -45,35 +45,18 @@
 import Data.Tuple.Utils
 
 {- Given a set of bad objects found by git fsck, which may not
- - be complete, finds and removes all corrupt objects,
- - and returns missing objects.
- -}
-cleanCorruptObjects :: FsckResults -> Repo -> IO FsckResults
+ - be complete, finds and removes all corrupt objects. -}
+cleanCorruptObjects :: FsckResults -> Repo -> IO ()
 cleanCorruptObjects fsckresults r = do
 	void $ explodePacks r
-	objs <- listLooseObjectShas r
-	mapM_ (tryIO . allowRead . looseObjectFile r) objs
-	bad <- findMissing objs r
-	void $ removeLoose r $ S.union bad (knownMissing fsckresults)
-	-- Rather than returning the loose objects that were removed, re-run
-	-- fsck. Other missing objects may have been in the packs,
-	-- and this way fsck will find them.
-	findBroken False r
-
-removeLoose :: Repo -> MissingObjects -> IO Bool
-removeLoose r s = do
-	fs <- filterM doesFileExist (map (looseObjectFile r) (S.toList s))
-	let count = length fs
-	if count > 0
-		then do
-			putStrLn $ unwords
-				[ "Removing"
-				, show count
-				, "corrupt loose objects."
-				]
-			mapM_ nukeFile fs
-			return True
-		else return False
+	mapM_ removeLoose (S.toList $ knownMissing fsckresults)
+	mapM_ removeBad =<< listLooseObjectShas r
+  where
+	removeLoose s = nukeFile (looseObjectFile r s)
+	removeBad s = do
+		void $ tryIO $ allowRead $  looseObjectFile r s
+		whenM (isMissing s r) $
+			removeLoose s
 
 {- Explodes all pack files, and deletes them.
  -
@@ -132,7 +115,9 @@
 				void $ copyObjects tmpr r
 				case stillmissing of
 					FsckFailed -> return $ FsckFailed
-					FsckFoundMissing s -> FsckFoundMissing <$> findMissing (S.toList s) r
+					FsckFoundMissing s t -> FsckFoundMissing 
+						<$> findMissing (S.toList s) r
+						<*> pure t
 			, return stillmissing
 			)
 	pullremotes tmpr (rmt:rmts) fetchrefs ms
@@ -145,9 +130,9 @@
 					void $ copyObjects tmpr r
 					case ms of
 						FsckFailed -> pullremotes tmpr rmts fetchrefs ms
-						FsckFoundMissing s -> do
+						FsckFoundMissing s t -> do
 							stillmissing <- findMissing (S.toList s) r
-							pullremotes tmpr rmts fetchrefs (FsckFoundMissing stillmissing)
+							pullremotes tmpr rmts fetchrefs (FsckFoundMissing stillmissing t)
 				, pullremotes tmpr rmts fetchrefs ms
 				)
 	fetchfrom fetchurl ps = runBool $
@@ -207,8 +192,11 @@
  - any branches (filtered by a predicate) that reference them
  - Returns a list of all removed branches.
  -}
-removeBadBranches :: (Ref -> Bool) -> MissingObjects -> GoodCommits -> Repo -> IO ([Branch], GoodCommits)
-removeBadBranches removablebranch missing goodcommits r =
+removeBadBranches :: (Ref -> Bool) -> Repo -> IO [Branch]
+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
   where
 	go removed gcs [] = return (removed, gcs)
@@ -220,6 +208,11 @@
 				nukeBranchRef b r
 				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.
  - git show-ref does not output refs to commits that are directly
  - corrupted, so it is not used.
@@ -295,7 +288,7 @@
 			then return (Just c, gcs')
 			else findfirst gcs' cs
 
-{- Verifies tha none of the missing objects in the set are used by
+{- Verifies that none of the missing objects in the set are used by
  - the commit. Also adds to a set of commit shas that have been verified to
  - be good, which can be passed into subsequent calls to avoid
  - redundant work when eg, chasing down branches to find the first
@@ -455,8 +448,12 @@
 	if foundBroken fsckresult
 		then runRepair' removablebranch fsckresult forced Nothing g
 		else do
-			putStrLn "No problems found."
-			return (True, [])
+			bad <- badBranches S.empty g
+			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 fsckresult removablebranch forced referencerepo g = do
@@ -465,14 +462,15 @@
 
 runRepair' :: (Ref -> Bool) -> FsckResults -> Bool -> Maybe FilePath -> Repo -> IO (Bool, [Branch])
 runRepair' removablebranch fsckresult forced referencerepo g = do
-	missing <- cleanCorruptObjects fsckresult g
+	cleanCorruptObjects fsckresult g
+	missing <- findBroken False g
 	stillmissing <- retrieveMissingObjects missing referencerepo g
 	case stillmissing of
-		FsckFoundMissing s
+		FsckFoundMissing s t
 			| S.null s -> if repoIsLocalBare g
-				then successfulfinish []
+				then checkbadbranches s
 				else ifM (checkIndex g)
-					( successfulfinish []
+					( checkbadbranches s
 					, do
 						putStrLn "No missing objects found, but the index file is corrupt!"
 						if forced
@@ -481,7 +479,7 @@
 					)
 			| otherwise -> if forced
 				then ifM (checkIndex g)
-					( continuerepairs s
+					( forcerepair s t
 					, corruptedindex
 					)
 				else do
@@ -493,17 +491,17 @@
 		FsckFailed
 			| forced -> ifM (pure (repoIsLocalBare g) <||> checkIndex g)
 				( do
-					missing' <- cleanCorruptObjects FsckFailed g
-					case missing' of
+					cleanCorruptObjects FsckFailed g
+					stillmissing' <- findBroken False g
+					case stillmissing' of
 						FsckFailed -> return (False, [])
-						FsckFoundMissing stillmissing' ->
-							continuerepairs stillmissing'
+						FsckFoundMissing s t -> forcerepair s t
 				, corruptedindex
 				)
 			| otherwise -> unsuccessfulfinish
   where
-	continuerepairs stillmissing = do
-		(removedbranches, goodcommits) <- removeBadBranches removablebranch stillmissing emptyGoodCommits g
+	repairbranches missing = do
+		(removedbranches, goodcommits) <- removeBadBranches' removablebranch missing emptyGoodCommits g
 		let remotebranches = filter isTrackingBranch removedbranches
 		unless (null remotebranches) $
 			putStrLn $ unwords
@@ -511,32 +509,53 @@
 				, show (length remotebranches)
 				, "remote tracking branches that referred to missing objects."
 				]
-		(resetbranches, deletedbranches, _) <- resetLocalBranches stillmissing goodcommits g
+		(resetbranches, deletedbranches, _) <- resetLocalBranches missing goodcommits g
 		displayList (map fromRef resetbranches)
 			"Reset these local branches to old versions before the missing objects were committed:"
 		displayList (map fromRef deletedbranches)
 			"Deleted these local branches, which could not be recovered due to missing objects:"
+		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
+		modifiedbranches <- repairbranches missing
 		deindexedfiles <- rewriteIndex 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."
-		let modifiedbranches = resetbranches ++ deletedbranches
-		if null resetbranches && null deletedbranches
-			then successfulfinish modifiedbranches
-			else do
-				unless (repoIsLocalBare g) $ do
-					mcurr <- Branch.currentUnsafe g
-					case mcurr of
-						Nothing -> return ()
-						Just curr -> when (any (== curr) modifiedbranches) $ do
+
+		-- When the fsck results were truncated, try
+		-- fscking again, and as long as different
+		-- missing objects are found, continue
+		-- the repair process.
+		if fscktruncated
+			then do
+				fsckresult' <- findBroken False g
+				case fsckresult' of
+					FsckFailed -> do
+						putStrLn "git fsck is failing"
+						return (False, modifiedbranches)
+					FsckFoundMissing s _
+						| S.null s -> successfulfinish modifiedbranches
+						| S.null (s `S.difference` missing) -> do
 							putStrLn $ unwords
-								[ "You currently have"
-								, fromRef curr
-								, "checked out. You may have staged changes in the index that can be committed to recover the lost state of this branch!"
+								[ show (S.size s)
+								, "missing objects could not be recovered!"
 								]
-				putStrLn "Successfully recovered repository!"
-				putStrLn "Please carefully check that the changes mentioned above are ok.."
-				return (True, modifiedbranches)
-	
+							return (False, modifiedbranches)	
+						| otherwise -> do
+							(ok, modifiedbranches') <- runRepairOf fsckresult' removablebranch forced referencerepo g
+							return (ok, modifiedbranches++modifiedbranches')
+			else successfulfinish modifiedbranches
+
 	corruptedindex = do
 		nukeFile (indexFile g)
 		-- The corrupted index can prevent fsck from finding other
@@ -546,12 +565,28 @@
 		putStrLn "Removed the corrupted index file. You should look at what files are present in your working tree and git add them back to the index when appropriate."
 		return result
 
-	successfulfinish modifiedbranches = do
-		mapM_ putStrLn
-			[ "Successfully recovered repository!"
-			, "You should run \"git fsck\" to make sure, but it looks like everything was recovered ok."
-			]
-		return (True, modifiedbranches)
+	successfulfinish modifiedbranches
+		| null modifiedbranches = do
+			mapM_ putStrLn
+				[ "Successfully recovered repository!"
+				, "You should run \"git fsck\" to make sure, but it looks like everything was recovered ok."
+				]
+			return (True, modifiedbranches)
+		| otherwise = do
+			unless (repoIsLocalBare g) $ do
+				mcurr <- Branch.currentUnsafe g
+				case mcurr of
+					Nothing -> return ()
+					Just curr -> when (any (== curr) modifiedbranches) $ do
+						putStrLn $ unwords
+							[ "You currently have"
+							, fromRef 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, modifiedbranches)
+	
 	unsuccessfulfinish = do
 		if repoIsLocalBare g
 			then do
diff --git a/Git/Types.hs b/Git/Types.hs
--- a/Git/Types.hs
+++ b/Git/Types.hs
@@ -11,6 +11,7 @@
 import qualified Data.Map as M
 import System.Posix.Types
 import Utility.SafeCommand
+import Utility.URI ()
 
 {- Support repositories on local disk, and repositories accessed via an URL.
  -
@@ -27,7 +28,7 @@
 	| LocalUnknown FilePath
 	| Url URI
 	| Unknown
-	deriving (Show, Eq)
+	deriving (Show, Eq, Ord)
 
 data Repo = Repo
 	{ location :: RepoLocation
@@ -41,7 +42,7 @@
 	, gitEnv :: Maybe [(String, String)]
 	-- global options to pass to git when running git commands
 	, gitGlobalOpts :: [CommandParam]
-	} deriving (Show, Eq)
+	} deriving (Show, Eq, Ord)
 
 type RemoteName = String
 
diff --git a/Git/UpdateIndex.hs b/Git/UpdateIndex.hs
--- a/Git/UpdateIndex.hs
+++ b/Git/UpdateIndex.hs
@@ -15,6 +15,7 @@
 	startUpdateIndex,
 	stopUpdateIndex,
 	lsTree,
+	lsSubTree,
 	updateIndexLine,
 	stageFile,
 	unstageFile,
@@ -29,7 +30,6 @@
 import Git.Sha
 
 import Control.Exception (bracket)
-import System.Process (std_in)
 
 {- Streamers are passed a callback and should feed it lines in the form
  - read by update-index, and generated by ls-tree. -}
@@ -74,6 +74,13 @@
 	void $ cleanup
   where
 	params = map Param ["ls-tree", "-z", "-r", "--full-tree", x]
+lsSubTree :: Ref -> FilePath -> Repo -> Streamer
+lsSubTree (Ref x) p repo streamer = do
+	(s, cleanup) <- pipeNullSplit params repo
+	mapM_ streamer s
+	void $ cleanup
+  where
+	params = map Param ["ls-tree", "-z", "-r", "--full-tree", x, p]
 
 {- Generates a line suitable to be fed into update-index, to add
  - a given file with a given sha. -}
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,6 @@
 
 # hothasktags chokes on some template haskell etc, so ignore errors
 tags:
-	find . | grep -v /.git/ | grep -v /tmp/ | grep -v /dist/ | grep -v /doc/ | egrep '\.hs$$' | xargs hothasktags > tags 2>/dev/null
+	(for f in $$(find . | grep -v /.git/ | grep -v /tmp/ | grep -v /dist/ | grep -v /doc/ | egrep '\.hs$$'); do hothasktags -c --cpp -c -traditional -c --include=dist/build/autogen/cabal_macros.h $$f; done) 2>/dev/null | sort > tags
 
 .PHONY: tags
diff --git a/Utility/Applicative.hs b/Utility/Applicative.hs
--- a/Utility/Applicative.hs
+++ b/Utility/Applicative.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.Applicative where
diff --git a/Utility/Batch.hs b/Utility/Batch.hs
--- a/Utility/Batch.hs
+++ b/Utility/Batch.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
@@ -16,7 +16,6 @@
 import System.Posix.Process
 #endif
 import qualified Control.Exception as E
-import System.Process (env)
 
 {- Runs an operation, at batch priority.
  -
diff --git a/Utility/CoProcess.hs b/Utility/CoProcess.hs
--- a/Utility/CoProcess.hs
+++ b/Utility/CoProcess.hs
@@ -3,7 +3,7 @@
  -
  - Copyright 2012-2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
@@ -37,8 +37,8 @@
 	}
 
 start :: Int -> FilePath -> [String] -> Maybe [(String, String)] -> IO CoProcessHandle
-start numrestarts cmd params env = do
-	s <- start' $ CoProcessSpec numrestarts cmd params env
+start numrestarts cmd params environ = do
+	s <- start' $ CoProcessSpec numrestarts cmd params environ
 	newMVar s
 
 start' :: CoProcessSpec -> IO CoProcessState
@@ -62,7 +62,7 @@
 	s <- readMVar ch
 	restartable s (send $ coProcessTo s) $ const $
 		restartable s (hFlush $ coProcessTo s) $ const $
-			restartable s (receive $ coProcessFrom s) $
+			restartable s (receive $ coProcessFrom s)
 				return
   where
   	restartable s a cont
diff --git a/Utility/Data.hs b/Utility/Data.hs
--- a/Utility/Data.hs
+++ b/Utility/Data.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.Data where
diff --git a/Utility/Directory.hs b/Utility/Directory.hs
--- a/Utility/Directory.hs
+++ b/Utility/Directory.hs
@@ -1,8 +1,8 @@
-{- directory manipulation
+{- directory traversal and manipulation
  -
  - Copyright 2011-2014 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
@@ -11,13 +11,21 @@
 
 import System.IO.Error
 import System.Directory
-import Control.Exception (throw)
+import Control.Exception (throw, bracket)
 import Control.Monad
 import Control.Monad.IfElse
 import System.FilePath
 import Control.Applicative
+import Control.Concurrent
 import System.IO.Unsafe (unsafeInterleaveIO)
+import Data.Maybe
 
+#ifdef mingw32_HOST_OS
+import qualified System.Win32 as Win32
+#else
+import qualified System.Posix as Posix
+#endif
+
 import Utility.PosixFiles
 import Utility.SafeCommand
 import Utility.Tmp
@@ -43,7 +51,7 @@
  - When the directory does not exist, no exception is thrown,
  - instead, [] is returned. -}
 dirContentsRecursive :: FilePath -> IO [FilePath]
-dirContentsRecursive topdir = dirContentsRecursiveSkipping (const False) True topdir
+dirContentsRecursive = dirContentsRecursiveSkipping (const False) True
 
 {- Skips directories whose basenames match the skipdir. -}
 dirContentsRecursiveSkipping :: (FilePath -> Bool) -> Bool -> FilePath -> IO [FilePath]
@@ -133,3 +141,90 @@
 #else
 	go = removeFile file
 #endif
+
+#ifndef mingw32_HOST_OS
+data DirectoryHandle = DirectoryHandle IsOpen Posix.DirStream
+#else
+data DirectoryHandle = DirectoryHandle IsOpen Win32.HANDLE Win32.FindData (MVar ())
+#endif
+
+type IsOpen = MVar () -- full when the handle is open
+
+openDirectory :: FilePath -> IO DirectoryHandle
+openDirectory path = do
+#ifndef mingw32_HOST_OS
+	dirp <- Posix.openDirStream path
+	isopen <- newMVar ()
+	return (DirectoryHandle isopen dirp)
+#else
+	(h, fdat) <- Win32.findFirstFile (path </> "*")
+	-- Indicate that the fdat contains a filename that readDirectory
+	-- has not yet returned, by making the MVar be full.
+	-- (There's always at least a "." entry.)
+	alreadyhave <- newMVar ()
+	isopen <- newMVar ()
+	return (DirectoryHandle isopen h fdat alreadyhave)
+#endif
+
+closeDirectory :: DirectoryHandle -> IO ()
+#ifndef mingw32_HOST_OS
+closeDirectory (DirectoryHandle isopen dirp) =
+	whenOpen isopen $
+		Posix.closeDirStream dirp
+#else
+closeDirectory (DirectoryHandle isopen h _ alreadyhave) =
+	whenOpen isopen $ do
+		_ <- tryTakeMVar alreadyhave
+		Win32.findClose h
+#endif
+  where
+	whenOpen :: IsOpen -> IO () -> IO ()
+	whenOpen mv f = do
+		v <- tryTakeMVar mv
+		when (isJust v) f
+
+{- |Reads the next entry from the handle. Once the end of the directory
+is reached, returns Nothing and automatically closes the handle.
+-}
+readDirectory :: DirectoryHandle -> IO (Maybe FilePath)
+#ifndef mingw32_HOST_OS
+readDirectory hdl@(DirectoryHandle _ dirp) = do
+	e <- Posix.readDirStream dirp
+	if null e
+		then do
+			closeDirectory hdl
+			return Nothing
+		else return (Just e)
+#else
+readDirectory hdl@(DirectoryHandle _ h fdat mv) = do
+	-- If the MVar is full, then the filename in fdat has
+	-- not yet been returned. Otherwise, need to find the next
+	-- file.
+	r <- tryTakeMVar mv
+	case r of
+		Just () -> getfn
+		Nothing -> do
+			more <- Win32.findNextFile h fdat
+			if more
+				then getfn
+				else do
+					closeDirectory hdl
+					return Nothing
+  where
+	getfn = do
+		filename <- Win32.getFindDataFileName fdat
+		return (Just filename)
+#endif
+
+-- True only when directory exists and contains nothing.
+-- Throws exception if directory does not exist.
+isDirectoryEmpty :: FilePath -> IO Bool
+isDirectoryEmpty d = bracket (openDirectory d) closeDirectory check
+  where
+	check h = do
+		v <- readDirectory h
+		case v of
+			Nothing -> return True
+			Just f
+				| not (dirCruft f) -> return False
+				| otherwise -> check h
diff --git a/Utility/Env.hs b/Utility/Env.hs
--- a/Utility/Env.hs
+++ b/Utility/Env.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
diff --git a/Utility/Exception.hs b/Utility/Exception.hs
--- a/Utility/Exception.hs
+++ b/Utility/Exception.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2011-2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE ScopedTypeVariables #-}
@@ -18,7 +18,7 @@
 
 {- Catches IO errors and returns a Bool -}
 catchBoolIO :: IO Bool -> IO Bool
-catchBoolIO a = catchDefaultIO False a
+catchBoolIO = catchDefaultIO False
 
 {- Catches IO errors and returns a Maybe -}
 catchMaybeIO :: IO a -> IO (Maybe a)
diff --git a/Utility/FileMode.hs b/Utility/FileMode.hs
--- a/Utility/FileMode.hs
+++ b/Utility/FileMode.hs
@@ -2,22 +2,25 @@
  -
  - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
 
 module Utility.FileMode where
 
-import Common
-
+import System.IO
+import Control.Monad
 import Control.Exception (bracket)
 import System.PosixCompat.Types
+import Utility.PosixFiles
 #ifndef mingw32_HOST_OS
 import System.Posix.Files
 #endif
 import Foreign (complement)
 
+import Utility.Exception
+
 {- Applies a conversion function to a file's mode. -}
 modifyFileMode :: FilePath -> (FileMode -> FileMode) -> IO ()
 modifyFileMode f convert = void $ modifyFileMode' f convert
@@ -56,6 +59,12 @@
 executeModes :: [FileMode]
 executeModes = [ownerExecuteMode, groupExecuteMode, otherExecuteMode]
 
+otherGroupModes :: [FileMode]
+otherGroupModes = 
+	[ groupReadMode, otherReadMode
+	, groupWriteMode, otherWriteMode
+	]
+
 {- Removes the write bits from a file. -}
 preventWrite :: FilePath -> IO ()
 preventWrite f = modifyFileMode f $ removeModes writeModes
@@ -99,13 +108,20 @@
 #ifndef mingw32_HOST_OS
 noUmask mode a
 	| mode == stdFileMode = a
-	| otherwise = bracket setup cleanup go
+	| otherwise = withUmask nullFileMode a
+#else
+noUmask _ a = a
+#endif
+
+withUmask :: FileMode -> IO a -> IO a
+#ifndef mingw32_HOST_OS
+withUmask umask a = bracket setup cleanup go
   where
-	setup = setFileCreationMask nullFileMode
+	setup = setFileCreationMask umask
 	cleanup = setFileCreationMask
 	go _ = a
 #else
-noUmask _ a = a
+withUmask _ a = a
 #endif
 
 combineModes :: [FileMode] -> FileMode
@@ -127,14 +143,16 @@
 #endif
 
 {- Writes a file, ensuring that its modes do not allow it to be read
- - by anyone other than the current user, before any content is written.
+ - or written by anyone other than the current user,
+ - before any content is written.
  -
+ - When possible, this is done using the umask.
+ -
  - On a filesystem that does not support file permissions, this is the same
  - as writeFile.
  -}
 writeFileProtected :: FilePath -> String -> IO ()
-writeFileProtected file content = withFile file WriteMode $ \h -> do
-	void $ tryIO $
-		modifyFileMode file $
-			removeModes [groupReadMode, otherReadMode]
-	hPutStr h content
+writeFileProtected file content = withUmask 0o0077 $
+	withFile file WriteMode $ \h -> do
+		void $ tryIO $ modifyFileMode file $ removeModes otherGroupModes
+		hPutStr h content
diff --git a/Utility/FileSystemEncoding.hs b/Utility/FileSystemEncoding.hs
--- a/Utility/FileSystemEncoding.hs
+++ b/Utility/FileSystemEncoding.hs
@@ -1,14 +1,17 @@
 {- GHC File system encoding handling.
  -
- - Copyright 2012-2013 Joey Hess <joey@kitenet.net>
+ - Copyright 2012-2014 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Utility.FileSystemEncoding (
 	fileEncoding,
 	withFilePath,
 	md5FilePath,
+	decodeBS,
 	decodeW8,
 	encodeW8,
 	truncateFilePath,
@@ -22,13 +25,24 @@
 import qualified Data.Hash.MD5 as MD5
 import Data.Word
 import Data.Bits.Utils
+import qualified Data.ByteString.Lazy as L
+#ifdef mingw32_HOST_OS
+import qualified Data.ByteString.Lazy.UTF8 as L8
+#endif
 
 {- Sets a Handle to use the filesystem encoding. This causes data
  - written or read from it to be encoded/decoded the same
  - as ghc 7.4 does to filenames etc. This special encoding
- - allows "arbitrary undecodable bytes to be round-tripped through it". -}
+ - allows "arbitrary undecodable bytes to be round-tripped through it".
+ -}
 fileEncoding :: Handle -> IO ()
+#ifndef mingw32_HOST_OS
 fileEncoding h = hSetEncoding h =<< Encoding.getFileSystemEncoding
+#else
+{- The file system encoding does not work well on Windows,
+ - and Windows only has utf FilePaths anyway. -}
+fileEncoding h = hSetEncoding h Encoding.utf8
+#endif
 
 {- Marshal a Haskell FilePath into a NUL terminated C string using temporary
  - storage. The FilePath is encoded using the filesystem encoding,
@@ -60,6 +74,16 @@
 md5FilePath :: FilePath -> MD5.Str
 md5FilePath = MD5.Str . _encodeFilePath
 
+{- Decodes a ByteString into a FilePath, applying the filesystem encoding. -}
+decodeBS :: L.ByteString -> FilePath
+#ifndef mingw32_HOST_OS
+decodeBS = encodeW8 . L.unpack
+#else
+{- On Windows, we assume that the ByteString is utf-8, since Windows
+ - only uses unicode for filenames. -}
+decodeBS = L8.toString
+#endif
+
 {- Converts a [Word8] to a FilePath, encoding using the filesystem encoding.
  -
  - w82c produces a String, which may contain Chars that are invalid
@@ -84,6 +108,7 @@
  - cost of efficiency when running on a large FilePath.
  -}
 truncateFilePath :: Int -> FilePath -> FilePath
+#ifndef mingw32_HOST_OS
 truncateFilePath n = go . reverse
   where
   	go f =
@@ -91,3 +116,17 @@
 		in if length bytes <= n
 			then reverse f
 			else go (drop 1 f)
+#else
+{- On Windows, count the number of bytes used by each utf8 character. -}
+truncateFilePath n = reverse . go [] n . L8.fromString
+  where
+	go coll cnt bs
+		| cnt <= 0 = coll
+		| otherwise = case L8.decode bs of
+			Just (c, x) | c /= L8.replacement_char ->
+				let x' = fromIntegral x
+				in if cnt - x' < 0
+					then coll
+					else go (c:coll) (cnt - x') (L8.drop 1 bs)
+			_ -> coll
+#endif
diff --git a/Utility/Format.hs b/Utility/Format.hs
--- a/Utility/Format.hs
+++ b/Utility/Format.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010, 2011 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.Format (
diff --git a/Utility/Metered.hs b/Utility/Metered.hs
--- a/Utility/Metered.hs
+++ b/Utility/Metered.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2012, 2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE TypeSynonymInstances #-}
diff --git a/Utility/Misc.hs b/Utility/Misc.hs
--- a/Utility/Misc.hs
+++ b/Utility/Misc.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010-2011 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
@@ -108,18 +108,6 @@
 		| val `isPrefixOf` s =
 			go (replacement:acc) vs (drop (length val) s)
 		| otherwise = go acc rest s
-
-{- Given two orderings, returns the second if the first is EQ and returns
- - the first otherwise.
- -
- - Example use:
- -
- - compare lname1 lname2 `thenOrd` compare fname1 fname2
- -}
-thenOrd :: Ordering -> Ordering -> Ordering
-thenOrd EQ x = x
-thenOrd x _ = x
-{-# INLINE thenOrd #-}
 
 {- Wrapper around hGetBufSome that returns a String.
  -
diff --git a/Utility/Monad.hs b/Utility/Monad.hs
--- a/Utility/Monad.hs
+++ b/Utility/Monad.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.Monad where
diff --git a/Utility/Path.hs b/Utility/Path.hs
--- a/Utility/Path.hs
+++ b/Utility/Path.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010-2014 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE PackageImports, CPP #-}
@@ -18,7 +18,6 @@
 import Control.Applicative
 
 #ifdef mingw32_HOST_OS
-import Data.Char
 import qualified System.FilePath.Posix as Posix
 #else
 import System.Posix.Files
diff --git a/Utility/PosixFiles.hs b/Utility/PosixFiles.hs
--- a/Utility/PosixFiles.hs
+++ b/Utility/PosixFiles.hs
@@ -4,7 +4,7 @@
  -
  - Copyright 2014 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
diff --git a/Utility/Process.hs b/Utility/Process.hs
--- a/Utility/Process.hs
+++ b/Utility/Process.hs
@@ -3,14 +3,14 @@
  -
  - Copyright 2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP, Rank2Types #-}
 
 module Utility.Process (
 	module X,
-	CreateProcess,
+	CreateProcess(..),
 	StdHandle(..),
 	readProcess,
 	readProcessEnv,
@@ -31,6 +31,7 @@
 	stdinHandle,
 	stdoutHandle,
 	stderrHandle,
+	processHandle,
 	devNull,
 ) where
 
@@ -166,10 +167,10 @@
 processTranscript cmd opts input = processTranscript' cmd opts Nothing input
 
 processTranscript' :: String -> [String] -> Maybe [(String, String)] -> (Maybe String) -> IO (String, Bool)
+processTranscript' cmd opts environ input = do
 #ifndef mingw32_HOST_OS
 {- This implementation interleves stdout and stderr in exactly the order
  - the process writes them. -}
-processTranscript' cmd opts environ input = do
 	(readf, writef) <- createPipe
 	readh <- fdToHandle readf
 	writeh <- fdToHandle writef
@@ -183,24 +184,13 @@
 	hClose writeh
 
 	get <- mkreader readh
-
-	-- now write and flush any input
-	case input of
-		Just s -> do
-			let inh = stdinHandle p
-			unless (null s) $ do
-				hPutStr inh s
-				hFlush inh
-			hClose inh
-		Nothing -> return ()
-
+	writeinput input p
 	transcript <- get
 
 	ok <- checkSuccessProcess pid
 	return (transcript, ok)
 #else
 {- This implementation for Windows puts stderr after stdout. -}
-processTranscript' cmd opts environ input = do
 	p@(_, _, _, pid) <- createProcess $
 		(proc cmd opts)
 			{ std_in = if isJust input then CreatePipe else Inherit
@@ -211,17 +201,9 @@
 
 	getout <- mkreader (stdoutHandle p)
 	geterr <- mkreader (stderrHandle p)
-
-	case input of
-		Just s -> do
-			let inh = stdinHandle p
-			unless (null s) $ do
-				hPutStr inh s
-				hFlush inh
-			hClose inh
-		Nothing -> return ()
-	
+	writeinput input p
 	transcript <- (++) <$> getout <*> geterr
+
 	ok <- checkSuccessProcess pid
 	return (transcript, ok)
 #endif
@@ -236,6 +218,14 @@
 			takeMVar v
 			return s
 
+	writeinput (Just s) p = do
+		let inh = stdinHandle p
+		unless (null s) $ do
+			hPutStr inh s
+			hFlush inh
+		hClose inh
+	writeinput Nothing _ = return ()
+
 {- Runs a CreateProcessRunner, on a CreateProcess structure, that
  - is adjusted to pipe only from/to a single StdHandle, and passes
  - the resulting Handle to an action. -}
@@ -312,6 +302,9 @@
 bothHandles :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Handle, Handle)
 bothHandles (Just hin, Just hout, _, _) = (hin, hout)
 bothHandles _ = error "expected bothHandles"
+
+processHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> ProcessHandle
+processHandle (_, _, _, pid) = pid
 
 {- Debugging trace for a CreateProcess. -}
 debugProcess :: CreateProcess -> IO ()
diff --git a/Utility/QuickCheck.hs b/Utility/QuickCheck.hs
--- a/Utility/QuickCheck.hs
+++ b/Utility/QuickCheck.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2012-2014 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# OPTIONS_GHC -fno-warn-orphans #-}
@@ -28,10 +28,10 @@
 
 {- Times before the epoch are excluded. -}
 instance Arbitrary POSIXTime where
-	arbitrary = nonNegative arbitrarySizedIntegral
+	arbitrary = fromInteger <$> nonNegative arbitrarySizedIntegral
 
 instance Arbitrary EpochTime where
-	arbitrary = nonNegative arbitrarySizedIntegral
+	arbitrary = fromInteger <$> nonNegative arbitrarySizedIntegral
 
 {- Pids are never negative, or 0. -}
 instance Arbitrary ProcessID where
diff --git a/Utility/Rsync.hs b/Utility/Rsync.hs
--- a/Utility/Rsync.hs
+++ b/Utility/Rsync.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.Rsync where
@@ -124,6 +124,9 @@
  - after the \r is the number of bytes processed. After the number,
  - there must appear some whitespace, or we didn't get the whole number,
  - and return the \r and part we did get, for later processing.
+ -
+ - In some locales, the number will have one or more commas in the middle
+ - of it.
  -}
 parseRsyncProgress :: String -> (Maybe Integer, String)
 parseRsyncProgress = go [] . reverse . progresschunks
@@ -142,7 +145,7 @@
 	parsebytes s = case break isSpace s of
 		([], _) -> Nothing
 		(_, []) -> Nothing
-		(b, _) -> readish b
+		(b, _) -> readish $ filter (/= ',') b
 
 {- Filters options to those that are safe to pass to rsync in server mode,
  - without causing it to eg, expose files. -}
diff --git a/Utility/SafeCommand.hs b/Utility/SafeCommand.hs
--- a/Utility/SafeCommand.hs
+++ b/Utility/SafeCommand.hs
@@ -2,14 +2,13 @@
  -
  - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 module Utility.SafeCommand where
 
 import System.Exit
 import Utility.Process
-import System.Process (env)
 import Data.String.Utils
 import Control.Applicative
 import System.FilePath
diff --git a/Utility/ThreadScheduler.hs b/Utility/ThreadScheduler.hs
--- a/Utility/ThreadScheduler.hs
+++ b/Utility/ThreadScheduler.hs
@@ -3,17 +3,20 @@
  - Copyright 2012, 2013 Joey Hess <joey@kitenet.net>
  - Copyright 2011 Bas van Dijk & Roel van Dijk
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
 
 module Utility.ThreadScheduler where
 
-import Common
-
+import Control.Monad
 import Control.Concurrent
 #ifndef mingw32_HOST_OS
+import Control.Monad.IfElse
+import System.Posix.IO
+#endif
+#ifndef mingw32_HOST_OS
 import System.Posix.Signals
 #ifndef __ANDROID__
 import System.Posix.Terminal
@@ -54,8 +57,7 @@
 waitForTermination :: IO ()
 waitForTermination = do
 #ifdef mingw32_HOST_OS
-	runEvery (Seconds 600) $
-		void getLine
+	forever $ threadDelaySeconds (Seconds 6000)
 #else
 	lock <- newEmptyMVar
 	let check sig = void $
diff --git a/Utility/Tmp.hs b/Utility/Tmp.hs
--- a/Utility/Tmp.hs
+++ b/Utility/Tmp.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
@@ -25,13 +25,20 @@
  - then moving it into place. The temp file is stored in the same
  - directory as the final file to avoid cross-device renames. -}
 viaTmp :: (FilePath -> String -> IO ()) -> FilePath -> String -> IO ()
-viaTmp a file content = do
-	let (dir, base) = splitFileName file
-	createDirectoryIfMissing True dir
-	(tmpfile, handle) <- openTempFile dir (base ++ ".tmp")
-	hClose handle
-	a tmpfile content
-	rename tmpfile file
+viaTmp a file content = bracket setup cleanup use
+  where
+	(dir, base) = splitFileName file
+	template = base ++ ".tmp"
+	setup = do
+		createDirectoryIfMissing True dir
+		openTempFile dir template
+	cleanup (tmpfile, handle) = do
+		_ <- tryIO $ hClose handle
+		tryIO $ removeFile tmpfile
+	use (tmpfile, handle) = do
+		hClose handle
+		a tmpfile content
+		rename tmpfile file
 
 {- Runs an action with a tmp file located in the system's tmp directory
  - (or in "." if there is none) then removes the file. -}
diff --git a/Utility/URI.hs b/Utility/URI.hs
new file mode 100644
--- /dev/null
+++ b/Utility/URI.hs
@@ -0,0 +1,18 @@
+{- Network.URI
+ -
+ - Copyright 2014 Joey Hess <joey@kitenet.net>
+ -
+ - License: BSD-2-clause
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module Utility.URI where
+
+-- Old versions of network lacked an Ord for URI
+#if ! MIN_VERSION_network(2,4,0)
+import Network.URI
+
+instance Ord URI where
+	a `compare` b = show a `compare` show b
+#endif
diff --git a/Utility/UserInfo.hs b/Utility/UserInfo.hs
--- a/Utility/UserInfo.hs
+++ b/Utility/UserInfo.hs
@@ -2,7 +2,7 @@
  -
  - Copyright 2012 Joey Hess <joey@kitenet.net>
  -
- - Licensed under the GNU GPL version 3 or higher.
+ - License: BSD-2-clause
  -}
 
 {-# LANGUAGE CPP #-}
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,25 @@
+git-repair (1.20140914) unstable; urgency=medium
+
+  * Update to build with optparse-applicative 0.10. Closes: #761552
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 14 Sep 2014 12:48:27 -0400
+
+git-repair (1.20140815) unstable; urgency=medium
+
+  * 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.
+  * Merge from git-annex.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 15 Aug 2014 13:49:09 -0400
+
+git-repair (1.20140423) unstable; urgency=medium
+
+  * Improve memory usage when git fsck finds a great many broken objects.
+  * Merge from git-annex.
+
+ -- Joey Hess <joeyh@debian.org>  Wed, 23 Apr 2014 14:01:30 -0400
+
 git-repair (1.20140227) unstable; urgency=medium
 
   * Optimise unpacking of pack files, and avoid repeated error
diff --git a/debian/control b/debian/control
--- a/debian/control
+++ b/debian/control
@@ -15,7 +15,7 @@
 	libghc-quickcheck2-dev,
 	libghc-utf8-string-dev,
 	libghc-async-dev,
-	libghc-optparse-applicative-dev
+	libghc-optparse-applicative-dev (>= 0.10.0)
 Maintainer: Joey Hess <joeyh@debian.org>
 Standards-Version: 3.9.5
 Vcs-Git: git://git-repair.branchable.com/
diff --git a/debian/copyright b/debian/copyright
--- a/debian/copyright
+++ b/debian/copyright
@@ -7,3 +7,29 @@
  The full text of version 3 of the GPL is distributed as doc/GPL in
  this package's source, or in /usr/share/common-licenses/GPL-3 on
  Debian systems.
+
+Files: Utility/*
+Copyright: 2012-2014 Joey Hess <joey@kitenet.net>
+License: BSD-2-clause
+
+License: BSD-2-clause
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ .
+ THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
diff --git a/git-repair.cabal b/git-repair.cabal
--- a/git-repair.cabal
+++ b/git-repair.cabal
@@ -1,12 +1,13 @@
 Name: git-repair
-Version: 1.20140227
-Cabal-Version: >= 1.6
+Version: 1.20140914
+Cabal-Version: >= 1.8
 License: GPL
 Maintainer: Joey Hess <joey@kitenet.net>
 Author: Joey Hess
 Stability: Stable
 Copyright: 2013 Joey Hess
 License-File: GPL
+Extra-Source-Files: CHANGELOG
 Build-Type: Custom
 Homepage: http://git-repair.branchable.com/
 Category: Utility
@@ -27,7 +28,7 @@
   Build-Depends: MissingH, hslogger, directory, filepath, containers, mtl,
    network, extensible-exceptions, unix-compat, bytestring,
    base >= 4.5, base < 5, IfElse, text, process, time, QuickCheck,
-   utf8-string, async, optparse-applicative
+   utf8-string, async, optparse-applicative (>= 0.10.0)
 
   if (! os(windows))
     Build-Depends: unix
diff --git a/git-repair.hs b/git-repair.hs
--- a/git-repair.hs
+++ b/git-repair.hs
@@ -37,7 +37,7 @@
 		( long "retry"
 		<> help "Retry tests in git-repair-test.log"
 		)
-	<*> option
+	<*> option auto
 		( long "numtests"
 		<> short 'n'
 		<> metavar "N"
