packages feed

git-annex 3.20120123 → 3.20120227

raw patch · 143 files changed

+1749/−686 lines, 143 filesdep +IfElse

Dependencies added: IfElse

Files

Annex.hs view
@@ -21,12 +21,13 @@ 	setField, 	getFlag, 	getField,+	addCleanup, 	gitRepo, 	inRepo, 	fromRepo, ) where -import Control.Monad.State+import Control.Monad.State.Strict import Control.Monad.Trans.Control (StM, MonadBaseControl, liftBaseWith, restoreM) import Control.Monad.Base (liftBase, MonadBase) import System.Posix.Types (Fd)@@ -35,12 +36,14 @@ import qualified Git import qualified Git.Config import Git.CatFile+import Git.CheckAttr import qualified Git.Queue import Types.Backend import qualified Types.Remote import Types.Crypto import Types.BranchState import Types.TrustLevel+import Utility.State import qualified Utility.Matcher import qualified Data.Map as M @@ -74,13 +77,14 @@ 	{ repo :: Git.Repo 	, backends :: [BackendA Annex] 	, remotes :: [Types.Remote.RemoteA Annex]-	, repoqueue :: Git.Queue.Queue 	, output :: OutputType 	, force :: Bool 	, fast :: Bool 	, auto :: Bool 	, branchstate :: BranchState+	, repoqueue :: Maybe Git.Queue.Queue 	, catfilehandle :: Maybe CatFileHandle+	, checkattrhandle :: Maybe CheckAttrHandle 	, forcebackend :: Maybe String 	, forcenumcopies :: Maybe Int 	, limit :: Matcher (FilePath -> Annex Bool)@@ -90,6 +94,7 @@ 	, lockpool :: M.Map FilePath Fd 	, flags :: M.Map String Bool 	, fields :: M.Map String String+	, cleanup :: M.Map String (Annex ()) 	}  newState :: Git.Repo -> AnnexState@@ -97,13 +102,14 @@ 	{ repo = gitrepo 	, backends = [] 	, remotes = []-	, repoqueue = Git.Queue.new 	, output = NormalOutput 	, force = False 	, fast = False 	, auto = False 	, branchstate = startBranchState+	, repoqueue = Nothing 	, catfilehandle = Nothing+	, checkattrhandle = Nothing 	, forcebackend = Nothing 	, forcenumcopies = Nothing 	, limit = Left []@@ -113,6 +119,7 @@ 	, lockpool = M.empty 	, flags = M.empty 	, fields = M.empty+	, cleanup = M.empty 	}  {- Create and returns an Annex state object for the specified git repo. -}@@ -125,27 +132,20 @@ eval :: AnnexState -> Annex a -> IO a eval s a = evalStateT (runAnnex a) s -{- Gets a value from the internal state, selected by the passed value- - constructor. -}-getState :: (AnnexState -> a) -> Annex a-getState = gets--{- Applies a state mutation function to change the internal state. - -- - Example: changeState $ \s -> s { output = QuietOutput }- -}-changeState :: (AnnexState -> AnnexState) -> Annex ()-changeState = modify- {- Sets a flag to True -} setFlag :: String -> Annex () setFlag flag = changeState $ \s ->-	s { flags = M.insert flag True $ flags s }+	s { flags = M.insertWith' const flag True $ flags s }  {- Sets a field to a value -} setField :: String -> String -> Annex () setField field value = changeState $ \s ->-	s { fields = M.insert field value $ fields s }+	s { fields = M.insertWith' const field value $ fields s }++{- Adds a cleanup action to perform. -}+addCleanup :: String -> Annex () -> Annex ()+addCleanup uid a = changeState $ \s ->+	s { cleanup = M.insertWith' const uid a $ cleanup s }  {- Checks if a flag was set. -} getFlag :: String -> Annex Bool
Annex/Branch.hs view
@@ -1,6 +1,6 @@ {- management of the git-annex branch  -- - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2012 Joey Hess <joey@kitenet.net>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -18,6 +18,7 @@ 	get, 	change, 	commit,+	stage, 	files, ) where @@ -32,7 +33,7 @@ import qualified Git.Ref import qualified Git.Branch import qualified Git.UnionMerge-import qualified Git.HashObject+import Git.HashObject import qualified Git.Index import Annex.CatFile @@ -114,14 +115,14 @@ 	-- ensure branch exists, and get its current ref 	branchref <- getBranch 	-- check what needs updating before taking the lock-	dirty <- journalDirty+	dirty <- unCommitted 	(refs, branches) <- unzip <$> filterM isnewer pairs-	if (not dirty && null refs)+	if not dirty && null refs 		then updateIndex branchref 		else withIndex $ lockJournal $ do 			when dirty stageJournal 			let merge_desc = if null branches-				then "update" +				then "update" 				else "merging " ++ 					unwords (map Git.Ref.describe branches) ++  					" into " ++ show name@@ -172,7 +173,7 @@  - modifes the current content of the file on the branch.  -} change :: FilePath -> (String -> String) -> Annex ()-change file a = lockJournal $ getStale file >>= return . a >>= set file+change file a = lockJournal $ a <$> getStale file >>= set file  {- Records new content of a file into the journal and cache. -} set :: FilePath -> String -> Annex ()@@ -182,15 +183,21 @@  {- Stages the journal, and commits staged changes to the branch. -} commit :: String -> Annex ()-commit message = whenM journalDirty $ lockJournal $ do+commit message = whenM unCommitted $ lockJournal $ do 	stageJournal 	ref <- getBranch 	withIndex $ commitBranch ref message [fullname] +{- Stages the journal, not making a commit to the branch. -}+stage :: Annex ()+stage = whenM journalDirty $ lockJournal $ do+	stageJournal+	setUnCommitted+ {- Commits the staged changes in the index to the branch.  -   - Ensures that the branch's index file is first updated to the state- - of the brannch at branchref, before running the commit action. This+ - of the branch at branchref, before running the commit action. This  - is needed because the branch may have had changes pushed to it, that  - are not yet reflected in the index.  -@@ -213,6 +220,7 @@ 	parentrefs <- commitparents <$> catObject committedref 	when (racedetected branchref parentrefs) $ 		fixrace committedref parentrefs+	setCommitted 	where 		-- look for "parent ref" lines and return the refs 		commitparents = map (Git.Ref . snd) . filter isparent .@@ -301,19 +309,39 @@         lock <- fromRepo gitAnnexIndexLock 	liftIO $ writeFile lock $ show ref ++ "\n" +{- Checks if there are uncommitted changes in the branch's index or journal. -}+unCommitted :: Annex Bool+unCommitted = do+	d <- liftIO . doesFileExist =<< fromRepo gitAnnexIndexDirty+	if d+		then return d+		else journalDirty++setUnCommitted :: Annex ()+setUnCommitted = do+	file <- fromRepo gitAnnexIndexDirty+	liftIO $ writeFile file "1"++setCommitted :: Annex ()+setCommitted = do+	file <- fromRepo gitAnnexIndexDirty+	_ <- liftIO $ tryIO $ removeFile file+	return ()+ {- Stages the journal into the index. -} stageJournal :: Annex () stageJournal = do 	fs <- getJournalFiles 	g <- gitRepo 	withIndex $ liftIO $ do-		let dir = gitAnnexJournalDir g-		let paths = map (dir </>) fs-		(shas, cleanup) <- Git.HashObject.hashFiles paths g-		Git.UnionMerge.update_index g $-			index_lines shas (map fileJournal fs)-		cleanup-		mapM_ removeFile paths+		h <- hashObjectStart g+		Git.UnionMerge.stream_update_index g+			[genstream (gitAnnexJournalDir g) h fs]+		hashObjectStop h 	where-		index_lines shas = map genline . zip shas-		genline (sha, file) = Git.UnionMerge.update_index_line sha file+		genstream dir h fs streamer = forM_ fs $ \file -> do+			let path = dir </> file+			sha <- hashFile h path+			_ <- streamer $ Git.UnionMerge.update_index_line+				sha (fileJournal file)+			removeFile path
+ Annex/CheckAttr.hs view
@@ -0,0 +1,35 @@+{- git check-attr interface, with handle automatically stored in the Annex monad+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Annex.CheckAttr (+	checkAttr,+	checkAttrHandle+) where++import Common.Annex+import qualified Git.CheckAttr as Git+import qualified Annex++{- All gitattributes used by git-annex. -}+annexAttrs :: [Git.Attr]+annexAttrs =+	[ "annex.backend"+	, "annex.numcopies"+	]++checkAttr :: Git.Attr -> FilePath -> Annex String+checkAttr attr file = do+	h <- checkAttrHandle+	liftIO $ Git.checkAttr h attr file++checkAttrHandle :: Annex Git.CheckAttrHandle+checkAttrHandle = maybe startup return =<< Annex.getState Annex.checkattrhandle+	where+		startup = do+			h <- inRepo $ Git.checkAttrStart annexAttrs+			Annex.changeState $ \s -> s { Annex.checkattrhandle = Just h }+			return h
Annex/Content.hs view
@@ -25,7 +25,6 @@ 	preseedTmp, ) where -import System.IO.Error (try) import Control.Exception (bracket_) import System.Posix.Types @@ -33,6 +32,7 @@ import Logs.Location import Annex.UUID import qualified Git+import qualified Git.Config import qualified Annex import qualified Annex.Queue import qualified Annex.Branch@@ -79,7 +79,7 @@ 	where 		lock Nothing = return Nothing 		lock (Just l) = do-			v <- try $ setLock l (WriteLock, AbsoluteSeek, 0, 0)+			v <- tryIO $ setLock l (WriteLock, AbsoluteSeek, 0, 0) 			case v of 				Left _ -> error "content is locked" 				Right _ -> return $ Just l@@ -245,20 +245,33 @@ 	let dir = parentDir file 	a (dir, file) +cleanObjectLoc :: Key -> Annex ()+cleanObjectLoc key = do+	file <- inRepo $ gitAnnexLocation key+	liftIO $ removeparents file (3 :: Int)+	where+		removeparents _ 0 = return ()+		removeparents file n = do+			let dir = parentDir file+			maybe (return ()) (const $ removeparents dir (n-1))+				=<< catchMaybeIO (removeDirectory dir)+ {- Removes a key's file from .git/annex/objects/ -} removeAnnex :: Key -> Annex ()-removeAnnex key = withObjectLoc key $ \(dir, file) -> liftIO $ do-	allowWrite dir-	removeFile file-	removeDirectory dir+removeAnnex key = withObjectLoc key $ \(dir, file) -> do+	liftIO $ do+		allowWrite dir+		removeFile file+	cleanObjectLoc key  {- Moves a key's file out of .git/annex/objects/ -} fromAnnex :: Key -> FilePath -> Annex ()-fromAnnex key dest = withObjectLoc key $ \(dir, file) -> liftIO $ do-	allowWrite dir-	allowWrite file-	moveFile file dest-	removeDirectory dir+fromAnnex key dest = withObjectLoc key $ \(dir, file) -> do+	liftIO $ do+		allowWrite dir+		allowWrite file+		moveFile file dest+	cleanObjectLoc key  {- Moves a key out of .git/annex/objects/ into .git/annex/bad, and  - returns the file it was moved to. -}@@ -271,7 +284,7 @@ 		createDirectoryIfMissing True (parentDir dest) 		allowWrite (parentDir src) 		moveFile src dest-		removeDirectory (parentDir src)+	cleanObjectLoc key 	logStatus key InfoMissing 	return dest @@ -291,11 +304,20 @@ 			let files = concat contents 			return $ mapMaybe (fileKey . takeFileName) files -{- Things to do to record changes to content. -}-saveState :: Annex ()-saveState = do+{- Things to do to record changes to content when shutting down.+ -+ - It's acceptable to avoid committing changes to the branch,+ - especially if performing a short-lived action.+ -}+saveState :: Bool -> Annex ()+saveState oneshot = do 	Annex.Queue.flush False-	Annex.Branch.commit "update"+	unless oneshot $ do+		alwayscommit <- fromMaybe True . Git.configTrue+			<$> fromRepo (Git.Config.get "annex.alwayscommit" "")+		if alwayscommit+			then Annex.Branch.commit "update"+			else Annex.Branch.stage  {- Downloads content from any of a list of urls. -} downloadUrl :: [Url.URLString] -> FilePath -> Annex Bool
Annex/Journal.hs view
@@ -91,4 +91,4 @@ {- Runs an action, catching failure and running something to fix it up, and  - retrying if necessary. -} doRedo :: IO a -> IO b -> IO a-doRedo a b = catch a $ const $ b >> a+doRedo a b = catchIO a $ const $ b >> a
Annex/Queue.hs view
@@ -12,30 +12,42 @@ ) where  import Common.Annex-import Annex+import Annex hiding (new) import qualified Git.Queue+import qualified Git.Config  {- Adds a git command to the queue. -} add :: String -> [CommandParam] -> [FilePath] -> Annex () add command params files = do-	q <- getState repoqueue+	q <- get 	store $ Git.Queue.add q command params files  {- Runs the queue if it is full. Should be called periodically. -} flushWhenFull :: Annex () flushWhenFull = do-	q <- getState repoqueue+	q <- get 	when (Git.Queue.full q) $ flush False  {- Runs (and empties) the queue. -} flush :: Bool -> Annex () flush silent = do-	q <- getState repoqueue+	q <- get 	unless (0 == Git.Queue.size q) $ do 		unless silent $ 			showSideAction "Recording state in git" 		q' <- inRepo $ Git.Queue.flush q 		store q' +get :: Annex Git.Queue.Queue+get = maybe new return =<< getState repoqueue++new :: Annex Git.Queue.Queue+new = do+	q <- Git.Queue.new <$> fromRepo queuesize+	store q+	return q+	where+		queuesize r = readish =<< Git.Config.getMaybe "annex.queuesize" r+ store :: Git.Queue.Queue -> Annex ()-store q = changeState $ \s -> s { repoqueue = q }+store q = changeState $ \s -> s { repoqueue = Just q }
Annex/Ssh.hs view
@@ -11,12 +11,12 @@ ) where  import qualified Data.Map as M-import System.IO.Error (try)  import Common.Annex import Annex.LockPool import qualified Git import qualified Git.Config+import qualified Build.SysConfig as SysConfig  {- Generates parameters to ssh to a given host (or user@host) on a given  - port, with connection caching. -}@@ -33,17 +33,18 @@ 		-- If the lock pool is empty, this is the first ssh of this 		-- run. There could be stale ssh connections hanging around 		-- from a previous git-annex run that was interrupted.-		cleanstale = whenM (null . filter isLock . M.keys <$> getPool) $+		cleanstale = whenM (not . any isLock . M.keys <$> getPool) $ 			sshCleanup  sshInfo :: (String, Maybe Integer) -> Annex (Maybe FilePath, [CommandParam]) sshInfo (host, port) = do-	caching <- Git.configTrue <$> fromRepo (Git.Config.get "annex.sshcaching" "true")+	caching <- fromMaybe SysConfig.sshconnectioncaching . Git.configTrue+		<$> fromRepo (Git.Config.get "annex.sshcaching" "") 	if caching 		then do-			dir <- fromRepo $ gitAnnexSshDir+			dir <- fromRepo gitAnnexSshDir 			let socketfile = dir </> hostport2socket host port-		 	return $ (Just socketfile, cacheParams socketfile)+		 	return (Just socketfile, cacheParams socketfile) 		else return (Nothing, [])  cacheParams :: FilePath -> [CommandParam]@@ -59,7 +60,7 @@ {- Stop any unused ssh processes. -} sshCleanup :: Annex () sshCleanup = do-	dir <- fromRepo $ gitAnnexSshDir+	dir <- fromRepo gitAnnexSshDir 	liftIO $ createDirectoryIfMissing True dir 	sockets <- filter (not . isLock) <$> liftIO (dirContents dir) 	forM_ sockets cleanup@@ -72,18 +73,20 @@ 			let lockfile = socket2lock socketfile 			unlockFile lockfile 			fd <- liftIO $ openFd lockfile ReadWrite (Just stdFileMode) defaultFileFlags-			v <- liftIO $ try $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)+			v <- liftIO $ tryIO $+				setLock fd (WriteLock, AbsoluteSeek, 0, 0) 			case v of 				Left _ -> return () 				Right _ -> stopssh socketfile 			liftIO $ closeFd fd 		stopssh socketfile = do-			(_, params) <- sshInfo $ socket2hostport socketfile+			let (host, port) = socket2hostport socketfile+			(_, params) <- sshInfo (host, port) 			_ <- liftIO $ do 				-- "ssh -O stop" is noisy on stderr even with -q 				let cmd = unwords $ toCommand $ 					[ Params "-O stop"-					] ++ params+					] ++ params ++ [Param host] 				_ <- boolSystem "sh" 					[ Param "-c" 					, Param $ "ssh " ++ cmd ++ " >/dev/null 2>/dev/null"@@ -101,7 +104,7 @@ socket2hostport :: FilePath -> (String, Maybe Integer) socket2hostport socket 	| null p = (h, Nothing)-	| otherwise = (h, readMaybe p)+	| otherwise = (h, readish p) 	where 		(h, p) = separate (== '!') $ takeFileName socket 
Backend.hs view
@@ -6,23 +6,21 @@  -}  module Backend (-	BackendFile, 	list, 	orderedList, 	genKey, 	lookupFile,-	chooseBackends,+	chooseBackend, 	lookupBackendName, 	maybeLookupBackendName ) where -import System.IO.Error (try) import System.Posix.Files  import Common.Annex import qualified Git.Config-import qualified Git.CheckAttr import qualified Annex+import Annex.CheckAttr import Types.Key import qualified Types.Backend as B @@ -62,7 +60,7 @@ genKey' :: [Backend] -> FilePath -> Annex (Maybe (Key, Backend)) genKey' [] _ = return Nothing genKey' (b:bs) file = do-	r <- (B.getKey b) file+	r <- B.getKey b file 	case r of 		Nothing -> genKey' bs file 		Just k -> return $ Just (makesane k, b)@@ -77,7 +75,7 @@  - by examining what the file symlinks to. -} lookupFile :: FilePath -> Annex (Maybe (Key, Backend)) lookupFile file = do-	tl <- liftIO $ try getsymlink+	tl <- liftIO $ tryIO getsymlink 	case tl of 		Left _ -> return Nothing 		Right l -> makekey l@@ -94,20 +92,15 @@ 						bname ++ ")" 					return Nothing -type BackendFile = (Maybe Backend, FilePath)--{- Looks up the backends that should be used for each file in a list.+{- Looks up the backend that should be used for a file.  - That can be configured on a per-file basis in the gitattributes file.  -}-chooseBackends :: [FilePath] -> Annex [BackendFile]-chooseBackends fs = Annex.getState Annex.forcebackend >>= go+chooseBackend :: FilePath -> Annex (Maybe Backend)+chooseBackend f = Annex.getState Annex.forcebackend >>= go 	where-		go Nothing = do-			pairs <- inRepo $ Git.CheckAttr.lookup "annex.backend" fs-			return $ map (\(f,b) -> (maybeLookupBackendName b, f)) pairs-		go (Just _) = do-			l <- orderedList-			return $ map (\f -> (Just $ Prelude.head l, f)) fs+		go Nothing =  maybeLookupBackendName <$>+			checkAttr "annex.backend" f+		go (Just _) = Just . Prelude.head <$> orderedList  {- Looks up a backend by name. May fail if unknown. -} lookupBackendName :: String -> Backend
Backend/URL.hs view
@@ -10,6 +10,8 @@ 	fromUrl ) where +import Data.Hash.MD5+ import Common.Annex import Types.Backend import Types.Key@@ -24,5 +26,16 @@ 	fsckKey = Nothing } -fromUrl :: String -> Key-fromUrl url = stubKey { keyName = url, keyBackendName = "URL" }+fromUrl :: String -> Maybe Integer -> Key+fromUrl url size = stubKey+	{ keyName = key+	, keyBackendName = "URL"+	, keySize = size+	}+	where+		-- when it's not too long, use the url as the key name+		-- 256 is the absolute filename max, but use a shorter+		-- length because this is not the entire key filename.+		key+			| length url < 128 = url+			| otherwise = take 128 url ++ "-" ++ md5s (Str url)
CHANGELOG view
@@ -1,3 +1,49 @@+git-annex (3.20120227) unstable; urgency=low++  * Modifications to support ghc 7.4's handling of filenames.+    This version can only be built with ghc 7.4 or newer. See the ghc7.0+    branch for older ghcs.+  * S3: Fix irrefutable pattern failure when accessing encrypted S3+    credentials.+  * Use the haskell IfElse library.+  * Fix teardown of stale cached ssh connections.+  * Fixed to use the strict state monad, to avoid leaking all kinds of memory+    due to lazy state update thunks when adding/fixing many files.+  * Fixed some memory leaks that occurred when committing journal files.+  * Added a annex.queuesize setting, useful when adding hundreds of thousands+    of files on a system with plenty of memory.+  * whereis: Prints the urls of files that the web special remote knows about.+  * addurl --fast: Verifies that the url can be downloaded (only getting+    its head), and records the size in the key.+  * When checking that an url has a key, verify that the Content-Length,+    if available, matches the size of the key.+  * addurl: Added a --file option, which can be used to specify what+    file the url is added to. This can be used to override the default+    filename that is used when adding an url, which is based on the url.+    Or, when the file already exists, the url is recorded as another+    location of the file.+  * addurl: Normalize badly encoded urls.+  * addurl: Add --pathdepth option.+  * rekey: New plumbing level command, can be used to change the keys used+    for files en masse.+  * Store web special remote url info in a more efficient location.+    (Urls stored with this version will not be visible to older versions.)+  * Deal with NFS problem that caused a failure to remove a directory+    when removing content from the annex.+  * Make a single location log commit after a remote has received or+    dropped files. Uses a new "git-annex-shell commit" command when available.+  * To avoid commits of data to the git-annex branch after each command+    is run, set annex.alwayscommit=false. Its data will then be committed+    less frequently, when a merge or sync is done.+  * configure: Check if ssh connection caching is supported by the installed+    version of ssh and default annex.sshcaching accordingly.+  * move --from, copy --from: Now 10 times faster when scanning to find+    files in a remote on a local disk; rather than go through the location log+    to see which files are present on the remote, it simply looks at the +    disk contents directly.++ -- Joey Hess <joeyh@debian.org>  Mon, 27 Feb 2012 12:58:21 -0400+ git-annex (3.20120123) unstable; urgency=low    * fsck --from: Fscking a remote is now supported. It's done by retrieving
CmdLine.hs view
@@ -11,8 +11,8 @@ 	shutdown ) where -import qualified System.IO.Error as IO import qualified Control.Exception as E+import qualified Data.Map as M import Control.Exception (throw) import System.Console.GetOpt @@ -40,7 +40,7 @@ 			(actions, state') <- Annex.run state $ do 				sequence_ flags 				prepCommand cmd params-			tryRun state' cmd $ [startup] ++ actions ++ [shutdown]+			tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdoneshot cmd] 	where 		(flags, cmd, params) = parseCmd args cmds commonoptions header @@ -72,14 +72,18 @@ tryRun' errnum _ cmd [] 	| errnum > 0 = error $ cmdname cmd ++ ": " ++ show errnum ++ " failed" 	| otherwise = return ()-tryRun' errnum state cmd (a:as) = run >>= handle+tryRun' errnum state cmd (a:as) = do+	r <- run+	handle $! r 	where-		run = IO.try $ Annex.run state $ do+		run = tryIO $ Annex.run state $ do 			Annex.Queue.flushWhenFull 			a 		handle (Left err) = showerr err >> cont False state 		handle (Right (success, state')) = cont success state'-		cont success s = tryRun' (if success then errnum else errnum + 1) s cmd as+		cont success s = do+			let errnum' = if success then errnum else errnum + 1+			(tryRun' $! errnum') s cmd as 		showerr err = Annex.eval state $ do 			showErr err 			showEndFail@@ -89,9 +93,10 @@ startup = return True  {- Cleanup actions. -}-shutdown :: Annex Bool-shutdown = do-	saveState+shutdown :: Bool -> Annex Bool+shutdown oneshot = do+	saveState oneshot+	sequence_ =<< M.elems <$> Annex.getState Annex.cleanup 	liftIO Git.Command.reap -- zombies from long-running git processes 	sshCleanup -- ssh connection caching 	return True
Command.hs view
@@ -8,6 +8,7 @@ module Command ( 	command, 	noRepo,+	oneShot, 	withOptions, 	next, 	stop,@@ -18,6 +19,7 @@ 	ifAnnexed, 	notBareRepo, 	isBareRepo,+	numCopies, 	autoCopies, 	module ReExported ) where@@ -34,11 +36,16 @@ import Usage as ReExported import Logs.Trust import Config+import Annex.CheckAttr  {- Generates a normal command -} command :: String -> String -> [CommandSeek] -> String -> Command-command = Command [] Nothing commonChecks+command = Command [] Nothing commonChecks False +{- Makes a command run in oneshot mode. -}+oneShot :: Command -> Command+oneShot c = c { cmdoneshot = True }+ {- Adds a fallback action to a command, that will be run if it's used  - outside a git repository. -} noRepo :: IO () -> Command -> Command@@ -98,17 +105,22 @@ isBareRepo :: Annex Bool isBareRepo = fromRepo Git.repoIsLocalBare +numCopies :: FilePath  -> Annex (Maybe Int)+numCopies file = readish <$> checkAttr "annex.numcopies" file+ {- Used for commands that have an auto mode that checks the number of known  - copies of a key.  -  - In auto mode, first checks that the number of known  - copies of the key is > or < than the numcopies setting, before running  - the action. -}-autoCopies :: Key -> (Int -> Int -> Bool) -> Maybe Int -> CommandStart -> CommandStart-autoCopies key vs numcopiesattr a = Annex.getState Annex.auto >>= auto+autoCopies :: FilePath -> Key -> (Int -> Int -> Bool) -> (Maybe Int -> CommandStart) -> CommandStart+autoCopies file key vs a = do+	numcopiesattr <- numCopies file+	Annex.getState Annex.auto >>= auto numcopiesattr 	where-		auto False = a-		auto True = do+		auto numcopiesattr False = a numcopiesattr+		auto numcopiesattr True = do 			needed <- getNumCopies numcopiesattr 			(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key-			if length have `vs` needed then a else stop+			if length have `vs` needed then a numcopiesattr else stop
Command/Add.hs view
@@ -16,7 +16,6 @@ import Logs.Location import Annex.Content import Utility.Touch-import Backend  def :: [Command] def = [command "add" paramPaths seek "add files to annex"]@@ -28,8 +27,8 @@ {- The add subcommand annexes a file, storing it in a backend, and then  - moving it into the annex directory and setting up the symlink pointing  - to its content. -}-start :: BackendFile -> CommandStart-start p@(_, file) = notBareRepo $ ifAnnexed file fixup add+start :: FilePath -> CommandStart+start file = notBareRepo $ ifAnnexed file fixup add 	where 		add = do 			s <- liftIO $ getSymbolicLinkStatus file@@ -37,7 +36,7 @@ 				then stop 				else do 					showStart "add" file-					next $ perform p+					next $ perform file 		fixup (key, _) = do 			-- fixup from an interrupted add; the symlink 			-- is present but not yet added to git@@ -45,8 +44,10 @@ 			liftIO $ removeFile file 			next $ next $ cleanup file key =<< inAnnex key -perform :: BackendFile -> CommandPerform-perform (backend, file) = Backend.genKey file backend >>= go+perform :: FilePath -> CommandPerform+perform file = do+	backend <- Backend.chooseBackend file+	Backend.genKey file backend >>= go 	where 		go Nothing = stop 		go (Just (key, _)) = do
Command/AddUrl.hs view
@@ -15,37 +15,62 @@ import qualified Command.Add import qualified Annex import qualified Backend.URL+import qualified Utility.Url as Url import Annex.Content import Logs.Web+import qualified Option+import Types.Key  def :: [Command]-def = [command "addurl" (paramRepeating paramUrl) seek "add urls to annex"]+def = [withOptions [fileOption, pathdepthOption] $+	command "addurl" (paramRepeating paramUrl) seek "add urls to annex"] +fileOption :: Option+fileOption = Option.field [] "file" paramFile "specify what file the url is added to"++pathdepthOption :: Option+pathdepthOption = Option.field [] "pathdepth" paramNumber "path components to use in filename"+ seek :: [CommandSeek]-seek = [withStrings start]+seek = [withField fileOption return $ \f ->+	withField pathdepthOption (return . maybe Nothing readish) $ \d ->+	withStrings $ start f d] -start :: String -> CommandStart-start s = notBareRepo $ go $ parseURI s+start :: Maybe FilePath -> Maybe Int -> String -> CommandStart+start optfile pathdepth s = notBareRepo $ go $ fromMaybe bad $ parseURI s 	where-		go Nothing = error $ "bad url " ++ s-		go (Just url) = do-			file <- liftIO $ url2file url+		bad = fromMaybe (error $ "bad url " ++ s) $+			parseURI $ escapeURIString isUnescapedInURI s+		go url = do+			let file = fromMaybe (url2file url pathdepth) optfile 			showStart "addurl" file 			next $ perform s file  perform :: String -> FilePath -> CommandPerform-perform url file = do-	fast <- Annex.getState Annex.fast-	if fast then nodownload url file else download url file+perform url file = ifAnnexed file addurl geturl+	where+		geturl = do+			liftIO $ createDirectoryIfMissing True (parentDir file)+			fast <- Annex.getState Annex.fast+			if fast then nodownload url file else download url file+		addurl (key, _backend) = do+			ok <- liftIO $ Url.check url (keySize key)+			if ok+				then do+					setUrlPresent key url+					next $ return True+				else do+					warning $ "failed to verify url: " ++ url+					stop  download :: String -> FilePath -> CommandPerform download url file = do 	showAction $ "downloading " ++ url ++ " "-	let dummykey = Backend.URL.fromUrl url+	let dummykey = Backend.URL.fromUrl url Nothing 	tmp <- fromRepo $ gitAnnexTmpLocation dummykey 	liftIO $ createDirectoryIfMissing True (parentDir tmp) 	stopUnless (downloadUrl [url] tmp) $ do-		[(backend, _)] <- Backend.chooseBackends [file]+		backend <- Backend.chooseBackend file 		k <- Backend.genKey tmp backend 		case k of 			Nothing -> stop@@ -56,16 +81,27 @@  nodownload :: String -> FilePath -> CommandPerform nodownload url file = do-	let key = Backend.URL.fromUrl url-	setUrlPresent key url-	next $ Command.Add.cleanup file key False+	(exists, size) <- liftIO $ Url.exists url+	if exists+		then do+			let key = Backend.URL.fromUrl url size+			setUrlPresent key url+			next $ Command.Add.cleanup file key False+		else do+			warning $ "unable to access url: " ++ url+			stop -url2file :: URI -> IO FilePath-url2file url = do-	whenM (doesFileExist file) $-		error $ "already have this url in " ++ file-	return file+url2file :: URI -> Maybe Int -> FilePath+url2file url pathdepth = case pathdepth of+	Nothing -> filesize $ escape fullurl+	Just depth+		| depth > 0 -> frombits $ drop depth+		| depth < 0 -> frombits $ reverse . take (negate depth) . reverse+		| otherwise -> error "bad --pathdepth" 	where-		file = escape $ uriRegName auth ++ uriPath url ++ uriQuery url-		escape = replace "/" "_" . replace "?" "_"+		fullurl = uriRegName auth ++ uriPath url ++ uriQuery url+		frombits a = join "/" $ a urlbits+		urlbits = map (filesize . escape) $ filter (not . null) $ split "/" fullurl 		auth = fromMaybe (error $ "bad url " ++ show url) $ uriAuthority url+		filesize = take 255+		escape = replace "/" "_" . replace "?" "_"
+ Command/Commit.hs view
@@ -0,0 +1,23 @@+{- git-annex command+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.Commit where++import Command+import qualified Annex.Branch++def :: [Command]+def = [command "commit" paramNothing seek+	"commits any staged changes to the git-annex branch"]++seek :: [CommandSeek]+seek = [withNothing start]++start :: CommandStart+start = next $ next $ do+	Annex.Branch.commit "update"+	return True
Command/ConfigList.hs view
@@ -12,7 +12,7 @@ import Annex.UUID  def :: [Command]-def = [command "configlist" paramNothing seek+def = [oneShot $ command "configlist" paramNothing seek 	"outputs relevant git configuration"]  seek :: [CommandSeek]
Command/Copy.hs view
@@ -19,10 +19,10 @@ seek :: [CommandSeek] seek = [withField Command.Move.toOption Remote.byName $ \to -> 		withField Command.Move.fromOption Remote.byName $ \from ->-			withNumCopies $ \n -> whenAnnexed $ start to from n]+			withFilesInGit $ whenAnnexed $ start to from]  -- A copy is just a move that does not delete the source file. -- However, --auto mode avoids unnecessary copies.-start :: Maybe Remote -> Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart-start to from numcopies file (key, backend) = autoCopies key (<) numcopies $+start :: Maybe Remote -> Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart+start to from file (key, backend) = autoCopies file key (<) $ \_numcopies -> 	Command.Move.start to from False file (key, backend)
Command/Drop.hs view
@@ -26,11 +26,11 @@ fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote"  seek :: [CommandSeek]-seek = [withField fromOption Remote.byName $ \from -> withNumCopies $ \n ->-	whenAnnexed $ start from n]+seek = [withField fromOption Remote.byName $ \from ->+	withFilesInGit $ whenAnnexed $ start from] -start :: Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart-start from numcopies file (key, _) = autoCopies key (>) numcopies $ do+start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart+start from file (key, _) = autoCopies file key (>) $ \numcopies -> 	case from of 		Nothing -> startLocal file numcopies key 		Just remote -> do
Command/DropKey.hs view
@@ -14,7 +14,7 @@ import Annex.Content  def :: [Command]-def = [command "dropkey" (paramRepeating paramKey) seek+def = [oneShot $ command "dropkey" (paramRepeating paramKey) seek 	"drops annexed content for specified keys"]   seek :: [CommandSeek]
Command/Find.hs view
@@ -36,7 +36,7 @@ seek = [withField formatOption formatconverter $ \f -> 		withFilesInGit $ whenAnnexed $ start f] 	where-		formatconverter = return . maybe Nothing (Just . Utility.Format.gen)+		formatconverter = return . fmap Utility.Format.gen  start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart start format file (key, _) = do
Command/Fsck.hs view
@@ -36,12 +36,13 @@ seek :: [CommandSeek] seek = 	[ withField fromOption Remote.byName $ \from ->-		withNumCopies $ \n -> whenAnnexed $ start from n+		withFilesInGit $ whenAnnexed $ start from 	, withBarePresentKeys startBare 	] -start :: Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart-start from numcopies file (key, backend) = do+start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart+start from file (key, backend) = do+	numcopies <- numCopies file 	showStart "fsck" file 	case from of 		Nothing -> next $ perform key file backend numcopies@@ -81,7 +82,7 @@ 			t <- fromRepo gitAnnexTmpDir 			let tmp = t </> "fsck" ++ show pid ++ "." ++ keyFile key 			liftIO $ createDirectoryIfMissing True t-			let cleanup = liftIO $ catch (removeFile tmp) (const $ return ())+			let cleanup = liftIO $ catchIO (removeFile tmp) (const $ return ()) 			cleanup 			cleanup `after` a tmp 		getfile tmp = do
Command/Get.hs view
@@ -19,11 +19,11 @@  seek :: [CommandSeek] seek = [withField Command.Move.fromOption Remote.byName $ \from ->-	withNumCopies $ \n -> whenAnnexed $ start from n]+	withFilesInGit $ whenAnnexed $ start from] -start :: Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart-start from numcopies file (key, _) = stopUnless (not <$> inAnnex key) $-	autoCopies key (<) numcopies $ do+start :: Maybe Remote -> FilePath -> (Key, Backend) -> CommandStart+start from file (key, _) = stopUnless (not <$> inAnnex key) $+	autoCopies file key (<) $ \_numcopies -> 		case from of 			Nothing -> go $ perform key 			Just src -> do@@ -36,7 +36,7 @@ 			next a	  perform :: Key -> CommandPerform-perform key = stopUnless (getViaTmp key $ getKeyFile key) $ do+perform key = stopUnless (getViaTmp key $ getKeyFile key) $ 	next $ return True -- no cleanup needed  {- Try to find a copy of the file in one of the remotes,
Command/InAnnex.hs view
@@ -12,7 +12,7 @@ import Annex.Content  def :: [Command]-def = [command "inannex" (paramRepeating paramKey) seek+def = [oneShot $ command "inannex" (paramRepeating paramKey) seek 	"checks if keys are present in the annex"]  seek :: [CommandSeek]
Command/Lock.hs view
@@ -10,7 +10,6 @@ import Common.Annex import Command import qualified Annex.Queue-import Backend 	 def :: [Command] def = [command "lock" paramPaths seek "undo unlock command"]@@ -18,9 +17,8 @@ seek :: [CommandSeek] seek = [withFilesUnlocked start, withFilesUnlockedToBeCommitted start] -{- Undo unlock -}-start :: BackendFile -> CommandStart-start (_, file) = do+start :: FilePath -> CommandStart+start file = do 	showStart "lock" file 	next $ perform file 
Command/Log.hs view
@@ -55,7 +55,7 @@ gourceOption = Option.flag [] "gource" "format output for gource"  seek :: [CommandSeek]-seek = [withValue (Remote.uuidDescriptions) $ \m ->+seek = [withValue Remote.uuidDescriptions $ \m -> 	withValue (liftIO getCurrentTimeZone) $ \zone -> 	withValue (concat <$> mapM getoption passthruOptions) $ \os -> 	withFlag gourceOption $ \gource ->@@ -65,7 +65,7 @@ 			Annex.getField (Option.name o) 		use o v = [Param ("--" ++ Option.name o), Param v] -start :: (M.Map UUID String) -> TimeZone -> [CommandParam] -> Bool ->+start :: M.Map UUID String -> TimeZone -> [CommandParam] -> Bool -> 	FilePath -> (Key, Backend) -> CommandStart start m zone os gource file (key, _) = do 	showLog output =<< readLog <$> getLog key os@@ -91,7 +91,7 @@ 			catObject ref  normalOutput :: (UUID -> String) -> FilePath -> TimeZone -> Outputter-normalOutput lookupdescription file zone present ts us = do+normalOutput lookupdescription file zone present ts us = 	liftIO $ mapM_ (putStrLn . format) us 	where 		time = showTimeStamp zone ts@@ -100,7 +100,7 @@ 			fromUUID u ++ " -- " ++ lookupdescription u ]  gourceOutput :: (UUID -> String) -> FilePath -> Outputter-gourceOutput lookupdescription file present ts us = do+gourceOutput lookupdescription file present ts us = 	liftIO $ mapM_ (putStrLn . intercalate "|" . format) us 	where 		time = takeWhile isDigit $ show ts
Command/Merge.hs view
@@ -26,4 +26,6 @@ perform :: CommandPerform perform = do 	Annex.Branch.update+	-- commit explicitly, in case no remote branches were merged+	Annex.Branch.commit "update" 	next $ return True
Command/Migrate.hs view
@@ -12,19 +12,18 @@ import qualified Backend import qualified Types.Key import Annex.Content-import qualified Command.Add-import Logs.Web+import qualified Command.ReKey  def :: [Command] def = [command "migrate" paramPaths seek "switch data to different backend"]  seek :: [CommandSeek]-seek = [withBackendFilesInGit $ \(b, f) -> whenAnnexed (start b) f]+seek = [withFilesInGit $ whenAnnexed start] -start :: Maybe Backend -> FilePath -> (Key, Backend) -> CommandStart-start b file (key, oldbackend) = do+start :: FilePath -> (Key, Backend) -> CommandStart+start file (key, oldbackend) = do 	exists <- inAnnex key-	newbackend <- choosebackend b+	newbackend <- choosebackend =<< Backend.chooseBackend file 	if (newbackend /= oldbackend || upgradableKey key) && exists 		then do 			showStart "migrate" file@@ -48,32 +47,18 @@  - generate.  -} perform :: FilePath -> Key -> Backend -> CommandPerform-perform file oldkey newbackend = do-	src <- inRepo $ gitAnnexLocation oldkey-	tmp <- fromRepo gitAnnexTmpDir-	let tmpfile = tmp </> takeFileName file-	cleantmp tmpfile-	liftIO $ createLink src tmpfile-	k <- Backend.genKey tmpfile $ Just newbackend-	cleantmp tmpfile-	case k of-		Nothing -> stop-		Just (newkey, _) -> stopUnless (link src newkey) $ do-			-- Update symlink to use the new key.-			liftIO $ removeFile file--			-- If the old key had some-			-- associated urls, record them for-			-- the new key as well.-			urls <- getUrls oldkey-			unless (null urls) $-				mapM_ (setUrlPresent newkey) urls--			next $ Command.Add.cleanup file newkey True+perform file oldkey newbackend = maybe stop go =<< genkey 	where+		go newkey = stopUnless (Command.ReKey.linkKey oldkey newkey) $+			next $ Command.ReKey.cleanup file oldkey newkey+		genkey = do+			src <- inRepo $ gitAnnexLocation oldkey+			tmp <- fromRepo gitAnnexTmpDir+			let tmpfile = tmp </> takeFileName file+			cleantmp tmpfile+			liftIO $ createLink src tmpfile+			newkey <- liftM fst <$>+				Backend.genKey tmpfile (Just newbackend)+			cleantmp tmpfile+			return newkey 		cleantmp t = liftIO $ whenM (doesFileExist t) $ removeFile t-		link src newkey = getViaTmpUnchecked newkey $ \t -> do-			-- Make a hard link to the old backend's-			-- cached key, to avoid wasting disk space.-			liftIO $ unlessM (doesFileExist t) $ createLink src t-			return True	
Command/Move.hs view
@@ -120,10 +120,15 @@ 			showMoveAction move file 			next $ fromPerform src move key fromOk :: Remote -> Key -> Annex Bool-fromOk src key = do-	u <- getUUID-	remotes <- Remote.keyPossibilities key-	return $ u /= Remote.uuid src && any (== src) remotes+fromOk src key+	| Remote.hasKeyCheap src =+		either (const expensive) return =<< Remote.hasKey src key+	| otherwise = expensive+	where+		expensive = do+			u <- getUUID+			remotes <- Remote.keyPossibilities key+			return $ u /= Remote.uuid src && any (== src) remotes fromPerform :: Remote -> Bool -> Key -> CommandPerform fromPerform src move key = moveLock move key $ do 	ishere <- inAnnex key
Command/PreCommit.hs view
@@ -10,7 +10,6 @@ import Command import qualified Command.Add import qualified Command.Fix-import Backend  def :: [Command] def = [command "pre-commit" paramPaths seek "run by git pre-commit hook"]@@ -22,12 +21,12 @@ 	[ withFilesToBeCommitted $ whenAnnexed Command.Fix.start 	, withFilesUnlockedToBeCommitted start] -start :: BackendFile -> CommandStart-start p = next $ perform p+start :: FilePath -> CommandStart+start file = next $ perform file -perform :: BackendFile -> CommandPerform-perform pair@(_, file) = do-	ok <- doCommand $ Command.Add.start pair+perform :: FilePath -> CommandPerform+perform file = do+	ok <- doCommand $ Command.Add.start file 	if ok 		then next $ return True 		else error $ "failed to add " ++ file ++ "; canceling commit"
+ Command/ReKey.hs view
@@ -0,0 +1,64 @@+{- git-annex command+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Command.ReKey where++import Common.Annex+import Command+import qualified Annex+import Types.Key+import Annex.Content+import qualified Command.Add+import Logs.Web++def :: [Command]+def = [command "rekey"+	(paramOptional $ paramRepeating $ paramPair paramPath paramKey)+	seek "change keys used for files"]++seek :: [CommandSeek]+seek = [withPairs start]++start :: (FilePath, String) -> CommandStart+start (file, keyname) = ifAnnexed file go stop+	where+		newkey = fromMaybe (error "bad key") $ readKey keyname+		go (oldkey, _)+			| oldkey == newkey = stop+			| otherwise = do+				showStart "rekey" file+				next $ perform file oldkey newkey++perform :: FilePath -> Key -> Key -> CommandPerform+perform file oldkey newkey = do+	present <- inAnnex oldkey+	_ <- if present+		then linkKey oldkey newkey+		else do+			unlessM (Annex.getState Annex.force) $+				error $ file ++ " is not available (use --force to override)"+			return True+	next $ cleanup file oldkey newkey++{- Make a hard link to the old key content, to avoid wasting disk space. -}+linkKey :: Key -> Key -> Annex Bool+linkKey oldkey newkey = getViaTmpUnchecked newkey $ \tmp -> do+	src <- inRepo $ gitAnnexLocation oldkey+	liftIO $ unlessM (doesFileExist tmp) $ createLink src tmp+	return True++cleanup :: FilePath -> Key -> Key -> CommandCleanup+cleanup file oldkey newkey = do+	-- If the old key had some associated urls, record them for+	-- the new key as well.+	urls <- getUrls oldkey+	unless (null urls) $+		mapM_ (setUrlPresent newkey) urls++	-- Update symlink to use the new key.+	liftIO $ removeFile file+	Command.Add.cleanup file newkey True
Command/RecvKey.hs view
@@ -14,7 +14,7 @@ import Utility.RsyncFile  def :: [Command]-def = [command "recvkey" paramKey seek+def = [oneShot $ command "recvkey" paramKey seek 	"runs rsync in server mode to receive content"]  seek :: [CommandSeek]@@ -28,7 +28,7 @@ 	if ok 		then do 			-- forcibly quit after receiving one key,-			-- and shutdown cleanly so queued git commands run-			_ <- shutdown+			-- and shutdown cleanly+			_ <- shutdown True 			liftIO exitSuccess 		else liftIO exitFailure
Command/Reinject.hs view
@@ -23,7 +23,7 @@ start :: [FilePath] -> CommandStart start (src:dest:[]) 	| src == dest = stop-	| otherwise = do+	| otherwise = 		ifAnnexed src 			(error $ "cannot used annexed file as src: " ++ src) 			go
Command/SendKey.hs view
@@ -13,7 +13,7 @@ import Utility.RsyncFile  def :: [Command]-def = [command "sendkey" paramKey seek+def = [oneShot $ command "sendkey" paramKey seek 	"runs rsync in server mode to send content"]  seek :: [CommandSeek]
Command/Status.hs view
@@ -7,7 +7,7 @@  module Command.Status where -import Control.Monad.State+import Control.Monad.State.Strict import qualified Data.Map as M import qualified Data.Set as S import Data.Set (Set)@@ -66,8 +66,8 @@ 	, bad_data_size 	, local_annex_keys 	, local_annex_size-	, visible_annex_keys-	, visible_annex_size+	, known_annex_keys+	, known_annex_size 	, backend_usage 	] @@ -113,7 +113,7 @@  remote_list :: TrustLevel -> String -> Stat remote_list level desc = stat n $ nojson $ lift $ do-	us <- M.keys <$> (M.union <$> uuidMap <*> remoteMap)+	us <- M.keys <$> (M.union <$> uuidMap <*> remoteMap Remote.name) 	rs <- fst <$> trustPartition level us 	s <- prettyPrintUUIDs n rs 	return $ if null s then "0" else show (length rs) ++ "\n" ++ beginning s@@ -128,12 +128,12 @@ local_annex_keys = stat "local annex keys" $ json show $ 	S.size <$> cachedKeysPresent -visible_annex_size :: Stat-visible_annex_size = stat "visible annex size" $ json id $+known_annex_size :: Stat+known_annex_size = stat "known annex size" $ json id $ 	keySizeSum <$> cachedKeysReferenced -visible_annex_keys :: Stat-visible_annex_keys = stat "visible annex keys" $ json show $+known_annex_keys :: Stat+known_annex_keys = stat "known annex keys" $ json show $ 	S.size <$> cachedKeysReferenced  tmp_size :: Stat
Command/Sync.hs view
@@ -33,7 +33,7 @@ seek rs = do 	!branch <- fromMaybe nobranch <$> inRepo Git.Branch.current 	remotes <- syncRemotes rs-	return $ concat $+	return $ concat 		[ [ commit ] 		, [ mergeLocal branch ] 		, [ pullRemote remote branch | remote <- remotes ]@@ -75,6 +75,7 @@ 	showStart "commit" "" 	next $ next $ do 		showOutput+		Annex.Branch.commit "update" 		-- Commit will fail when the tree is clean, so ignore failure. 		_ <- inRepo $ Git.Command.runBool "commit" 			[Param "-a", Param "-m", Param "git-annex automatic sync"]@@ -137,9 +138,9 @@ 			showStart "push" (Remote.name remote) 			next $ next $ do 				showOutput-				inRepo $ Git.Command.runBool "push" $+				inRepo $ Git.Command.runBool "push" 					[ Param (Remote.name remote)-					, Param (show $ Annex.Branch.name)+					, Param (show Annex.Branch.name) 					, Param refspec 					] 		refspec = show (Git.Ref.base branch) ++ ":" ++ show (Git.Ref.base syncbranch)
Command/Uninit.hs view
@@ -7,8 +7,6 @@  module Command.Uninit where -import qualified Data.ByteString.Lazy.Char8 as B- import Common.Annex import Command import qualified Git@@ -29,7 +27,7 @@ 	when (b == Annex.Branch.name) $ error $ 		"cannot uninit when the " ++ show b ++ " branch is checked out" 	where-		current_branch = Git.Ref . Prelude.head . lines . B.unpack <$> revhead+		current_branch = Git.Ref . Prelude.head . lines <$> revhead 		revhead = inRepo $ Git.Command.pipeRead  			[Params "rev-parse --abbrev-ref HEAD"] @@ -57,7 +55,7 @@ 	mapM_ removeAnnex =<< getKeysPresent 	liftIO $ removeDirectoryRecursive annexdir 	-- avoid normal shutdown-	saveState+	saveState False 	inRepo $ Git.Command.run "branch" 		[Param "-D", Param $ show Annex.Branch.name] 	liftIO exitSuccess
Command/Unused.hs view
@@ -10,7 +10,8 @@ module Command.Unused where  import qualified Data.Set as S-import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Encoding as L  import Common.Annex import Command@@ -38,7 +39,7 @@ fromOption = Option.field ['f'] "from" paramRemote "remote to check for unused content"  seek :: [CommandSeek]-seek = [withNothing $ start]+seek = [withNothing start]  {- Finds unused content in the annex. -}  start :: CommandStart@@ -161,7 +162,7 @@ 		refs = map (Git.Ref .  snd) . 			nubBy uniqref . 			filter ourbranches .-			map (separate (== ' ')) . lines . L.unpack+			map (separate (== ' ')) . lines 		uniqref (a, _) (b, _) = a == b 		ourbranchend = '/' : show Annex.Branch.name 		ourbranches (_, b) = not $ ourbranchend `isSuffixOf` b@@ -202,7 +203,7 @@ 		findkeys c [] = return c 		findkeys c (l:ls) 			| isSymLink (LsTree.mode l) = do-				content <- catFile ref $ LsTree.file l+				content <- L.decodeUtf8 <$> catFile ref (LsTree.file l) 				case fileKey (takeFileName $ L.unpack content) of 					Nothing -> findkeys c ls 					Just k -> findkeys (k:c) ls
Command/Version.hs view
@@ -13,7 +13,7 @@ import Annex.Version  def :: [Command]-def = [noRepo showPackageVersion $ dontCheck repoExists $+def = [oneShot $ noRepo showPackageVersion $ dontCheck repoExists $ 	command "version" paramNothing seek "show version info"]  seek :: [CommandSeek]
Command/Whereis.hs view
@@ -7,6 +7,8 @@  module Command.Whereis where +import qualified Data.Map as M+ import Common.Annex import Command import Remote@@ -17,24 +19,36 @@ 	"lists repositories that have file content"]  seek :: [CommandSeek]-seek = [withFilesInGit $ whenAnnexed start]+seek = [withValue (remoteMap id) $ \m ->+	withFilesInGit $ whenAnnexed $ start m] -start :: FilePath -> (Key, Backend) -> CommandStart-start file (key, _) = do+start :: M.Map UUID Remote -> FilePath -> (Key, Backend) -> CommandStart+start remotemap file (key, _) = do 	showStart "whereis" file-	next $ perform key+	next $ perform remotemap key -perform :: Key -> CommandPerform-perform key = do-	(untrustedlocations, safelocations) <- trustPartition UnTrusted =<< keyLocations key+perform :: M.Map UUID Remote -> Key -> CommandPerform+perform remotemap key = do+	locations <- keyLocations key+	(untrustedlocations, safelocations) <- trustPartition UnTrusted locations 	let num = length safelocations 	showNote $ show num ++ " " ++ copiesplural num 	pp <- prettyPrintUUIDs "whereis" safelocations 	unless (null safelocations) $ showLongNote pp 	pp' <- prettyPrintUUIDs "untrusted" untrustedlocations 	unless (null untrustedlocations) $ showLongNote $ untrustedheader ++ pp'+	forM_ (catMaybes $ map (`M.lookup` remotemap) locations) $+		performRemote key 	if null safelocations then stop else next $ return True 	where 		copiesplural 1 = "copy" 		copiesplural _ = "copies" 		untrustedheader = "The following untrusted locations may also have copies:\n"++performRemote :: Key -> Remote -> Annex () +performRemote key remote = case whereisKey remote of+	Nothing -> return ()+	Just a -> do+		ls <- a key+		unless (null ls) $ showLongNote $+			unlines $ map (\l -> name remote ++ ": " ++ l) ls
Common.hs view
@@ -1,8 +1,9 @@ module Common (module X) where  import Control.Monad as X hiding (join)+import Control.Monad.IfElse as X import Control.Applicative as X-import Control.Monad.State as X (liftIO)+import Control.Monad.State.Strict as X (liftIO) import Control.Exception.Extensible as X (IOException)  import Data.Maybe as X@@ -20,7 +21,7 @@ import System.Exit as X  import Utility.Misc as X-import Utility.Conditional as X+import Utility.Exception as X import Utility.SafeCommand as X import Utility.Path as X import Utility.Directory as X
Config.hs view
@@ -40,7 +40,7 @@ remoteCost :: Git.Repo -> Int -> Annex Int remoteCost r def = do 	cmd <- getConfig r "cost-command" ""-	(fromMaybe def . readMaybe) <$>+	(fromMaybe def . readish) <$> 		if not $ null cmd 			then liftIO $ snd <$> pipeFrom "sh" ["-c", cmd] 			else getConfig r "cost" ""@@ -69,7 +69,7 @@  {- Checks if a repo should be ignored. -} repoNotIgnored :: Git.Repo -> Annex Bool-repoNotIgnored r = not . Git.configTrue <$> getConfig r "ignore" "false"+repoNotIgnored r = not . fromMaybe False . Git.configTrue <$> getConfig r "ignore" ""  {- If a value is specified, it is used; otherwise the default is looked up  - in git config. forcenumcopies overrides everything. -}@@ -78,7 +78,7 @@ 	where 		use (Just n) = return n 		use Nothing = perhaps (return 1) =<< -			readMaybe <$> fromRepo (Git.Config.get config "1")+			readish <$> fromRepo (Git.Config.get config "1") 		perhaps fallback = maybe fallback (return . id) 		config = "annex.numcopies" 
Git.hs view
@@ -85,7 +85,8 @@ 		else error $ "acting on non-local git repo " ++  repoDescribe repo ++  				" not supported" configBare :: Repo -> Bool-configBare repo = maybe unknown configTrue $ M.lookup "core.bare" $ config repo+configBare repo = maybe unknown (fromMaybe False . configTrue) $+	M.lookup "core.bare" $ config repo 	where 		unknown = error $ "it is not known if git repo " ++ 			repoDescribe repo ++@@ -112,5 +113,10 @@ workTree Repo { location = Unknown } = undefined  {- Checks if a string from git config is a true value. -}-configTrue :: String -> Bool-configTrue s = map toLower s == "true"+configTrue :: String -> Maybe Bool+configTrue s+	| s' == "true" = Just True+	| s' == "false" = Just False+	| otherwise = Nothing+	where+		s' = map toLower s
Git/Branch.hs view
@@ -7,8 +7,6 @@  module Git.Branch where -import qualified Data.ByteString.Lazy.Char8 as L- import Common import Git import Git.Sha@@ -19,15 +17,15 @@ current r = parse <$> pipeRead [Param "symbolic-ref", Param "HEAD"] r 	where 		parse v-			| L.null v = Nothing-			| otherwise = Just $ Git.Ref $ firstLine $ L.unpack v+			| null v = Nothing+			| otherwise = Just $ Git.Ref $ firstLine v  {- Checks if the second branch has any commits not present on the first  - branch. -} changed :: Branch -> Branch -> Repo -> IO Bool changed origbranch newbranch repo 	| origbranch == newbranch = return False-	| otherwise = not . L.null <$> diffs+	| otherwise = not . null <$> diffs 	where 		diffs = pipeRead 			[ Param "log"@@ -73,15 +71,14 @@  - with the specified parent refs, and returns the committed sha -} commit :: String -> Branch -> [Ref] -> Repo -> IO Sha commit message branch parentrefs repo = do-	tree <- getSha "write-tree" $ asString $+	tree <- getSha "write-tree" $ 		pipeRead [Param "write-tree"] repo-	sha <- getSha "commit-tree" $ asString $+	sha <- getSha "commit-tree" $ 		ignorehandle $ pipeWriteRead 			(map Param $ ["commit-tree", show tree] ++ ps)-			(L.pack message) repo+			message repo 	run "update-ref" [Param $ show branch, Param $ show sha] repo 	return sha 	where 		ignorehandle a = snd <$> a-		asString a = L.unpack <$> a 		ps = concatMap (\r -> ["-p", show r]) parentrefs
Git/CatFile.hs view
@@ -13,8 +13,6 @@ 	catObject ) where -import Control.Monad.State-import System.Cmd.Utils import System.IO import qualified Data.ByteString.Char8 as S import qualified Data.ByteString.Lazy.Char8 as L@@ -23,20 +21,18 @@ import Git import Git.Sha import Git.Command+import qualified Utility.CoProcess as CoProcess -type CatFileHandle = (PipeHandle, Handle, Handle)+type CatFileHandle = CoProcess.CoProcessHandle -{- Starts git cat-file running in batch mode in a repo and returns a handle. -} catFileStart :: Repo -> IO CatFileHandle-catFileStart repo = hPipeBoth "git" $ toCommand $-	gitCommandLine [Param "cat-file", Param "--batch"] repo+catFileStart = CoProcess.start "git" . toCommand . gitCommandLine+	[ Param "cat-file"+	, Param "--batch"+	] -{- Stops git cat-file. -} catFileStop :: CatFileHandle -> IO ()-catFileStop (pid, from, to) = do-	hClose to-	hClose from-	forceSuccess pid+catFileStop = CoProcess.stop  {- Reads a file from a specified branch. -} catFile :: CatFileHandle -> Branch -> FilePath -> IO L.ByteString@@ -45,23 +41,26 @@ {- 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-catObject (_, from, to) object = do-	hPutStrLn to $ show object-	hFlush to-	header <- hGetLine from-	case words header of-		[sha, objtype, size]-			| length sha == shaSize &&-			  validobjtype objtype -> handle size-			| otherwise -> dne-		_-			| header == show object ++ " missing" -> dne-			| otherwise -> error $ "unknown response from git cat-file " ++ header+catObject h object = CoProcess.query h send receive 	where-		handle size = case reads size of-			[(bytes, "")] -> readcontent bytes-			_ -> dne-		readcontent bytes = do+		send to = do+			fileEncoding to+			hPutStrLn to $ show object+		receive from = do+			fileEncoding from+			header <- hGetLine from+			case words header of+				[sha, objtype, size]+					| length sha == shaSize &&+					  validobjtype objtype -> +						case reads size of+							[(bytes, "")] -> readcontent bytes from+							_ -> dne+					| otherwise -> dne+				_+					| header == show object ++ " missing" -> dne+					| otherwise -> error $ "unknown response from git cat-file " ++ show (header, object)+		readcontent bytes from = do 			content <- S.hGet from bytes 			c <- hGetChar from 			when (c /= '\n') $
Git/CheckAttr.hs view
@@ -1,39 +1,55 @@ {- git check-attr interface  -- - Copyright 2010, 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2010-2012 Joey Hess <joey@kitenet.net>  -  - Licensed under the GNU GPL version 3 or higher.  -}  module Git.CheckAttr where -import System.Exit- import Common import Git import Git.Command-import qualified Git.Filename import qualified Git.Version+import qualified Utility.CoProcess as CoProcess -{- Efficiently looks up a gitattributes value for each file in a list. -}-lookup :: String -> [FilePath] -> Repo -> IO [(FilePath, String)]-lookup attr files repo = do+type CheckAttrHandle = (CoProcess.CoProcessHandle, [Attr], String)++type Attr = String++{- Starts git check-attr running to look up the specified gitattributes+ - values and returns a handle.  -}+checkAttrStart :: [Attr] -> Repo -> IO CheckAttrHandle+checkAttrStart attrs repo = do 	cwd <- getCurrentDirectory-	(_, fromh, toh) <- hPipeBoth "git" (toCommand params)-        _ <- forkProcess $ do-		hClose fromh-                hPutStr toh $ join "\0" $ input cwd-                hClose toh-                exitSuccess-        hClose toh-	output cwd . lines <$> hGetContents fromh+	h <- CoProcess.start "git" $ toCommand $ gitCommandLine params repo+	return (h, attrs, cwd) 	where-		params = gitCommandLine -				[ Param "check-attr"-				, Param attr-				, Params "-z --stdin"-				] repo+		params =+			[ Param "check-attr" +			, Params "-z --stdin"+			] ++ map Param attrs +++			[ Param "--" ] +checkAttrStop :: CheckAttrHandle -> IO ()+checkAttrStop (h, _, _) = CoProcess.stop h++{- Gets an attribute of a file. -}+checkAttr :: CheckAttrHandle -> Attr -> FilePath -> IO String+checkAttr (h, attrs, cwd) want file = do+	pairs <- CoProcess.query h send receive+	let vals = map snd $ filter (\(attr, _) -> attr == want) pairs+	case vals of+		[v] -> return v+		_ -> error $ "unable to determine " ++ want ++ " attribute of " ++ file+	where+		send to = do+			fileEncoding to+			hPutStr to $ file' ++ "\0"+		receive from = forM attrs $ \attr -> do+			fileEncoding from+			l <- hGetLine from+			return (attr, attrvalue attr l) 		{- Before git 1.7.7, git check-attr worked best with 		 - absolute filenames; using them worked around some bugs 		 - with relative filenames.@@ -42,25 +58,10 @@ 		 - filenames, and the bugs that necessitated them were fixed, 		 - so use relative filenames. -} 		oldgit = Git.Version.older "1.7.7"-		input cwd-			| oldgit = map (absPathFrom cwd) files-			| otherwise = map (relPathDirToFile cwd . absPathFrom cwd) files-		output cwd-			| oldgit = map (torel cwd . topair)-			| otherwise = map topair--		topair l = (Git.Filename.decode file, value)-			where -				file = join sep $ beginning bits-				value = end bits !! 0+		file'+			| oldgit = absPathFrom cwd file+			| otherwise = relPathDirToFile cwd $ absPathFrom cwd file+		attrvalue attr l = end bits !! 0+			where 				bits = split sep l 				sep = ": " ++ attr ++ ": "--		torel cwd (file, value) = (relfile, value)-			where-				relfile-					| startswith cwd' file = drop (length cwd') file-					| otherwise = relPathDirToFile top' file-				top = workTree repo-				cwd' = cwd ++ "/"-				top' = top ++ "/"
Git/Command.hs view
@@ -7,7 +7,10 @@  module Git.Command where -import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.IO as L+import Control.Concurrent+import Control.Exception (finally)  import Common import Git@@ -30,49 +33,47 @@ {- Runs git in the specified repo, throwing an error if it fails. -} run :: String -> [CommandParam] -> Repo -> IO () run subcommand params repo = assertLocal repo $-	runBool subcommand params repo-		>>! error $ "git " ++ show params ++ " failed"+	unlessM (runBool subcommand params repo) $+		error $ "git " ++ subcommand ++ " " ++ show params ++ " failed"  {- Runs a git subcommand and returns its output, lazily.   -  - Note that this leaves the git process running, and so zombies will  - result unless reap is called.  -}-pipeRead :: [CommandParam] -> Repo -> IO L.ByteString+pipeRead :: [CommandParam] -> Repo -> IO String pipeRead params repo = assertLocal repo $ do 	(_, h) <- hPipeFrom "git" $ toCommand $ gitCommandLine params repo-	hSetBinaryMode h True-	L.hGetContents h+	fileEncoding h+	hGetContents h  {- Runs a git subcommand, feeding it input.  - You should call either getProcessStatus or forceSuccess on the PipeHandle. -}-pipeWrite :: [CommandParam] -> L.ByteString -> Repo -> IO PipeHandle+pipeWrite :: [CommandParam] -> L.Text -> Repo -> IO PipeHandle pipeWrite params s repo = assertLocal repo $ do 	(p, h) <- hPipeTo "git" (toCommand $ gitCommandLine params repo)-	L.hPut h s+	L.hPutStr h s 	hClose h 	return p  {- Runs a git subcommand, feeding it input, and returning its output.  - You should call either getProcessStatus or forceSuccess on the PipeHandle. -}-pipeWriteRead :: [CommandParam] -> L.ByteString -> Repo -> IO (PipeHandle, L.ByteString)+pipeWriteRead :: [CommandParam] -> String -> Repo -> IO (PipeHandle, String) pipeWriteRead params s repo = assertLocal repo $ do 	(p, from, to) <- hPipeBoth "git" (toCommand $ gitCommandLine params repo)-	hSetBinaryMode from True-	L.hPut to s-	hClose to-	c <- L.hGetContents from+	fileEncoding to+	fileEncoding from+	_ <- forkIO $ finally (hPutStr to s) (hClose to)+	c <- hGetContents from 	return (p, c)  {- Reads null terminated output of a git command (as enabled by the -z   - parameter), and splits it. -} pipeNullSplit :: [CommandParam] -> Repo -> IO [String]-pipeNullSplit params repo = map L.unpack <$> pipeNullSplitB params repo--{- For when Strings are not needed. -}-pipeNullSplitB ::[CommandParam] -> Repo -> IO [L.ByteString]-pipeNullSplitB params repo = filter (not . L.null) . L.split '\0' <$>-	pipeRead params repo+pipeNullSplit params repo =+	filter (not . null) . split sep <$> pipeRead params repo+	where+		sep = "\0"  {- Reaps any zombie git processes. -} reap :: IO ()
Git/Construct.hs view
@@ -9,6 +9,7 @@ 	fromCurrent, 	fromCwd, 	fromAbsPath,+	fromPath, 	fromUrl, 	fromUnknown, 	localToUrl,
Git/HashObject.hs view
@@ -1,6 +1,6 @@ {- git hash-object interface  -- - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2012 Joey Hess <joey@kitenet.net>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -10,23 +10,25 @@ import Common import Git import Git.Command+import qualified Utility.CoProcess as CoProcess -{- Injects a set of files into git, returning the shas of the objects- - and an IO action to call ones the the shas have been used. -}-hashFiles :: [FilePath] -> Repo -> IO ([Sha], IO ())-hashFiles paths repo = do-	(pid, fromh, toh) <- hPipeBoth "git" $ toCommand $ git_hash_object repo-	_ <- forkProcess (feeder toh)-	hClose toh-	shas <- map Ref . lines <$> hGetContentsStrict fromh-	return (shas, ender fromh pid)+type HashObjectHandle = CoProcess.CoProcessHandle++hashObjectStart :: Repo -> IO HashObjectHandle+hashObjectStart = CoProcess.start "git" . toCommand . gitCommandLine+	[ Param "hash-object"+	, Param "-w"+	, Param "--stdin-paths"+	]++hashObjectStop :: HashObjectHandle -> IO ()+hashObjectStop = CoProcess.stop++{- Injects a file into git, returning the shas of the objects. -}+hashFile :: HashObjectHandle -> FilePath -> IO Sha+hashFile h file = CoProcess.query h send receive 	where-		git_hash_object = gitCommandLine-			[Param "hash-object", Param "-w", Param "--stdin-paths"]-		feeder toh = do-			hPutStr toh $ unlines paths-			hClose toh-			exitSuccess-		ender fromh pid = do-			hClose fromh-			forceSuccess pid+		send to = do+			fileEncoding to+			hPutStrLn to file+		receive from = Ref <$> hGetLine from
Git/LsFiles.hs view
@@ -65,7 +65,13 @@ typeChanged = typeChanged' []  typeChanged' :: [CommandParam] -> [FilePath] -> Repo -> IO [FilePath]-typeChanged' ps l = pipeNullSplit $ prefix ++ ps ++ suffix+typeChanged' ps l repo = do+	fs <- pipeNullSplit (prefix ++ ps ++ suffix) repo+	-- 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 = workTree repo+	cwd <- getCurrentDirectory+	return $ map (\f -> relPathDirToFile cwd $ top </> f) fs 	where 		prefix = [Params "diff --name-only --diff-filter=T -z"] 		suffix = Param "--" : map File l
Git/LsTree.hs view
@@ -14,7 +14,6 @@ import Numeric import Control.Applicative import System.Posix.Types-import qualified Data.ByteString.Lazy.Char8 as L  import Common import Git@@ -31,22 +30,22 @@ {- Lists the contents of a Ref -} lsTree :: Ref -> Repo -> IO [TreeItem] lsTree t repo = map parseLsTree <$>-	pipeNullSplitB [Params "ls-tree --full-tree -z -r --", File $ show t] repo+	pipeNullSplit [Params "ls-tree --full-tree -z -r --", File $ show t] repo  {- Parses a line of ls-tree output.  - (The --long format is not currently supported.) -}-parseLsTree :: L.ByteString -> TreeItem+parseLsTree :: String -> TreeItem parseLsTree l = TreeItem -	{ mode = fst $ Prelude.head $ readOct $ L.unpack m-	, typeobj = L.unpack t-	, sha = L.unpack s-	, file = Git.Filename.decode $ L.unpack f+	{ mode = fst $ Prelude.head $ readOct m+	, typeobj = t+	, sha = s+	, file = Git.Filename.decode f 	} 	where 		-- l = <mode> SP <type> SP <sha> TAB <file> 		-- All fields are fixed, so we can pull them out of 		-- specific positions in the line.-		(m, past_m) = L.splitAt 7 l-		(t, past_t) = L.splitAt 4 past_m-		(s, past_s) = L.splitAt 40 $ L.tail past_t-		f = L.tail past_s+		(m, past_m) = splitAt 7 l+		(t, past_t) = splitAt 4 past_m+		(s, past_s) = splitAt 40 $ Prelude.tail past_t+		f = Prelude.tail past_s
Git/Queue.hs view
@@ -5,21 +5,23 @@  - Licensed under the GNU GPL version 3 or higher.  -} +{-# LANGUAGE BangPatterns #-}+ module Git.Queue ( 	Queue, 	new, 	add, 	size, 	full,-	flush+	flush, ) where  import qualified Data.Map as M import System.IO import System.Cmd.Utils import Data.String.Utils-import Utility.SafeCommand +import Utility.SafeCommand import Common import Git import Git.Command@@ -34,7 +36,11 @@ {- A queue of actions to perform (in any order) on a git repository,  - with lists of files to perform them on. This allows coalescing   - similar git commands. -}-data Queue = Queue Int (M.Map Action [FilePath])+data Queue = Queue+	{ size :: Int+	, _limit :: Int+	, _items :: M.Map Action [FilePath]+	} 	deriving (Show, Eq)  {- A recommended maximum size for the queue, after which it should be@@ -46,37 +52,33 @@  - above 20k, so this is a fairly good balance -- the queue will buffer  - only a few megabytes of stuff and a minimal number of commands will be  - run by xargs. -}-maxSize :: Int-maxSize = 10240+defaultLimit :: Int+defaultLimit = 10240  {- Constructor for empty queue. -}-new :: Queue-new = Queue 0 M.empty+new :: Maybe Int -> Queue+new lim = Queue 0 (fromMaybe defaultLimit lim) M.empty  {- Adds an action to a queue. -} add :: Queue -> String -> [CommandParam] -> [FilePath] -> Queue-add (Queue n m) subcommand params files = Queue (n + 1) m'+add (Queue cur lim m) subcommand params files = Queue (cur + 1) lim m' 	where 		action = Action subcommand params 		-- There are probably few items in the map, but there 		-- can be a lot of files per item. So, optimise adding 		-- files. 		m' = M.insertWith' const action fs m-		fs = files ++ M.findWithDefault [] action m--{- Number of items in a queue. -}-size :: Queue -> Int-size (Queue n _) = n+		!fs = files ++ M.findWithDefault [] action m  {- Is a queue large enough that it should be flushed? -} full :: Queue -> Bool-full (Queue n _) = n > maxSize+full (Queue cur lim  _) = cur > lim  {- Runs a queue on a git repository. -} flush :: Queue -> Repo -> IO Queue-flush (Queue _ m) repo = do+flush (Queue _ lim m) repo = do 	forM_ (M.toList m) $ uncurry $ runAction repo-	return new+	return $ Queue 0 lim M.empty  {- Runs an Action on a list of files in a git repository.  -@@ -90,4 +92,6 @@ 	where 		params = toCommand $ gitCommandLine 			(Param (getSubcommand action):getParams action) repo-		feedxargs h = hPutStr h $ join "\0" files+		feedxargs h = do+			fileEncoding h+			hPutStr h $ join "\0" files
Git/Ref.hs view
@@ -7,8 +7,6 @@  module Git.Ref where -import qualified Data.ByteString.Lazy.Char8 as L- import Common import Git import Git.Command@@ -40,7 +38,7 @@  {- Get the sha of a fully qualified git ref, if it exists. -} sha :: Branch -> Repo -> IO (Maybe Sha)-sha branch repo = process . L.unpack <$> showref repo+sha branch repo = process <$> showref repo 	where 		showref = pipeRead [Param "show-ref", 			Param "--hash", -- get the hash@@ -52,7 +50,7 @@ matching :: Ref -> Repo -> IO [(Ref, Branch)] matching ref repo = do 	r <- pipeRead [Param "show-ref", Param $ show ref] repo-	return $ map (gen . L.unpack) (L.lines r)+	return $ map gen (lines r) 	where 		gen l = let (r, b) = separate (== ' ') l in 			(Ref r, Ref b)
Git/UnionMerge.hs view
@@ -15,7 +15,8 @@ ) where  import System.Cmd.Utils-import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.Text.Lazy as L+import qualified Data.Text.Lazy.Encoding as L import qualified Data.Set as S  import Common@@ -56,6 +57,7 @@ stream_update_index :: Repo -> [Streamer] -> IO () stream_update_index repo as = do 	(p, h) <- hPipeTo "git" (toCommand $ gitCommandLine params repo)+	fileEncoding h 	forM_ as (stream h) 	hClose h 	forceSuccess p@@ -83,13 +85,14 @@  {- For merging a single tree into the index. -} merge_tree_index :: Ref -> CatFileHandle -> Repo -> Streamer-merge_tree_index (Ref x) h = calc_merge h $ "diff-index":diff_opts ++ ["--cached", x]+merge_tree_index (Ref x) h = calc_merge h $+	"diff-index" : diff_opts ++ ["--cached", x]  diff_opts :: [String] diff_opts = ["--raw", "-z", "-r", "--no-renames", "-l0"]  {- Calculates how to perform a merge, using git to get a raw diff,- - and returning a list suitable for update_index. -}+ - and generating update-index input. -} calc_merge :: CatFileHandle -> [String] -> Repo -> Streamer calc_merge ch differ repo streamer = gendiff >>= go 	where@@ -100,27 +103,28 @@ 		go (_:[]) = error "calc_merge parse error"  {- Given an info line from a git raw diff, and the filename, generates- - a line suitable for update_index that union merges the two sides of the+ - a line suitable for update-index that union merges the two sides of the  - diff. -} mergeFile :: String -> FilePath -> CatFileHandle -> Repo -> IO (Maybe String) mergeFile info file h repo = case filter (/= nullSha) [Ref asha, Ref bsha] of 	[] -> return Nothing 	(sha:[]) -> use sha-	shas -> use =<< either return (hashObject repo . L.unlines) =<<+	shas -> use =<< either return (hashObject repo . unlines) =<< 		calcMerge . zip shas <$> mapM getcontents shas 	where 		[_colonmode, _bmode, asha, bsha, _status] = words info-		getcontents s = L.lines <$> catObject h s+		getcontents s = map L.unpack . L.lines .+			L.decodeUtf8 <$> catObject h s 		use sha = return $ Just $ update_index_line sha file  {- Injects some content into git, returning its Sha. -}-hashObject :: Repo -> L.ByteString -> IO Sha+hashObject :: Repo -> String -> IO Sha hashObject repo content = getSha subcmd $ do 	(h, s) <- pipeWriteRead (map Param params) content repo-	L.length s `seq` do+	length s `seq` do 		forceSuccess h 		reap -- XXX unsure why this is needed-		return $ L.unpack s+		return s 	where 		subcmd = "hash-object" 		params = [subcmd, "-w", "--stdin"]@@ -130,7 +134,7 @@  - When possible, reuses the content of an existing ref, rather than  - generating new content.  -}-calcMerge :: [(Ref, [L.ByteString])] -> Either Ref [L.ByteString]+calcMerge :: [(Ref, [String])] -> Either Ref [String] calcMerge shacontents 	| null reuseable = Right $ new 	| otherwise = Left $ fst $ Prelude.head reuseable
Git/Url.hs view
@@ -45,7 +45,7 @@ port r =  	case authpart uriPort r of 		":" -> Nothing-		(':':p) -> readMaybe p+		(':':p) -> readish p 		_ -> Nothing  {- Hostname of an URL repo, including any username (ie, "user@host") -}
GitAnnex.hs view
@@ -28,6 +28,7 @@ import qualified Command.Get import qualified Command.FromKey import qualified Command.DropKey+import qualified Command.ReKey import qualified Command.Reinject import qualified Command.Fix import qualified Command.Init@@ -80,6 +81,7 @@ 	, Command.Dead.def 	, Command.FromKey.def 	, Command.DropKey.def+	, Command.ReKey.def 	, Command.Fix.def 	, Command.Fsck.def 	, Command.Unused.def@@ -119,7 +121,7 @@ 		"skip files not using a key-value backend" 	] ++ Option.matcher 	where-		setnumcopies v = Annex.changeState $ \s -> s {Annex.forcenumcopies = readMaybe v }+		setnumcopies v = Annex.changeState $ \s -> s {Annex.forcenumcopies = readish v } 		setgitconfig :: String -> Annex () 		setgitconfig v = do 			newg <- inRepo $ Git.Config.store v
INSTALL view
@@ -21,7 +21,7 @@ To build and use git-annex, you will need:  * Haskell stuff-  * [The Haskell Platform](http://haskell.org/platform/)+  * [The Haskell Platform](http://haskell.org/platform/) (GHC 7.4 or newer)   * [MissingH](http://github.com/jgoerzen/missingh/wiki)   * [pcre-light](http://hackage.haskell.org/package/pcre-light)   * [utf8-string](http://hackage.haskell.org/package/utf8-string)@@ -34,6 +34,7 @@   * [HTTP](http://hackage.haskell.org/package/HTTP)   * [hS3](http://hackage.haskell.org/package/hS3)   * [json](http://hackage.haskell.org/package/json)+  * [IfElse](http://hackage.haskell.org/package/IfElse) * Shell commands   * [git](http://git-scm.com/)   * [uuid](http://www.ossp.org/pkg/lib/uuid/)
Init.hs view
@@ -68,7 +68,7 @@ 				" Edit it to remove call to git annex."  unlessBare :: Annex () -> Annex ()-unlessBare = unlessM $ fromRepo $ Git.repoIsLocalBare+unlessBare = unlessM $ fromRepo Git.repoIsLocalBare  preCommitHook :: Annex FilePath preCommitHook = (</>) <$> fromRepo Git.gitDir <*> pure "hooks/pre-commit"
Limit.hs view
@@ -84,7 +84,7 @@  - of copies. -} addCopies :: String -> Annex () addCopies num =-	case readMaybe num :: Maybe Int of+	case readish num :: Maybe Int of 		Nothing -> error "bad number for --copies" 		Just n -> addLimit $ check n 	where
Locations.hs view
@@ -22,6 +22,7 @@ 	gitAnnexJournalLock, 	gitAnnexIndex, 	gitAnnexIndexLock,+	gitAnnexIndexDirty, 	gitAnnexSshDir, 	isLinkToAnnex, 	annexHashes,@@ -142,6 +143,10 @@ {- Lock file for .git/annex/index. -} gitAnnexIndexLock :: Git.Repo -> FilePath gitAnnexIndexLock r = gitAnnexDir r </> "index.lck"++{- Flag file for .git/annex/index. -}+gitAnnexIndexDirty :: Git.Repo -> FilePath+gitAnnexIndexDirty r = gitAnnexDir r </> "index.dirty"  {- .git/annex/ssh/ is used for ssh connection caching -} gitAnnexSshDir :: Git.Repo -> FilePath
Logs/Remote.hs view
@@ -79,7 +79,7 @@ 				num = takeWhile isNumber s 				r = drop (length num) s 				rest = drop 1 r-				ok = not (null num) && take 1 r == ";"+				ok = not (null num) && ";" `isPrefixOf` r  {- for quickcheck -} prop_idempotent_configEscape :: String -> Bool
Logs/Web.hs view
@@ -23,30 +23,37 @@ webUUID :: UUID webUUID = UUID "00000000-0000-0000-0000-000000000001" -{- The urls for a key are stored in remote/web/hash/key.log - - in the git-annex branch. -} urlLog :: Key -> FilePath-urlLog key = "remote/web" </> hashDirLower key </> keyFile key ++ ".log"-oldurlLog :: Key -> FilePath-{- A bug used to store the urls elsewhere. -}-oldurlLog key = "remote/web" </> hashDirLower key </> show key ++ ".log"+urlLog key = hashDirLower key </> keyFile key ++ ".log.web" +{- Used to store the urls elsewhere. -}+oldurlLogs :: Key -> [FilePath]+oldurlLogs key = +	[ "remote/web" </> hashDirLower key </> show key ++ ".log"+	, "remote/web" </> hashDirLower key </> keyFile key ++ ".log"+	]+ {- Gets all urls that a key might be available from. -} getUrls :: Key -> Annex [URLString]-getUrls key = do-	us <- currentLog (urlLog key)-	if null us-		then currentLog (oldurlLog key)-		else return us+getUrls key = go $ urlLog key : oldurlLogs key+	where+		go [] = return []+		go (l:ls) = do+			us <- currentLog l+			if null us+				then go ls+				else return us  {- Records a change in an url for a key. -} setUrl :: Key -> URLString -> LogStatus -> Annex () setUrl key url status = do-	addLog (urlLog key) =<< logNow status url--	-- update location log to indicate that the web has the key, or not 	us <- getUrls key-	logChange key webUUID (if null us then InfoMissing else InfoPresent)+	unless (status == InfoPresent && url `elem` us) $ do+		addLog (urlLog key) =<< logNow status url++		-- update location log to indicate that the web has the key, or not+		us' <- getUrls key+		logChange key webUUID (if null us' then InfoMissing else InfoPresent)  setUrlPresent :: Key -> URLString -> Annex () setUrlPresent key url = setUrl key url InfoPresent
Messages.hs view
@@ -119,25 +119,20 @@ showRaw :: String -> Annex () showRaw s = handle q $ putStrLn s -{- By default, haskell honors the user's locale in its output to stdout- - and stderr. While that's great for proper unicode support, for git-annex- - all that's really needed is the ability to display simple messages- - (currently untranslated), and importantly, to display filenames exactly- - as they are written on disk, no matter what their encoding. So, force- - raw mode. - -- - NB: Once git-annex gets localized, this will need a rethink. -}+{- This avoids ghc's output layer crashing on invalid encoded characters in+ - filenames when printing them out.+ -} setupConsole :: IO () setupConsole = do-	hSetBinaryMode stdout True-	hSetBinaryMode stderr True+	fileEncoding stdout+	fileEncoding stderr  handle :: IO () -> IO () -> Annex () handle json normal = Annex.getState Annex.output >>= go 	where 		go Annex.NormalOutput = liftIO normal 		go Annex.QuietOutput = q-		go Annex.JSONOutput = liftIO $ flushed $ json+		go Annex.JSONOutput = liftIO $ flushed json  q :: Monad m => m () q = return ()
Option.hs view
@@ -37,7 +37,7 @@ 		"allow verbose output (default)" 	, Option ['j'] ["json"] (NoArg (setoutput Annex.JSONOutput)) 		"enable JSON output"-	, Option ['d'] ["debug"] (NoArg (setdebug))+	, Option ['d'] ["debug"] (NoArg setdebug) 		"show debug messages" 	, Option ['b'] ["backend"] (ReqArg setforcebackend paramName) 		"specify key-value backend to use"
Remote.hs view
@@ -15,6 +15,7 @@ 	removeKey, 	hasKey, 	hasKeyCheap,+	whereisKey,  	remoteTypes, 	remoteList,@@ -48,16 +49,16 @@ import Logs.Location import Remote.List -{- Map of UUIDs of Remotes and their names. -}-remoteMap :: Annex (M.Map UUID String)-remoteMap = M.fromList . map (\r -> (uuid r, name r)) .+{- Map from UUIDs of Remotes to a calculated value. -}+remoteMap :: (Remote -> a) -> Annex (M.Map UUID a)+remoteMap c = M.fromList . map (\r -> (uuid r, c r)) . 	filter (\r -> uuid r /= NoUUID) <$> remoteList  {- Map of UUIDs and their descriptions.  - The names of Remotes are added to suppliment any description that has  - been set for a repository. -} uuidDescriptions :: Annex (M.Map UUID String)-uuidDescriptions = M.unionWith addName <$> uuidMap <*> remoteMap+uuidDescriptions = M.unionWith addName <$> uuidMap <*> remoteMap name  addName :: String -> String -> String addName desc n@@ -214,4 +215,4 @@  - key to the remote, or removing the key from it *may* log the change  - on the remote, but this cannot always be relied on. -} logStatus :: Remote -> Key -> LogStatus -> Annex ()-logStatus remote key present = logChange key (uuid remote) present+logStatus remote key = logChange key (uuid remote)
Remote/Bup.hs view
@@ -8,7 +8,6 @@ module Remote.Bup (remote) where  import qualified Data.ByteString.Lazy.Char8 as L-import System.IO.Error import qualified Data.Map as M import System.Process @@ -54,6 +53,7 @@ 			removeKey = remove, 			hasKey = checkPresent r bupr', 			hasKeyCheap = bupLocal buprepo,+			whereisKey = Nothing, 			config = c, 			repo = r, 			remotetype = remote@@ -69,7 +69,7 @@ 	-- bup init will create the repository. 	-- (If the repository already exists, bup init again appears safe.) 	showAction "bup init"-	bup "init" buprepo [] >>! error "bup init failed"+	unlessM (bup "init" buprepo []) $ error "bup init failed"  	storeBupUUID u buprepo @@ -167,9 +167,9 @@ 	if Git.repoIsUrl r 		then do 			showAction "storing uuid"-			onBupRemote r boolSystem "git"-				[Params $ "config annex.uuid " ++ v]-					>>! error "ssh failed"+			unlessM (onBupRemote r boolSystem "git"+				[Params $ "config annex.uuid " ++ v]) $+					error "ssh failed" 		else liftIO $ do 			r' <- Git.Config.read r 			let olduuid = Git.Config.get "annex.uuid" "" r'@@ -200,7 +200,7 @@ getBupUUID r u 	| Git.repoIsUrl r = return (u, r) 	| otherwise = liftIO $ do-		ret <- try $ Git.Config.read r+		ret <- tryIO $ Git.Config.read r 		case ret of 			Right r' -> return (toUUID $ Git.Config.get "annex.uuid" "" r', r') 			Left _ -> return (NoUUID, r)
Remote/Directory.hs view
@@ -45,6 +45,7 @@ 			removeKey = remove dir, 			hasKey = checkPresent dir, 			hasKeyCheap = True,+			whereisKey = Nothing, 			config = Nothing, 			repo = r, 			remotetype = remote@@ -55,8 +56,8 @@ 	-- verify configuration is sane 	let dir = fromMaybe (error "Specify directory=") $ 		M.lookup "directory" c-	liftIO $ doesDirectoryExist dir-		>>! error $ "Directory does not exist: " ++ dir+	liftIO $ unlessM (doesDirectoryExist dir) $+		error $ "Directory does not exist: " ++ dir 	c' <- encryptionSetup c  	-- The directory is stored in git config, not in this remote's
Remote/Git.hs view
@@ -1,6 +1,6 @@ {- Standard git remotes.  -- - Copyright 2011 Joey Hess <joey@kitenet.net>+ - Copyright 2011-2012 Joey Hess <joey@kitenet.net>  -  - Licensed under the GNU GPL version 3 or higher.  -}@@ -20,13 +20,16 @@ import qualified Git.Config import qualified Git.Construct import qualified Annex+import Logs.Presence import Annex.UUID import qualified Annex.Content import qualified Annex.BranchState+import qualified Annex.Branch import qualified Utility.Url as Url import Utility.TempFile import Config import Init+import Types.Key  remote :: RemoteType remote = RemoteType {@@ -79,6 +82,7 @@ 		removeKey = dropKey r', 		hasKey = inAnnex r', 		hasKeyCheap = cheap,+		whereisKey = Nothing, 		config = Nothing, 		repo = r', 		remotetype = remote@@ -142,7 +146,8 @@ 			where 				go e [] = return $ Left e 				go _ (u:us) = do-					res <- catchMsgIO $ Url.exists u+					res <- catchMsgIO $+						Url.check u (keySize key) 					case res of 						Left e -> go e us 						v -> return v@@ -192,8 +197,16 @@  dropKey :: Git.Repo -> Key -> Annex Bool dropKey r key+	| not $ Git.repoIsUrl r = commitOnCleanup r $ liftIO $ onLocal r $ do+		ensureInitialized+		whenM (Annex.Content.inAnnex key) $ do+			Annex.Content.lockContent key $+				Annex.Content.removeAnnex key+			Annex.Content.logStatus key InfoMissing+			Annex.Content.saveState True+		return True 	| Git.repoIsHttp r = error "dropping from http repo not supported"-	| otherwise = onRemote r (boolSystem, False) "dropkey"+	| otherwise = commitOnCleanup r $ onRemote r (boolSystem, False) "dropkey" 		[ Params "--quiet --force" 		, Param $ show key 		]@@ -224,16 +237,16 @@ {- Tries to copy a key's content to a remote's annex. -} copyToRemote :: Git.Repo -> Key -> Annex Bool copyToRemote r key-	| not $ Git.repoIsUrl r = do+	| not $ Git.repoIsUrl r = commitOnCleanup r $ do 		keysrc <- inRepo $ gitAnnexLocation key 		params <- rsyncParams r 		-- run copy from perspective of remote 		liftIO $ onLocal r $ do 			ensureInitialized-			Annex.Content.saveState `after`+			Annex.Content.saveState True `after` 				Annex.Content.getViaTmp key 					(rsyncOrCopyFile params keysrc)-	| Git.repoIsSsh r = do+	| Git.repoIsSsh r = commitOnCleanup r $ do 		keysrc <- inRepo $ gitAnnexLocation key 		rsyncHelper =<< rsyncParamsRemote r False key keysrc 	| otherwise = error "copying to non-ssh repo not supported"@@ -289,3 +302,23 @@ 	where  		-- --inplace to resume partial files 		options = [Params "-p --progress --inplace"]++commitOnCleanup :: Git.Repo -> Annex a -> Annex a+commitOnCleanup r a = go `after` a+	where+		go = Annex.addCleanup (Git.repoLocation r) cleanup+		cleanup+			| not $ Git.repoIsUrl r = liftIO $ onLocal r $+				Annex.Branch.commit "update"+			| otherwise = do+				Just (shellcmd, shellparams) <-+					git_annex_shell r "commit" []+				-- Throw away stderr, since the remote may not+				-- have a new enough git-annex shell to+				-- support committing.+				let cmd = shellcmd ++ " "+					++ unwords (map shellEscape $ toCommand shellparams)+					++ ">/dev/null 2>/dev/null"+				_ <- liftIO $+					boolSystem "sh" [Param "-c", Param cmd]+				return ()
Remote/Hook.hs view
@@ -45,6 +45,7 @@ 			removeKey = remove hooktype, 			hasKey = checkPresent r hooktype, 			hasKeyCheap = False,+			whereisKey = Nothing, 			config = Nothing, 			repo = r, 			remotetype = remote
Remote/Rsync.hs view
@@ -52,6 +52,7 @@ 			removeKey = remove o, 			hasKey = checkPresent r o, 			hasKeyCheap = False,+			whereisKey = Nothing, 			config = Nothing, 			repo = r, 			remotetype = remote@@ -181,8 +182,8 @@ 	liftIO $ createDirectoryIfMissing True tmp 	nuke tmp `after` a tmp 	where-		nuke d = liftIO $ -			doesDirectoryExist d >>? removeDirectoryRecursive d+		nuke d = liftIO $ whenM (doesDirectoryExist d) $+			removeDirectoryRecursive d  rsyncRemote :: RsyncOpts -> [CommandParam] -> Annex Bool rsyncRemote o params = do
Remote/S3.hs view
@@ -57,6 +57,7 @@ 			removeKey = remove this, 			hasKey = checkPresent this, 			hasKeyCheap = False,+			whereisKey = Nothing, 			config = c, 			repo = r, 			remotetype = remote@@ -272,26 +273,32 @@ {- S3 creds come from the environment if set.   - Otherwise, might be stored encrypted in the remote's config. -} s3GetCreds :: RemoteConfig -> Annex (Maybe (String, String))-s3GetCreds c = do-	ak <- getEnvKey s3AccessKey-	sk <- getEnvKey s3SecretKey-	if null ak || null sk-		then do+s3GetCreds c = maybe fromconfig (return . Just) =<< liftIO getenv+	where+		getenv = liftM2 (,)+			<$> get s3AccessKey+			<*> get s3SecretKey+			where+				get = catchMaybeIO . getEnv+		setenv (ak, sk) = do+			setEnv s3AccessKey ak True+			setEnv s3SecretKey sk True+		fromconfig = do 			mcipher <- remoteCipher c 			case (M.lookup "s3creds" c, mcipher) of-				(Just encrypted, Just cipher) -> do-					s <- liftIO $ withDecryptedContent cipher-						(return $ L.pack $ fromB64 encrypted)-						(return . L.unpack)-					let [ak', sk', _rest] = lines s-					liftIO $ do-						setEnv s3AccessKey ak True-						setEnv s3SecretKey sk True-					return $ Just (ak', sk')+				(Just s3creds, Just cipher) ->+					liftIO $ decrypt s3creds cipher 				_ -> return Nothing-		else return $ Just (ak, sk)-	where-		getEnvKey s = liftIO $ catchDefaultIO (getEnv s) ""+		decrypt s3creds cipher = do+			creds <- lines <$>+				withDecryptedContent cipher+					(return $ L.pack $ fromB64 s3creds)+					(return . L.unpack)+			case creds of+				[ak, sk] -> do+					setenv (ak, sk)+					return $ Just (ak, sk)+				_ -> do error "bad s3creds"  {- Stores S3 creds encrypted in the remote's config if possible. -} s3SetCreds :: RemoteConfig -> Annex RemoteConfig
Remote/Web.hs view
@@ -15,6 +15,7 @@ import Config import Logs.Web import qualified Utility.Url as Url+import Types.Key  remote :: RemoteType remote = RemoteType {@@ -44,6 +45,7 @@ 		removeKey = dropKey, 		hasKey = checkKey, 		hasKeyCheap = False,+		whereisKey = Just getUrls, 		config = Nothing, 		repo = r, 		remotetype = remote@@ -77,8 +79,8 @@ 	us <- getUrls key 	if null us 		then return $ Right False-		else return . Right =<< checkKey' us-checkKey' :: [URLString] -> Annex Bool-checkKey' us = untilTrue us $ \u -> do+		else return . Right =<< checkKey' key us+checkKey' :: Key -> [URLString] -> Annex Bool+checkKey' key us = untilTrue us $ \u -> do 	showAction $ "checking " ++ u-	liftIO $ Url.exists u+	liftIO $ Url.check u (keySize key)
Seek.hs view
@@ -14,11 +14,9 @@ import Common.Annex import Types.Command import Types.Key-import Backend import qualified Annex import qualified Git import qualified Git.LsFiles as LsFiles-import qualified Git.CheckAttr import qualified Limit import qualified Option @@ -28,26 +26,12 @@ withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params -withAttrFilesInGit :: String -> ((FilePath, String) -> CommandStart) -> CommandSeek-withAttrFilesInGit attr a params = do-	files <- seekHelper LsFiles.inRepo params-	prepFilteredGen a fst $ inRepo $ Git.CheckAttr.lookup attr files--withNumCopies :: (Maybe Int -> FilePath -> CommandStart) -> CommandSeek-withNumCopies a params = withAttrFilesInGit "annex.numcopies" go params-	where-		go (file, v) = a (readMaybe v) file--withBackendFilesInGit :: (BackendFile -> CommandStart) -> CommandSeek-withBackendFilesInGit a params =-	prepBackendPairs a =<< seekHelper LsFiles.inRepo params--withFilesNotInGit :: (BackendFile -> CommandStart) -> CommandSeek+withFilesNotInGit :: (FilePath -> CommandStart) -> CommandSeek withFilesNotInGit a params = do 	{- dotfiles are not acted on unless explicitly listed -} 	files <- filter (not . dotfile) <$> seek ps 	dotfiles <- if null dotps then return [] else seek dotps-	prepBackendPairs a $ preserveOrder params (files++dotfiles)+	prepFiltered a $ return $ preserveOrder params (files++dotfiles) 	where 		(dotps, ps) = partition dotfile params 		seek l = do@@ -61,24 +45,29 @@ withStrings :: (String -> CommandStart) -> CommandSeek withStrings a params = return $ map a params +withPairs :: ((String, String) -> CommandStart) -> CommandSeek+withPairs a params = return $ map a $ pairs [] params+	where+		pairs c [] = reverse c+		pairs c (x:y:xs) = pairs ((x,y):c) xs+		pairs _ _ = error "expected pairs"+ withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek withFilesToBeCommitted a params = prepFiltered a $ 	seekHelper LsFiles.stagedNotDeleted params -withFilesUnlocked :: (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlocked :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked = withFilesUnlocked' LsFiles.typeChanged -withFilesUnlockedToBeCommitted :: (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlockedToBeCommitted :: (FilePath -> CommandStart) -> CommandSeek withFilesUnlockedToBeCommitted = withFilesUnlocked' LsFiles.typeChangedStaged -withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO [FilePath]) -> (BackendFile -> CommandStart) -> CommandSeek+withFilesUnlocked' :: ([FilePath] -> Git.Repo -> IO [FilePath]) -> (FilePath -> CommandStart) -> CommandSeek withFilesUnlocked' typechanged a params = do 	-- unlocked files have changed type from a symlink to a regular file-	top <- fromRepo Git.workTree 	typechangedfiles <- seekHelper typechanged params-	unlockedfiles <- liftIO $ filterM notSymlink $-		map (\f -> top ++ "/" ++ f) typechangedfiles-	prepBackendPairs a unlockedfiles+	let unlockedfiles = liftIO $ filterM notSymlink typechangedfiles+	prepFiltered a unlockedfiles  withKeys :: (Key -> CommandStart) -> CommandSeek withKeys a params = return $ map (a . parse) params@@ -107,20 +96,13 @@   prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [CommandStart]-prepFiltered a = prepFilteredGen a id--prepBackendPairs :: (BackendFile -> CommandStart) -> CommandSeek-prepBackendPairs a fs = prepFilteredGen a snd (chooseBackends fs)--prepFilteredGen :: (b -> CommandStart) -> (b -> FilePath) -> Annex [b] -> Annex [CommandStart]-prepFilteredGen a d fs = do+prepFiltered a fs = do 	matcher <- Limit.getMatcher 	map (proc matcher) <$> fs 	where-		proc matcher v = do-			let f = d v+		proc matcher f = do 			ok <- matcher f-			if ok then a v else return Nothing+			if ok then a f else return Nothing  notSymlink :: FilePath -> IO Bool notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
Types/Command.hs view
@@ -7,6 +7,8 @@  module Types.Command where +import Data.Ord+ import Types  {- A command runs in these stages.@@ -36,6 +38,7 @@ 	{ cmdoptions :: [Option]     -- command-specific options 	, cmdnorepo :: Maybe (IO ()) -- an action to run when not in a repo 	, cmdcheck :: [CommandCheck] -- check stage+	, cmdoneshot :: Bool         -- don't save state after running 	, cmdname :: String 	, cmdparamdesc :: String     -- description of params for usage 	, cmdseek :: [CommandSeek]   -- seek stage@@ -45,3 +48,10 @@ {- CommandCheck functions can be compared using their unique id. -} instance Eq CommandCheck where 	a == b = idCheck a == idCheck b++instance Eq Command where+	a == b = cmdname a == cmdname b++{- Order commands by name -}+instance Ord Command where+	compare = comparing cmdname
Types/Key.hs view
@@ -69,8 +69,8 @@ 		findfields _ v = v  		addbackend k v = Just k { keyBackendName = v }-		addfield 's' k v = Just k { keySize = readMaybe v }-		addfield 'm' k v = Just k { keyMtime = readMaybe v }+		addfield 's' k v = Just k { keySize = readish v }+		addfield 'm' k v = Just k { keyMtime = readish v } 		addfield _ _ _ = Nothing  prop_idempotent_key_read_show :: Key -> Bool
Types/Remote.hs view
@@ -55,6 +55,8 @@ 	-- Some remotes can check hasKey without an expensive network 	-- operation. 	hasKeyCheap :: Bool,+	-- Some remotes can provide additional details for whereis.+	whereisKey :: Maybe (Key -> a [String]), 	-- a Remote can have a persistent configuration store 	config :: Maybe RemoteConfig, 	-- git configuration for the remote
Upgrade/V0.hs view
@@ -7,8 +7,6 @@  module Upgrade.V0 where -import System.IO.Error (try)- import Common.Annex import Annex.Content import qualified Upgrade.V1@@ -47,7 +45,7 @@ 			return $ map fileKey0 files 	where 		present d = do-			result <- try $+			result <- tryIO $ 				getFileStatus $ dir ++ "/" ++ takeFileName d 			case result of 				Right s -> return $ isRegularFile s
Upgrade/V1.hs view
@@ -7,7 +7,6 @@  module Upgrade.V1 where -import System.IO.Error (try) import System.Posix.Types import Data.Char @@ -183,7 +182,7 @@  lookupFile1 :: FilePath -> Annex (Maybe (Key, Backend)) lookupFile1 file = do-	tl <- liftIO $ try getsymlink+	tl <- liftIO $ tryIO getsymlink 	case tl of 		Left _ -> return Nothing 		Right l -> makekey l@@ -216,7 +215,7 @@ 			liftIO $ filterM present files 	where 		present f = do-			result <- try $ getFileStatus f+			result <- tryIO $ getFileStatus f 			case result of 				Right s -> return $ isRegularFile s 				Left _ -> return False
Upgrade/V2.hs view
@@ -50,7 +50,7 @@ 		mapM_ (\(k, f) -> inject f $ logFile k) =<< locationLogs 		mapM_ (\f -> inject f f) =<< logFiles old -	saveState+	saveState False 	showProgress  	when e $ do
Usage.hs view
@@ -7,10 +7,10 @@  module Usage where +import Common.Annex import System.Console.GetOpt  import Types.Command-import Types.Option  {- Usage message with lists of commands and options. -} usage :: String -> [Command] -> [Option] -> String@@ -30,7 +30,7 @@ 		alloptlines = filter (not . null) $ 			lines $ usageInfo "" $ 				concatMap cmdoptions cmds ++ commonoptions-		(cmdlines, optlines) = go cmds alloptlines []+		(cmdlines, optlines) = go (sort cmds) alloptlines [] 		go [] os ls = (ls, os) 		go (c:cs) os ls = go cs os' (ls++(l:o)) 			where@@ -76,6 +76,8 @@ paramDate = "DATE" paramFormat :: String paramFormat = "FORMAT"+paramFile :: String+paramFile = "FILE" paramKeyValue :: String paramKeyValue = "K=V" paramNothing :: String
+ Utility/CoProcess.hs view
@@ -0,0 +1,35 @@+{- Interface for running a shell command as a coprocess,+ - sending it queries and getting back results.+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.CoProcess (+	CoProcessHandle,+	start,+	stop,+	query+) where++import System.Cmd.Utils++import Common++type CoProcessHandle = (PipeHandle, Handle, Handle)++start :: FilePath -> [String] -> IO CoProcessHandle+start command params = hPipeBoth command params++stop :: CoProcessHandle -> IO ()+stop (pid, from, to) = do+	hClose to+	hClose from+	forceSuccess pid++query :: CoProcessHandle -> (Handle -> IO a) -> (Handle -> IO b) -> IO b+query (_, from, to) send receive = do+	_ <- send to+	hFlush to+	receive from
− Utility/Conditional.hs
@@ -1,26 +0,0 @@-{- monadic conditional operators- -- - Copyright 2011 Joey Hess <joey@kitenet.net>- -- - Licensed under the GNU GPL version 3 or higher.- -}--module Utility.Conditional where--import Control.Monad (when, unless)--whenM :: Monad m => m Bool -> m () -> m ()-whenM c a = c >>= flip when a--unlessM :: Monad m => m Bool -> m () -> m ()-unlessM c a = c >>= flip unless a--(>>?) :: Monad m => m Bool -> m () -> m ()-(>>?) = whenM--(>>!) :: Monad m => m Bool -> m () -> m ()-(>>!) = unlessM---- low fixity allows eg, foo bar >>! error $ "failed " ++ meep-infixr 0 >>?-infixr 0 >>!
Utility/CopyFile.hs view
@@ -8,8 +8,8 @@ module Utility.CopyFile (copyFileExternal) where  import System.Directory (doesFileExist, removeFile)+import Control.Monad.IfElse -import Utility.Conditional import Utility.SafeCommand import qualified Build.SysConfig as SysConfig 
Utility/Directory.hs view
@@ -12,15 +12,16 @@ import System.Directory import Control.Exception (throw) import Control.Monad+import Control.Monad.IfElse  import Utility.SafeCommand-import Utility.Conditional import Utility.TempFile+import Utility.Exception  {- Moves one filename to another.  - First tries a rename, but falls back to moving across devices if needed. -} moveFile :: FilePath -> FilePath -> IO ()-moveFile src dest = try (rename src dest) >>= onrename+moveFile src dest = tryIO (rename src dest) >>= onrename 	where 		onrename (Right _) = return () 		onrename (Left e)@@ -40,11 +41,10 @@ 						Param src, Param tmp] 					unless ok $ do 						-- delete any partial-						_ <- try $-							removeFile tmp+						_ <- tryIO $ removeFile tmp 						rethrow 		isdir f = do-			r <- try (getFileStatus f)+			r <- tryIO $ getFileStatus f 			case r of 				(Left _) -> return False 				(Right s) -> return $ isDirectory s
+ Utility/Exception.hs view
@@ -0,0 +1,39 @@+{- Simple IO exception handling+ -+ - Copyright 2011-2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.Exception where++import Prelude hiding (catch)+import Control.Exception+import Control.Applicative++{- Catches IO errors and returns a Bool -}+catchBoolIO :: IO Bool -> IO Bool+catchBoolIO a = catchDefaultIO a False++{- Catches IO errors and returns a Maybe -}+catchMaybeIO :: IO a -> IO (Maybe a)+catchMaybeIO a = catchDefaultIO (Just <$> a) Nothing++{- Catches IO errors and returns a default value. -}+catchDefaultIO :: IO a -> a -> IO a+catchDefaultIO a def = catchIO a (const $ return def)++{- Catches IO errors and returns the error message. -}+catchMsgIO :: IO a -> IO (Either String a)+catchMsgIO a = dispatch <$> tryIO a+	where+		dispatch (Left e) = Left $ show e+		dispatch (Right v) = Right v++{- catch specialized for IO errors only -}+catchIO :: IO a -> (IOException -> IO a) -> IO a+catchIO = catch++{- try specialized for IO errors only -}+tryIO :: IO a -> IO (Either IOException a)+tryIO = try
Utility/Format.hs view
@@ -88,13 +88,13 @@ 			| c == '}' = foundvar f var (readjustify $ reverse p) cs 			| otherwise = inpad (c:p) f var cs 		inpad p f var [] = Const (novar $ p++";"++var) : f-		readjustify = getjustify . fromMaybe 0 . readMaybe+		readjustify = getjustify . fromMaybe 0 . readish 		getjustify i 			| i == 0 = UnJustified 			| i < 0 = LeftJustified (-1 * i) 			| otherwise = RightJustified i 		novar v = "${" ++ reverse v-		foundvar f v p cs = scan (Var (reverse v) p : f) cs+		foundvar f v p = scan (Var (reverse v) p : f)  empty :: Frag -> Bool empty (Const "") = True
Utility/Misc.hs view
@@ -8,10 +8,16 @@ module Utility.Misc where  import System.IO-import System.IO.Error (try) import Control.Monad-import Control.Applicative+import GHC.IO.Encoding +{- 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 et. This special encoding+ - allows "arbitrary undecodable bytes to be round-tripped through it". -}+fileEncoding :: Handle -> IO ()+fileEncoding h = hSetEncoding h =<< getFileSystemEncoding+ {- A version of hgetContents that is not lazy. Ensures file is   - all read before it gets closed. -} hGetContentsStrict :: Handle -> IO String@@ -37,22 +43,3 @@ {- Breaks out the first line. -} firstLine :: String-> String firstLine = takeWhile (/= '\n')--{- Catches IO errors and returns a Bool -}-catchBoolIO :: IO Bool -> IO Bool-catchBoolIO a = catchDefaultIO a False--{- Catches IO errors and returns a Maybe -}-catchMaybeIO :: IO a -> IO (Maybe a)-catchMaybeIO a = catchDefaultIO (Just <$> a) Nothing--{- Catches IO errors and returns a default value. -}-catchDefaultIO :: IO a -> a -> IO a-catchDefaultIO a def = catch a (const $ return def)--{- Catches IO errors and returns the error message. -}-catchMsgIO :: IO a -> IO (Either String a)-catchMsgIO a = dispatch <$> try a-	where-		dispatch (Left e) = Left $ show e-		dispatch (Right v) = Right v
Utility/PartialPrelude.hs view
@@ -7,8 +7,10 @@  module Utility.PartialPrelude where +import qualified Data.Maybe+ {- read should be avoided, as it throws an error- - Instead, use: readMaybe -}+ - Instead, use: readish -} read :: Read a => String -> a read = Prelude.read @@ -36,16 +38,18 @@  -  - Ignores leading/trailing whitespace, and throws away any trailing  - text after the part that can be read.+ -+ - readMaybe is available in Text.Read in new versions of GHC,+ - but that one requires the entire string to be consumed.  -}-readMaybe :: Read a => String -> Maybe a-readMaybe s = case reads s of+readish :: Read a => String -> Maybe a+readish s = case reads s of 	((x,_):_) -> Just x 	_ -> Nothing  {- Like head but Nothing on empty list. -} headMaybe :: [a] -> Maybe a-headMaybe [] = Nothing-headMaybe v = Just $ Prelude.head v+headMaybe = Data.Maybe.listToMaybe  {- Like last but Nothing on empty list. -} lastMaybe :: [a] -> Maybe a
Utility/Path.hs view
@@ -47,7 +47,10 @@ 		a' = norm a 		b' = norm b -{- Converts a filename into a normalized, absolute path. -}+{- Converts a filename into a normalized, absolute path.+ -+ - Unlike Directory.canonicalizePath, this does not require the path+ - already exists. -} absPath :: FilePath -> IO FilePath absPath file = do 	cwd <- getCurrentDirectory
Utility/StatFS.hsc view
@@ -50,9 +50,12 @@ import Foreign import Foreign.C.Types import Foreign.C.String-import Data.ByteString (useAsCString)-import Data.ByteString.Char8 (pack)+import GHC.IO.Encoding (getFileSystemEncoding)+import GHC.Foreign as GHC +withFilePath :: FilePath -> (CString -> IO a) -> IO a+withFilePath fp f = getFileSystemEncoding >>= \enc -> GHC.withCString enc fp f+ #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) # include <sys/param.h> # include <sys/mount.h>@@ -105,7 +108,7 @@   return Nothing #else   allocaBytes (#size struct statfs) $ \vfs ->-  useAsCString (pack path) $ \cpath -> do+  withFilePath path $ \cpath -> do     res <- c_statfs cpath vfs     if res == -1 then return Nothing       else do
+ Utility/State.hs view
@@ -0,0 +1,26 @@+{- state monad support+ -+ - Copyright 2012 Joey Hess <joey@kitenet.net>+ -+ - Licensed under the GNU GPL version 3 or higher.+ -}++module Utility.State where++import Control.Monad.State.Strict++{- Modifies Control.Monad.State's state, forcing a strict update.+ - This avoids building thunks in the state and leaking.+ - Why it's not the default, I don't know.+ -+ - Example: changeState $ \s -> s { foo = bar }+ -}+changeState :: MonadState s m => (s -> s) -> m ()+changeState f = do+	x <- get+	put $! f x++{- Gets a value from the internal state, selected by the passed value+ - constructor. -}+getState :: MonadState s m => (s -> a) -> m a+getState = gets
Utility/TempFile.hs view
@@ -12,7 +12,7 @@ import System.Posix.Process hiding (executeFile) import System.Directory -import Utility.Misc+import Utility.Exception import Utility.Path  {- Runs an action like writeFile, writing to a temp file first and
Utility/Touch.hsc view
@@ -16,7 +16,12 @@ import Foreign import Foreign.C import Control.Monad (when)+import GHC.IO.Encoding (getFileSystemEncoding)+import GHC.Foreign as GHC +withFilePath :: FilePath -> (CString -> IO a) -> IO a+withFilePath fp f = getFileSystemEncoding >>= \enc -> GHC.withCString enc fp f+ newtype TimeSpec = TimeSpec CTime  {- Changes the access and modification times of an existing file.@@ -64,7 +69,7 @@  touchBoth file atime mtime follow =  	allocaArray 2 $ \ptr ->-	withCString file $ \f -> do+	withFilePath file $ \f -> do 		pokeArray ptr [atime, mtime] 		r <- c_utimensat at_fdcwd f ptr flags 		when (r /= 0) $ throwErrno "touchBoth"@@ -101,7 +106,7 @@  touchBoth file atime mtime follow =  	allocaArray 2 $ \ptr ->-	withCString file $ \f -> do+	withFilePath file $ \f -> do 		pokeArray ptr [atime, mtime] 		r <- syscall f ptr 		if (r /= 0)
Utility/Url.hs view
@@ -7,6 +7,7 @@  module Utility.Url ( 	URLString,+	check, 	exists, 	canDownload, 	download,@@ -14,25 +15,39 @@ ) where  import Control.Applicative+import Control.Monad import qualified Network.Browser as Browser import Network.HTTP import Network.URI+import Data.Maybe  import Utility.SafeCommand import Utility.Path  type URLString = String -{- Checks that an url exists and could be successfully downloaded. -}-exists :: URLString -> IO Bool+{- Checks that an url exists and could be successfully downloaded,+ - also checking that its size, if available, matches a specified size. -}+check :: URLString -> Maybe Integer -> IO Bool+check url expected_size = handle <$> exists url+	where+		handle (False, _) = False+		handle (True, Nothing) = True+		handle (True, s) = expected_size == s++{- Checks that an url exists and could be successfully downloaded,+ - also returning its size if available. -}+exists :: URLString -> IO (Bool, Maybe Integer) exists url = 	case parseURI url of-		Nothing -> return False+		Nothing -> return (False, Nothing) 		Just u -> do 			r <- request u HEAD 			case rspCode r of-				(2,_,_) -> return True-				_ -> return False+				(2,_,_) -> return (True, size r)+				_ -> return (False, Nothing)+	where+		size = liftM read . lookupHeader HdrContentLength . rspHeaders  canDownload :: IO Bool canDownload = (||) <$> inPath "wget" <*> inPath "curl"@@ -73,12 +88,31 @@  {- Makes a http request of an url. For example, HEAD can be used to  - check if the url exists, or GET used to get the url content (best for- - small urls). -}+ - small urls).+ -+ - This does its own redirect following because Browser's is buggy for HEAD+ - requests.+ -} request :: URI -> RequestMethod -> IO (Response String)-request url requesttype = Browser.browse $ do-	Browser.setErrHandler ignore-	Browser.setOutHandler ignore-	Browser.setAllowRedirects True-	snd <$> Browser.request (mkRequest requesttype url :: Request_String)+request url requesttype = go 5 url 	where+		go :: Int -> URI -> IO (Response String)+		go 0 _ = error "Too many redirects "+		go n u = do+			rsp <- Browser.browse $ do+				Browser.setErrHandler ignore+				Browser.setOutHandler ignore+				Browser.setAllowRedirects False+				snd <$> Browser.request (mkRequest requesttype u :: Request_String)+			case rspCode rsp of+				(3,0,x) | x /= 5 -> redir (n - 1) u rsp+				_ -> return rsp 		ignore = const $ return ()+		redir n u rsp = case retrieveHeaders HdrLocation rsp of+			[] -> return rsp+			(Header _ newu:_) ->+				case parseURIReference newu of+					Nothing -> return rsp+					Just newURI -> go n newURI_abs+						where+							newURI_abs = fromMaybe newURI (newURI `relativeTo` u)
configure.hs view
@@ -4,9 +4,11 @@ import Data.List import Data.Maybe import System.Cmd.Utils+import Control.Applicative  import Build.TestConfig import Utility.StatFS+import Utility.SafeCommand  tests :: [TestCase] tests =@@ -23,6 +25,7 @@ 	, TestCase "wget" $ testCmd "wget" "wget --version >/dev/null" 	, TestCase "bup" $ testCmd "bup" "bup --version >/dev/null" 	, TestCase "gpg" $ testCmd "gpg" "gpg --version >/dev/null"+	, TestCase "ssh connection caching" getSshConnectionCaching 	, TestCase "StatFS" testStatFS 	] ++ shaTestCases [1, 256, 512, 224, 384] @@ -65,6 +68,10 @@ 	(_, s) <- pipeFrom "git" ["--version"] 	let version = last $ words $ head $ lines s 	return $ Config "gitversion" (StringConfig version)++getSshConnectionCaching :: Test+getSshConnectionCaching = Config "sshconnectioncaching" . BoolConfig <$>+	boolSystem "sh" [Param "-c", Param "ssh -o ControlPersist=yes -V >/dev/null 2>/dev/null"]  testStatFS :: Test testStatFS = do
debian/changelog view
@@ -1,3 +1,49 @@+git-annex (3.20120227) unstable; urgency=low++  * Modifications to support ghc 7.4's handling of filenames.+    This version can only be built with ghc 7.4 or newer. See the ghc7.0+    branch for older ghcs.+  * S3: Fix irrefutable pattern failure when accessing encrypted S3+    credentials.+  * Use the haskell IfElse library.+  * Fix teardown of stale cached ssh connections.+  * Fixed to use the strict state monad, to avoid leaking all kinds of memory+    due to lazy state update thunks when adding/fixing many files.+  * Fixed some memory leaks that occurred when committing journal files.+  * Added a annex.queuesize setting, useful when adding hundreds of thousands+    of files on a system with plenty of memory.+  * whereis: Prints the urls of files that the web special remote knows about.+  * addurl --fast: Verifies that the url can be downloaded (only getting+    its head), and records the size in the key.+  * When checking that an url has a key, verify that the Content-Length,+    if available, matches the size of the key.+  * addurl: Added a --file option, which can be used to specify what+    file the url is added to. This can be used to override the default+    filename that is used when adding an url, which is based on the url.+    Or, when the file already exists, the url is recorded as another+    location of the file.+  * addurl: Normalize badly encoded urls.+  * addurl: Add --pathdepth option.+  * rekey: New plumbing level command, can be used to change the keys used+    for files en masse.+  * Store web special remote url info in a more efficient location.+    (Urls stored with this version will not be visible to older versions.)+  * Deal with NFS problem that caused a failure to remove a directory+    when removing content from the annex.+  * Make a single location log commit after a remote has received or+    dropped files. Uses a new "git-annex-shell commit" command when available.+  * To avoid commits of data to the git-annex branch after each command+    is run, set annex.alwayscommit=false. Its data will then be committed+    less frequently, when a merge or sync is done.+  * configure: Check if ssh connection caching is supported by the installed+    version of ssh and default annex.sshcaching accordingly.+  * move --from, copy --from: Now 10 times faster when scanning to find+    files in a remote on a local disk; rather than go through the location log+    to see which files are present on the remote, it simply looks at the +    disk contents directly.++ -- Joey Hess <joeyh@debian.org>  Mon, 27 Feb 2012 12:58:21 -0400+ git-annex (3.20120123) unstable; urgency=low    * fsck --from: Fscking a remote is now supported. It's done by retrieving
debian/control view
@@ -3,7 +3,7 @@ Priority: optional Build-Depends:  	debhelper (>= 9),-	ghc,+	ghc (>= 7.4), 	libghc-missingh-dev, 	libghc-hslogger-dev, 	libghc-pcre-light-dev,@@ -17,11 +17,13 @@ 	libghc-monad-control-dev (>= 0.3), 	libghc-lifted-base-dev, 	libghc-json-dev,+	libghc-ifelse-dev, 	ikiwiki, 	perlmagick, 	git, 	uuid, 	rsync,+	openssh-client, Maintainer: Joey Hess <joeyh@debian.org> Standards-Version: 3.9.2 Vcs-Git: git://git.kitenet.net/git-annex@@ -35,13 +37,13 @@ 	uuid, 	rsync, 	wget | curl,-	openssh-client+	openssh-client (>= 1:5.6p1) Suggests: graphviz, bup, gnupg Description: manage files with git, without checking their contents into git  git-annex allows managing files with git, without checking the file  contents into git. While that may seem paradoxical, it is useful when  dealing with files larger than git can currently easily handle, whether due- to limitations in memory, checksumming time, or disk space.+ to limitations in memory, time, or disk space.  .  Even without file content tracking, being able to manage files with git,  move files around and delete files with versioned directory trees, and use
debian/copyright view
@@ -1,4 +1,4 @@-Format: http://dep.debian.net/deps/dep5/+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Source: native package  Files: *
− debian/manpages
@@ -1,1 +0,0 @@-git-annex.1
doc/bugs.mdwn view
@@ -2,3 +2,5 @@  [[!inline pages="./bugs/* and !./bugs/done and !link(done)  and !*/Discussion" actions=yes postform=yes show=0 archive=yes]]++[[!edittemplate template=templates/bugtemplate match="bugs/*" silent=yes]]
+ doc/bugs/copy_doesn__39__t_scale.mdwn view
@@ -0,0 +1,38 @@+It seems that git-annex copies every individual file in a separate+transaction. This is quite costly for mass transfers: each file involves a+separate rsync invocation and the creation of a new commit. Even with a+meager thousand files or so in the annex, I have to wait for fifteen+minutes to copy the contents to another disk, simply because every+individual file involves some disk thrashing. Also, it seems suspicious+that the git-annex branch would get a thousands commits of history from the+simple procedure of copying everything to a new repository. Surely it would+be better to first copy everything and then create only a single commit+that registers the changes to the files' availability?++> git-annex is very careful to commit as infrequently as possible,+> and the current version makes *1* commit after all the copies are+> complete, even if it transferred a billion files. The only overhead+> incurred for each file is writing a journal file.+> You must have an old version.+> --[[Joey]]++(I'm also not quite clear on why rsync is being used when both repositories+are local. It seems to be just overhead.)++> Even when copying to another disk it's often on +> some slow bus, and the file is by definition large. So it's+> nice to support resumes of interrupted transfers of files.+> Also because rsync has a handy progress display that is hard to get with cp.+> +> (However, if the copy is to another directory in the same disk, it does+> use cp, and even supports really fast copies on COW filesystems.)+> --[[Joey]]++---++Oneshot mode is now implemented, making git-annex-shell and other+short lifetime processes not bother with committing changes.+[[done]] --[[Joey]] ++Update: Now it makes one commit at the very end of such a mass transfer.+--[[Joey]]
+ doc/bugs/copy_doesn__39__t_scale/comment_1_7c12499c9ac28a9883c029f8c659eb57._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawk6QAwUsFHpr3Km1yQbg8hf3S7RDYf7hX4"+ nickname="Lauri"+ subject="comment 1"+ date="2012-01-28T00:17:37Z"+ content="""+To me it very much seems that a commit per file is indeed created at the remote end, although not at the local end. See the following transcript: <https://gist.github.com/1691714>.+++"""]]
+ doc/bugs/copy_doesn__39__t_scale/comment_2_f85d8023cdbc203bb439644cf7245d4e._comment view
@@ -0,0 +1,15 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 2"+ date="2012-01-28T19:32:36Z"+ content="""+Ah, I see, I was not thinking about the location log update that's done on the remote side.++For transfers over ssh, that's a separate git-annex-shell invoked per change. For local-local transfers, it's all done in a single process but it spins up a state to handle the remote and then immediately shuts it down, also generating a commit.++In either case, I think there is a nice fix. Since git-annex *does* have a journal nowadays, and goes to all the bother to+support recovery if a process was interrupted and journalled changes that did not get committed, there's really no reason in either of these cases for the remote end to do anything more than journal the change. The next time git-annex is actually run on the remote, and needs to look up location information, it will merge the journalled changes into the branch, in a single commit.++My only real concern is that some remotes might *never* have git-annex run in them directly, and would just continue to accumulate journal files forever. Although due to the way the journal is structured, it can have, at a maximum, the number of files in the git-annex branch. However, the number of files in it is expected to be relatively smal and it might get a trifle innefficient, as it lacks directory hashing. These performance problems could certainly be dealt with if they do turn out to be a problem.+"""]]
+ doc/bugs/copy_doesn__39__t_scale/comment_3_4592765c3d77bb5664b8d16867e9d79c._comment view
@@ -0,0 +1,11 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawk6QAwUsFHpr3Km1yQbg8hf3S7RDYf7hX4"+ nickname="Lauri"+ subject="comment 3"+ date="2012-01-29T01:51:35Z"+ content="""+That sounds just fine, but indeed my use case was a bare backup/transfer repository that is meant to always be only at the remote end of git-annex operations. So why not as well do a single commit after everything has been copied and journaled? That's what's done at the other end too, after all. Or, if commits are to be minimized, just stage the journal into the index before finishing, but don't commit it yet?++(I would actually prefer this mode of usage for other git-annex operations, too. In git you can add stuff little by little and commit them all in one go. In git-annex the add immediately creates a commit, which is unexpected and a bit annoying.)++"""]]
+ doc/bugs/git_annex_add_memory_leak.mdwn view
@@ -0,0 +1,39 @@+For the record, `git annex add` has had a series of memory leaks.+Mostly these are minor -- until you need to check in a few+million files in a single operation. ++If this happens to you, git-annex will run out of memory and stop.+(Generally well before your system runs out of memory, since it has some+built-in ulimits.) You can recover by just re-running the `git annex add`+-- it will automatically pick up where it left off.++A history of the leaks:++* Originally, `git annex add` remembered all the files+  it had added, and fed them to git at the end. Of course+  that made its memory use grow, so it was fixed to periodically+  flush its buffer. Fixed in version 0.20110417.++* Something called a "lazy state monad" caused "thunks" to build+  up and memory to leak. Also affected other git annex commands+  than `add`. Adding files using a SHA* backend hit the worst.+  Fixed in versions afer 3.20120123.++* Committing journal files turned out to have another memory leak.+  After adding a lot of files ran out of memory, this left the journal+  behind and could affect other git-annex commands. Fixed in versions afer+  3.20120123.++* The count of the number of failed commands was updated lazily, which+  caused a slow leak when running on a lot of files. Fixed in versions afer+  3.20120123.++* (Note that `git ls-files --others`, which is used to find files to add,+  also uses surpsisingly large amounts+  of memory when you have a lot of files. It buffers+  the entire list, so it can compare it with the files in the index,+  before outputting anything.+  This is Not Our Problem, but I'm sure the git developers+  would appreciate a patch that fixes it.)++[[done]]
+ doc/bugs/nfs_mounted_repo_results_in_errors_on_drop_move.mdwn view
@@ -0,0 +1,59 @@+I'm on an nfs mounted filesystem (some netapp somewhere).  This is repeatable, every time.++    git init repo; cd repo;+    git annex init repo+    truncate -s 20M big+    git annex add big+    git commit -m "annexed file"+    cd ..+    git clone repo repo_copy+    cd repo_copy;+    git annex get .+    git annex whereis big++    #whereis big (2 copies) +    #   	9310b242-6021-4621-8cef-4548a00907ff -- here+    #   	b3526e4d-38d7-4781-a9c3-436007899f1b -- origin (repo)+    #ok++    git annex drop big++    #git-annex: /nfspath/repo_copy/.git/annex/objects/fM/4k/SHA1-s20971520--9674344c90c2f0646f0b78026e127c9b86e3ad77: removeDirectory: unsatisified constraints (Directory not empty)+    #failed+    #git-annex: drop: 1 failed++    git annex drop big  # no error second time, I suspect nfs has caught up by now.+    git annex fsck      # Doesn't know that the second drop succeeded.++    #fsck big (fixing location log) +    #  ** Based on the location log, big+    #  ** was expected to be present, but its content is missing.+    #failed+    #git-annex: fsck: 1 failed++    git annex fsck++    #fsck big ok++    git annex whereis big++    #whereis big (1 copy) +    #  	b3526e4d-38d7-4781-a9c3-436007899f1b -- origin (repo)+    #ok++I suspect git-annex is just too fast and optimistic for big slow nfs directories.++> git-annex locks files while it is operating on their content+> to avoid race conditions with other git-annex processes. +> Quite likely this problem (which I can reproduce) is due to+> NFS having bad (non-POSIX) locking semantics. +> +> Probably the+> lock is represented on the NFS server as some form of lock file+> next to the file being locked, and so when that file is deleted, with+> the lock still held, the directory, which should then be empty, still+> contains this lock file.+> +> So, this can be worked around by it not failing when the directory+> unexpectedly cannot be removed. I've made that change. [[done]]+> --[[Joey]]
doc/bugs/problems_with_utf8_names.mdwn view
@@ -1,3 +1,16 @@+This bug is reopened to track some new UTF-8 filename issues caused by GHC+7.4. In this version of GHC, git-annex's hack to support filenames in any+encoding no longer works. Even unicode filenames fail to work when+git-annex is built with 7.4. --[[Joey]]++This bug is now fixed in current master. Once again, git-annex will work+for all filename encodings, and all system encodings. It will+only build with the new GHC. [[done]] --[[Joey]] ++----++Old, now fixed bug report follows:+ There are problems with displaying filenames in UTF8 encoding, as shown here:      $ echo $LANG@@ -45,7 +58,7 @@ >    outputting a filename (assuming the filename is encoded using the >    user's configured encoding), and allow haskell's output encoding to then >    encode it according to the user's locale configuration.->    > This is now [[implemented|done]]. I'm not very happy that I have to watch+>    > This is now implemented. I'm not very happy that I have to watch >    > out for any place that a filename is output and call `filePathToString` >    > on it, but there are really not too many such places in git-annex. >    >@@ -66,39 +79,3 @@ >    > On second thought, I switched to this. Any decoding of a filename >    > is going to make someone unhappy; the previous approach broke >    > non-utf8 filenames.--------Simpler test case:--<pre>-import Codec.Binary.UTF8.String-import System.Environment--main = do-        args <- getArgs-        let file = decodeString $ head args-        putStrLn $ "file is: " ++ file-        putStr =<< readFile file-</pre>--If I pass this a filename like 'ü', it will fail, and notice-the bad encoding of the filename in the error message:--<pre>-$ echo hi > ü; runghc foo.hs ü-file is: ü-foo.hs: �: openFile: does not exist (No such file or directory)-</pre>--On the other hand, if I remove the decodeString, it prints the filename-wrong, while accessing it right:--<pre>-$ runghc foo.hs ü-file is: üa-hi-</pre>--The only way that seems to consistently work is to delay decoding the-filename to places where it's output. But then it's easy to miss some.
+ doc/bugs/problems_with_utf8_names/comment_5_519cda534c7aea7f5ad5acd3f76e21fa._comment view
@@ -0,0 +1,11 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawk6QAwUsFHpr3Km1yQbg8hf3S7RDYf7hX4"+ nickname="Lauri"+ subject="comment 5"+ date="2012-01-26T22:13:18Z"+ content="""+I also encountered Adam's bug. The problem seems to be that communication with the git process is done with `Char8`-bytestrings. So, when `L.unpack` is called, all filenames that git outputs (with `ls-files` or `ls-tree`) are interpreted to be in latin-1, which wreaks havoc if they are really in UTF-8.++I suspect that it would be enough to just switch to standard `String`s (or `Data.Text.Text`) instead of bytestrings for textual data, and to `Word8`-bytestrings for pure binary data. GHC should nowadays handle locale-dependent encoding of `String`s transparently.++"""]]
+ doc/bugs/problems_with_utf8_names/comment_6_52e0bfff2b177b6f92e226b25d2f3ff1._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 6"+ date="2012-01-27T21:00:06Z"+ content="""+Lauri, what version of GHC do you have that behaves this way? 7.0.4 does not. +"""]]
+ doc/bugs/problems_with_utf8_names/comment_7_0cc588f787d6eecfa19a8f6cee4b07b5._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="https://www.google.com/accounts/o8/id?id=AItOawk6QAwUsFHpr3Km1yQbg8hf3S7RDYf7hX4"+ nickname="Lauri"+ subject="comment 7"+ date="2012-01-28T00:21:40Z"+ content="""+7.2. nomeata already explained the issue. I got utf-8 filenames to work on a utf-8 locale by switching from Char8-bytestrings to UTF8-bytestrings, and adding `hSetEncoding h localeEncoding` to suitable places. Making things work properly with an arbitrary locale encoding would be more complicated.+"""]]
+ doc/bugs/problems_with_utf8_names/comment_8_ff5c6da9eadfee20c18c86b648a62c47._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 8"+ date="2012-01-28T19:40:34Z"+ content="""+Lauri a scratch patch would be very helpful. Encoding stuff makes my head explode.++However, I am very worried by haskell's changes WRT unicode and filenames. Based on user input, git-annex users like to use it on diverse sets of files, with diverse and ill-defined encodings. Faffing about with converting between encodings seems likely to speactacularly fail.+"""]]
+ doc/bugs/show_version_without_having_to_be_in_a_git_repo.mdwn view
@@ -0,0 +1,11 @@+It'd be nice to be able to run "git annex version" -- and maybe some other+commands, like "git annex" itself for the help text, without having to be+inside a git repo. Right now it requires you to be in a git repo even if+it's not a git-annex repo.++> You need a newer verison of git-annex. --[[Joey]] ++	joey@gnu:/>git annex version+	git-annex version: 3.20120124++[[done]]
doc/bugs/signal_weirdness.mdwn view
@@ -25,14 +25,24 @@ It's more usual for a `system` like thing to block SIGINT, letting the child catch it and exit, and then detecting the child's exit status and terminating.  However, since rsync *is* trapping SIGINT, and exiting nonzero explicitly,-git-annex can't tell that rsync failed due to a SIGINT.+git-annex can't tell that rsync failed due to a SIGINT by examining the+`waitpid` result. And, git-annex typically doesn't stop when a single child fails. In the example above, it would go on to copy `b` after a ctrl-c!  A further complication is that git-annex is itself a child process-of git, which does not block SIGINT either. So if git-annex blocks sigint,+of git, which does not block SIGINT either. So if git-annex blocks SIGINT, it will be left running in the background after git exits, and continuing-with further actions too. (Probably this is a bug in git.)+with further actions too. (Perhaps its SIGINT handling is a bug in git.) +Now, rsync does have a documented exit code it uses after a SIGINT.+But other programs git-annex runs generally do not. So it would be possible+to special case in support for rsync, blocking SIGINT while running it,+noticing it exited with 20, and git-annex then stopping. But this is+ugly and failure prone if rsync's code 20 changes. And it only+would fix the rsync case, not helping with other commands like wget, unless+it assumes they never trap SIGINT on their own.+ Which is why the current behavior of not blocking SIGINT was chosen,-as a less bad alternative. Still, I'd like to find a better one. --[[Joey]] +as a less bad alternative. Still, I'd like to find a better one.+--[[Joey]] 
doc/download.mdwn view
@@ -15,3 +15,21 @@  Some operating systems include git-annex in easily prepackaged form and others need some manual work. See [[install]] for details.++## git branches++The git repository has some branches:++* `debian-stable` contains the latest backport of git-annex to Debian+  stable.+* `no-s3` disables the S3 special remote, for systems that lack the+ necessary haskell library.+* `old-monad-control` is for systems that don't have a newer monad-control+  library.+* `tweak-fetch` adds support for the git tweak-fetch hook, which has+  been proposed and implemented but not yet accepted into git.+* `ghc7.0` supports versions of ghc older than 7.4, which+  had a major change to filename encoding.+* `setup` contains configuration for this website+* `pristine-tar` contains [pristine-tar](http://kitenet.net/~joey/code/pristine-tar)+  data to create tarballs of any past git-annex release.
+ doc/forum/Automatic_commit_messages_for_git_annex_sync.mdwn view
@@ -0,0 +1,1 @@+Not really important (who reads git annex commit messages anyways ;-)), but nice to have and maybe a nice task for someone who wants to play around with Haskell and git annex: It would be shiny if the auto-commit done by git annex sync would automatically create a sensible commmit message and description. E.g. if just one file is added, it could say „Added blubb“. If a few files are added, it could say „Added blubb, bla and n other files“, based on the file name length, and list the files in the long description. Lots of room for playing around :-)
+ doc/forum/How_to_expire_old_versions_of_files_that_have_been_edited__63__.mdwn view
@@ -0,0 +1,7 @@+My annex contains several large files that I have unlocked, edited, and committed again, i.e. the annex contains the version history of those files. However, I don't want the history -- keeping the latest version is good enough for me. Running `git annex unused` won't detect those old versions, though, because they aren't unused as old Git revisions still refer to them. So I wonder:++1. What is the best way to get rid of the old versions of files in the annex?++2. What is the best way to detect old versions of files in the annex?++I guess, I could run `git rebase -i` to squash commits to those files into one commit, thereby getting rid of the references to the old copies, but that approach feels awkward and error prone. Is anyone aware of a better way?
+ doc/forum/cloud_services_to_support.mdwn view
@@ -0,0 +1,16 @@+git-annex can already be used to store data in several cloud services:+Amazon S3, rsync.net, Tahoe-LAFFS, The Internet Archive.++I would like to support as many other cloud services as possible/reasonable.++* [[swift|todo/wishlist:_swift_backend]]+* Dropbox (I had been reluctant to go there due to it using a non-free client,+  which I have no interest in installing, but there is actually an API,+  and already a+  [haskell module to use it](http://hackage.haskell.org/package/dropbox-sdk).+  Would need to register for an API key.+  <http://www.dropbox.com/developers/start/core>.+  Annoyingly, Dropbox reviews each app before granting it production status.+  Whoops my interest level dropped by 99%.)++Post others in the comments. --[[Joey]]
+ doc/forum/fsck_gives_false_positives.mdwn view
@@ -0,0 +1,6 @@+Hi,++I use git-annex 3.20120123 on a debian-testing amd-64 machine with software RAID6 and LVM2 on it. I needed to move the whole `/home` directory to another LV (the new LV is on encrypted PV, the old LV is encrypted and not properly aligned; I'm changing from encrypted `/home` only to encrypted everything except `/boot`), so I have used the `rsync -aAXH` from a `ro` mounted `/home` to a new LV mounted on `/mnt/home_2`. After the move was complete I run the `git annex fsck` on my (4TB of) data. The fsck finds some files bad, and moves them to the `..../bad` directory. So far so good, this is how it should be, right? But then- I have a file with sha1sum of all my files. So - I checked the 'bad' file against that. It was OK. Then I computed the SHA256 of the file - this is used by `git annex fsck`. It was OK, too. So how did it happen, that the file was marked as bad? Do I miss something here? Could it be related to the hardware (HDDs) and silent data corruption? Or is it the undesirable effect of rsync? Or maybe the fsck is at fault here?++Any ideas?+
+ doc/forum/fsck_gives_false_positives/comment_1_b91070218b9d5fb687eeee1f244237ad._comment view
@@ -0,0 +1,15 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 1"+ date="2012-02-14T16:58:33Z"+ content="""+Well, it should only move files to `.git/annex/bad/` if their filesize is wrong, or their checksum is wrong.++You can try moving a file out of `.git/annex/bad/` and re-run fsck and see if it fails it again. (And if it does, paste in a log!) ++To do that -- +Suppose you have a file `.git/annex/bad/SHA256-s33--5dc45521382f1c7974d9dbfcff1246370404b952` and you know that file `foobar` was supposed to have that content (you can check that `foobar` is a symlink to that SHA value). Then reinject it:++`git annex reinject .git/annex/bad/SHA256-s33--5dc45521382f1c7974d9dbfcff1246370404b952 foobar`+"""]]
+ doc/forum/fsck_gives_false_positives/comment_2_f51c53f3f6e6ee1ad463992657db5828._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="antymat"+ ip="77.190.74.127"+ subject="comment 2"+ date="2012-02-14T22:48:37Z"+ content="""+Thanks, joey, but I still do not know, why the file that has been (and *is*) OK according to separate sha1 and sha256 checks, has been marked 'bad' by `fsck` and moved to `.git/annex/bad`. What could be a reason for that? Could have `rsync` caused it? I know too little about internal workings of `git-annex` to answer this question.++But one thing I know for certain - the false positives should *not* happen, unless something *is* wrong with the file. Otherwise, if it is unreliable, if I have to check twice, it is useless. I might as well just keep checksums of all the files and do all checks by hand...+"""]]
+ doc/forum/fsck_gives_false_positives/comment_3_692d6d4cd2f75a497e7d314041a768d2._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 3"+ date="2012-02-14T22:57:29Z"+ content="""+All that git annex fsck does is checksum the file and move it away if the checksum fails. ++If bad data was somehow read from the disk that one time, what you describe could occur. I cannot think of any other way it could happen.+"""]]
+ doc/forum/fsck_gives_false_positives/comment_4_7ceb395bf8a2e6a041ccd8de63b1b6eb._comment view
@@ -0,0 +1,10 @@+[[!comment format=mdwn+ username="antymat"+ ip="77.190.74.127"+ subject="comment 4"+ date="2012-02-15T07:13:12Z"+ content="""+OK, thanks. I was just wondering - since there are links in git(-annex), and a hard links too, that maybe the issue has been caused by `rsync`.++I will keep my eye on that and run checks with my own checksum and `fsck` from time to time, and see what happens. I will post my results here, but the whole run (`fsck` or checksum) takes almost 2 days, so I will not do it too often... ;)+"""]]
+ doc/forum/fsck_gives_false_positives/comment_5_86484a504c3bbcecd5876982b9c95688._comment view
@@ -0,0 +1,13 @@+[[!comment format=mdwn+ username="http://joey.kitenet.net/"+ nickname="joey"+ subject="comment 5"+ date="2012-02-15T15:22:56Z"+ content="""+The symlinks are in the git repository. So if the rsync damanged one, git would see the change. And nothing that happens to the symlinks can affect fsck.++git-annex does not use hard links at all.++fsck corrects mangled file permissions. It is possible to screw up the permissions so badly that it cannot see the files at all (ie, chmod 000 on a file under .git/annex/objects), but then fsck will complain and give up, not move the files to bad.+So I don't see how a botched rsync could result in fsck moving a file with correct content to bad.+"""]]
+ doc/forum/nfs_mounted_repo_results_in_errors_on_drop__47__move.mdwn view
@@ -0,0 +1,4 @@+Bug reports should be posted in [[bugs]], not in the forum. I have moved+this misplaced bug report to+[[bugs/nfs_mounted_repo_results_in_errors_on_drop_move]].+--[[Joey]] 
doc/future_proofing.mdwn view
@@ -21,7 +21,8 @@   formats.  * What filesystem is used on the drive? Will that filesystem still be-  available?+  available? Whatever you choose to use, git-annex can put files on it.+  Even if you choose (ugh) FAT.  * What is the hardware interface of the drive? Will hardware still exist   to talk to it?
doc/git-annex-shell.mdwn view
@@ -46,6 +46,10 @@    This runs rsync in server mode to transfer out the content of a key. +* commit++  This commits any staged changes to the git-annex branch.+ # OPTIONS  Most options are the same as in git-annex. The ones specific
doc/git-annex.mdwn view
@@ -145,10 +145,21 @@  * addurl [url ...] -  Downloads each url to a file, which is added to the annex.+  Downloads each url to its own file, which is added to the annex. -  To avoid immediately downloading the url, specify --fast+  To avoid immediately downloading the url, specify --fast. +  Normally the filename is based on the full url, so will look like+  "www.example.com_dir_subdir_bigfile". For a shorter filename, specify+  --pathdepth=N. For example, --pathdepth=1 will use "dir/subdir/bigfile",+  while --pathdepth=3 will use "bigfile". It can also be negative;+  --pathdepth=-2 will use the last two parts of the url.++  Or, to directly specify what file the url is added to, specify --file.+  This changes the behavior; now all the specified urls are recorded as+  alternate locations from which the file can be downloaded. In this mode,+  addurl can be used both to add new files, or to add urls to existing files.+ # REPOSITORY SETUP COMMANDS  * init [description]@@ -388,6 +399,14 @@  	git annex dropkey SHA1-s10-7da006579dd64330eb2456001fd01948430572f2 +* rekey [file key ...]+  +  This plumbing-level command is similar to migrate, but you specify+  both the file, and the new key to use for it.++  With --force, even files whose content is not currently available will+  be rekeyed. Use with caution.+ # OPTIONS  * --force@@ -571,13 +590,29 @@    The default reserve is 1 megabyte. +* `annex.queuesize`++  git-annex builds a queue of git commands, in order to combine similar+  commands for speed. By default the size of the queue is limited to+  10240 commands; this can be used to change the size. If you have plenty+  of memory and are working with very large numbers of files, increasing+  the queue size can speed it up.+ * `annex.version`    Automatically maintained, and used to automate upgrades between versions.  * `annex.sshcaching` -  By default, git-annex caches ssh connections. To disable this, set to `false`.+  By default, git-annex caches ssh connections+  (if built using a new enough ssh). To disable this, set to `false`.++* `annex.alwayscommit`++  By default, git-annex automatically commits data to the git-annex branch+  after each command is run. To disable these commits,+  set to `false`. Then data will only be committed when+  running `git annex merge` (or by automatic merges) or `git annex sync`.  * `remote.<name>.annex-cost` 
doc/index.mdwn view
@@ -49,9 +49,15 @@ * [[encryption]] * [[bare_repositories]] * [[internals]]+* [[scalability]] * [[design]] * [[what git annex is not|not]] * [[sitemap]]++## talks++<video controls src="http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm" width="500"></video>  +A [15 minute introduction to git-annex](http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm), presented by Richard Hartmann.  <br clear="all" /> 
doc/install.mdwn view
@@ -21,7 +21,7 @@ To build and use git-annex, you will need:  * Haskell stuff-  * [The Haskell Platform](http://haskell.org/platform/)+  * [The Haskell Platform](http://haskell.org/platform/) (GHC 7.4 or newer)   * [MissingH](http://github.com/jgoerzen/missingh/wiki)   * [pcre-light](http://hackage.haskell.org/package/pcre-light)   * [utf8-string](http://hackage.haskell.org/package/utf8-string)@@ -34,6 +34,7 @@   * [HTTP](http://hackage.haskell.org/package/HTTP)   * [hS3](http://hackage.haskell.org/package/hS3)   * [json](http://hackage.haskell.org/package/json)+  * [IfElse](http://hackage.haskell.org/package/IfElse) * Shell commands   * [git](http://git-scm.com/)   * [uuid](http://www.ossp.org/pkg/lib/uuid/)
+ doc/news/Presentation_at_FOSDEM.mdwn view
@@ -0,0 +1,4 @@+git-annex will be briefly presented at FOSDEM, on Sunday February 4th at 15:40.+[Details](http://fosdem.org/2012/schedule/event/gitannex).++Thanks to Richard Hartmann for making this presentation.
− doc/news/version_3.20120106.mdwn
@@ -1,6 +0,0 @@-git-annex 3.20120106 released with [[!toggle text="these changes"]]-[[!toggleable text="""-   * Support unescaped repository urls, like git does.-   * log: New command that displays the location log for files,-     showing each repository they were added to and removed from.-   * Fix overbroad gpg --no-tty fix from last release."""]]
+ doc/news/version_3.20120227.mdwn view
@@ -0,0 +1,43 @@+git-annex 3.20120227 released with [[!toggle text="these changes"]]+[[!toggleable text="""+   * Modifications to support ghc 7.4's handling of filenames.+     This version can only be built with ghc 7.4 or newer. See the ghc7.0+     branch for older ghcs.+   * S3: Fix irrefutable pattern failure when accessing encrypted S3+     credentials.+   * Use the haskell IfElse library.+   * Fix teardown of stale cached ssh connections.+   * Fixed to use the strict state monad, to avoid leaking all kinds of memory+     due to lazy state update thunks when adding/fixing many files.+   * Fixed some memory leaks that occurred when committing journal files.+   * Added a annex.queuesize setting, useful when adding hundreds of thousands+     of files on a system with plenty of memory.+   * whereis: Prints the urls of files that the web special remote knows about.+   * addurl --fast: Verifies that the url can be downloaded (only getting+     its head), and records the size in the key.+   * When checking that an url has a key, verify that the Content-Length,+     if available, matches the size of the key.+   * addurl: Added a --file option, which can be used to specify what+     file the url is added to. This can be used to override the default+     filename that is used when adding an url, which is based on the url.+     Or, when the file already exists, the url is recorded as another+     location of the file.+   * addurl: Normalize badly encoded urls.+   * addurl: Add --pathdepth option.+   * rekey: New plumbing level command, can be used to change the keys used+     for files en masse.+   * Store web special remote url info in a more efficient location.+     (Urls stored with this version will not be visible to older versions.)+   * Deal with NFS problem that caused a failure to remove a directory+     when removing content from the annex.+   * Make a single location log commit after a remote has received or+     dropped files. Uses a new "git-annex-shell commit" command when available.+   * To avoid commits of data to the git-annex branch after each command+     is run, set annex.alwayscommit=false. Its data will then be committed+     less frequently, when a merge or sync is done.+   * configure: Check if ssh connection caching is supported by the installed+     version of ssh and default annex.sshcaching accordingly.+   * move --from, copy --from: Now 10 times faster when scanning to find+     files in a remote on a local disk; rather than go through the location log+     to see which files are present on the remote, it simply looks at the+     disk contents directly."""]]
+ doc/scalability.mdwn view
@@ -0,0 +1,44 @@+git-annex is designed for scalability. The key points are:++* Arbitrarily large files can be managed. The only constraint+  on file size are how large a file your filesystem can hold.++  While git-annex does checksum files by default, there+  is a [[WORM_backend|backends]] available that avoids the checksumming+  overhead, so you can add new, enormous files, very fast. This also+  allows it to be used on systems with very slow disk IO.++* Memory usage should be constant. This is a "should", because there+  can sometimes be leaks (and this is one of haskell's weak spots),+  but git-annex is designed so that it does not need to hold all+  the details about your repository in memory.++  The one exception is that [[todo/git-annex_unused_eats_memory]],+  because it *does* need to hold the whole repo state in memory. But+  that is still considered a bug, and hoped to be solved one day.+  Luckily, that command is not often used.++* Many files can be managed. The limiting factor is git's own+  limitations in scaling to repositories with a lot of files, and as git+  improves this will improve. Scaling to hundreds of thousands of files+  is not a problem, scaling beyond that and git will start to get slow.++  To some degree, git-annex works around innefficiencies in git; for+  example it batches input sent to certian git commands that are slow+  when run in an emormous repository.++* It can use as much, or as little bandwidth as is available. In+  particular, any interrupted file transfer can be resumed by git-annex.++## scalability tips++* If the files are so big that checksumming becomes a bottleneck, consider+  using the [[WORM_backend|backends]]. You can always `git annex migrate`+  files to a checksumming backend later on.++* If you're adding a huge number of files at once (hundreds of thousands),+  you'll soon notice that git-annex periodically stops and say+  "Recording state in git" while it runs a `git add` command that+  becomes increasingly expensive. Consider adjusting the `annex.queuesize`+  to a higher value, at the expense of it using more memory.+
doc/summary.mdwn view
@@ -1,7 +1,7 @@ git-annex allows managing files with git, without checking the file contents into git. While that may seem paradoxical, it is useful when dealing with files larger than git can currently easily handle, whether due-to limitations in memory, checksumming time, or disk space.+to limitations in memory, time, or disk space.  Even without file content tracking, being able to manage files with git, move files around and delete files with versioned directory trees, and use
+ doc/sync/comment_1_59681be5568f568f5c54eb0445163dd2._comment view
@@ -0,0 +1,8 @@+[[!comment format=mdwn+ username="http://adamspiers.myopenid.com/"+ nickname="Adam"+ subject="very nice"+ date="2012-02-25T15:02:18Z"+ content="""+Here's a way to get from a starting point of two or more peer directory trees *not* tracked by git or git-annex, to the point where they can be synced in the manner described above: [[forum/syncing_non-git_trees_with_git-annex/]]+"""]]
+ doc/templates/bugtemplate.mdwn view
@@ -0,0 +1,12 @@+What steps will reproduce the problem?+++What is the expected output? What do you see instead?+++What version of git-annex are you using? On what operating system?+++Please provide any additional information below.++
+ doc/tips/assume-unstaged.mdwn view
@@ -0,0 +1,31 @@+[[!meta title="using assume-unstages to speed up git with large trees of annexed files"]]++Git update-index's assume-unstaged feature can be used to speed+up `git status` and stuff by not statting the whole tree looking for changed+files.++This feature works quite well with git-annex. Especially because git+annex's files are immutable, so arn't going to change out from under it,+this is a nice fit. If you have a very large tree and `git status` is+annoyingly slow, you can turn it on:++	git config core.ignoreStat true++When git mv and git rm are used, those changes *do* get noticed, even+on assume-unchanged files. When new files are added, eg by `git annex add`,+they are also noticed.++There are two gotchas. Both occur because `git add` does not stage+assume-unchanged files.++1. When an annexed file is moved to a different directory, it updates+   the symlink, and runs `git add` on it. So the file will move,+   but the changed symlink will not be noticed by git and it will commit a+   dangling symlink.+2. When using `git annex migrate`, it changes the symlink and `git adds`+   it. Again this won't be committed.++These can be worked around by running `git update-index --really-refresh`+after performing such operations. I hope that `git add` will be changed+to stage changes to assume-unchanged files, which would remove this+only complication. --[[Joey]] 
doc/tips/using_the_web_as_a_special_remote.mdwn view
@@ -8,10 +8,10 @@ Now the file is downloaded, and has been added to the annex like any other file. So it can be renamed, copied to other repositories, and so on. -Note that git-annex assumes that, if the web site does not 404, the file is-still present on the web, and this counts as one [[copy|copies]] of the-file. So it will let you remove your last copy, trusting it can be-downloaded again:+Note that git-annex assumes that, if the web site does not 404, and has the+right file size, the file is still present on the web, and this counts as+one [[copy|copies]] of the file. So it will let you remove your last copy,+trusting it can be downloaded again:  	# git annex drop example.com_video.mpeg 	drop example.com_video.mpeg (checking http://example.com/video.mpeg) ok
doc/todo/fsck_special_remotes.mdwn view
@@ -9,3 +9,5 @@  The WORM backend doesn't care about file content, so it would be nice to avoid transferring the content at all, and only send the size.++> [[done]] --[[Joey]] 
+ doc/todo/redundancy_stats_in_status.mdwn view
@@ -0,0 +1,23 @@+Currently, `git annex status` only shows the size of 1 copy of each file.+If numcopies is being used for redundancy, much more disk can actually be+in use than status shows.++One idea:++	known annex size: 2 terabytes (plus 4 terabytes of redundant copies)++But, to get that number, it would have to walk every location log, +counting how many copies currently exist of each file. That would make+status a lot slower than it is.++One option is to just put it at the end of the status:++	redundancy: 300% (4 terabytes of copies)++And ctrl-c if it's taking too long.++Hmm, fsck looks at that same info. Maybe it could cache the redundancy+level it discovers? Since fsck can be run incrementally, it would be tricky+to get an overall number. And the number would tend to be stale, but+then again it might also be nice if status shows how long ago the last fsck+was.
doc/todo/windows_support.mdwn view
@@ -1,25 +1,16 @@-short answer: no--Long answer, quoting from a mail to someone else:--Well, I can tell you that it assumes a POSIX system, both in available-utilities and system calls, So you'd need to use cygwin or something-like that. (Perhaps you already are for git, I think git also assumes a-POSIX system.) So you need a Haskell that can target that. What this-page refers to as "GHC-Cygwin":-<http://www.haskell.org/ghc/docs/6.6/html/building/platforms.html>-I don't know where to get one. Did find this:-<http://copilotco.com/mail-archives/haskell-cafe.2007/msg00824.html>+Can it be built on Windows? -(There are probably also still some places where it assumes / as a path-separator, although I fixed some. Probably almost all are fixed now.)+short answer: not yet -FWIW, git-annex works fine on OS X and other fine proprietary unixen. ;P---[[Joey]]+First, you need to get some unix utilities for windows. Git of course.+Also rsync, and a `cp` command that understands at least `cp -p`, and+`uuid`, and `xargs` and `sha1sum`. Note that some of these could be+replaced with haskell libraries to some degree. -----+There are probably still some places where it assumes / as a path+separator, although I fixed probably almost all by now. -Alternatively, windows versions of these functions could be found,+Then windows versions of these functions could be found, which are all the ones that need POSIX, I think. A fair amount of this, the stuff to do with signals and users, could be empty stubs in windows. The file manipulation, particularly symlinks, would probably be the main@@ -63,3 +54,8 @@ sigINT unionFileModes </pre>++A good starting point is+<http://hackage.haskell.org/package/unix-compat-0.3.0.1>. However, note+that its implementations of stuff like `createSymbolicLink` are stubs.+--[[Joey]] 
git-annex-shell.hs view
@@ -20,6 +20,7 @@ import qualified Command.DropKey import qualified Command.RecvKey import qualified Command.SendKey+import qualified Command.Commit  cmds_readonly :: [Command] cmds_readonly = concat@@ -32,6 +33,7 @@ cmds_notreadonly = concat 	[ Command.RecvKey.def 	, Command.DropKey.def+	, Command.Commit.def 	]  cmds :: [Command]
git-annex.cabal view
@@ -1,5 +1,5 @@ Name: git-annex-Version: 3.20120123+Version: 3.20120227 Cabal-Version: >= 1.6 License: GPL Maintainer: Joey Hess <joey@kitenet.net>@@ -7,7 +7,7 @@ Stability: Stable Copyright: 2010-2012 Joey Hess License-File: GPL-Extra-Source-Files: ./debian/NEWS ./debian/control ./debian/rules ./debian/manpages ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./Messages/JSON.hs ./NEWS ./Option.hs ./configure.hs ./Annex.hs ./Seek.hs ./Crypto.hs ./Common.hs ./README ./Command/Merge.hs ./Command/AddUrl.hs ./Command/Whereis.hs ./Command/Unlock.hs ./Command/Version.hs ./Command/FromKey.hs ./Command/PreCommit.hs ./Command/Sync.hs ./Command/Map.hs ./Command/Unused.hs ./Command/Uninit.hs ./Command/Migrate.hs ./Command/Init.hs ./Command/ConfigList.hs ./Command/Trust.hs ./Command/Lock.hs ./Command/SendKey.hs ./Command/RecvKey.hs ./Command/Reinject.hs ./Command/Add.hs ./Command/Fix.hs ./Command/Untrust.hs ./Command/Dead.hs ./Command/InitRemote.hs ./Command/Semitrust.hs ./Command/Fsck.hs ./Command/Move.hs ./Command/DropUnused.hs ./Command/Get.hs ./Command/Upgrade.hs ./Command/Describe.hs ./Command/Drop.hs ./Command/DropKey.hs ./Command/InAnnex.hs ./Command/Find.hs ./Command/Log.hs ./Command/Unannex.hs ./Command/Status.hs ./Command/Copy.hs ./Init.hs ./Types.hs ./Common/Annex.hs ./Makefile ./git-annex.cabal ./Upgrade/V1.hs ./Upgrade/V2.hs ./Upgrade/V0.hs ./CHANGELOG ./Git/LsFiles.hs ./Git/Version.hs ./Git/UnionMerge.hs ./Git/Url.hs ./Git/HashObject.hs ./Git/Types.hs ./Git/CatFile.hs ./Git/Queue.hs ./Git/Ref.hs ./Git/Filename.hs ./Git/Branch.hs ./Git/Sha.hs ./Git/LsTree.hs ./Git/Config.hs ./Git/CheckAttr.hs ./Git/Command.hs ./Git/Construct.hs ./Git/Index.hs ./doc/upgrades.mdwn ./doc/forum/Recommended_number_of_repositories.mdwn ./doc/forum/unlock__47__lock_always_gets_me.mdwn ./doc/forum/wishlist:_define_remotes_that_must_have_all_files.mdwn ./doc/forum/Will_git_annex_work_on_a_FAT32_formatted_key__63__.mdwn ./doc/forum/wishlist:_command_options_changes/comment_2_f6a637c78c989382e3c22d41b7fb4cc2._comment ./doc/forum/wishlist:_command_options_changes/comment_3_bf1114533d2895804e531e76eb6b8095._comment ./doc/forum/wishlist:_command_options_changes/comment_1_bfba72a696789bf21b2435dea15f967a._comment ./doc/forum/pure_git-annex_only_workflow/comment_4_dc8a3f75533906ad3756fcc47f7e96bb._comment ./doc/forum/pure_git-annex_only_workflow/comment_15_cb7c856d8141b2de3cc95874753f1ee5._comment ./doc/forum/pure_git-annex_only_workflow/comment_7_33db51096f568c65b22b4be0b5538c0d._comment ./doc/forum/pure_git-annex_only_workflow/comment_9_ace319652f9c7546883b5152ddc82591._comment ./doc/forum/pure_git-annex_only_workflow/comment_12_ca8ca35d6cd4a9f94568536736c12adc._comment ./doc/forum/pure_git-annex_only_workflow/comment_10_683768c9826b0bf0f267e8734b9eb872._comment ./doc/forum/pure_git-annex_only_workflow/comment_11_6b541ed834ef45606f3b98779a25a148._comment ./doc/forum/pure_git-annex_only_workflow/comment_14_b63568b327215ef8f646a39d760fdfc0._comment ./doc/forum/pure_git-annex_only_workflow/comment_8_6e5b42fdb7801daadc0b3046cbc3d51e._comment ./doc/forum/pure_git-annex_only_workflow/comment_1_a32f7efd18d174845099a4ed59e6feae._comment ./doc/forum/pure_git-annex_only_workflow/comment_13_00c82d320c7b4bb51078beba17e14dc8._comment ./doc/forum/pure_git-annex_only_workflow/comment_3_9b7d89da52f7ebb7801f9ec8545c3aba._comment ./doc/forum/pure_git-annex_only_workflow/comment_6_3660d45c5656f68924acbd23790024ee._comment ./doc/forum/pure_git-annex_only_workflow/comment_2_66dc9b65523a9912411db03c039ba848._comment ./doc/forum/pure_git-annex_only_workflow/comment_5_afe5035a6b35ed2c7e193fb69cc182e2._comment ./doc/forum/wishlist:_git-annex_replicate/comment_3_c13f4f9c3d5884fc6255fd04feadc2b1._comment ./doc/forum/wishlist:_git-annex_replicate/comment_1_9926132ec6052760cdf28518a24e2358._comment ./doc/forum/wishlist:_git-annex_replicate/comment_2_c43932f4194aba8fb2470b18e0817599._comment ./doc/forum/wishlist:_git-annex_replicate/comment_4_63f24abf086d644dced8b01e1a9948c9._comment ./doc/forum/Podcast_syncing_use-case/comment_1_ace6f9d3a950348a3ac0ff592b62e786._comment ./doc/forum/Podcast_syncing_use-case/comment_2_930a6620b4d516e69ed952f9da5371bb._comment ./doc/forum/Behaviour_of_fsck/comment_4_e4911dc6793f98fb81151daacbe49968._comment ./doc/forum/Behaviour_of_fsck/comment_3_97848f9a3db89c0427cfb671ba13300e._comment ./doc/forum/Behaviour_of_fsck/comment_2_ead36a23c3e6efa1c41e4555f93e014e._comment ./doc/forum/Behaviour_of_fsck/comment_1_0e40f158b3f4ccdcaab1408d858b68b8._comment ./doc/forum/incompatible_versions__63__/comment_1_629f28258746d413e452cbd42a1a43f4._comment ./doc/forum/vlc_and_git-annex/comment_1_9c9ab8ce463cf74418aa2f385955f165._comment ./doc/forum/vlc_and_git-annex/comment_2_037f94c1deeac873dbdb36cd4c927e45._comment ./doc/forum/Recommended_number_of_repositories/comment_1_3ef256230756be8a9679b107cdbfd018._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/git_annex_ls___47___metadata_in_git_annex_whereis.mdwn ./doc/forum/unlock__47__lock_always_gets_me/comment_1_dee73a7ea3e1a5154601adb59782831f._comment ./doc/forum/git-subtree_support__63__.mdwn ./doc/forum/Podcast_syncing_use-case.mdwn ./doc/forum/hashing_objects_directories/comment_5_ef6cfd49d24c180c2d0a062e5bd3a0be._comment ./doc/forum/hashing_objects_directories/comment_1_c55c56076be4f54251b0b7f79f28a607._comment ./doc/forum/hashing_objects_directories/comment_2_504c96959c779176f991f4125ea22009._comment ./doc/forum/hashing_objects_directories/comment_3_9134bde0a13aac0b6a4e5ebabd7f22e8._comment ./doc/forum/hashing_objects_directories/comment_4_0de9170e429cbfea66f5afa8980d45ac._comment ./doc/forum/location_tracking_cleanup.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/confusion_with_remotes__44___map.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/A_really_stupid_question.mdwn ./doc/forum/git_tag_missing_for_3.20111011/comment_1_7a53bf273f3078ab3351369ef2b5f2a6._comment ./doc/forum/version_3_upgrade/comment_1_05fc9c9cad26c520bebb98c852c71e35._comment ./doc/forum/example_of_massively_disconnected_operation.mdwn ./doc/forum/git-annex_communication_channels/comment_2_c7aeefa6ef9a2e75d8667b479ade1b7f._comment ./doc/forum/git-annex_communication_channels/comment_5_404b723a681eb93fee015cea8024b6bc._comment ./doc/forum/git-annex_communication_channels/comment_1_198325d2e9337c90f026396de89eec0e._comment ./doc/forum/git-annex_communication_channels/comment_4_1ba6ddf54843c17c7d19a9996f2ab712._comment ./doc/forum/git-annex_communication_channels/comment_6_0d87d0e26461494b1d7f8a701a924729._comment ./doc/forum/git-annex_communication_channels/comment_3_1ff08a3e0e63fa0e560cbc9602245caa._comment ./doc/forum/git-annex_communication_channels/comment_7_2c87c7a0648fe87c2bf6b4391f1cc468._comment ./doc/forum/wishlist:_git-annex_replicate.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment ./doc/forum/performance_improvement:_git_on_ssd__44___annex_on_spindle_disk.mdwn ./doc/forum/Behaviour_of_fsck.mdwn ./doc/forum/git_annex_add_crash_and_subsequent_recovery.mdwn ./doc/forum/Handling_web_special_remote_when_content_changes__63__.mdwn ./doc/forum/working_without_git-annex_commits.mdwn ./doc/forum/migrate_existing_git_repository_to_git-annex.mdwn ./doc/forum/__34__git_annex_lock__34___very_slow_for_big_repo.mdwn ./doc/forum/wishlist:_traffic_accounting_for_git-annex.mdwn ./doc/forum/git_pull_remote_git-annex.mdwn ./doc/forum/rsync_over_ssh__63__.mdwn ./doc/forum/unannex_alternatives/comment_3_b1687fc8f9e7744327bbeb6f0635d1cd._comment ./doc/forum/unannex_alternatives/comment_1_dcd4cd41280b41512bbdffafaf307993._comment ./doc/forum/unannex_alternatives/comment_2_58a72a9fe0f58c7af0b4d7927a2dd21d._comment ./doc/forum/wishlist:_git_annex_status/comment_4_9aeeb83d202dc8fb33ff364b0705ad94._comment ./doc/forum/wishlist:_git_annex_status/comment_3_d1fd70c67243971c96d59e1ffb7ef6e7._comment ./doc/forum/wishlist:_git_annex_status/comment_2_c2b0ce025805b774dc77ce264a222824._comment ./doc/forum/wishlist:_git_annex_status/comment_1_994bfd12c5d82e08040d6116915c5090._comment ./doc/forum/git_pull_remote_git-annex/comment_7_24c45ee981b18bc78325c768242e635d._comment ./doc/forum/git_pull_remote_git-annex/comment_2_0f7f4a311b0ec1d89613e80847e69b42._comment ./doc/forum/git_pull_remote_git-annex/comment_1_9c245db3518d8b889ecdf5115ad9e053._comment ./doc/forum/git_pull_remote_git-annex/comment_5_4f2a05ef6551806dd0ec65372f183ca4._comment ./doc/forum/git_pull_remote_git-annex/comment_4_646f2077edcabc000a7d9cb75a93cf55._comment ./doc/forum/git_pull_remote_git-annex/comment_8_7e76ee9b6520cbffaf484c9299a63ad3._comment ./doc/forum/git_pull_remote_git-annex/comment_3_1aa89725b5196e40a16edeeb5ccfa371._comment ./doc/forum/git_pull_remote_git-annex/comment_6_3925d1aa56bce9380f712e238d63080f._comment ./doc/forum/wishlist:_command_options_changes.mdwn ./doc/forum/location_tracking_cleanup/comment_3_c15428cec90e969284a5e690fb4b2fde._comment ./doc/forum/location_tracking_cleanup/comment_2_e7395cb6e01f42da72adf71ea3ebcde4._comment ./doc/forum/location_tracking_cleanup/comment_1_7d6319e8c94dfe998af9cfcbf170efb2._comment ./doc/forum/new_microfeatures/comment_2_41ad904c68e89c85e1fc49c9e9106969._comment ./doc/forum/new_microfeatures/comment_1_058bd517c6fffaf3446b1f5d5be63623._comment ./doc/forum/new_microfeatures/comment_3_a1a9347b5bc517f2a89a8b292c3f8517._comment ./doc/forum/new_microfeatures/comment_7_94045b9078b1fff877933b012d1b49e2._comment ./doc/forum/new_microfeatures/comment_5_3c627d275586ff499d928a8f8136babf._comment ./doc/forum/new_microfeatures/comment_4_5a6786dc52382fff5cc42fdb05770196._comment ./doc/forum/new_microfeatures/comment_6_31ea08c008500560c0b96c6601bc6362._comment ./doc/forum/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM.mdwn ./doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn ./doc/forum/OSX__39__s_default_sshd_behaviour_has_limited_paths_set.mdwn ./doc/forum/Auto_archiving.mdwn ./doc/forum/bainstorming:_git_annex_push___38___pull.mdwn ./doc/forum/sparse_git_checkouts_with_annex.mdwn ./doc/forum/Need_new_build_instructions_for_Debian_stable.mdwn ./doc/forum/batch_check_on_remote_when_using_copy.mdwn ./doc/forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs.mdwn ./doc/forum/wishlist:alias_system.mdwn ./doc/forum/incompatible_versions__63__.mdwn ./doc/forum/Preserving_file_access_rights_in_directory_tree_below_objects__47__.mdwn ./doc/forum/A_really_stupid_question/comment_1_40e02556de0b00b94f245a0196b5a89f._comment ./doc/forum/wishlist:_git_backend_for_git-annex.mdwn ./doc/forum/vlc_and_git-annex.mdwn ./doc/forum/seems_to_build_fine_on_haskell_platform_2011.mdwn ./doc/forum/rsync_over_ssh__63__/comment_1_ee21f32e90303e20339e0a568321bbbe._comment ./doc/forum/rsync_over_ssh__63__/comment_2_aa690da6ecfb2b30fc5080ad76dc77b1._comment ./doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn ./doc/forum/OSX__39__s_haskell-platform_statically_links_things.mdwn ./doc/forum/using_git_annex_to_merge_and_synchronize_2_directories___40__like_unison__41__.mdwn ./doc/forum/git_tag_missing_for_3.20111011.mdwn ./doc/forum/unannex_alternatives.mdwn ./doc/forum/can_git-annex_replace_ddm__63__.mdwn ./doc/forum/sparse_git_checkouts_with_annex/comment_2_e357db3ccc4079f07a291843975535eb._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_1_c7dc199c5740a0e7ba606dfb5e3e579a._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_3_fcfafca994194d57dccf5319c7c9e646._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_4_04dc14880f31eee2b6d767d4d4258c5a._comment ./doc/forum/syncing_non-git_trees_with_git-annex.mdwn ./doc/forum/git-annex_communication_channels.mdwn ./doc/forum/can_git-annex_replace_ddm__63__/comment_3_4c69097fe2ee81359655e59a03a9bb8d._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_2_008554306dd082d7f543baf283510e92._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_1_aa05008dfe800474ff76678a400099e1._comment ./doc/forum/version_3_upgrade.mdwn ./doc/forum/relying_on_git_for_numcopies/comment_3_43d8e1513eb9947f8a503f094c03f307._comment ./doc/forum/relying_on_git_for_numcopies/comment_2_be6acbc26008a9cb54e7b8f498f2c2a2._comment ./doc/forum/relying_on_git_for_numcopies/comment_1_8ad3cccd7f66f6423341d71241ba89fc._comment ./doc/forum/confusion_with_remotes__44___map/comment_1_a38ded23b7f288292a843abcb1a56f38._comment ./doc/forum/confusion_with_remotes__44___map/comment_2_cd1c98b1276444e859a22c3dbd6f2a79._comment ./doc/forum/confusion_with_remotes__44___map/comment_6_496b0d9b86869bbac3a1356d53a3dda4._comment ./doc/forum/confusion_with_remotes__44___map/comment_5_27801584325d259fa490f67273f2ff71._comment ./doc/forum/confusion_with_remotes__44___map/comment_4_3b89b6d1518267fcbc050c9de038b9ca._comment ./doc/forum/confusion_with_remotes__44___map/comment_3_18531754089c991b6caefc57a5c17fe9._comment ./doc/forum/confusion_with_remotes__44___map/comment_7_9a456f61f956a3d5e81e723d5a90794c._comment ./doc/forum/--print0_option_as_in___34__find__34__.mdwn ./doc/forum/relying_on_git_for_numcopies.mdwn ./doc/forum/wishlist:_do_round_robin_downloading_of_data.mdwn ./doc/forum/pure_git-annex_only_workflow.mdwn ./doc/forum/wishlist:_git_annex_status.mdwn ./doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn ./doc/forum/Problems_with_large_numbers_of_files.mdwn ./doc/forum/Can_I_store_normal_files_in_the_git-annex_git_repository__63__.mdwn ./doc/forum/Is_an_automagic_upgrade_of_the_object_directory_safe__63__.mdwn ./doc/forum/wishlist:_push_to_cia.vc_from_the_website__39__s_repo__44___not_your_personal_one.mdwn ./doc/forum/new_microfeatures.mdwn ./doc/forum/git-subtree_support__63__/comment_2_73d2a015b1ac79ec99e071a8b1e29034._comment ./doc/forum/git-subtree_support__63__/comment_1_4f333cb71ed1ff259bbfd86704806aa6._comment ./doc/forum/git-subtree_support__63__/comment_4_75b0e072e668aa46ff0a8d62a6620306._comment ./doc/forum/git-subtree_support__63__/comment_3_c533400e22c306c033fcd56e64761b0b._comment ./doc/forum/git-subtree_support__63__/comment_6_85df530f7b6d76b74ac8017c6034f95e._comment ./doc/forum/git-subtree_support__63__/comment_5_f5ec9649d9f1dc122e715de5533bc674._comment ./doc/backends.mdwn ./doc/bugs/__39__annex_add__39___fails_to___39__git_add__39___for_parent_relative_path.mdwn ./doc/bugs/uuid.log_trust.log_and_remote.log_merge_wackiness.mdwn ./doc/bugs/Remote_repo_and_set_operation_with_find.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/uninit_should_not_run_when_branch_git-annex_is_checked_out.mdwn ./doc/bugs/ordering.mdwn ./doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn ./doc/bugs/fsck__47__fix_should_check__47__fix_the_permissions_of_.git__47__annex.mdwn ./doc/bugs/dropunused_doesn__39__t_handle_double_spaces_in_filename.mdwn ./doc/bugs/add_script-friendly_output_options.mdwn ./doc/bugs/uninit_does_not_work_in_old_repos.mdwn ./doc/bugs/conq:_invalid_command_syntax.mdwn ./doc/bugs/git_annex_get_choke_when_remote_is_an_ssh_url_with_a_port.mdwn ./doc/bugs/unannex_and_uninit_do_not_work_when_git_index_is_broken.mdwn ./doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn ./doc/bugs/git_annex_unused_failes_on_empty_repository.mdwn ./doc/bugs/git_annex_add_eats_files_when_filename_is_too_long.mdwn ./doc/bugs/S3_bucket_uses_the_same_key_for_encryption_and_hashing.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3.mdwn ./doc/bugs/git_annex_copy_--fast_does_not_copy_files.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing.mdwn ./doc/bugs/dotdot_problem.mdwn ./doc/bugs/Problems_running_make_on_osx.mdwn ./doc/bugs/free_space_checking/comment_2_8a65f6d3dcf5baa3f7f2dbe1346e2615._comment ./doc/bugs/free_space_checking/comment_3_0fc6ff79a357b1619d13018ccacc7c10._comment ./doc/bugs/free_space_checking/comment_1_a868e805be43c5a7c19c41f1af8e41e6._comment ./doc/bugs/Error_when_moving_annexed_file_to_a_.gitignored_location.mdwn ./doc/bugs/done.mdwn ./doc/bugs/git_annex_copy_-f_REMOTE_._doesn__39__t_work_as_expected.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong/comment_2_8b240de1d5ae9229fa2d77d1cc15a552._comment ./doc/bugs/Displayed_copy_speed_is_wrong/comment_1_74de3091e8bfd7acd6795e61f39f07c6._comment ./doc/bugs/Build_error_on_Mac_OSX_10.6.mdwn ./doc/bugs/bare_git_repos.mdwn ./doc/bugs/fat_support.mdwn ./doc/bugs/problems_with_utf8_names.mdwn ./doc/bugs/error_with_file_names_starting_with_dash.mdwn ./doc/bugs/not_possible_to_have_annex_on_a_separate_filesystem.mdwn ./doc/bugs/Lost_S3_Remote.mdwn ./doc/bugs/unhappy_without_UTF8_locale.mdwn ./doc/bugs/S3_memory_leaks.mdwn ./doc/bugs/git_annex_map_has_problems_with_urls_containing___126__.mdwn ./doc/bugs/interrupting_migration_causes_problems.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/cyclic_drop.mdwn ./doc/bugs/Problems_running_make_on_osx/comment_9_cc283b485b3c95ba7eebc8f0c96969b3._comment ./doc/bugs/Problems_running_make_on_osx/comment_4_c52be386f79f14c8570a8f1397c68581._comment ./doc/bugs/Problems_running_make_on_osx/comment_18_64fab50d95de619eb2e8f08f90237de1._comment ./doc/bugs/Problems_running_make_on_osx/comment_16_5c2dd6002aadaab30841b77a5f5aed34._comment ./doc/bugs/Problems_running_make_on_osx/comment_10_94e4ac430140042a2d0fb5a16d86b4e5._comment ./doc/bugs/Problems_running_make_on_osx/comment_15_6b8867b8e48bf807c955779c9f8f0909._comment ./doc/bugs/Problems_running_make_on_osx/comment_11_56f1143fa191361d63b441741699e17f._comment ./doc/bugs/Problems_running_make_on_osx/comment_20_7db27d1a22666c831848bc6c06d66a84._comment ./doc/bugs/Problems_running_make_on_osx/comment_12_ec5131624d0d2285d3b6880e47033f97._comment ./doc/bugs/Problems_running_make_on_osx/comment_3_68f0f8ae953589ae26d57310b40c878d._comment ./doc/bugs/Problems_running_make_on_osx/comment_14_89a960b6706ed703b390a81a8bc4e311._comment ./doc/bugs/Problems_running_make_on_osx/comment_17_62fccb04b0e4b695312f7a3f32fb96ee._comment ./doc/bugs/Problems_running_make_on_osx/comment_19_4253988ed178054c8b6400beeed68a29._comment ./doc/bugs/Problems_running_make_on_osx/comment_6_0c46f5165ceb5a7b9ea9689c33b3a4f8._comment ./doc/bugs/Problems_running_make_on_osx/comment_1_34120e82331ace01a6a4960862d38f2d._comment ./doc/bugs/Problems_running_make_on_osx/comment_7_237a137cce58a28abcc736cbf2c420b0._comment ./doc/bugs/Problems_running_make_on_osx/comment_5_7f1330a1e541b0f3e2192e596d7f7bee._comment ./doc/bugs/Problems_running_make_on_osx/comment_2_cc53d1681d576186dbc868dd9801d551._comment ./doc/bugs/Problems_running_make_on_osx/comment_13_88ed095a448096bf8a69015a04e64df1._comment ./doc/bugs/Problems_running_make_on_osx/comment_8_efafa203addf8fa79e33e21a87fb5a2b._comment ./doc/bugs/git-annex_directory_hashing_problems_on_osx.mdwn ./doc/bugs/No_easy_way_to_re-inject_a_file_into_an_annex.mdwn ./doc/bugs/git_annex_unlock_is_not_atomic.mdwn ./doc/bugs/free_space_checking.mdwn ./doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong.mdwn ./doc/bugs/git_annex_should_use___39__git_add_-f__39___internally.mdwn ./doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn ./doc/bugs/__34__make_test__34___fails_silently.mdwn ./doc/bugs/No_version_information_from_cli.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/bugs/conq:_invalid_command_syntax/comment_1_f33b83025ce974e496f83f248275a66a._comment ./doc/bugs/conq:_invalid_command_syntax/comment_2_195106ca8dedad5f4d755f625e38e8af._comment ./doc/bugs/conq:_invalid_command_syntax/comment_3_55af43e2f43a4c373f7a0a33678d0b1c._comment ./doc/bugs/old_data_isn__39__t_unused_after_migration.mdwn ./doc/bugs/fails_to_handle_lot_of_files.mdwn ./doc/bugs/tests_fail_when_there_is_no_global_.gitconfig_for_the_user.mdwn ./doc/bugs/git_annex_initremote_walks_.git-annex.mdwn ./doc/bugs/signal_weirdness.mdwn ./doc/bugs/git-annex_incorrectly_parses_bare_IPv6_addresses.mdwn ./doc/bugs/git_annex_version_should_without_being_in_a_repo_.mdwn ./doc/bugs/Can__39__t___34__git-annex_get__34___with_3.20111203.mdwn ./doc/bugs/Prevent_accidental_merges/comment_1_4c46a193915eab8f308a04175cb2e40a._comment ./doc/bugs/conflicting_haskell_packages/comment_1_e552a6cc6d7d1882e14130edfc2d6b3b._comment ./doc/bugs/tmp_file_handling.mdwn ./doc/bugs/annex_add_in_annex.mdwn ./doc/bugs/making_annex-merge_try_a_fast-forward.mdwn ./doc/bugs/bad_behaviour_with_file_names_with_newline_in_them.mdwn ./doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment ./doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment ./doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment ./doc/bugs/on--git-dir_and_--work-tree_options.mdwn ./doc/bugs/Makefile_is_missing_dependancies.mdwn ./doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn ./doc/bugs/git_annex_upgrade_output_is_inconsistent_and_spammy.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_4_91439d4dbbf1461e281b276eb0003691._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_6_f360f0006bc9115bc5a3e2eb9fe58abd._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_3_86d9e7244ae492bcbe62720b8c4fc4a9._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_2_cd0123392b16d89db41b45464165c247._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_1_5f60006c9bb095167d817f234a14d20b._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_5_ca33a9ca0df33f7c1b58353d7ffb943d._comment ./doc/bugs/wishlist:_query_things_like_description__44___trust_level.mdwn ./doc/bugs/git_rename_detection_on_file_move/comment_9_75e0973f6d573df615e01005ebcea87d._comment ./doc/bugs/git_rename_detection_on_file_move/comment_7_7f20d0b2f6ed1c34021a135438037306._comment ./doc/bugs/git_rename_detection_on_file_move/comment_3_57010bcaca42089b451ad8659a1e018e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_4_79d96599f757757f34d7b784e6c0e81c._comment ./doc/bugs/git_rename_detection_on_file_move/comment_5_d61f5693d947b9736b29fca1dbc7ad76._comment ./doc/bugs/git_rename_detection_on_file_move/comment_8_6a00500b24ba53248c78e1ffc8d1a591._comment ./doc/bugs/git_rename_detection_on_file_move/comment_2_7101d07400ad5935f880dc00d89bf90e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_6_f63de6fe2f7189c8c2908cc41c4bc963._comment ./doc/bugs/git_rename_detection_on_file_move/comment_1_0531dcfa833b0321a7009526efe3df33._comment ./doc/bugs/fsck_claims_failed_checksum_when_less_copies_than_required_are_found.mdwn ./doc/bugs/add_range_argument_to___34__git_annex_dropunused__34___.mdwn ./doc/bugs/git_rename_detection_on_file_move.mdwn ./doc/bugs/problem_commit_normal_links.mdwn ./doc/bugs/extraneous_shell_escaping_for_rsync_remotes.mdwn ./doc/bugs/problems_with_utf8_names/comment_1_3c7e3f021c2c94277eecf9c8af6cec5f._comment ./doc/bugs/problems_with_utf8_names/comment_4_93bee35f5fa7744834994bc7a253a6f9._comment ./doc/bugs/problems_with_utf8_names/comment_3_4f936a5d3f9c7df64c8a87e62b7fbfdc._comment ./doc/bugs/problems_with_utf8_names/comment_2_bad4c4c5f54358d1bc0ab2adc713782a._comment ./doc/bugs/git_annex_unused_seems_to_check_for_current_path.mdwn ./doc/bugs/Makefile_is_missing_dependancies/comment_6_24119fc5d5963ce9dd669f7dcf006859._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_3_c38b6f4abc9b9ad413c3b83ca04386c3._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_8_a3555e3286cdc2bfeb9cde0ff727ba74._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_2_416f12dbd0c2b841fac8164645b81df5._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_5_0a1c52e2c96d19b9c3eb7e99b8c2434f._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_7_96fd4725df4b54e670077a18d3ac4943._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_1_5a3da5f79c8563c7a450aa29728abe7c._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_4_cc13873175edf191047282700315beee._comment ./doc/bugs/Trouble_initializing_git_annex_on_NFS.mdwn ./doc/bugs/touch.hsc_has_problems_on_non-linux_based_systems.mdwn ./doc/bugs/build_issue_with_latest_release_0.20110522-1-gde817ba.mdwn ./doc/bugs/weird_local_clone_confuses.mdwn ./doc/bugs/fsck_output.mdwn ./doc/bugs/git-annex_branch_push_race.mdwn ./doc/bugs/wishlist:_allow_users_to_provide_UUID_when_running___96__git_annex_init__96__.mdwn ./doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn ./doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn ./doc/bugs/unannex_vs_unlock_hook_confusion.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing/comment_2_4f4d8e1e00a2a4f7e8a8ab082e16adac._comment ./doc/bugs/Cabal_dependency_monadIO_missing/comment_1_14be660aa57fadec0d81b32a8b52c66f._comment ./doc/bugs/conflicting_haskell_packages.mdwn ./doc/bugs/git_command_line_constructed_by_unannex_command_has_tons_of_redundant_-a_paramters.mdwn ./doc/bugs/backend_version_upgrade_leaves_repo_unusable.mdwn ./doc/bugs/fat_support/comment_3_df3b943bc1081a8f3f7434ae0c8e061e._comment ./doc/bugs/fat_support/comment_4_90a8a15bedd94480945a374f9d706b86._comment ./doc/bugs/fat_support/comment_5_64bbf89de0836673224b83fdefa0407b._comment ./doc/bugs/fat_support/comment_1_04bcc4795d431e8cb32293aab29bbfe2._comment ./doc/bugs/fat_support/comment_2_bb4a97ebadb5c53809fc78431eabd7c8._comment ./doc/bugs/unannex_command_doesn__39__t_all_files.mdwn ./doc/bugs/annex_unannex__47__uninit_should_handle_copies.mdwn ./doc/bugs/git-annex_branch_corruption.mdwn ./doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn ./doc/bugs/Unfortunate_interaction_with_Calibre.mdwn ./doc/bugs/error_propigation.mdwn ./doc/bugs/wishlist:_support_drop__44___find_on_special_remotes.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/uninit_does_not_work_in_old_repos/comment_1_bc0619c6e17139df74639448aa6a0f72._comment ./doc/bugs/git-annex_losing_rsync_remotes_with_encryption_enabled.mdwn ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/fails_to_handle_lot_of_files/comment_1_09d8e4e66d8273fab611bd29e82dc7fc._comment ./doc/bugs/fails_to_handle_lot_of_files/comment_2_fd2ec05f4b5a7a6ae6bd9f5dbc3156de._comment ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/concurrent_git-annex_processes_can_lead_to_locking_issues.mdwn ./doc/bugs/Prevent_accidental_merges.mdwn ./doc/bugs/building_on_lenny.mdwn ./doc/bugs/case_sensitivity_on_FAT.mdwn ./doc/bugs/encrypted_S3_stalls.mdwn ./doc/bugs/support_bare_git_repo__44___with_the_annex_directory_exposed_to_http.mdwn ./doc/bugs/WORM:_Handle_long_filenames_correctly.mdwn ./doc/logo_small.png ./doc/tips.mdwn ./doc/use_case/Bob.mdwn ./doc/use_case/Alice.mdwn ./doc/upgrades/SHA_size.mdwn ./doc/news/sharebox_a_FUSE_filesystem_for_git-annex.mdwn ./doc/news/version_3.20120113.mdwn ./doc/news/version_3.20120115.mdwn ./doc/news/version_3.20120116.mdwn ./doc/news/version_3.20120123.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20120106.mdwn ./doc/news/version_3.20120106/comment_1_fb1a3135e2d9f39f2c372ccc2c50c85a._comment ./doc/news/version_3.20120106/comment_2_ae292ca7294b6790233e545086c3ac2f._comment ./doc/news/version_3.20120106/comment_3_29ccda9ac458fd5cc9ec5508c62df6ea._comment ./doc/git-union-merge.mdwn ./doc/special_remotes/S3.mdwn ./doc/special_remotes/web.mdwn ./doc/special_remotes/bup.mdwn ./doc/special_remotes/directory.mdwn ./doc/special_remotes/hook.mdwn ./doc/special_remotes/rsync.mdwn ./doc/how_it_works.mdwn ./doc/sitemap.mdwn ./doc/users/joey.mdwn ./doc/users/fmarier.mdwn ./doc/users/chrysn.mdwn ./doc/users/gebi.mdwn ./doc/templates/walkthrough.tmpl ./doc/templates/bare.tmpl ./doc/index.mdwn ./doc/summary.mdwn ./doc/forum.mdwn ./doc/encryption/comment_1_1afca8d7182075d46db41f6ad3dd5911._comment ./doc/transferring_data.mdwn ./doc/download.mdwn ./doc/future_proofing.mdwn ./doc/todo/auto_remotes/discussion.mdwn ./doc/todo/link_file_to_remote_repo_feature.mdwn ./doc/todo/file_copy_progress_bar.mdwn ./doc/todo/use_cp_reflink.mdwn ./doc/todo/gitrm.mdwn ./doc/todo/exclude_files_on_a_given_remote.mdwn ./doc/todo/git-annex_unused_eats_memory.mdwn ./doc/todo/Please_abort_build_if___34__make_test__34___fails.mdwn ./doc/todo/fsck_special_remotes.mdwn ./doc/todo/add_--exclude_option_to_git_annex_find.mdwn ./doc/todo/tahoe_lfs_for_reals/comment_2_80b9e848edfdc7be21baab7d0cef0e3a._comment ./doc/todo/tahoe_lfs_for_reals/comment_1_0a4793ce6a867638f6e510e71dd4bb44._comment ./doc/todo/object_dir_reorg_v2.mdwn ./doc/todo/S3.mdwn ./doc/todo/auto_remotes.mdwn ./doc/todo/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/todo/support-non-utf8-locales.mdwn ./doc/todo/done.mdwn ./doc/todo/fsck.mdwn ./doc/todo/support_S3_multipart_uploads.mdwn ./doc/todo/wishlist:_Provide_a___34__git_annex__34___command_that_will_skip_duplicates.mdwn ./doc/todo/pushpull.mdwn ./doc/todo/smudge.mdwn ./doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment ./doc/todo/windows_support.mdwn ./doc/todo/optimise_git-annex_merge.mdwn ./doc/todo/object_dir_reorg_v2/comment_3_79bdf9c51dec9f52372ce95b53233bb2._comment ./doc/todo/object_dir_reorg_v2/comment_7_42501404c82ca07147e2cce0cff59474._comment ./doc/todo/object_dir_reorg_v2/comment_5_821c382987f105da72a50e0a5ce61fdc._comment ./doc/todo/object_dir_reorg_v2/comment_1_ba03333dc76ff49eccaba375e68cb525._comment ./doc/todo/object_dir_reorg_v2/comment_2_81276ac309959dc741bc90101c213ab7._comment ./doc/todo/object_dir_reorg_v2/comment_6_8834c3a3f1258c4349d23aff8549bf35._comment ./doc/todo/object_dir_reorg_v2/comment_4_93aada9b1680fed56cc6f0f7c3aca5e5._comment ./doc/todo/union_mounting.mdwn ./doc/todo/wishlist:_swift_backend.mdwn ./doc/todo/Please_add_support_for_monad-control_0.3.x.mdwn ./doc/todo/wishlist:_swift_backend/comment_1_e6efbb35f61ee521b473a92674036788._comment ./doc/todo/wishlist:_swift_backend/comment_2_5d8c83b0485112e98367b7abaab3f4e3._comment ./doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn ./doc/todo/gitolite_and_gitosis_support.mdwn ./doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn ./doc/todo/hidden_files.mdwn ./doc/todo/smudge/comment_1_4ea616bcdbc9e9a6fae9f2e2795c31c9._comment ./doc/todo/smudge/comment_2_e04b32caa0d2b4c577cdaf382a3ff7f6._comment ./doc/todo/network_remotes.mdwn ./doc/todo/add_a_git_backend.mdwn ./doc/todo/parallel_possibilities/comment_1_d8e34fc2bc4e5cf761574608f970d496._comment ./doc/todo/parallel_possibilities/comment_2_adb76f06a7997abe4559d3169a3181c3._comment ./doc/todo/checkout.mdwn ./doc/todo/immutable_annexed_files.mdwn ./doc/todo/wishlist:_Prevent_repeated_password_prompts_for_one_command.mdwn ./doc/todo/git-annex-shell.mdwn ./doc/todo/speed_up_fsck.mdwn ./doc/todo/tahoe_lfs_for_reals.mdwn ./doc/todo/avoid_unnecessary_union_merges.mdwn ./doc/todo/support_fsck_in_bare_repos.mdwn ./doc/todo/add_-all_option.mdwn ./doc/todo/using_url_backend.mdwn ./doc/todo/branching.mdwn ./doc/todo/rsync.mdwn ./doc/todo/backendSHA1.mdwn ./doc/todo/cache_key_info.mdwn ./doc/todo/parallel_possibilities.mdwn ./doc/todo/symlink_farming_commit_hook.mdwn ./doc/feeds.mdwn ./doc/sync.mdwn ./doc/copies.mdwn ./doc/repomap.png ./doc/special_remotes.mdwn ./doc/todo.mdwn ./doc/walkthrough/using_ssh_remotes.mdwn ./doc/walkthrough/automatically_managing_content.mdwn ./doc/walkthrough/removing_files/comment_1_cb65e7c510b75be1c51f655b058667c6._comment ./doc/walkthrough/removing_files/comment_2_64709ea4558915edd5c8ca4486965b07._comment ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/walkthrough/creating_a_repository.mdwn ./doc/walkthrough/adding_a_remote.mdwn ./doc/walkthrough/adding_a_remote/comment_1_0a59355bd33a796aec97173607e6adc9._comment ./doc/walkthrough/adding_a_remote/comment_2_f8cd79ef1593a8181a7f1086a87713e8._comment ./doc/walkthrough/adding_a_remote/comment_3_60691af4400521b5a8c8d75efe3b44cb._comment ./doc/walkthrough/adding_a_remote/comment_4_6f7cf5c330272c96b3abeb6612075c9d._comment ./doc/walkthrough/removing_files:_When_things_go_wrong.mdwn ./doc/walkthrough/renaming_files.mdwn ./doc/walkthrough/using_bup.mdwn ./doc/walkthrough/fsck:_when_things_go_wrong.mdwn ./doc/walkthrough/syncing.mdwn ./doc/walkthrough/adding_files.mdwn ./doc/walkthrough/unused_data.mdwn ./doc/walkthrough/fsck:_verifying_your_data.mdwn ./doc/walkthrough/getting_file_content.mdwn ./doc/walkthrough/backups.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/GPL ./doc/logo.png ./doc/location_tracking.mdwn ./doc/design.mdwn ./doc/news.mdwn ./doc/tips/visualizing_repositories_with_gource/screenshot.jpg ./doc/tips/untrusted_repositories.mdwn ./doc/tips/automatically_getting_files_on_checkout.mdwn ./doc/tips/what_to_do_when_a_repository_is_corrupted.mdwn ./doc/tips/Internet_Archive_via_S3.mdwn ./doc/tips/what_to_do_when_you_lose_a_repository.mdwn ./doc/tips/using_Amazon_S3.mdwn ./doc/tips/using_the_web_as_a_special_remote.mdwn ./doc/tips/powerful_file_matching.mdwn ./doc/tips/using_gitolite_with_git-annex.mdwn ./doc/tips/recover_data_from_lost+found.mdwn ./doc/tips/visualizing_repositories_with_gource.mdwn ./doc/tips/centralized_git_repository_tutorial.mdwn ./doc/tips/finding_duplicate_files/comment_1_ddb477ca242ffeb21e0df394d8fdf5d2._comment ./doc/tips/using_git_annex_with_no_fixed_hostname_and_optimising_ssh.mdwn ./doc/tips/using_gitolite_with_git-annex/comment_4_eb81f824aadc97f098379c5f7e4fba4c._comment ./doc/tips/using_gitolite_with_git-annex/comment_6_3e203e010a4df5bf03899f867718adc5._comment ./doc/tips/using_gitolite_with_git-annex/comment_1_9a2a2a8eac9af97e0c984ad105763a73._comment ./doc/tips/using_gitolite_with_git-annex/comment_8_8249772c142117f88e37975d058aa936._comment ./doc/tips/using_gitolite_with_git-annex/comment_5_f688309532d2993630e9e72e87fb9c46._comment ./doc/tips/using_gitolite_with_git-annex/comment_7_f8fd08b6ab47378ad88c87348057220d._comment ./doc/tips/using_gitolite_with_git-annex/comment_3_807035f38509ccb9f93f1929ecd37417._comment ./doc/tips/using_gitolite_with_git-annex/comment_9_28418635a6ed7231b89e02211cd3c236._comment ./doc/tips/using_gitolite_with_git-annex/comment_2_d8efea4ab9576555fadbb47666ecefa9._comment ./doc/tips/finding_duplicate_files.mdwn ./doc/tips/centralised_repository:_starting_from_nothing.mdwn ./doc/tips/using_the_SHA1_backend.mdwn ./doc/tips/migrating_data_to_a_new_backend.mdwn ./doc/encryption.mdwn ./doc/not.mdwn ./doc/download/comment_7_a5eebd214b135f34b18274a682211943._comment ./doc/download/comment_3_cf6044ebe99f71158034e21197228abd._comment ./doc/download/comment_5_c6b1bc40226fc2c8ba3e558150856992._comment ./doc/download/comment_6_3a52993d3553deb9a413debec9a5f92d._comment ./doc/download/comment_2_f85f72b33aedc3425f0c0c47867d02f3._comment ./doc/download/comment_8_59a976de6c7d333709b92f7cd5830850._comment ./doc/download/comment_4_10fc013865c7542c2ed9d6c0963bb391._comment ./doc/download/comment_1_fbd8b6d39e9d3c71791551358c863966._comment ./doc/contact.mdwn ./doc/trust.mdwn ./doc/bare_repositories.mdwn ./doc/NixOS.mdwn ./doc/distributed_version_control.mdwn ./doc/install.mdwn ./doc/git-annex-shell.mdwn ./doc/bugs.mdwn ./doc/meta.mdwn ./doc/design/encryption/comment_2_a610b3d056a059899178859a3a821ea5._comment ./doc/design/encryption/comment_3_cca186a9536cd3f6e86994631b14231c._comment ./doc/design/encryption/comment_1_4715ffafb3c4a9915bc33f2b26aaa9c1._comment ./doc/design/encryption/comment_4_8f3ba3e504b058791fc6e6f9c38154cf._comment ./doc/design/encryption.mdwn ./doc/comments.mdwn ./doc/git-annex.mdwn ./doc/internals.mdwn ./doc/users.mdwn ./doc/install/Ubuntu.mdwn ./doc/install/Debian.mdwn ./doc/install/openSUSE.mdwn ./doc/install/OSX.mdwn ./doc/install/FreeBSD.mdwn ./doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment ./doc/install/ArchLinux.mdwn ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/Fedora.mdwn ./doc/walkthrough.mdwn ./CmdLine.hs ./Messages.hs ./Setup.hs ./GPL ./mdwn2man ./Remote.hs ./git-annex.hs ./Locations.hs ./Usage.hs ./Backend/WORM.hs ./Backend/URL.hs ./Backend/SHA.hs ./git-annex-shell.hs ./test.hs ./INSTALL ./Remote/Web.hs ./Remote/Helper/Encryptable.hs ./Remote/Helper/Ssh.hs ./Remote/Helper/Special.hs ./Remote/Directory.hs ./Remote/List.hs ./Remote/Bup.hs ./Remote/Hook.hs ./Remote/S3.hs ./Remote/Rsync.hs ./Remote/Git.hs ./Build/TestConfig.hs ./Upgrade.hs ./Annex/Version.hs ./Annex/Exception.hs ./Annex/Journal.hs ./Annex/CatFile.hs ./Annex/Ssh.hs ./Annex/Queue.hs ./Annex/Branch.hs ./Annex/UUID.hs ./Annex/LockPool.hs ./Annex/BranchState.hs ./Annex/Content.hs ./git-union-merge.hs ./Utility/Path.hs ./Utility/Matcher.hs ./Utility/TempFile.hs ./Utility/Url.hs ./Utility/DataUnits.hs ./Utility/StatFS.hsc ./Utility/Conditional.hs ./Utility/Monad.hs ./Utility/RsyncFile.hs ./Utility/CopyFile.hs ./Utility/Misc.hs ./Utility/SafeCommand.hs ./Utility/Gpg.hs ./Utility/Directory.hs ./Utility/FileMode.hs ./Utility/PartialPrelude.hs ./Utility/JSONStream.hs ./Utility/Dot.hs ./Utility/Touch.hsc ./Utility/Base64.hs ./Utility/Format.hs ./Types/Option.hs ./Types/Crypto.hs ./Types/UUID.hs ./Types/TrustLevel.hs ./Types/Remote.hs ./Types/BranchState.hs ./Types/Command.hs ./Types/Backend.hs ./Types/Key.hs ./Config.hs ./Checks.hs ./Command.hs ./.gitignore ./Backend.hs ./Logs/Web.hs ./Logs/UUIDBased.hs ./Logs/Trust.hs ./Logs/Location.hs ./Logs/UUID.hs ./Logs/Remote.hs ./Logs/Presence.hs ./.gitattributes ./Git.hs ./Limit.hs ./GitAnnex.hs+Extra-Source-Files: ./debian/NEWS ./debian/control ./debian/rules ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./Messages/JSON.hs ./NEWS ./Option.hs ./configure.hs ./Annex.hs ./Seek.hs ./Crypto.hs ./Common.hs ./README ./Command/Merge.hs ./Command/AddUrl.hs ./Command/Whereis.hs ./Command/Unlock.hs ./Command/Commit.hs ./Command/Version.hs ./Command/FromKey.hs ./Command/PreCommit.hs ./Command/Sync.hs ./Command/Map.hs ./Command/Unused.hs ./Command/Uninit.hs ./Command/ReKey.hs ./Command/Migrate.hs ./Command/Init.hs ./Command/ConfigList.hs ./Command/Trust.hs ./Command/Lock.hs ./Command/SendKey.hs ./Command/RecvKey.hs ./Command/Reinject.hs ./Command/Add.hs ./Command/Fix.hs ./Command/Untrust.hs ./Command/Dead.hs ./Command/InitRemote.hs ./Command/Semitrust.hs ./Command/Fsck.hs ./Command/Move.hs ./Command/DropUnused.hs ./Command/Get.hs ./Command/Upgrade.hs ./Command/Describe.hs ./Command/Drop.hs ./Command/DropKey.hs ./Command/InAnnex.hs ./Command/Find.hs ./Command/Log.hs ./Command/Unannex.hs ./Command/Status.hs ./Command/Copy.hs ./Init.hs ./Types.hs ./Common/Annex.hs ./Makefile ./git-annex.cabal ./Upgrade/V1.hs ./Upgrade/V2.hs ./Upgrade/V0.hs ./CHANGELOG ./Git/LsFiles.hs ./Git/Version.hs ./Git/UnionMerge.hs ./Git/Url.hs ./Git/HashObject.hs ./Git/Types.hs ./Git/CatFile.hs ./Git/Queue.hs ./Git/Ref.hs ./Git/Filename.hs ./Git/Branch.hs ./Git/Sha.hs ./Git/LsTree.hs ./Git/Config.hs ./Git/CheckAttr.hs ./Git/Command.hs ./Git/Construct.hs ./Git/Index.hs ./doc/upgrades.mdwn ./doc/forum/Recommended_number_of_repositories.mdwn ./doc/forum/unlock__47__lock_always_gets_me.mdwn ./doc/forum/wishlist:_define_remotes_that_must_have_all_files.mdwn ./doc/forum/Will_git_annex_work_on_a_FAT32_formatted_key__63__.mdwn ./doc/forum/wishlist:_command_options_changes/comment_2_f6a637c78c989382e3c22d41b7fb4cc2._comment ./doc/forum/wishlist:_command_options_changes/comment_3_bf1114533d2895804e531e76eb6b8095._comment ./doc/forum/wishlist:_command_options_changes/comment_1_bfba72a696789bf21b2435dea15f967a._comment ./doc/forum/pure_git-annex_only_workflow/comment_4_dc8a3f75533906ad3756fcc47f7e96bb._comment ./doc/forum/pure_git-annex_only_workflow/comment_15_cb7c856d8141b2de3cc95874753f1ee5._comment ./doc/forum/pure_git-annex_only_workflow/comment_7_33db51096f568c65b22b4be0b5538c0d._comment ./doc/forum/pure_git-annex_only_workflow/comment_9_ace319652f9c7546883b5152ddc82591._comment ./doc/forum/pure_git-annex_only_workflow/comment_12_ca8ca35d6cd4a9f94568536736c12adc._comment ./doc/forum/pure_git-annex_only_workflow/comment_10_683768c9826b0bf0f267e8734b9eb872._comment ./doc/forum/pure_git-annex_only_workflow/comment_11_6b541ed834ef45606f3b98779a25a148._comment ./doc/forum/pure_git-annex_only_workflow/comment_14_b63568b327215ef8f646a39d760fdfc0._comment ./doc/forum/pure_git-annex_only_workflow/comment_8_6e5b42fdb7801daadc0b3046cbc3d51e._comment ./doc/forum/pure_git-annex_only_workflow/comment_1_a32f7efd18d174845099a4ed59e6feae._comment ./doc/forum/pure_git-annex_only_workflow/comment_13_00c82d320c7b4bb51078beba17e14dc8._comment ./doc/forum/pure_git-annex_only_workflow/comment_3_9b7d89da52f7ebb7801f9ec8545c3aba._comment ./doc/forum/pure_git-annex_only_workflow/comment_6_3660d45c5656f68924acbd23790024ee._comment ./doc/forum/pure_git-annex_only_workflow/comment_2_66dc9b65523a9912411db03c039ba848._comment ./doc/forum/pure_git-annex_only_workflow/comment_5_afe5035a6b35ed2c7e193fb69cc182e2._comment ./doc/forum/wishlist:_git-annex_replicate/comment_3_c13f4f9c3d5884fc6255fd04feadc2b1._comment ./doc/forum/wishlist:_git-annex_replicate/comment_1_9926132ec6052760cdf28518a24e2358._comment ./doc/forum/wishlist:_git-annex_replicate/comment_2_c43932f4194aba8fb2470b18e0817599._comment ./doc/forum/wishlist:_git-annex_replicate/comment_4_63f24abf086d644dced8b01e1a9948c9._comment ./doc/forum/Podcast_syncing_use-case/comment_1_ace6f9d3a950348a3ac0ff592b62e786._comment ./doc/forum/Podcast_syncing_use-case/comment_2_930a6620b4d516e69ed952f9da5371bb._comment ./doc/forum/Behaviour_of_fsck/comment_4_e4911dc6793f98fb81151daacbe49968._comment ./doc/forum/Behaviour_of_fsck/comment_3_97848f9a3db89c0427cfb671ba13300e._comment ./doc/forum/Behaviour_of_fsck/comment_2_ead36a23c3e6efa1c41e4555f93e014e._comment ./doc/forum/Behaviour_of_fsck/comment_1_0e40f158b3f4ccdcaab1408d858b68b8._comment ./doc/forum/incompatible_versions__63__/comment_1_629f28258746d413e452cbd42a1a43f4._comment ./doc/forum/vlc_and_git-annex/comment_1_9c9ab8ce463cf74418aa2f385955f165._comment ./doc/forum/vlc_and_git-annex/comment_2_037f94c1deeac873dbdb36cd4c927e45._comment ./doc/forum/Recommended_number_of_repositories/comment_1_3ef256230756be8a9679b107cdbfd018._comment ./doc/forum/migration_to_git-annex_and_rsync.mdwn ./doc/forum/hashing_objects_directories.mdwn ./doc/forum/git_annex_ls___47___metadata_in_git_annex_whereis.mdwn ./doc/forum/unlock__47__lock_always_gets_me/comment_1_dee73a7ea3e1a5154601adb59782831f._comment ./doc/forum/git-subtree_support__63__.mdwn ./doc/forum/Podcast_syncing_use-case.mdwn ./doc/forum/hashing_objects_directories/comment_5_ef6cfd49d24c180c2d0a062e5bd3a0be._comment ./doc/forum/hashing_objects_directories/comment_1_c55c56076be4f54251b0b7f79f28a607._comment ./doc/forum/hashing_objects_directories/comment_2_504c96959c779176f991f4125ea22009._comment ./doc/forum/hashing_objects_directories/comment_3_9134bde0a13aac0b6a4e5ebabd7f22e8._comment ./doc/forum/hashing_objects_directories/comment_4_0de9170e429cbfea66f5afa8980d45ac._comment ./doc/forum/location_tracking_cleanup.mdwn ./doc/forum/Wishlist:_Ways_of_selecting_files_based_on_meta-information.mdwn ./doc/forum/confusion_with_remotes__44___map.mdwn ./doc/forum/git-annex_on_OSX.mdwn ./doc/forum/A_really_stupid_question.mdwn ./doc/forum/git_tag_missing_for_3.20111011/comment_1_7a53bf273f3078ab3351369ef2b5f2a6._comment ./doc/forum/version_3_upgrade/comment_1_05fc9c9cad26c520bebb98c852c71e35._comment ./doc/forum/example_of_massively_disconnected_operation.mdwn ./doc/forum/git-annex_communication_channels/comment_2_c7aeefa6ef9a2e75d8667b479ade1b7f._comment ./doc/forum/git-annex_communication_channels/comment_5_404b723a681eb93fee015cea8024b6bc._comment ./doc/forum/git-annex_communication_channels/comment_1_198325d2e9337c90f026396de89eec0e._comment ./doc/forum/git-annex_communication_channels/comment_4_1ba6ddf54843c17c7d19a9996f2ab712._comment ./doc/forum/git-annex_communication_channels/comment_6_0d87d0e26461494b1d7f8a701a924729._comment ./doc/forum/git-annex_communication_channels/comment_3_1ff08a3e0e63fa0e560cbc9602245caa._comment ./doc/forum/git-annex_communication_channels/comment_7_2c87c7a0648fe87c2bf6b4391f1cc468._comment ./doc/forum/wishlist:_git-annex_replicate.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM/comment_1_96c354cac4b5ce5cf6664943bc84db1d._comment ./doc/forum/performance_improvement:_git_on_ssd__44___annex_on_spindle_disk.mdwn ./doc/forum/Behaviour_of_fsck.mdwn ./doc/forum/git_annex_add_crash_and_subsequent_recovery.mdwn ./doc/forum/Handling_web_special_remote_when_content_changes__63__.mdwn ./doc/forum/nfs_mounted_repo_results_in_errors_on_drop__47__move.mdwn ./doc/forum/working_without_git-annex_commits.mdwn ./doc/forum/migrate_existing_git_repository_to_git-annex.mdwn ./doc/forum/__34__git_annex_lock__34___very_slow_for_big_repo.mdwn ./doc/forum/wishlist:_traffic_accounting_for_git-annex.mdwn ./doc/forum/git_pull_remote_git-annex.mdwn ./doc/forum/rsync_over_ssh__63__.mdwn ./doc/forum/unannex_alternatives/comment_3_b1687fc8f9e7744327bbeb6f0635d1cd._comment ./doc/forum/unannex_alternatives/comment_1_dcd4cd41280b41512bbdffafaf307993._comment ./doc/forum/unannex_alternatives/comment_2_58a72a9fe0f58c7af0b4d7927a2dd21d._comment ./doc/forum/wishlist:_git_annex_status/comment_4_9aeeb83d202dc8fb33ff364b0705ad94._comment ./doc/forum/wishlist:_git_annex_status/comment_3_d1fd70c67243971c96d59e1ffb7ef6e7._comment ./doc/forum/wishlist:_git_annex_status/comment_2_c2b0ce025805b774dc77ce264a222824._comment ./doc/forum/wishlist:_git_annex_status/comment_1_994bfd12c5d82e08040d6116915c5090._comment ./doc/forum/git_pull_remote_git-annex/comment_7_24c45ee981b18bc78325c768242e635d._comment ./doc/forum/git_pull_remote_git-annex/comment_2_0f7f4a311b0ec1d89613e80847e69b42._comment ./doc/forum/git_pull_remote_git-annex/comment_1_9c245db3518d8b889ecdf5115ad9e053._comment ./doc/forum/git_pull_remote_git-annex/comment_5_4f2a05ef6551806dd0ec65372f183ca4._comment ./doc/forum/git_pull_remote_git-annex/comment_4_646f2077edcabc000a7d9cb75a93cf55._comment ./doc/forum/git_pull_remote_git-annex/comment_8_7e76ee9b6520cbffaf484c9299a63ad3._comment ./doc/forum/git_pull_remote_git-annex/comment_3_1aa89725b5196e40a16edeeb5ccfa371._comment ./doc/forum/git_pull_remote_git-annex/comment_6_3925d1aa56bce9380f712e238d63080f._comment ./doc/forum/wishlist:_command_options_changes.mdwn ./doc/forum/location_tracking_cleanup/comment_3_c15428cec90e969284a5e690fb4b2fde._comment ./doc/forum/location_tracking_cleanup/comment_2_e7395cb6e01f42da72adf71ea3ebcde4._comment ./doc/forum/location_tracking_cleanup/comment_1_7d6319e8c94dfe998af9cfcbf170efb2._comment ./doc/forum/new_microfeatures/comment_2_41ad904c68e89c85e1fc49c9e9106969._comment ./doc/forum/new_microfeatures/comment_1_058bd517c6fffaf3446b1f5d5be63623._comment ./doc/forum/new_microfeatures/comment_3_a1a9347b5bc517f2a89a8b292c3f8517._comment ./doc/forum/new_microfeatures/comment_7_94045b9078b1fff877933b012d1b49e2._comment ./doc/forum/new_microfeatures/comment_5_3c627d275586ff499d928a8f8136babf._comment ./doc/forum/new_microfeatures/comment_4_5a6786dc52382fff5cc42fdb05770196._comment ./doc/forum/new_microfeatures/comment_6_31ea08c008500560c0b96c6601bc6362._comment ./doc/forum/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/forum/How_to_expire_old_versions_of_files_that_have_been_edited__63__.mdwn ./doc/forum/advantages_of_SHA__42___over_WORM.mdwn ./doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn ./doc/forum/cloud_services_to_support.mdwn ./doc/forum/OSX__39__s_default_sshd_behaviour_has_limited_paths_set.mdwn ./doc/forum/Auto_archiving.mdwn ./doc/forum/bainstorming:_git_annex_push___38___pull.mdwn ./doc/forum/sparse_git_checkouts_with_annex.mdwn ./doc/forum/Need_new_build_instructions_for_Debian_stable.mdwn ./doc/forum/batch_check_on_remote_when_using_copy.mdwn ./doc/forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs.mdwn ./doc/forum/wishlist:alias_system.mdwn ./doc/forum/incompatible_versions__63__.mdwn ./doc/forum/Automatic_commit_messages_for_git_annex_sync.mdwn ./doc/forum/fsck_gives_false_positives.mdwn ./doc/forum/Preserving_file_access_rights_in_directory_tree_below_objects__47__.mdwn ./doc/forum/A_really_stupid_question/comment_1_40e02556de0b00b94f245a0196b5a89f._comment ./doc/forum/wishlist:_git_backend_for_git-annex.mdwn ./doc/forum/vlc_and_git-annex.mdwn ./doc/forum/seems_to_build_fine_on_haskell_platform_2011.mdwn ./doc/forum/rsync_over_ssh__63__/comment_1_ee21f32e90303e20339e0a568321bbbe._comment ./doc/forum/rsync_over_ssh__63__/comment_2_aa690da6ecfb2b30fc5080ad76dc77b1._comment ./doc/forum/wishlist:_special_remote_for_sftp_or_rsync.mdwn ./doc/forum/OSX__39__s_haskell-platform_statically_links_things.mdwn ./doc/forum/using_git_annex_to_merge_and_synchronize_2_directories___40__like_unison__41__.mdwn ./doc/forum/git_tag_missing_for_3.20111011.mdwn ./doc/forum/unannex_alternatives.mdwn ./doc/forum/can_git-annex_replace_ddm__63__.mdwn ./doc/forum/sparse_git_checkouts_with_annex/comment_2_e357db3ccc4079f07a291843975535eb._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_1_c7dc199c5740a0e7ba606dfb5e3e579a._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_3_fcfafca994194d57dccf5319c7c9e646._comment ./doc/forum/sparse_git_checkouts_with_annex/comment_4_04dc14880f31eee2b6d767d4d4258c5a._comment ./doc/forum/syncing_non-git_trees_with_git-annex.mdwn ./doc/forum/git-annex_communication_channels.mdwn ./doc/forum/fsck_gives_false_positives/comment_4_7ceb395bf8a2e6a041ccd8de63b1b6eb._comment ./doc/forum/fsck_gives_false_positives/comment_3_692d6d4cd2f75a497e7d314041a768d2._comment ./doc/forum/fsck_gives_false_positives/comment_5_86484a504c3bbcecd5876982b9c95688._comment ./doc/forum/fsck_gives_false_positives/comment_2_f51c53f3f6e6ee1ad463992657db5828._comment ./doc/forum/fsck_gives_false_positives/comment_1_b91070218b9d5fb687eeee1f244237ad._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_3_4c69097fe2ee81359655e59a03a9bb8d._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_2_008554306dd082d7f543baf283510e92._comment ./doc/forum/can_git-annex_replace_ddm__63__/comment_1_aa05008dfe800474ff76678a400099e1._comment ./doc/forum/version_3_upgrade.mdwn ./doc/forum/relying_on_git_for_numcopies/comment_3_43d8e1513eb9947f8a503f094c03f307._comment ./doc/forum/relying_on_git_for_numcopies/comment_2_be6acbc26008a9cb54e7b8f498f2c2a2._comment ./doc/forum/relying_on_git_for_numcopies/comment_1_8ad3cccd7f66f6423341d71241ba89fc._comment ./doc/forum/confusion_with_remotes__44___map/comment_1_a38ded23b7f288292a843abcb1a56f38._comment ./doc/forum/confusion_with_remotes__44___map/comment_2_cd1c98b1276444e859a22c3dbd6f2a79._comment ./doc/forum/confusion_with_remotes__44___map/comment_6_496b0d9b86869bbac3a1356d53a3dda4._comment ./doc/forum/confusion_with_remotes__44___map/comment_5_27801584325d259fa490f67273f2ff71._comment ./doc/forum/confusion_with_remotes__44___map/comment_4_3b89b6d1518267fcbc050c9de038b9ca._comment ./doc/forum/confusion_with_remotes__44___map/comment_3_18531754089c991b6caefc57a5c17fe9._comment ./doc/forum/confusion_with_remotes__44___map/comment_7_9a456f61f956a3d5e81e723d5a90794c._comment ./doc/forum/--print0_option_as_in___34__find__34__.mdwn ./doc/forum/relying_on_git_for_numcopies.mdwn ./doc/forum/wishlist:_do_round_robin_downloading_of_data.mdwn ./doc/forum/pure_git-annex_only_workflow.mdwn ./doc/forum/wishlist:_git_annex_status.mdwn ./doc/forum/getting_git_annex_to_do_a_force_copy_to_a_remote.mdwn ./doc/forum/Problems_with_large_numbers_of_files.mdwn ./doc/forum/Can_I_store_normal_files_in_the_git-annex_git_repository__63__.mdwn ./doc/forum/Is_an_automagic_upgrade_of_the_object_directory_safe__63__.mdwn ./doc/forum/wishlist:_push_to_cia.vc_from_the_website__39__s_repo__44___not_your_personal_one.mdwn ./doc/forum/new_microfeatures.mdwn ./doc/forum/git-subtree_support__63__/comment_2_73d2a015b1ac79ec99e071a8b1e29034._comment ./doc/forum/git-subtree_support__63__/comment_1_4f333cb71ed1ff259bbfd86704806aa6._comment ./doc/forum/git-subtree_support__63__/comment_4_75b0e072e668aa46ff0a8d62a6620306._comment ./doc/forum/git-subtree_support__63__/comment_3_c533400e22c306c033fcd56e64761b0b._comment ./doc/forum/git-subtree_support__63__/comment_6_85df530f7b6d76b74ac8017c6034f95e._comment ./doc/forum/git-subtree_support__63__/comment_5_f5ec9649d9f1dc122e715de5533bc674._comment ./doc/backends.mdwn ./doc/bugs/__39__annex_add__39___fails_to___39__git_add__39___for_parent_relative_path.mdwn ./doc/bugs/uuid.log_trust.log_and_remote.log_merge_wackiness.mdwn ./doc/bugs/Remote_repo_and_set_operation_with_find.mdwn ./doc/bugs/softlink_mtime.mdwn ./doc/bugs/uninit_should_not_run_when_branch_git-annex_is_checked_out.mdwn ./doc/bugs/ordering.mdwn ./doc/bugs/dropping_files_with_a_URL_backend_fails.mdwn ./doc/bugs/fsck__47__fix_should_check__47__fix_the_permissions_of_.git__47__annex.mdwn ./doc/bugs/dropunused_doesn__39__t_handle_double_spaces_in_filename.mdwn ./doc/bugs/add_script-friendly_output_options.mdwn ./doc/bugs/uninit_does_not_work_in_old_repos.mdwn ./doc/bugs/conq:_invalid_command_syntax.mdwn ./doc/bugs/git_annex_get_choke_when_remote_is_an_ssh_url_with_a_port.mdwn ./doc/bugs/unannex_and_uninit_do_not_work_when_git_index_is_broken.mdwn ./doc/bugs/git_annex_migrate_leaves_old_backend_versions_around.mdwn ./doc/bugs/git_annex_unused_failes_on_empty_repository.mdwn ./doc/bugs/git_annex_add_eats_files_when_filename_is_too_long.mdwn ./doc/bugs/S3_bucket_uses_the_same_key_for_encryption_and_hashing.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3.mdwn ./doc/bugs/git_annex_copy_--fast_does_not_copy_files.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing.mdwn ./doc/bugs/dotdot_problem.mdwn ./doc/bugs/Problems_running_make_on_osx.mdwn ./doc/bugs/free_space_checking/comment_2_8a65f6d3dcf5baa3f7f2dbe1346e2615._comment ./doc/bugs/free_space_checking/comment_3_0fc6ff79a357b1619d13018ccacc7c10._comment ./doc/bugs/free_space_checking/comment_1_a868e805be43c5a7c19c41f1af8e41e6._comment ./doc/bugs/copy_doesn__39__t_scale.mdwn ./doc/bugs/Error_when_moving_annexed_file_to_a_.gitignored_location.mdwn ./doc/bugs/done.mdwn ./doc/bugs/git_annex_copy_-f_REMOTE_._doesn__39__t_work_as_expected.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong/comment_2_8b240de1d5ae9229fa2d77d1cc15a552._comment ./doc/bugs/Displayed_copy_speed_is_wrong/comment_1_74de3091e8bfd7acd6795e61f39f07c6._comment ./doc/bugs/Build_error_on_Mac_OSX_10.6.mdwn ./doc/bugs/bare_git_repos.mdwn ./doc/bugs/nfs_mounted_repo_results_in_errors_on_drop_move.mdwn ./doc/bugs/fat_support.mdwn ./doc/bugs/problems_with_utf8_names.mdwn ./doc/bugs/error_with_file_names_starting_with_dash.mdwn ./doc/bugs/not_possible_to_have_annex_on_a_separate_filesystem.mdwn ./doc/bugs/Lost_S3_Remote.mdwn ./doc/bugs/unhappy_without_UTF8_locale.mdwn ./doc/bugs/S3_memory_leaks.mdwn ./doc/bugs/git_annex_map_has_problems_with_urls_containing___126__.mdwn ./doc/bugs/interrupting_migration_causes_problems.mdwn ./doc/bugs/upgrade_left_untracked_.git-annex__47____42___directories.mdwn ./doc/bugs/cyclic_drop.mdwn ./doc/bugs/Problems_running_make_on_osx/comment_9_cc283b485b3c95ba7eebc8f0c96969b3._comment ./doc/bugs/Problems_running_make_on_osx/comment_4_c52be386f79f14c8570a8f1397c68581._comment ./doc/bugs/Problems_running_make_on_osx/comment_18_64fab50d95de619eb2e8f08f90237de1._comment ./doc/bugs/Problems_running_make_on_osx/comment_16_5c2dd6002aadaab30841b77a5f5aed34._comment ./doc/bugs/Problems_running_make_on_osx/comment_10_94e4ac430140042a2d0fb5a16d86b4e5._comment ./doc/bugs/Problems_running_make_on_osx/comment_15_6b8867b8e48bf807c955779c9f8f0909._comment ./doc/bugs/Problems_running_make_on_osx/comment_11_56f1143fa191361d63b441741699e17f._comment ./doc/bugs/Problems_running_make_on_osx/comment_20_7db27d1a22666c831848bc6c06d66a84._comment ./doc/bugs/Problems_running_make_on_osx/comment_12_ec5131624d0d2285d3b6880e47033f97._comment ./doc/bugs/Problems_running_make_on_osx/comment_3_68f0f8ae953589ae26d57310b40c878d._comment ./doc/bugs/Problems_running_make_on_osx/comment_14_89a960b6706ed703b390a81a8bc4e311._comment ./doc/bugs/Problems_running_make_on_osx/comment_17_62fccb04b0e4b695312f7a3f32fb96ee._comment ./doc/bugs/Problems_running_make_on_osx/comment_19_4253988ed178054c8b6400beeed68a29._comment ./doc/bugs/Problems_running_make_on_osx/comment_6_0c46f5165ceb5a7b9ea9689c33b3a4f8._comment ./doc/bugs/Problems_running_make_on_osx/comment_1_34120e82331ace01a6a4960862d38f2d._comment ./doc/bugs/Problems_running_make_on_osx/comment_7_237a137cce58a28abcc736cbf2c420b0._comment ./doc/bugs/Problems_running_make_on_osx/comment_5_7f1330a1e541b0f3e2192e596d7f7bee._comment ./doc/bugs/Problems_running_make_on_osx/comment_2_cc53d1681d576186dbc868dd9801d551._comment ./doc/bugs/Problems_running_make_on_osx/comment_13_88ed095a448096bf8a69015a04e64df1._comment ./doc/bugs/Problems_running_make_on_osx/comment_8_efafa203addf8fa79e33e21a87fb5a2b._comment ./doc/bugs/git-annex_directory_hashing_problems_on_osx.mdwn ./doc/bugs/No_easy_way_to_re-inject_a_file_into_an_annex.mdwn ./doc/bugs/git_annex_unlock_is_not_atomic.mdwn ./doc/bugs/free_space_checking.mdwn ./doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn ./doc/bugs/Displayed_copy_speed_is_wrong.mdwn ./doc/bugs/git_annex_should_use___39__git_add_-f__39___internally.mdwn ./doc/bugs/minor_bug:_errors_are_not_verbose_enough.mdwn ./doc/bugs/__34__make_test__34___fails_silently.mdwn ./doc/bugs/No_version_information_from_cli.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./doc/bugs/conq:_invalid_command_syntax/comment_1_f33b83025ce974e496f83f248275a66a._comment ./doc/bugs/conq:_invalid_command_syntax/comment_2_195106ca8dedad5f4d755f625e38e8af._comment ./doc/bugs/conq:_invalid_command_syntax/comment_3_55af43e2f43a4c373f7a0a33678d0b1c._comment ./doc/bugs/old_data_isn__39__t_unused_after_migration.mdwn ./doc/bugs/fails_to_handle_lot_of_files.mdwn ./doc/bugs/tests_fail_when_there_is_no_global_.gitconfig_for_the_user.mdwn ./doc/bugs/git_annex_initremote_walks_.git-annex.mdwn ./doc/bugs/copy_doesn__39__t_scale/comment_1_7c12499c9ac28a9883c029f8c659eb57._comment ./doc/bugs/copy_doesn__39__t_scale/comment_2_f85d8023cdbc203bb439644cf7245d4e._comment ./doc/bugs/copy_doesn__39__t_scale/comment_3_4592765c3d77bb5664b8d16867e9d79c._comment ./doc/bugs/signal_weirdness.mdwn ./doc/bugs/git-annex_incorrectly_parses_bare_IPv6_addresses.mdwn ./doc/bugs/git_annex_version_should_without_being_in_a_repo_.mdwn ./doc/bugs/Can__39__t___34__git-annex_get__34___with_3.20111203.mdwn ./doc/bugs/Prevent_accidental_merges/comment_1_4c46a193915eab8f308a04175cb2e40a._comment ./doc/bugs/conflicting_haskell_packages/comment_1_e552a6cc6d7d1882e14130edfc2d6b3b._comment ./doc/bugs/tmp_file_handling.mdwn ./doc/bugs/annex_add_in_annex.mdwn ./doc/bugs/making_annex-merge_try_a_fast-forward.mdwn ./doc/bugs/bad_behaviour_with_file_names_with_newline_in_them.mdwn ./doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment ./doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment ./doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment ./doc/bugs/on--git-dir_and_--work-tree_options.mdwn ./doc/bugs/Makefile_is_missing_dependancies.mdwn ./doc/bugs/test_suite_shouldn__39__t_fail_silently.mdwn ./doc/bugs/git_annex_upgrade_output_is_inconsistent_and_spammy.mdwn ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_4_91439d4dbbf1461e281b276eb0003691._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_6_f360f0006bc9115bc5a3e2eb9fe58abd._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_3_86d9e7244ae492bcbe62720b8c4fc4a9._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_2_cd0123392b16d89db41b45464165c247._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_1_5f60006c9bb095167d817f234a14d20b._comment ./doc/bugs/problem_with_upgrade_v2_-__62___v3/comment_5_ca33a9ca0df33f7c1b58353d7ffb943d._comment ./doc/bugs/wishlist:_query_things_like_description__44___trust_level.mdwn ./doc/bugs/git_rename_detection_on_file_move/comment_9_75e0973f6d573df615e01005ebcea87d._comment ./doc/bugs/git_rename_detection_on_file_move/comment_7_7f20d0b2f6ed1c34021a135438037306._comment ./doc/bugs/git_rename_detection_on_file_move/comment_3_57010bcaca42089b451ad8659a1e018e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_4_79d96599f757757f34d7b784e6c0e81c._comment ./doc/bugs/git_rename_detection_on_file_move/comment_5_d61f5693d947b9736b29fca1dbc7ad76._comment ./doc/bugs/git_rename_detection_on_file_move/comment_8_6a00500b24ba53248c78e1ffc8d1a591._comment ./doc/bugs/git_rename_detection_on_file_move/comment_2_7101d07400ad5935f880dc00d89bf90e._comment ./doc/bugs/git_rename_detection_on_file_move/comment_6_f63de6fe2f7189c8c2908cc41c4bc963._comment ./doc/bugs/git_rename_detection_on_file_move/comment_1_0531dcfa833b0321a7009526efe3df33._comment ./doc/bugs/fsck_claims_failed_checksum_when_less_copies_than_required_are_found.mdwn ./doc/bugs/add_range_argument_to___34__git_annex_dropunused__34___.mdwn ./doc/bugs/git_rename_detection_on_file_move.mdwn ./doc/bugs/problem_commit_normal_links.mdwn ./doc/bugs/extraneous_shell_escaping_for_rsync_remotes.mdwn ./doc/bugs/problems_with_utf8_names/comment_1_3c7e3f021c2c94277eecf9c8af6cec5f._comment ./doc/bugs/problems_with_utf8_names/comment_7_0cc588f787d6eecfa19a8f6cee4b07b5._comment ./doc/bugs/problems_with_utf8_names/comment_8_ff5c6da9eadfee20c18c86b648a62c47._comment ./doc/bugs/problems_with_utf8_names/comment_4_93bee35f5fa7744834994bc7a253a6f9._comment ./doc/bugs/problems_with_utf8_names/comment_3_4f936a5d3f9c7df64c8a87e62b7fbfdc._comment ./doc/bugs/problems_with_utf8_names/comment_5_519cda534c7aea7f5ad5acd3f76e21fa._comment ./doc/bugs/problems_with_utf8_names/comment_6_52e0bfff2b177b6f92e226b25d2f3ff1._comment ./doc/bugs/problems_with_utf8_names/comment_2_bad4c4c5f54358d1bc0ab2adc713782a._comment ./doc/bugs/git_annex_unused_seems_to_check_for_current_path.mdwn ./doc/bugs/Makefile_is_missing_dependancies/comment_6_24119fc5d5963ce9dd669f7dcf006859._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_3_c38b6f4abc9b9ad413c3b83ca04386c3._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_8_a3555e3286cdc2bfeb9cde0ff727ba74._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_2_416f12dbd0c2b841fac8164645b81df5._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_5_0a1c52e2c96d19b9c3eb7e99b8c2434f._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_7_96fd4725df4b54e670077a18d3ac4943._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_1_5a3da5f79c8563c7a450aa29728abe7c._comment ./doc/bugs/Makefile_is_missing_dependancies/comment_4_cc13873175edf191047282700315beee._comment ./doc/bugs/Trouble_initializing_git_annex_on_NFS.mdwn ./doc/bugs/touch.hsc_has_problems_on_non-linux_based_systems.mdwn ./doc/bugs/build_issue_with_latest_release_0.20110522-1-gde817ba.mdwn ./doc/bugs/weird_local_clone_confuses.mdwn ./doc/bugs/fsck_output.mdwn ./doc/bugs/git_annex_add_memory_leak.mdwn ./doc/bugs/git-annex_branch_push_race.mdwn ./doc/bugs/wishlist:_allow_users_to_provide_UUID_when_running___96__git_annex_init__96__.mdwn ./doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn ./doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn ./doc/bugs/unannex_vs_unlock_hook_confusion.mdwn ./doc/bugs/Cabal_dependency_monadIO_missing/comment_2_4f4d8e1e00a2a4f7e8a8ab082e16adac._comment ./doc/bugs/Cabal_dependency_monadIO_missing/comment_1_14be660aa57fadec0d81b32a8b52c66f._comment ./doc/bugs/conflicting_haskell_packages.mdwn ./doc/bugs/git_command_line_constructed_by_unannex_command_has_tons_of_redundant_-a_paramters.mdwn ./doc/bugs/backend_version_upgrade_leaves_repo_unusable.mdwn ./doc/bugs/fat_support/comment_3_df3b943bc1081a8f3f7434ae0c8e061e._comment ./doc/bugs/fat_support/comment_4_90a8a15bedd94480945a374f9d706b86._comment ./doc/bugs/fat_support/comment_5_64bbf89de0836673224b83fdefa0407b._comment ./doc/bugs/fat_support/comment_1_04bcc4795d431e8cb32293aab29bbfe2._comment ./doc/bugs/fat_support/comment_2_bb4a97ebadb5c53809fc78431eabd7c8._comment ./doc/bugs/unannex_command_doesn__39__t_all_files.mdwn ./doc/bugs/annex_unannex__47__uninit_should_handle_copies.mdwn ./doc/bugs/git-annex_branch_corruption.mdwn ./doc/bugs/git_annex_fsck_is_a_no-op_in_bare_repos.mdwn ./doc/bugs/show_version_without_having_to_be_in_a_git_repo.mdwn ./doc/bugs/Unfortunate_interaction_with_Calibre.mdwn ./doc/bugs/error_propigation.mdwn ./doc/bugs/wishlist:_support_drop__44___find_on_special_remotes.mdwn ./doc/bugs/scp_interrupt_to_background.mdwn ./doc/bugs/copy_fast_confusing_with_broken_locationlog.mdwn ./doc/bugs/uninit_does_not_work_in_old_repos/comment_1_bc0619c6e17139df74639448aa6a0f72._comment ./doc/bugs/git-annex_losing_rsync_remotes_with_encryption_enabled.mdwn ./doc/bugs/configure_script_should_detect_uuidgen_instead_of_just_uuid.mdwn ./doc/bugs/check_for_curl_in_configure.hs.mdwn ./doc/bugs/fails_to_handle_lot_of_files/comment_1_09d8e4e66d8273fab611bd29e82dc7fc._comment ./doc/bugs/fails_to_handle_lot_of_files/comment_2_fd2ec05f4b5a7a6ae6bd9f5dbc3156de._comment ./doc/bugs/Name_scheme_does_not_follow_git__39__s_rules.mdwn ./doc/bugs/concurrent_git-annex_processes_can_lead_to_locking_issues.mdwn ./doc/bugs/Prevent_accidental_merges.mdwn ./doc/bugs/building_on_lenny.mdwn ./doc/bugs/case_sensitivity_on_FAT.mdwn ./doc/bugs/encrypted_S3_stalls.mdwn ./doc/bugs/support_bare_git_repo__44___with_the_annex_directory_exposed_to_http.mdwn ./doc/bugs/WORM:_Handle_long_filenames_correctly.mdwn ./doc/logo_small.png ./doc/tips.mdwn ./doc/use_case/Bob.mdwn ./doc/use_case/Alice.mdwn ./doc/upgrades/SHA_size.mdwn ./doc/news/sharebox_a_FUSE_filesystem_for_git-annex.mdwn ./doc/news/version_3.20120113.mdwn ./doc/news/version_3.20120115.mdwn ./doc/news/Presentation_at_FOSDEM.mdwn ./doc/news/version_3.20120116.mdwn ./doc/news/version_3.20120123.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20120227.mdwn ./doc/news/version_3.20120106/comment_1_fb1a3135e2d9f39f2c372ccc2c50c85a._comment ./doc/news/version_3.20120106/comment_2_ae292ca7294b6790233e545086c3ac2f._comment ./doc/news/version_3.20120106/comment_3_29ccda9ac458fd5cc9ec5508c62df6ea._comment ./doc/git-union-merge.mdwn ./doc/special_remotes/S3.mdwn ./doc/special_remotes/web.mdwn ./doc/special_remotes/bup.mdwn ./doc/special_remotes/directory.mdwn ./doc/special_remotes/hook.mdwn ./doc/special_remotes/rsync.mdwn ./doc/how_it_works.mdwn ./doc/sitemap.mdwn ./doc/users/joey.mdwn ./doc/users/fmarier.mdwn ./doc/users/chrysn.mdwn ./doc/users/gebi.mdwn ./doc/templates/walkthrough.tmpl ./doc/templates/bugtemplate.mdwn ./doc/templates/bare.tmpl ./doc/index.mdwn ./doc/sync/comment_1_59681be5568f568f5c54eb0445163dd2._comment ./doc/summary.mdwn ./doc/forum.mdwn ./doc/encryption/comment_1_1afca8d7182075d46db41f6ad3dd5911._comment ./doc/transferring_data.mdwn ./doc/download.mdwn ./doc/future_proofing.mdwn ./doc/todo/redundancy_stats_in_status.mdwn ./doc/todo/auto_remotes/discussion.mdwn ./doc/todo/link_file_to_remote_repo_feature.mdwn ./doc/todo/file_copy_progress_bar.mdwn ./doc/todo/use_cp_reflink.mdwn ./doc/todo/gitrm.mdwn ./doc/todo/exclude_files_on_a_given_remote.mdwn ./doc/todo/git-annex_unused_eats_memory.mdwn ./doc/todo/Please_abort_build_if___34__make_test__34___fails.mdwn ./doc/todo/fsck_special_remotes.mdwn ./doc/todo/add_--exclude_option_to_git_annex_find.mdwn ./doc/todo/tahoe_lfs_for_reals/comment_2_80b9e848edfdc7be21baab7d0cef0e3a._comment ./doc/todo/tahoe_lfs_for_reals/comment_1_0a4793ce6a867638f6e510e71dd4bb44._comment ./doc/todo/object_dir_reorg_v2.mdwn ./doc/todo/S3.mdwn ./doc/todo/auto_remotes.mdwn ./doc/todo/wishlist:_support_for_more_ssh_urls_.mdwn ./doc/todo/support-non-utf8-locales.mdwn ./doc/todo/done.mdwn ./doc/todo/fsck.mdwn ./doc/todo/support_S3_multipart_uploads.mdwn ./doc/todo/wishlist:_Provide_a___34__git_annex__34___command_that_will_skip_duplicates.mdwn ./doc/todo/pushpull.mdwn ./doc/todo/smudge.mdwn ./doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment ./doc/todo/windows_support.mdwn ./doc/todo/optimise_git-annex_merge.mdwn ./doc/todo/object_dir_reorg_v2/comment_3_79bdf9c51dec9f52372ce95b53233bb2._comment ./doc/todo/object_dir_reorg_v2/comment_7_42501404c82ca07147e2cce0cff59474._comment ./doc/todo/object_dir_reorg_v2/comment_5_821c382987f105da72a50e0a5ce61fdc._comment ./doc/todo/object_dir_reorg_v2/comment_1_ba03333dc76ff49eccaba375e68cb525._comment ./doc/todo/object_dir_reorg_v2/comment_2_81276ac309959dc741bc90101c213ab7._comment ./doc/todo/object_dir_reorg_v2/comment_6_8834c3a3f1258c4349d23aff8549bf35._comment ./doc/todo/object_dir_reorg_v2/comment_4_93aada9b1680fed56cc6f0f7c3aca5e5._comment ./doc/todo/union_mounting.mdwn ./doc/todo/wishlist:_swift_backend.mdwn ./doc/todo/Please_add_support_for_monad-control_0.3.x.mdwn ./doc/todo/wishlist:_swift_backend/comment_1_e6efbb35f61ee521b473a92674036788._comment ./doc/todo/wishlist:_swift_backend/comment_2_5d8c83b0485112e98367b7abaab3f4e3._comment ./doc/todo/git_annex_init_:_include_repo_description_and__47__or_UUID_in_commit_message.mdwn ./doc/todo/gitolite_and_gitosis_support.mdwn ./doc/todo/wishlist:___34__git_annex_add__34___multiple_processes.mdwn ./doc/todo/hidden_files.mdwn ./doc/todo/smudge/comment_1_4ea616bcdbc9e9a6fae9f2e2795c31c9._comment ./doc/todo/smudge/comment_2_e04b32caa0d2b4c577cdaf382a3ff7f6._comment ./doc/todo/network_remotes.mdwn ./doc/todo/add_a_git_backend.mdwn ./doc/todo/parallel_possibilities/comment_1_d8e34fc2bc4e5cf761574608f970d496._comment ./doc/todo/parallel_possibilities/comment_2_adb76f06a7997abe4559d3169a3181c3._comment ./doc/todo/checkout.mdwn ./doc/todo/immutable_annexed_files.mdwn ./doc/todo/wishlist:_Prevent_repeated_password_prompts_for_one_command.mdwn ./doc/todo/git-annex-shell.mdwn ./doc/todo/speed_up_fsck.mdwn ./doc/todo/tahoe_lfs_for_reals.mdwn ./doc/todo/avoid_unnecessary_union_merges.mdwn ./doc/todo/support_fsck_in_bare_repos.mdwn ./doc/todo/add_-all_option.mdwn ./doc/todo/using_url_backend.mdwn ./doc/todo/branching.mdwn ./doc/todo/rsync.mdwn ./doc/todo/backendSHA1.mdwn ./doc/todo/cache_key_info.mdwn ./doc/todo/parallel_possibilities.mdwn ./doc/todo/symlink_farming_commit_hook.mdwn ./doc/feeds.mdwn ./doc/sync.mdwn ./doc/copies.mdwn ./doc/repomap.png ./doc/scalability.mdwn ./doc/special_remotes.mdwn ./doc/todo.mdwn ./doc/walkthrough/using_ssh_remotes.mdwn ./doc/walkthrough/automatically_managing_content.mdwn ./doc/walkthrough/removing_files/comment_1_cb65e7c510b75be1c51f655b058667c6._comment ./doc/walkthrough/removing_files/comment_2_64709ea4558915edd5c8ca4486965b07._comment ./doc/walkthrough/moving_file_content_between_repositories.mdwn ./doc/walkthrough/modifying_annexed_files.mdwn ./doc/walkthrough/more.mdwn ./doc/walkthrough/removing_files.mdwn ./doc/walkthrough/creating_a_repository.mdwn ./doc/walkthrough/adding_a_remote.mdwn ./doc/walkthrough/adding_a_remote/comment_1_0a59355bd33a796aec97173607e6adc9._comment ./doc/walkthrough/adding_a_remote/comment_2_f8cd79ef1593a8181a7f1086a87713e8._comment ./doc/walkthrough/adding_a_remote/comment_3_60691af4400521b5a8c8d75efe3b44cb._comment ./doc/walkthrough/adding_a_remote/comment_4_6f7cf5c330272c96b3abeb6612075c9d._comment ./doc/walkthrough/removing_files:_When_things_go_wrong.mdwn ./doc/walkthrough/renaming_files.mdwn ./doc/walkthrough/using_bup.mdwn ./doc/walkthrough/fsck:_when_things_go_wrong.mdwn ./doc/walkthrough/syncing.mdwn ./doc/walkthrough/adding_files.mdwn ./doc/walkthrough/unused_data.mdwn ./doc/walkthrough/fsck:_verifying_your_data.mdwn ./doc/walkthrough/getting_file_content.mdwn ./doc/walkthrough/backups.mdwn ./doc/walkthrough/transferring_files:_When_things_go_wrong.mdwn ./doc/GPL ./doc/logo.png ./doc/location_tracking.mdwn ./doc/design.mdwn ./doc/news.mdwn ./doc/tips/visualizing_repositories_with_gource/screenshot.jpg ./doc/tips/untrusted_repositories.mdwn ./doc/tips/automatically_getting_files_on_checkout.mdwn ./doc/tips/what_to_do_when_a_repository_is_corrupted.mdwn ./doc/tips/Internet_Archive_via_S3.mdwn ./doc/tips/what_to_do_when_you_lose_a_repository.mdwn ./doc/tips/using_Amazon_S3.mdwn ./doc/tips/using_the_web_as_a_special_remote.mdwn ./doc/tips/assume-unstaged.mdwn ./doc/tips/powerful_file_matching.mdwn ./doc/tips/using_gitolite_with_git-annex.mdwn ./doc/tips/recover_data_from_lost+found.mdwn ./doc/tips/visualizing_repositories_with_gource.mdwn ./doc/tips/centralized_git_repository_tutorial.mdwn ./doc/tips/finding_duplicate_files/comment_1_ddb477ca242ffeb21e0df394d8fdf5d2._comment ./doc/tips/using_git_annex_with_no_fixed_hostname_and_optimising_ssh.mdwn ./doc/tips/using_gitolite_with_git-annex/comment_4_eb81f824aadc97f098379c5f7e4fba4c._comment ./doc/tips/using_gitolite_with_git-annex/comment_6_3e203e010a4df5bf03899f867718adc5._comment ./doc/tips/using_gitolite_with_git-annex/comment_1_9a2a2a8eac9af97e0c984ad105763a73._comment ./doc/tips/using_gitolite_with_git-annex/comment_8_8249772c142117f88e37975d058aa936._comment ./doc/tips/using_gitolite_with_git-annex/comment_5_f688309532d2993630e9e72e87fb9c46._comment ./doc/tips/using_gitolite_with_git-annex/comment_7_f8fd08b6ab47378ad88c87348057220d._comment ./doc/tips/using_gitolite_with_git-annex/comment_3_807035f38509ccb9f93f1929ecd37417._comment ./doc/tips/using_gitolite_with_git-annex/comment_9_28418635a6ed7231b89e02211cd3c236._comment ./doc/tips/using_gitolite_with_git-annex/comment_2_d8efea4ab9576555fadbb47666ecefa9._comment ./doc/tips/finding_duplicate_files.mdwn ./doc/tips/centralised_repository:_starting_from_nothing.mdwn ./doc/tips/using_the_SHA1_backend.mdwn ./doc/tips/migrating_data_to_a_new_backend.mdwn ./doc/encryption.mdwn ./doc/not.mdwn ./doc/download/comment_7_a5eebd214b135f34b18274a682211943._comment ./doc/download/comment_3_cf6044ebe99f71158034e21197228abd._comment ./doc/download/comment_5_c6b1bc40226fc2c8ba3e558150856992._comment ./doc/download/comment_6_3a52993d3553deb9a413debec9a5f92d._comment ./doc/download/comment_2_f85f72b33aedc3425f0c0c47867d02f3._comment ./doc/download/comment_8_59a976de6c7d333709b92f7cd5830850._comment ./doc/download/comment_4_10fc013865c7542c2ed9d6c0963bb391._comment ./doc/download/comment_1_fbd8b6d39e9d3c71791551358c863966._comment ./doc/contact.mdwn ./doc/trust.mdwn ./doc/bare_repositories.mdwn ./doc/NixOS.mdwn ./doc/distributed_version_control.mdwn ./doc/install.mdwn ./doc/git-annex-shell.mdwn ./doc/bugs.mdwn ./doc/meta.mdwn ./doc/design/encryption/comment_2_a610b3d056a059899178859a3a821ea5._comment ./doc/design/encryption/comment_3_cca186a9536cd3f6e86994631b14231c._comment ./doc/design/encryption/comment_1_4715ffafb3c4a9915bc33f2b26aaa9c1._comment ./doc/design/encryption/comment_4_8f3ba3e504b058791fc6e6f9c38154cf._comment ./doc/design/encryption.mdwn ./doc/comments.mdwn ./doc/git-annex.mdwn ./doc/internals.mdwn ./doc/users.mdwn ./doc/install/Ubuntu.mdwn ./doc/install/Debian.mdwn ./doc/install/openSUSE.mdwn ./doc/install/OSX.mdwn ./doc/install/FreeBSD.mdwn ./doc/install/OSX/comment_1_0a1760bf0db1f1ba89bdb4c62032f631._comment ./doc/install/ArchLinux.mdwn ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/Fedora.mdwn ./doc/walkthrough.mdwn ./CmdLine.hs ./Messages.hs ./Setup.hs ./GPL ./mdwn2man ./Remote.hs ./git-annex.hs ./Locations.hs ./Usage.hs ./Backend/WORM.hs ./Backend/URL.hs ./Backend/SHA.hs ./git-annex-shell.hs ./test.hs ./INSTALL ./Remote/Web.hs ./Remote/Helper/Encryptable.hs ./Remote/Helper/Ssh.hs ./Remote/Helper/Special.hs ./Remote/Directory.hs ./Remote/List.hs ./Remote/Bup.hs ./Remote/Hook.hs ./Remote/S3.hs ./Remote/Rsync.hs ./Remote/Git.hs ./Build/TestConfig.hs ./Upgrade.hs ./Annex/Version.hs ./Annex/Exception.hs ./Annex/Journal.hs ./Annex/CatFile.hs ./Annex/Ssh.hs ./Annex/Queue.hs ./Annex/Branch.hs ./Annex/UUID.hs ./Annex/LockPool.hs ./Annex/BranchState.hs ./Annex/CheckAttr.hs ./Annex/Content.hs ./git-union-merge.hs ./Utility/Path.hs ./Utility/Matcher.hs ./Utility/TempFile.hs ./Utility/State.hs ./Utility/Url.hs ./Utility/DataUnits.hs ./Utility/StatFS.hsc ./Utility/CoProcess.hs ./Utility/Exception.hs ./Utility/Monad.hs ./Utility/RsyncFile.hs ./Utility/CopyFile.hs ./Utility/Misc.hs ./Utility/SafeCommand.hs ./Utility/Gpg.hs ./Utility/Directory.hs ./Utility/FileMode.hs ./Utility/PartialPrelude.hs ./Utility/JSONStream.hs ./Utility/Dot.hs ./Utility/Touch.hsc ./Utility/Base64.hs ./Utility/Format.hs ./Types/Option.hs ./Types/Crypto.hs ./Types/UUID.hs ./Types/TrustLevel.hs ./Types/Remote.hs ./Types/BranchState.hs ./Types/Command.hs ./Types/Backend.hs ./Types/Key.hs ./Config.hs ./Checks.hs ./Command.hs ./.gitignore ./Backend.hs ./Logs/Web.hs ./Logs/UUIDBased.hs ./Logs/Trust.hs ./Logs/Location.hs ./Logs/UUID.hs ./Logs/Remote.hs ./Logs/Presence.hs ./.gitattributes ./Git.hs ./Limit.hs ./GitAnnex.hs Homepage: http://git-annex.branchable.com/ Build-type: Custom Category: Utility@@ -16,7 +16,7 @@  git-annex allows managing files with git, without checking the file  contents into git. While that may seem paradoxical, it is useful when  dealing with files larger than git can currently easily handle, whether due- to limitations in memory, checksumming time, or disk space.+ to limitations in memory, time, or disk space.  .  Even without file content tracking, being able to manage files with git,  move files around and delete files with versioned directory trees, and use@@ -31,7 +31,8 @@   Build-Depends: MissingH, hslogger, directory, filepath,    unix, containers, utf8-string, network, mtl, bytestring, old-locale, time,    pcre-light, extensible-exceptions, dataenc, SHA, process, hS3, json, HTTP,-   base < 5, monad-control, transformers-base, lifted-base, QuickCheck >= 2.1+   base < 5, monad-control, transformers-base, lifted-base, IfElse,+   QuickCheck >= 2.1  Executable git-annex-shell   Main-Is: git-annex-shell.hs
test.hs view
@@ -5,16 +5,16 @@  - Licensed under the GNU GPL version 3 or higher.  -} +{-# OPTIONS_GHC -fno-warn-orphans #-}+ import Test.HUnit import Test.HUnit.Tools import Test.QuickCheck  import System.Posix.Directory (changeWorkingDirectory) import System.Posix.Files-import Control.Exception (bracket_, bracket, throw)-import System.IO.Error import System.Posix.Env-import qualified Control.Exception.Extensible as E+import Control.Exception.Extensible import qualified Data.Map as M import System.IO.HVFS (SystemFS(..)) import Text.JSON@@ -131,7 +131,7 @@ 		reponame = "test repo"  test_add :: Test-test_add = "git-annex add" ~: TestList [basic, sha1dup, subdirs]+test_add = "git-annex add" ~: TestList [basic, sha1dup, sha1unicode, subdirs] 	where 		-- this test case runs in the main repo, to set up a basic 		-- annexed file that later tests will use@@ -158,6 +158,10 @@ 			git_annex "add" [sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed" 			annexed_present sha1annexedfiledup 			annexed_present sha1annexedfile+		sha1unicode = TestCase $ intmpclonerepo $ do+			writeFile sha1annexedfileunicode $ content sha1annexedfileunicode+			git_annex "add" [sha1annexedfileunicode, "--backend=SHA1"] @? "add of unicode filename failed"+			annexed_present sha1annexedfileunicode 		subdirs = TestCase $ intmpclonerepo $ do 			createDirectory "dir" 			writeFile "dir/foo" $ content annexedfile@@ -691,7 +695,7 @@ git_annex :: String -> [String] -> IO Bool git_annex command params = do 	-- catch all errors, including normally fatal errors-	r <- E.try (run)::IO (Either E.SomeException ())+	r <- try (run)::IO (Either SomeException ()) 	case r of 		Right _ -> return True 		Left _ -> return False@@ -757,7 +761,7 @@ 	-- any type of error and change back to cwd before 	-- rethrowing. 	r <- bracket_ (changeToTmpDir dir) (changeWorkingDirectory cwd)-		(E.try (a)::IO (Either E.SomeException ()))+		(try (a)::IO (Either SomeException ())) 	case r of 		Right () -> return () 		Left e -> throw e@@ -828,14 +832,14 @@  checkwritable :: FilePath -> Assertion checkwritable f = do-	r <- try $ writeFile f $ content f+	r <- tryIO $ writeFile f $ content f 	case r of 		Left _ -> assertFailure $ "unable to modify " ++ f 		Right _ -> return ()  checkdangling :: FilePath -> Assertion checkdangling f = do-	r <- try $ readFile f+	r <- tryIO $ readFile f 	case r of 		Left _ -> return () -- expected; dangling link 		Right _ -> assertFailure $ f ++ " was not a dangling link as expected"@@ -919,6 +923,9 @@ sha1annexedfiledup :: String sha1annexedfiledup = "sha1foodup" +sha1annexedfileunicode :: String+sha1annexedfileunicode = "foo¡"+ ingitfile :: String ingitfile = "bar" @@ -928,6 +935,7 @@ 	| f == ingitfile = "normal file content" 	| f == sha1annexedfile ="sha1 annexed file content" 	| f == sha1annexedfiledup = content sha1annexedfile+	| f == sha1annexedfileunicode ="sha1 annexed file content ¡ünicodé!" 	| f == wormannexedfile = "worm annexed file content" 	| otherwise = "unknown file " ++ f