diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -15,5 +15,4 @@
 .hpc
 Utility/Touch.hs
 Utility/StatFS.hs
-Remote/S3.hs
 dist
diff --git a/Annex.hs b/Annex.hs
--- a/Annex.hs
+++ b/Annex.hs
@@ -17,6 +17,10 @@
 	eval,
 	getState,
 	changeState,
+	setFlag,
+	setField,
+	getFlag,
+	getField,
 	gitRepo,
 	inRepo,
 	fromRepo,
@@ -36,9 +40,7 @@
 import Types.Crypto
 import Types.BranchState
 import Types.TrustLevel
-import Types.UUID
 import qualified Utility.Matcher
-import qualified Utility.Format
 import qualified Data.Map as M
 
 -- git-annex's monad
@@ -76,17 +78,16 @@
 	, force :: Bool
 	, fast :: Bool
 	, auto :: Bool
-	, format :: Maybe Utility.Format.Format
 	, branchstate :: BranchState
 	, catfilehandle :: Maybe CatFileHandle
 	, forcebackend :: Maybe String
 	, forcenumcopies :: Maybe Int
-	, toremote :: Maybe String
-	, fromremote :: Maybe String
 	, limit :: Matcher (FilePath -> Annex Bool)
-	, forcetrust :: [(UUID, TrustLevel)]
+	, forcetrust :: TrustMap
 	, trustmap :: Maybe TrustMap
 	, ciphers :: M.Map EncryptedCipher Cipher
+	, flags :: M.Map String Bool
+	, fields :: M.Map String String
 	}
 
 newState :: Git.Repo -> AnnexState
@@ -99,17 +100,16 @@
 	, force = False
 	, fast = False
 	, auto = False
-	, format = Nothing
 	, branchstate = startBranchState
 	, catfilehandle = Nothing
 	, forcebackend = Nothing
 	, forcenumcopies = Nothing
-	, toremote = Nothing
-	, fromremote = Nothing
 	, limit = Left []
-	, forcetrust = []
+	, forcetrust = M.empty
 	, trustmap = Nothing
 	, ciphers = M.empty
+	, flags = M.empty
+	, fields = M.empty
 	}
 
 {- Create and returns an Annex state object for the specified git repo. -}
@@ -133,6 +133,24 @@
  -}
 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 }
+
+{- Sets a field to a value -}
+setField :: String -> String -> Annex ()
+setField field value = changeState $ \s ->
+	s { fields = M.insert field value $ fields s }
+
+{- Checks if a flag was set. -}
+getFlag :: String -> Annex Bool
+getFlag flag = fromMaybe False . M.lookup flag <$> getState flags
+
+{- Gets the value of a field. -}
+getField :: String -> Annex (Maybe String)
+getField field = M.lookup field <$> getState fields
 
 {- Returns the annex's git repository. -}
 gitRepo :: Annex Git.Repo
diff --git a/Annex/Branch.hs b/Annex/Branch.hs
--- a/Annex/Branch.hs
+++ b/Annex/Branch.hs
@@ -6,6 +6,7 @@
  -}
 
 module Annex.Branch (
+	fullname,
 	name,
 	hasOrigin,
 	hasSibling,
@@ -67,15 +68,15 @@
 	return ()
 
 {- Returns the ref of the branch, creating it first if necessary. -}
-getBranch :: Annex (Git.Ref)
-getBranch = maybe (hasOrigin >>= go >>= use) (return) =<< branchsha
+getBranch :: Annex Git.Ref
+getBranch = maybe (hasOrigin >>= go >>= use) return =<< branchsha
 	where
 		go True = do
 			inRepo $ Git.Command.run "branch"
 				[Param $ show name, Param $ show originname]
 			fromMaybe (error $ "failed to create " ++ show name)
 				<$> branchsha
-		go False = withIndex' True $ do
+		go False = withIndex' True $
 			inRepo $ Git.Branch.commit "branch created" fullname []
 		use sha = do
 			setIndexSha sha
diff --git a/Annex/Content.hs b/Annex/Content.hs
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -20,7 +20,8 @@
 	fromAnnex,
 	moveBad,
 	getKeysPresent,
-	saveState
+	saveState,
+	downloadUrl,
 ) where
 
 import System.IO.Error (try)
@@ -36,6 +37,7 @@
 import qualified Annex.Branch
 import Utility.StatFS
 import Utility.FileMode
+import qualified Utility.Url as Url
 import Types.Key
 import Utility.DataUnits
 import Config
@@ -281,3 +283,10 @@
 saveState = do
 	Annex.Queue.flush False
 	Annex.Branch.commit "update"
+
+{- Downloads content from any of a list of urls. -}
+downloadUrl :: [Url.URLString] -> FilePath -> Annex Bool
+downloadUrl urls file = do
+	g <- gitRepo
+	o <- map Param . words <$> getConfig g "web-options" ""
+	liftIO $ anyM (\u -> Url.download u o file) urls
diff --git a/Annex/Ssh.hs b/Annex/Ssh.hs
deleted file mode 100644
--- a/Annex/Ssh.hs
+++ /dev/null
@@ -1,65 +0,0 @@
-{- git-annex remote access with ssh
- -
- - Copyright 2011 Joey Hess <joey@kitenet.net>
- -
- - Licensed under the GNU GPL version 3 or higher.
- -}
-
-module Annex.Ssh where
-
-import Common
-import qualified Git
-import qualified Git.Url
-import Types
-import Config
-import Annex.UUID
-
-{- Generates parameters to ssh to a repository's host and run a command.
- - Caller is responsible for doing any neccessary shellEscaping of the
- - passed command. -}
-sshToRepo :: Git.Repo -> [CommandParam] -> Annex [CommandParam]
-sshToRepo repo sshcmd = do
-	s <- getConfig repo "ssh-options" ""
-	let sshoptions = map Param (words s)
-	let sshport = case Git.Url.port repo of
-		Nothing -> []
-		Just p -> [Param "-p", Param (show p)]
-	let sshhost = Param $ Git.Url.hostuser repo
-	return $ sshoptions ++ sshport ++ [sshhost] ++ sshcmd
-
-{- Generates parameters to run a git-annex-shell command on a remote
- - repository. -}
-git_annex_shell :: Git.Repo -> String -> [CommandParam] -> Annex (Maybe (FilePath, [CommandParam]))
-git_annex_shell r command params
-	| not $ Git.repoIsUrl r = return $ Just (shellcmd, shellopts)
-	| Git.repoIsSsh r = do
-		uuid <- getRepoUUID r
-		sshparams <- sshToRepo r [Param $ sshcmd uuid ]
-		return $ Just ("ssh", sshparams)
-	| otherwise = return Nothing
-	where
-		dir = Git.workTree r
-		shellcmd = "git-annex-shell"
-		shellopts = Param command : File dir : params
-		sshcmd uuid = unwords $
-			shellcmd : map shellEscape (toCommand shellopts) ++
-			uuidcheck uuid
-		uuidcheck NoUUID = []
-		uuidcheck (UUID u) = ["--uuid", u]
-
-{- Uses a supplied function (such as boolSystem) to run a git-annex-shell
- - command on a remote.
- -
- - Or, if the remote does not support running remote commands, returns
- - a specified error value. -}
-onRemote 
-	:: Git.Repo
-	-> (FilePath -> [CommandParam] -> IO a, a)
-	-> String
-	-> [CommandParam]
-	-> Annex a
-onRemote r (with, errorval) command params = do
-	s <- git_annex_shell r command params
-	case s of
-		Just (c, ps) -> liftIO $ with c ps
-		Nothing -> return errorval
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,40 @@
+git-annex (3.20120113) unstable; urgency=low
+
+  * log: Add --gource mode, which generates output usable by gource.
+  * map: Fix display of remote repos
+  * Add annex-trustlevel configuration settings, which can be used to 
+    override the trust level of a remote.
+  * git-annex, git-union-merge: Support GIT_DIR and GIT_WORK_TREE.
+  * Add libghc-testpack-dev to build depends on all arches.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 13 Jan 2012 15:35:17 -0400
+
+git-annex (3.20120106) unstable; urgency=low
+
+  * 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.
+
+ -- Joey Hess <joeyh@debian.org>  Sat, 07 Jan 2012 13:16:23 -0400
+
+git-annex (3.20120105) unstable; urgency=low
+
+  * Added annex-web-options configuration settings, which can be
+    used to provide parameters to whichever of wget or curl git-annex uses
+    (depends on which is available, but most of their important options
+    suitable for use here are the same).
+  * Dotfiles, and files inside dotdirs are not added by "git annex add"
+    unless the dotfile or directory is explicitly listed. So "git annex add ."
+    will add all untracked files in the current directory except for those in
+    dotdirs.
+  * Added quickcheck to build dependencies, and fail if test suite cannot be
+    built.
+  * fsck: Do backend-specific check before checking numcopies is satisfied.
+  * Run gpg with --no-tty. Closes: #654721
+
+ -- Joey Hess <joeyh@debian.org>  Thu, 05 Jan 2012 13:44:12 -0400
+
 git-annex (3.20111231) unstable; urgency=low
 
   * sync: Improved to work well without a central bare repository.
diff --git a/Checks.hs b/Checks.hs
--- a/Checks.hs
+++ b/Checks.hs
@@ -13,30 +13,19 @@
 import Common.Annex
 import Types.Command
 import Init
-import qualified Annex
 
 commonChecks :: [CommandCheck]
-commonChecks = [fromOpt, toOpt, repoExists]
+commonChecks = [repoExists]
 
 repoExists :: CommandCheck
 repoExists = CommandCheck 0 ensureInitialized
 
-fromOpt :: CommandCheck
-fromOpt = CommandCheck 1 $ do
-	v <- Annex.getState Annex.fromremote
-	unless (isNothing v) $ error "cannot use --from with this command"
-
-toOpt :: CommandCheck
-toOpt = CommandCheck 2 $ do
-	v <- Annex.getState Annex.toremote
-	unless (isNothing v) $ error "cannot use --to with this command"
-
 dontCheck :: CommandCheck -> Command -> Command
 dontCheck check cmd = mutateCheck cmd $ \c -> filter (/= check) c
 
 addCheck :: Annex () -> Command -> Command
-addCheck check cmd = mutateCheck cmd $
-	\c -> CommandCheck (length c + 100) check : c
+addCheck check cmd = mutateCheck cmd $ \c ->
+	CommandCheck (length c + 100) check : c
 
 mutateCheck :: Command -> ([CommandCheck] -> [CommandCheck]) -> Command
 mutateCheck cmd@(Command { cmdcheck = c }) a = cmd { cmdcheck = a c }
diff --git a/CmdLine.hs b/CmdLine.hs
--- a/CmdLine.hs
+++ b/CmdLine.hs
@@ -29,7 +29,7 @@
 
 {- Runs the passed command line. -}
 dispatch :: Params -> [Command] -> [Option] -> String -> IO Git.Repo -> IO ()
-dispatch args cmds options header getgitrepo = do
+dispatch args cmds commonoptions header getgitrepo = do
 	setupConsole
 	r <- E.try getgitrepo :: IO (Either E.SomeException Git.Repo)
 	case r of
@@ -41,37 +41,26 @@
 				prepCommand cmd params
 			tryRun state' cmd $ [startup] ++ actions ++ [shutdown]
 	where
-		(flags, cmd, params) = parseCmd args cmds options header
+		(flags, cmd, params) = parseCmd args cmds commonoptions header
 
 {- Parses command line, and returns actions to run to configure flags,
  - the Command being run, and the remaining parameters for the command. -} 
 parseCmd :: Params -> [Command] -> [Option] -> String -> (Flags, Command, Params)
-parseCmd argv cmds options header = check $ getOpt Permute options argv
+parseCmd argv cmds commonoptions header
+	| isNothing name = err "missing command"
+	| null matches = err $ "unknown command " ++ fromJust name
+	| otherwise = check $ getOpt Permute (commonoptions ++ cmdoptions cmd) args
 	where
-		check (_, [], []) = err "missing command"
-		check (flags, name:rest, [])
-			| null matches = err $ "unknown command " ++ name
-			| otherwise = (flags, Prelude.head matches, rest)
-			where
-				matches = filter (\c -> name == cmdname c) cmds
+		(name, args) = findname argv []
+		findname [] c = (Nothing, reverse c)
+		findname (a:as) c
+			| "-" `isPrefixOf` a = findname as (a:c)
+			| otherwise = (Just a, reverse c ++ as)
+		matches = filter (\c -> name == Just (cmdname c)) cmds
+		cmd = Prelude.head matches
+		check (flags, rest, []) = (flags, cmd, rest)
 		check (_, _, errs) = err $ concat errs
-		err msg = error $ msg ++ "\n\n" ++ usage header cmds options
-
-{- Usage message with lists of commands and options. -}
-usage :: String -> [Command] -> [Option] -> String
-usage header cmds options = usageInfo top options ++ commands
-	where
-		top = header ++ "\n\nOptions:"
-		commands = "\nCommands:\n" ++ cmddescs
-		cmddescs = unlines $ map (indent . showcmd) cmds
-		showcmd c =
-			cmdname c ++
-			pad (longest cmdname + 1) (cmdname c) ++
-			cmdparams c ++
-			pad (longest cmdparams + 2) (cmdparams c) ++
-			cmddesc c
-		pad n s = replicate (n - length s) ' '
-		longest f = foldl max 0 $ map (length . f) cmds
+		err msg = error $ msg ++ "\n\n" ++ usage header cmds commonoptions
 
 {- Runs a list of Annex actions. Catches IO errors and continues
  - (but explicitly thrown errors terminate the whole command).
diff --git a/Command.hs b/Command.hs
--- a/Command.hs
+++ b/Command.hs
@@ -8,6 +8,7 @@
 module Command (
 	command,
 	noRepo,
+	withOptions,
 	next,
 	stop,
 	stopUnless,
@@ -25,23 +26,28 @@
 import qualified Backend
 import qualified Annex
 import qualified Git
+import qualified Remote
 import Types.Command as ReExported
+import Types.Option as ReExported
 import Seek as ReExported
 import Checks as ReExported
-import Options as ReExported
+import Usage as ReExported
 import Logs.Trust
-import Logs.Location
 import Config
 
 {- Generates a normal command -}
 command :: String -> String -> [CommandSeek] -> String -> Command
-command = Command Nothing commonChecks
+command = Command [] Nothing commonChecks
 
 {- Adds a fallback action to a command, that will be run if it's used
  - outside a git repository. -}
 noRepo :: IO () -> Command -> Command
 noRepo a c = c { cmdnorepo = Just a }
 
+{- Adds options to a command. -}
+withOptions :: [Option] -> Command -> Command
+withOptions o c = c { cmdoptions = o }
+
 {- For start and perform stages to indicate what step to run next. -}
 next :: a -> Annex (Maybe a)
 next a = return $ Just a
@@ -104,5 +110,5 @@
 		auto False = a
 		auto True = do
 			needed <- getNumCopies numcopiesattr
-			(_, have) <- trustPartition UnTrusted =<< keyLocations key
+			(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key
 			if length have `vs` needed then a else stop
diff --git a/Command/AddUrl.hs b/Command/AddUrl.hs
--- a/Command/AddUrl.hs
+++ b/Command/AddUrl.hs
@@ -12,7 +12,6 @@
 import Common.Annex
 import Command
 import qualified Backend
-import qualified Utility.Url as Url
 import qualified Command.Add
 import qualified Annex
 import qualified Backend.URL
@@ -45,7 +44,7 @@
 	let dummykey = Backend.URL.fromUrl url
 	tmp <- fromRepo $ gitAnnexTmpLocation dummykey
 	liftIO $ createDirectoryIfMissing True (parentDir tmp)
-	stopUnless (liftIO $ Url.download url tmp) $ do
+	stopUnless (downloadUrl [url] tmp) $ do
 		[(backend, _)] <- Backend.chooseBackends [file]
 		k <- Backend.genKey tmp backend
 		case k of
diff --git a/Command/Copy.hs b/Command/Copy.hs
--- a/Command/Copy.hs
+++ b/Command/Copy.hs
@@ -10,17 +10,19 @@
 import Common.Annex
 import Command
 import qualified Command.Move
+import qualified Remote
 
 def :: [Command]
-def = [dontCheck toOpt $ dontCheck fromOpt $ 
-	command "copy" paramPaths seek
+def = [withOptions Command.Move.options $ command "copy" paramPaths seek
 	"copy content of files to/from another repository"]
 
 seek :: [CommandSeek]
-seek = [withNumCopies $ \n -> whenAnnexed $ start n]
+seek = [withField Command.Move.toOption Remote.byName $ \to ->
+		withField Command.Move.fromOption Remote.byName $ \from ->
+			withNumCopies $ \n -> whenAnnexed $ start to from n]
 
 -- A copy is just a move that does not delete the source file.
 -- However, --auto mode avoids unnecessary copies.
-start :: Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
-start numcopies file (key, backend) = autoCopies key (<) numcopies $
-	Command.Move.start False file (key, backend)
+start :: Maybe Remote -> Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
+start to from numcopies file (key, backend) = autoCopies key (<) numcopies $
+	Command.Move.start to from False file (key, backend)
diff --git a/Command/Drop.hs b/Command/Drop.hs
--- a/Command/Drop.hs
+++ b/Command/Drop.hs
@@ -16,21 +16,24 @@
 import Logs.Trust
 import Annex.Content
 import Config
+import qualified Option
 
 def :: [Command]
-def = [dontCheck fromOpt $ command "drop" paramPaths seek
+def = [withOptions [fromOption] $ command "drop" paramPaths seek
 	"indicate content of files not currently wanted"]
 
+fromOption :: Option
+fromOption = Option.field ['f'] "from" paramRemote "drop content from a remote"
+
 seek :: [CommandSeek]
-seek = [withNumCopies $ \n -> whenAnnexed $ start n]
+seek = [withField fromOption Remote.byName $ \from -> withNumCopies $ \n ->
+	whenAnnexed $ start from n]
 
-start :: Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
-start numcopies file (key, _) = autoCopies key (>) numcopies $ do
-	from <- Annex.getState Annex.fromremote
+start :: Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
+start from numcopies file (key, _) = autoCopies key (>) numcopies $ do
 	case from of
 		Nothing -> startLocal file numcopies key
-		Just name -> do
-			remote <- Remote.byName name
+		Just remote -> do
 			u <- getUUID
 			if Remote.uuid remote == u
 				then startLocal file numcopies key
diff --git a/Command/DropUnused.hs b/Command/DropUnused.hs
--- a/Command/DropUnused.hs
+++ b/Command/DropUnused.hs
@@ -15,13 +15,15 @@
 import qualified Command.Drop
 import qualified Remote
 import qualified Git
+import qualified Option
 import Types.Key
 
 type UnusedMap = M.Map String Key
 
 def :: [Command]
-def = [dontCheck fromOpt $ command "dropunused" (paramRepeating paramNumber)
-	seek "drop unused file content"]
+def = [withOptions [Command.Drop.fromOption] $
+	command "dropunused" (paramRepeating paramNumber)
+		seek "drop unused file content"]
 
 seek :: [CommandSeek]
 seek = [withUnusedMaps]
@@ -50,14 +52,14 @@
 					next $ a key
 
 perform :: Key -> CommandPerform
-perform key = maybe droplocal dropremote =<< Annex.getState Annex.fromremote
+perform key = maybe droplocal dropremote =<< Remote.byName =<< from
 	where
-		dropremote name = do
-			r <- Remote.byName name
+		dropremote r = do
 			showAction $ "from " ++ Remote.name r
 			ok <- Remote.removeKey r key
 			next $ Command.Drop.cleanupRemote key r ok
 		droplocal = Command.Drop.performLocal key (Just 0) -- force drop
+		from = Annex.getField $ Option.name Command.Drop.fromOption
 
 performOther :: (Key -> Git.Repo -> FilePath) -> Key -> CommandPerform
 performOther filespec key = do
diff --git a/Command/Find.hs b/Command/Find.hs
--- a/Command/Find.hs
+++ b/Command/Find.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010 Joey Hess <joey@kitenet.net>
+ - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -17,21 +17,34 @@
 import qualified Utility.Format
 import Utility.DataUnits
 import Types.Key
+import qualified Option
 
 def :: [Command]
-def = [command "find" paramPaths seek "lists available files"]
+def = [withOptions [formatOption, print0Option] $
+	command "find" paramPaths seek "lists available files"]
 
+formatOption :: Option
+formatOption = Option.field [] "format" paramFormat "control format of output"
+
+print0Option :: Option
+print0Option = Option.Option [] ["print0"] (Option.NoArg set)
+	"terminate output with null"
+	where
+		set = Annex.setField (Option.name formatOption) "${file}\0"
+
 seek :: [CommandSeek]
-seek = [withFilesInGit $ whenAnnexed start]
+seek = [withField formatOption formatconverter $ \f ->
+		withFilesInGit $ whenAnnexed $ start f]
+	where
+		formatconverter = return . maybe Nothing (Just . Utility.Format.gen)
 
-start :: FilePath -> (Key, Backend) -> CommandStart
-start file (key, _) = do
+start :: Maybe Utility.Format.Format -> FilePath -> (Key, Backend) -> CommandStart
+start format file (key, _) = do
 	-- only files inAnnex are shown, unless the user has requested
 	-- others via a limit
 	whenM (liftM2 (||) limited (inAnnex key)) $
-		unlessM (showFullJSON vars) $ do
-			f <- Annex.getState Annex.format
-			case f of
+		unlessM (showFullJSON vars) $
+			case format of
 				Nothing -> liftIO $ putStrLn file
 				Just formatter -> liftIO $ putStr $
 					Utility.Format.format formatter $
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -40,8 +40,8 @@
 	-- order matters
 	[ verifyLocationLog key file
 	, checkKeySize key
-	, checkKeyNumCopies key file numcopies
 	, checkBackend backend key
+	, checkKeyNumCopies key file numcopies
 	]
 
 {- To fsck a bare repository, fsck each key in the location log. -}
@@ -52,7 +52,7 @@
 		go True = do
 			unless (null params) $
 				error "fsck should be run without parameters in a bare repository"
-			prepStart a loggedKeys
+			map a <$> loggedKeys
 
 startBare :: Key -> CommandStart
 startBare key = case Backend.maybeLookupBackendName (Types.Key.keyBackendName key) of
@@ -93,7 +93,7 @@
 			preventWrite (parentDir f)
 
 	u <- getUUID
-        uuids <- keyLocations key
+	uuids <- Remote.keyLocations key
 
 	case (present, u `elem` uuids) of
 		(True, False) -> do
@@ -142,7 +142,7 @@
 checkKeyNumCopies :: Key -> FilePath -> Maybe Int -> Annex Bool
 checkKeyNumCopies key file numcopies = do
 	needed <- getNumCopies numcopies
-	(untrustedlocations, safelocations) <- trustPartition UnTrusted =<< keyLocations key
+	(untrustedlocations, safelocations) <- trustPartition UnTrusted =<< Remote.keyLocations key
 	let present = length safelocations
 	if present < needed
 		then do
diff --git a/Command/Get.hs b/Command/Get.hs
--- a/Command/Get.hs
+++ b/Command/Get.hs
@@ -9,27 +9,25 @@
 
 import Common.Annex
 import Command
-import qualified Annex
 import qualified Remote
 import Annex.Content
 import qualified Command.Move
 
 def :: [Command]
-def = [dontCheck fromOpt $ command "get" paramPaths seek
+def = [withOptions [Command.Move.fromOption] $ command "get" paramPaths seek
 	"make content of annexed files available"]
 
 seek :: [CommandSeek]
-seek = [withNumCopies $ \n -> whenAnnexed $ start n]
+seek = [withField Command.Move.fromOption Remote.byName $ \from ->
+	withNumCopies $ \n -> whenAnnexed $ start from n]
 
-start :: Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
-start numcopies file (key, _) = stopUnless (not <$> inAnnex key) $
+start :: Maybe Remote -> Maybe Int -> FilePath -> (Key, Backend) -> CommandStart
+start from numcopies file (key, _) = stopUnless (not <$> inAnnex key) $
 	autoCopies key (<) numcopies $ do
-		from <- Annex.getState Annex.fromremote
 		case from of
 			Nothing -> go $ perform key
-			Just name -> do
+			Just src -> do
 				-- get --from = copy --from
-				src <- Remote.byName name
 				stopUnless (Command.Move.fromOk src key) $
 					go $ Command.Move.fromPerform src False key
 	where
diff --git a/Command/Log.hs b/Command/Log.hs
new file mode 100644
--- /dev/null
+++ b/Command/Log.hs
@@ -0,0 +1,172 @@
+{- git-annex command
+ -
+ - Copyright 2012 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Command.Log where
+
+import qualified Data.Set as S
+import qualified Data.Map as M
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.Time.Clock.POSIX
+import Data.Time
+import System.Locale
+import Data.Char
+
+import Common.Annex
+import Command
+import qualified Logs.Location
+import qualified Logs.Presence
+import Annex.CatFile
+import qualified Annex.Branch
+import qualified Git
+import Git.Command
+import qualified Remote
+import qualified Option
+import qualified Annex
+
+data RefChange = RefChange 
+	{ changetime :: POSIXTime
+	, oldref :: Git.Ref
+	, newref :: Git.Ref
+	}
+
+type Outputter = Bool -> POSIXTime -> [UUID] -> Annex ()
+
+def :: [Command]
+def = [withOptions options $
+	command "log" paramPaths seek "shows location log"]
+
+options :: [Option]
+options = passthruOptions ++ [gourceOption]
+
+passthruOptions :: [Option]
+passthruOptions = map odate ["since", "after", "until", "before"] ++
+	[ Option.field ['n'] "max-count" paramNumber
+		"limit number of logs displayed"
+	]
+	where
+		odate n = Option.field [] n paramDate $
+			"show log " ++ n ++ " date"
+
+gourceOption :: Option
+gourceOption = Option.flag [] "gource" "format output for gource"
+
+seek :: [CommandSeek]
+seek = [withValue (Remote.uuidDescriptions) $ \m ->
+	withValue (liftIO getCurrentTimeZone) $ \zone ->
+	withValue (concat <$> mapM getoption passthruOptions) $ \os ->
+	withFlag gourceOption $ \gource ->
+	withFilesInGit $ whenAnnexed $ start m zone os gource]
+	where
+		getoption o = maybe [] (use o) <$>
+			Annex.getField (Option.name o)
+		use o v = [Param ("--" ++ Option.name o), Param v]
+
+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
+	liftIO Git.Command.reap
+	stop
+	where
+		output
+			| gource = gourceOutput lookupdescription file
+			| otherwise = normalOutput lookupdescription file zone
+		lookupdescription u = fromMaybe (fromUUID u) $ M.lookup u m
+
+showLog :: Outputter -> [RefChange] -> Annex ()
+showLog outputter ps = do
+	sets <- mapM (getset newref) ps
+	previous <- maybe (return genesis) (getset oldref) (lastMaybe ps)
+	sequence_ $ compareChanges outputter $ sets ++ [previous]
+	where
+		genesis = (0, S.empty)
+		getset select change = do
+			s <- S.fromList <$> get (select change)
+			return (changetime change, s)
+		get ref = map toUUID . Logs.Presence.getLog . L.unpack <$>
+			catObject ref
+
+normalOutput :: (UUID -> String) -> FilePath -> TimeZone -> Outputter
+normalOutput lookupdescription file zone present ts us = do
+	liftIO $ mapM_ (putStrLn . format) us
+	where
+		time = showTimeStamp zone ts
+		addel = if present then "+" else "-"
+		format u = unwords [ addel, time, file, "|", 
+			fromUUID u ++ " -- " ++ lookupdescription u ]
+
+gourceOutput :: (UUID -> String) -> FilePath -> Outputter
+gourceOutput lookupdescription file present ts us = do
+	liftIO $ mapM_ (putStrLn . intercalate "|" . format) us
+	where
+		time = takeWhile isDigit $ show ts
+		addel = if present then "A" else "M"
+		format u = [ time, lookupdescription u, addel, file ]
+
+{- Generates a display of the changes (which are ordered with newest first),
+ - by comparing each change with the previous change.
+ - Uses a formatter to generate a display of items that are added and
+ - removed. -}
+compareChanges :: Ord a => (Bool -> POSIXTime -> [a] -> b) -> [(POSIXTime, S.Set a)] -> [b]
+compareChanges format changes = concatMap diff $ zip changes (drop 1 changes)
+	where
+		diff ((ts, new), (_, old)) =
+			[format True ts added, format False ts removed]
+			where
+				added = S.toList $ S.difference new old
+				removed = S.toList $ S.difference old new
+
+{- Gets the git log for a given location log file.
+ -
+ - This is complicated by git log using paths relative to the current
+ - directory, even when looking at files in a different branch. A wacky
+ - relative path to the log file has to be used.
+ -
+ - The --remove-empty is a significant optimisation. It relies on location
+ - log files never being deleted in normal operation. Letting git stop
+ - once the location log file is gone avoids it checking all the way back
+ - to commit 0 to see if it used to exist, so generally speeds things up a
+ - *lot* for newish files. -}
+getLog :: Key -> [CommandParam] -> Annex [String]
+getLog key os = do
+	top <- fromRepo Git.workTree
+	p <- liftIO $ relPathCwdToFile top
+	let logfile = p </> Logs.Location.logFile key
+	inRepo $ pipeNullSplit $
+		[ Params "log -z --pretty=format:%ct --raw --abbrev=40"
+		, Param "--remove-empty"
+		] ++ os ++
+		[ Param $ show Annex.Branch.fullname
+		, Param "--"
+		, Param logfile
+		]
+
+readLog :: [String] -> [RefChange]
+readLog = mapMaybe (parse . lines)
+	where
+		parse (ts:raw:[]) = let (old, new) = parseRaw raw in
+			Just RefChange
+				{ changetime = parseTimeStamp ts
+				, oldref = old
+				, newref = new
+				}
+		parse _ = Nothing
+
+-- Parses something like ":100644 100644 oldsha newsha M"
+parseRaw :: String -> (Git.Ref, Git.Ref)
+parseRaw l = (Git.Ref oldsha, Git.Ref newsha)
+	where
+		ws = words l
+		oldsha = ws !! 2
+		newsha = ws !! 3
+
+parseTimeStamp :: String -> POSIXTime
+parseTimeStamp = utcTimeToPOSIXSeconds . fromMaybe (error "bad timestamp") .
+	parseTime defaultTimeLocale "%s"
+
+showTimeStamp :: TimeZone -> POSIXTime -> String
+showTimeStamp zone = show . utcToLocalTime zone . posixSecondsToUTCTime
diff --git a/Command/Map.hs b/Command/Map.hs
--- a/Command/Map.hs
+++ b/Command/Map.hs
@@ -20,7 +20,7 @@
 import Annex.UUID
 import Logs.UUID
 import Logs.Trust
-import Annex.Ssh
+import Remote.Helper.Ssh
 import qualified Utility.Dot as Dot
 
 -- a link from the first repository to the second (its remote)
@@ -155,6 +155,7 @@
 absRepo :: Git.Repo -> Git.Repo -> Annex Git.Repo
 absRepo reference r
 	| Git.repoIsUrl reference = return $ Git.Construct.localToUrl reference r
+	| Git.repoIsUrl r = return r
 	| otherwise = liftIO $ Git.Construct.fromAbsPath =<< absPath (Git.workTree r)
 
 {- Checks if two repos are the same. -}
diff --git a/Command/Move.hs b/Command/Move.hs
--- a/Command/Move.hs
+++ b/Command/Move.hs
@@ -14,28 +14,33 @@
 import Annex.Content
 import qualified Remote
 import Annex.UUID
+import qualified Option
 
 def :: [Command]
-def = [dontCheck toOpt $ dontCheck fromOpt $
-	command "move" paramPaths seek
+def = [withOptions options $ command "move" paramPaths seek
 	"move content of files to/from another repository"]
 
+fromOption :: Option
+fromOption = Option.field ['f'] "from" paramRemote "source remote"
+
+toOption :: Option
+toOption = Option.field ['t'] "to" paramRemote "destination remote"
+
+options :: [Option]
+options = [fromOption, toOption]
+
 seek :: [CommandSeek]
-seek = [withFilesInGit $ whenAnnexed $ start True]
+seek = [withField toOption Remote.byName $ \to ->
+		withField fromOption Remote.byName $ \from ->
+			withFilesInGit $ whenAnnexed $ start to from True]
 
-start :: Bool -> FilePath -> (Key, Backend) -> CommandStart
-start move file (key, _) = do
+start :: Maybe Remote -> Maybe Remote -> Bool -> FilePath -> (Key, Backend) -> CommandStart
+start to from move file (key, _) = do
 	noAuto
-	to <- Annex.getState Annex.toremote
-	from <- Annex.getState Annex.fromremote
 	case (from, to) of
 		(Nothing, Nothing) -> error "specify either --from or --to"
-		(Nothing, Just name) -> do
-			dest <- Remote.byName name
-			toStart dest move file key
-		(Just name, Nothing) -> do
-			src <- Remote.byName name
-			fromStart src move file key
+		(Nothing, Just dest) -> toStart dest move file key
+		(Just src, Nothing) -> fromStart src move file key
 		(_ ,  _) -> error "only one of --from or --to can be specified"
 	where
 		noAuto = when move $ whenM (Annex.getState Annex.auto) $ error
diff --git a/Command/Status.hs b/Command/Status.hs
--- a/Command/Status.hs
+++ b/Command/Status.hs
@@ -144,9 +144,9 @@
 
 backend_usage :: Stat
 backend_usage = stat "backend usage" $ nojson $
-	usage <$> cachedKeysReferenced <*> cachedKeysPresent
+	calc <$> cachedKeysReferenced <*> cachedKeysPresent
 	where
-		usage a b = pp "" $ reverse . sort $ map swap $ splits $ S.toList $ S.union a b
+		calc a b = pp "" $ reverse . sort $ map swap $ splits $ S.toList $ S.union a b
 		splits :: [Key] -> [(String, Integer)]
 		splits ks = M.toList $ M.fromListWith (+) $ map tcount ks
 		tcount k = (keyBackendName k, 1)
diff --git a/Command/Sync.hs b/Command/Sync.hs
--- a/Command/Sync.hs
+++ b/Command/Sync.hs
@@ -61,7 +61,7 @@
 		wanted
 			| null rs = good =<< available
 			| otherwise = listed
-		listed = mapM Remote.byName rs
+		listed = catMaybes <$> mapM (Remote.byName . Just) rs
 		available = filter nonspecial <$> Remote.enabledRemoteList
 		good = filterM $ Remote.Git.repoAvail . Types.Remote.repo
 		nonspecial r = Types.Remote.remotetype r == Remote.Git.remote
diff --git a/Command/Unused.hs b/Command/Unused.hs
--- a/Command/Unused.hs
+++ b/Command/Unused.hs
@@ -27,19 +27,23 @@
 import qualified Backend
 import qualified Remote
 import qualified Annex.Branch
+import qualified Option
 import Annex.CatFile
 
 def :: [Command]
-def = [dontCheck fromOpt $ command "unused" paramNothing seek
+def = [withOptions [fromOption] $ command "unused" paramNothing seek
 	"look for unused file content"]
 
+fromOption :: Option
+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
 start = do
-	from <- Annex.getState Annex.fromremote
+	from <- Annex.getField $ Option.name fromOption
 	let (name, action) = case from of
 		Nothing -> (".", checkUnused)
 		Just "." -> (".", checkUnused)
@@ -63,7 +67,7 @@
 
 checkRemoteUnused :: String -> CommandPerform
 checkRemoteUnused name = do
-	checkRemoteUnused' =<< Remote.byName name
+	checkRemoteUnused' =<< fromJust <$> Remote.byName (Just name)
 	next $ return True
 
 checkRemoteUnused' :: Remote -> Annex ()
diff --git a/Command/Whereis.hs b/Command/Whereis.hs
--- a/Command/Whereis.hs
+++ b/Command/Whereis.hs
@@ -8,7 +8,6 @@
 module Command.Whereis where
 
 import Common.Annex
-import Logs.Location
 import Command
 import Remote
 import Logs.Trust
diff --git a/Config.hs b/Config.hs
--- a/Config.hs
+++ b/Config.hs
@@ -30,7 +30,7 @@
 	def' <- fromRepo $ Git.Config.get ("annex." ++ key) def
 	fromRepo $ Git.Config.get (remoteConfig r key) def'
 
-{- Looks up a per-remote config setting in git config. -}
+{- A per-remote config setting in git config. -}
 remoteConfig :: Git.Repo -> ConfigKey -> String
 remoteConfig r key = "remote." ++ fromMaybe "" (Git.remoteName r) ++ ".annex-" ++ key
 
@@ -67,9 +67,7 @@
 	, semiCheapRemoteCost + encryptedRemoteCostAdj < expensiveRemoteCost
 	]
 
-{- Checks if a repo should be ignored, based either on annex-ignore
- - setting, or on command-line options. Allows command-line to override
- - annex-ignore. -}
+{- Checks if a repo should be ignored. -}
 repoNotIgnored :: Git.Repo -> Annex Bool
 repoNotIgnored r = not . Git.configTrue <$> getConfig r "ignore" "false"
 
@@ -83,3 +81,7 @@
 			readMaybe <$> fromRepo (Git.Config.get config "1")
 		perhaps fallback = maybe fallback (return . id)
 		config = "annex.numcopies"
+
+{- Gets the trust level set for a remote in git config. -}
+getTrustLevel :: Git.Repo -> Annex (Maybe String)
+getTrustLevel r = fromRepo $ Git.Config.getMaybe $ remoteConfig r "trustlevel"
diff --git a/Git.hs b/Git.hs
--- a/Git.hs
+++ b/Git.hs
@@ -29,7 +29,7 @@
 
 import qualified Data.Map as M
 import Data.Char
-import Network.URI (uriPath, uriScheme)
+import Network.URI (uriPath, uriScheme, unEscapeString)
 
 import Common
 import Git.Types
@@ -107,7 +107,7 @@
  -
  - Note that for URL repositories, this is the path on the remote host. -}
 workTree :: Repo -> FilePath
-workTree Repo { location = Url u } = uriPath u
+workTree Repo { location = Url u } = unEscapeString $ uriPath u
 workTree Repo { location = Dir d } = d
 workTree Repo { location = Unknown } = undefined
 
diff --git a/Git/Config.hs b/Git/Config.hs
--- a/Git/Config.hs
+++ b/Git/Config.hs
@@ -20,6 +20,10 @@
 get :: String -> String -> Repo -> String
 get key defaultValue repo = M.findWithDefault defaultValue key (config repo)
 
+{- Returns a single git config setting, if set. -}
+getMaybe :: String -> Repo -> Maybe String
+getMaybe key repo = M.lookup key (config repo)
+
 {- Runs git config and populates a repo with its config. -}
 read :: Repo -> IO Repo
 read repo@(Repo { location = Dir d }) = do
diff --git a/Git/Construct.hs b/Git/Construct.hs
--- a/Git/Construct.hs
+++ b/Git/Construct.hs
@@ -6,6 +6,7 @@
  -}
 
 module Git.Construct (
+	fromCurrent,
 	fromCwd,
 	fromAbsPath,
 	fromUrl,
@@ -19,6 +20,8 @@
 ) where
 
 import System.Posix.User
+import System.Posix.Env (getEnv, unsetEnv)
+import System.Posix.Directory (changeWorkingDirectory)
 import qualified Data.Map as M hiding (map, split)
 import Network.URI
 
@@ -27,13 +30,40 @@
 import Git
 import qualified Git.Url as Url
 
-{- Finds the current git repository, which may be in a parent directory. -}
+{- Finds the current git repository.
+ -
+ - GIT_DIR can override the location of the .git directory.
+ -
+ - When GIT_WORK_TREE is set, chdir to it, so that anything using
+ - this repository runs in the right location. However, this chdir is
+ - done after determining GIT_DIR; git does not let GIT_WORK_TREE
+ - influence the git directory.
+ -
+ - Both environment variables are unset, to avoid confusing other git
+ - commands that also look at them. This would particularly be a problem
+ - when GIT_DIR is relative and we chdir for GIT_WORK_TREE. Instead,
+ - the Git module passes --work-tree and --git-dir to git commands it runs.
+ -}
+fromCurrent :: IO Repo
+fromCurrent = do
+	r <- maybe fromCwd fromPath =<< getEnv "GIT_DIR"
+	maybe (return ()) changeWorkingDirectory =<< getEnv "GIT_WORK_TREE"
+	unsetEnv "GIT_DIR"
+	unsetEnv "GIT_WORK_TREE"
+	return r
+
+{- Finds the git repository used for the Cwd, which may be in a parent
+ - directory. -}
 fromCwd :: IO Repo
 fromCwd = getCurrentDirectory >>= seekUp isRepoTop >>= maybe norepo makerepo
 	where
 		makerepo = return . newFrom . Dir
 		norepo = error "Not in a git repository."
 
+{- Local Repo constructor, accepts a relative or absolute path. -}
+fromPath :: FilePath -> IO Repo
+fromPath dir = fromAbsPath =<< absPath dir
+
 {- Local Repo constructor, requires an absolute path to the repo be
  - specified. -}
 fromAbsPath :: FilePath -> IO Repo
@@ -60,14 +90,23 @@
 	where
 		ret = return . newFrom . Dir
 
-{- Remote Repo constructor. Throws exception on invalid url. -}
+{- Remote Repo constructor. Throws exception on invalid url.
+ -
+ - Git is somewhat forgiving about urls to repositories, allowing
+ - eg spaces that are not normally allowed unescaped in urls.
+ -}
 fromUrl :: String -> IO Repo
 fromUrl url
+	| not (isURI url) = fromUrlStrict $ escapeURIString isUnescapedInURI url
+	| otherwise = fromUrlStrict url
+
+fromUrlStrict :: String -> IO Repo
+fromUrlStrict url
 	| startswith "file://" url = fromAbsPath $ uriPath u
 	| otherwise = return $ newFrom $ Url u
-		where
-			u = fromMaybe bad $ parseURI url
-			bad = error $ "bad url " ++ url
+	where
+		u = fromMaybe bad $ parseURI url
+		bad = error $ "bad url " ++ url
 
 {- Creates a repo that has an unknown location. -}
 fromUnknown :: IO Repo
@@ -117,7 +156,7 @@
 	where
 		gen v	
 			| scpstyle v = fromUrl $ scptourl v
-			| isURI v = fromUrl v
+			| urlstyle v = fromUrl v
 			| otherwise = fromRemotePath v repo
 		-- insteadof config can rewrite remote location
 		calcloc l
@@ -137,6 +176,7 @@
 						M.toList $ fullconfig repo
 				splitconfigs (k, vs) = map (\v -> (k, v)) vs
 				(prefix, suffix) = ("url." , ".insteadof")
+		urlstyle v = isURI v || ":" `isInfixOf` v && "//" `isInfixOf` v
 		-- git remotes can be written scp style -- [user@]host:dir
 		scpstyle v = ":" `isInfixOf` v && not ("//" `isInfixOf` v)
 		scptourl v = "ssh://" ++ host ++ slash dir
diff --git a/Git/Sha.hs b/Git/Sha.hs
--- a/Git/Sha.hs
+++ b/Git/Sha.hs
@@ -34,3 +34,6 @@
 {- Size of a git sha. -}
 shaSize :: Int
 shaSize = 40
+
+nullSha :: Ref		
+nullSha = Ref $ replicate shaSize '0'
diff --git a/Git/UnionMerge.hs b/Git/UnionMerge.hs
--- a/Git/UnionMerge.hs
+++ b/Git/UnionMerge.hs
@@ -103,14 +103,13 @@
  - 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
+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) =<<
 		calcMerge . zip shas <$> mapM getcontents shas
 	where
 		[_colonmode, _bmode, asha, bsha, _status] = words info
-		nullsha = Ref $ replicate shaSize '0'
 		getcontents s = L.lines <$> catObject h s
 		use sha = return $ Just $ update_index_line sha file
 
diff --git a/GitAnnex.hs b/GitAnnex.hs
--- a/GitAnnex.hs
+++ b/GitAnnex.hs
@@ -18,7 +18,7 @@
 import qualified Annex
 import qualified Remote
 import qualified Limit
-import qualified Utility.Format
+import qualified Option
 
 import qualified Command.Add
 import qualified Command.Unannex
@@ -41,6 +41,7 @@
 import qualified Command.PreCommit
 import qualified Command.Find
 import qualified Command.Whereis
+import qualified Command.Log
 import qualified Command.Merge
 import qualified Command.Status
 import qualified Command.Migrate
@@ -85,6 +86,7 @@
 	, Command.DropUnused.def
 	, Command.Find.def
 	, Command.Whereis.def
+	, Command.Log.def
 	, Command.Merge.def
 	, Command.Status.def
 	, Command.Migrate.def
@@ -94,12 +96,8 @@
 	]
 
 options :: [Option]
-options = commonOptions ++
-	[ Option ['t'] ["to"] (ReqArg setto paramRemote)
-		"specify to where to transfer content"
-	, Option ['f'] ["from"] (ReqArg setfrom paramRemote)
-		"specify from where to transfer content"
-	, Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber)
+options = Option.common ++
+	[ Option ['N'] ["numcopies"] (ReqArg setnumcopies paramNumber)
 		"override default number of copies"
 	, Option [] ["trust"] (ReqArg (Remote.forceTrust Trusted) paramRemote)
 		"override trust setting"
@@ -109,10 +107,6 @@
 		"override trust setting to untrusted"
 	, Option ['c'] ["config"] (ReqArg setgitconfig "NAME=VALUE")
 		"override git configuration setting"
-	, Option [] ["print0"] (NoArg setprint0)
-		"terminate output with null"
-	, Option [] ["format"] (ReqArg setformat paramFormat)
-		"control format of output"
 	, Option ['x'] ["exclude"] (ReqArg Limit.addExclude paramGlob)
 		"skip files matching the glob pattern"
 	, Option ['I'] ["include"] (ReqArg Limit.addInclude paramGlob)
@@ -123,13 +117,9 @@
 		"skip files with fewer copies"
 	, Option ['B'] ["inbackend"] (ReqArg Limit.addInBackend paramName)
 		"skip files not using a key-value backend"
-	] ++ matcherOptions
+	] ++ Option.matcher
 	where
-		setto v = Annex.changeState $ \s -> s { Annex.toremote = Just v }
-		setfrom v = Annex.changeState $ \s -> s { Annex.fromremote = Just v }
 		setnumcopies v = Annex.changeState $ \s -> s {Annex.forcenumcopies = readMaybe v }
-		setformat v = Annex.changeState $ \s -> s { Annex.format = Just $ Utility.Format.gen v }
-		setprint0 = setformat "${file}\0"
 		setgitconfig :: String -> Annex ()
 		setgitconfig v = do
 			newg <- inRepo $ Git.Config.store v
@@ -139,4 +129,4 @@
 header = "Usage: git-annex command [option ..]"
 
 run :: [String] -> IO ()
-run args = dispatch args cmds options header Git.Construct.fromCwd
+run args = dispatch args cmds options header Git.Construct.fromCurrent
diff --git a/INSTALL b/INSTALL
--- a/INSTALL
+++ b/INSTALL
@@ -6,6 +6,9 @@
 * [[Fedora]]
 * [[FreeBSD]]
 * [[openSUSE]]
+* [[ArchLinux]]
+* [[NixOS]]
+* Windows: [[sorry, not possible yet|todo/windows_support]]
 
 ## Using cabal
 
@@ -29,7 +32,7 @@
   * [TestPack](http://hackage.haskell.org/cgi-bin/hackage-scripts/package/testpack)
   * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck)
   * [HTTP](http://hackage.haskell.org/package/HTTP)
-  * [hS3](http://hackage.haskell.org/package/hS3) (optional, but recommended)
+  * [hS3](http://hackage.haskell.org/package/hS3)
   * [json](http://hackage.haskell.org/package/json)
 * Shell commands
   * [git](http://git-scm.com/)
diff --git a/Limit.hs b/Limit.hs
--- a/Limit.hs
+++ b/Limit.hs
@@ -15,7 +15,6 @@
 import qualified Utility.Matcher
 import qualified Remote
 import qualified Backend
-import Logs.Location
 import Annex.Content
 
 type Limit = Utility.Matcher.Token (FilePath -> Annex Bool)
@@ -78,7 +77,7 @@
 		handle a (Just (key, _)) = a key
 		inremote key = do
 			u <- Remote.nameToUUID name
-			us <- keyLocations key
+			us <- Remote.keyLocations key
 			return $ u `elem` us
 
 {- Adds a limit to skip files not believed to have the specified number
@@ -92,7 +91,7 @@
 		check n = Backend.lookupFile >=> handle n
 		handle _ Nothing = return False
 		handle n (Just (key, _)) = do
-			us <- keyLocations key
+			us <- Remote.keyLocations key
 			return $ length us >= n
 
 {- Adds a limit to skip files not using a specified key-value backend. -}
diff --git a/Logs/Location.hs b/Logs/Location.hs
--- a/Logs/Location.hs
+++ b/Logs/Location.hs
@@ -16,8 +16,7 @@
 module Logs.Location (
 	LogStatus(..),
 	logChange,
-	readLog,
-	keyLocations,
+	loggedLocations,
 	loggedKeys,
 	loggedKeysFor,
 	logFile,
@@ -27,7 +26,6 @@
 import Common.Annex
 import qualified Annex.Branch
 import Logs.Presence
-import Logs.Trust
 
 {- Log a change in the presence of a key's value in a repository. -}
 logChange :: Key -> UUID -> LogStatus -> Annex ()
@@ -36,13 +34,9 @@
 
 {- Returns a list of repository UUIDs that, according to the log, have
  - the value of a key.
- -
- - Dead repositories are skipped.
  -}
-keyLocations :: Key -> Annex [UUID]
-keyLocations key = do
-	l <- map toUUID <$> (currentLog . logFile) key
-	snd <$> trustPartition DeadTrusted l
+loggedLocations :: Key -> Annex [UUID]
+loggedLocations key = map toUUID <$> (currentLog . logFile) key
 
 {- Finds all keys that have location log information.
  - (There may be duplicate keys in the list.) -}
@@ -57,7 +51,7 @@
 		{- This should run strictly to avoid the filterM
 		 - building many thunks containing keyLocations data. -}
 		isthere k = do
-			us <- keyLocations k
+			us <- loggedLocations k
 			let !there = u `elem` us
 			return there
 
diff --git a/Logs/Presence.hs b/Logs/Presence.hs
--- a/Logs/Presence.hs
+++ b/Logs/Presence.hs
@@ -13,14 +13,15 @@
 
 module Logs.Presence (
 	LogStatus(..),
+	LogLine,
 	addLog,
 	readLog,
+	getLog,
 	parseLog,
 	showLog,
 	logNow,
 	compactLog,
 	currentLog,
-	LogLine
 ) where
 
 import Data.Time.Clock.POSIX
@@ -79,6 +80,10 @@
 {- Reads a log and returns only the info that is still in effect. -}
 currentLog :: FilePath -> Annex [String]
 currentLog file = map info . filterPresent <$> readLog file
+
+{- Given a log, returns only the info that is are still in effect. -}
+getLog :: String -> [String]
+getLog = map info . filterPresent . parseLog
 
 {- Returns the info from LogLines that are in effect. -}
 filterPresent :: [LogLine] -> [LogLine]
diff --git a/Logs/Trust.hs b/Logs/Trust.hs
--- a/Logs/Trust.hs
+++ b/Logs/Trust.hs
@@ -1,6 +1,6 @@
-{- git-annex trust
+{- git-annex trust log
  -
- - Copyright 2010 Joey Hess <joey@kitenet.net>
+ - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -9,7 +9,7 @@
 	TrustLevel(..),
 	trustGet,
 	trustSet,
-	trustPartition
+	trustPartition,
 ) where
 
 import qualified Data.Map as M
@@ -20,6 +20,9 @@
 import qualified Annex.Branch
 import qualified Annex
 import Logs.UUIDBased
+import Remote.List
+import Config
+import qualified Types.Remote
 
 {- Filename of trust.log. -}
 trustLog :: FilePath
@@ -32,6 +35,15 @@
 trustGet :: TrustLevel -> Annex [UUID]
 trustGet level = M.keys . M.filter (== level) <$> trustMap
 
+{- Changes the trust level for a uuid in the trustLog. -}
+trustSet :: UUID -> TrustLevel -> Annex ()
+trustSet uuid@(UUID _) level = do
+	ts <- liftIO getPOSIXTime
+	Annex.Branch.change trustLog $
+		showLog showTrust . changeLog ts uuid level . parseLog (Just . parseTrust)
+	Annex.changeState $ \s -> s { Annex.trustmap = Nothing }
+trustSet NoUUID _ = error "unknown UUID; cannot modify trust level"
+
 {- Partitions a list of UUIDs to those matching a TrustLevel and not. -}
 trustPartition :: TrustLevel -> [UUID] -> Annex ([UUID], [UUID])
 trustPartition level ls
@@ -46,18 +58,30 @@
 		return $ partition (`elem` candidates) ls
 
 {- Read the trustLog into a map, overriding with any
- - values from forcetrust. The map is cached for speed. -}
+ - values from forcetrust or the git config. The map is cached for speed. -}
 trustMap :: Annex TrustMap
 trustMap = do
 	cached <- Annex.getState Annex.trustmap
 	case cached of
 		Just m -> return m
 		Nothing -> do
-			overrides <- M.fromList <$> Annex.getState Annex.forcetrust
-			m <- (M.union overrides . simpleMap . parseLog (Just . parseTrust)) <$>
+			overrides <- Annex.getState Annex.forcetrust
+			logged <- simpleMap . parseLog (Just . parseTrust) <$>
 				Annex.Branch.get trustLog
+			configured <- M.fromList . catMaybes <$>
+				(mapM configuredtrust =<< remoteList)
+			let m = M.union overrides $ M.union configured logged
 			Annex.changeState $ \s -> s { Annex.trustmap = Just m }
 			return m
+	where
+		configuredtrust r =
+			maybe Nothing (\l -> Just (Types.Remote.uuid r, l)) <$>
+				maybe Nothing convert <$>
+					getTrustLevel (Types.Remote.repo r)
+		convert "trusted" = Just Trusted
+		convert "untrusted" = Just UnTrusted
+		convert "semitrusted" = Just SemiTrusted
+		convert _ = Nothing
 
 {- The trust.log used to only list trusted repos, without a field for the
  - trust status, which is why this defaults to Trusted. -}
@@ -74,12 +98,3 @@
 showTrust UnTrusted = "0"
 showTrust DeadTrusted = "X"
 showTrust SemiTrusted = "?"
-
-{- Changes the trust level for a uuid in the trustLog. -}
-trustSet :: UUID -> TrustLevel -> Annex ()
-trustSet uuid@(UUID _) level = do
-	ts <- liftIO getPOSIXTime
-	Annex.Branch.change trustLog $
-		showLog showTrust . changeLog ts uuid level . parseLog (Just . parseTrust)
-	Annex.changeState $ \s -> s { Annex.trustmap = Nothing }
-trustSet NoUUID _ = error "unknown UUID; cannot modify trust level"
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@
 
 bins=git-annex git-annex-shell git-union-merge
 mans=git-annex.1 git-annex-shell.1 git-union-merge.1
-sources=Build/SysConfig.hs Utility/StatFS.hs Utility/Touch.hs Remote/S3.hs
+sources=Build/SysConfig.hs Utility/StatFS.hs Utility/Touch.hs
 
 all=$(bins) $(mans) docs
 
@@ -33,18 +33,7 @@
 	hsc2hs $<
 	perl -i -pe 's/^{-# INCLUDE.*//' $@
 
-Remote/S3.hs:
-	@ln -sf S3real.hs Remote/S3.hs
-
-Remote/S3.o: Remote/S3.hs
-	@if ! $(GHCMAKE) Remote/S3.hs; then \
-		ln -sf S3stub.hs Remote/S3.hs; \
-		echo "** building without S3 support"; \
-	fi
-
-sources: $(sources)
-
-$(bins): sources Remote/S3.o
+$(bins): $(sources)
 	$(GHCMAKE) $@
 
 git-annex.1: doc/git-annex.mdwn
@@ -66,12 +55,11 @@
 
 test:
 	@if ! $(GHCMAKE) -O0 test; then \
-		echo "** not running test suite" >&2; \
-	else \
-		if ! ./test; then \
-			echo "** test suite failed!" >&2; \
-			exit 1; \
-		fi; \
+		echo "** failed to build the test suite" >&2; \
+		exit 1; \
+	elif ! ./test; then \
+		echo "** test suite failed!" >&2; \
+		exit 1; \
 	fi
 
 testcoverage:
@@ -111,5 +99,9 @@
 	@sed -e "s!\(Extra-Source-Files: \).*!\1$(shell find . -name .git -prune -or -not -name \\*.orig -not -type d -print | perl -ne 'print unless length >= 100')!i" < git-annex.cabal.orig > git-annex.cabal
 	@cabal sdist
 	@mv git-annex.cabal.orig git-annex.cabal
+
+# Upload to hackage.
+hackage: sdist
+	@cabal upload dist/*.tar.gz
 
 .PHONY: $(bins) test install
diff --git a/Option.hs b/Option.hs
new file mode 100644
--- /dev/null
+++ b/Option.hs
@@ -0,0 +1,79 @@
+{- git-annex command-line options
+ -
+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Option (
+	common,
+	matcher,
+	flag,
+	field,
+	name,
+	ArgDescr(..),
+	OptDescr(..),
+) where
+
+import System.Console.GetOpt
+import System.Log.Logger
+
+import Common.Annex
+import qualified Annex
+import Limit
+import Usage
+
+common :: [Option]
+common =
+	[ Option [] ["force"] (NoArg (setforce True))
+		"allow actions that may lose annexed data"
+	, Option ['F'] ["fast"] (NoArg (setfast True))
+		"avoid slow operations"
+	, Option ['a'] ["auto"] (NoArg (setauto True))
+		"automatic mode"
+	, Option ['q'] ["quiet"] (NoArg (setoutput Annex.QuietOutput))
+		"avoid verbose output"
+	, Option ['v'] ["verbose"] (NoArg (setoutput Annex.NormalOutput))
+		"allow verbose output (default)"
+	, Option ['j'] ["json"] (NoArg (setoutput Annex.JSONOutput))
+		"enable JSON output"
+	, Option ['d'] ["debug"] (NoArg (setdebug))
+		"show debug messages"
+	, Option ['b'] ["backend"] (ReqArg setforcebackend paramName)
+		"specify key-value backend to use"
+	]
+	where
+		setforce v = Annex.changeState $ \s -> s { Annex.force = v }
+		setfast v = Annex.changeState $ \s -> s { Annex.fast = v }
+		setauto v = Annex.changeState $ \s -> s { Annex.auto = v }
+		setoutput v = Annex.changeState $ \s -> s { Annex.output = v }
+		setforcebackend v = Annex.changeState $ \s -> s { Annex.forcebackend = Just v }
+		setdebug = liftIO $ updateGlobalLogger rootLoggerName $
+			setLevel DEBUG
+
+matcher :: [Option]
+matcher =
+	[ longopt "not" "negate next option"
+	, longopt "and" "both previous and next option must match"
+	, longopt "or" "either previous or next option must match"
+	, shortopt "(" "open group of options"
+	, shortopt ")" "close group of options"
+	]
+	where
+		longopt o = Option [] [o] $ NoArg $ addToken o
+		shortopt o = Option o [] $ NoArg $ addToken o
+
+{- An option that sets a flag. -}
+flag :: String -> String -> String -> Option
+flag short opt description = 
+	Option short [opt] (NoArg (Annex.setFlag opt)) description
+
+{- An option that sets a field. -}
+field :: String -> String -> String -> String -> Option
+field short opt paramdesc description = 
+	Option short [opt] (ReqArg (Annex.setField opt) paramdesc) description
+
+{- The flag or field name used for an option. -}
+name :: Option -> String
+name (Option _ o _ _) = Prelude.head o
+
diff --git a/Options.hs b/Options.hs
deleted file mode 100644
--- a/Options.hs
+++ /dev/null
@@ -1,96 +0,0 @@
-{- git-annex command-line options
- -
- - Copyright 2010-2011 Joey Hess <joey@kitenet.net>
- -
- - Licensed under the GNU GPL version 3 or higher.
- -}
-
-module Options where
-
-import System.Console.GetOpt
-import System.Log.Logger
-
-import Common.Annex
-import qualified Annex
-import Limit
-
-{- Each dashed command-line option results in generation of an action
- - in the Annex monad that performs the necessary setting.
- -}
-type Option = OptDescr (Annex ())
-
-commonOptions :: [Option]
-commonOptions =
-	[ Option [] ["force"] (NoArg (setforce True))
-		"allow actions that may lose annexed data"
-	, Option ['F'] ["fast"] (NoArg (setfast True))
-		"avoid slow operations"
-	, Option ['a'] ["auto"] (NoArg (setauto True))
-		"automatic mode"
-	, Option ['q'] ["quiet"] (NoArg (setoutput Annex.QuietOutput))
-		"avoid verbose output"
-	, Option ['v'] ["verbose"] (NoArg (setoutput Annex.NormalOutput))
-		"allow verbose output (default)"
-	, Option ['j'] ["json"] (NoArg (setoutput Annex.JSONOutput))
-		"enable JSON output"
-	, Option ['d'] ["debug"] (NoArg (setdebug))
-		"show debug messages"
-	, Option ['b'] ["backend"] (ReqArg setforcebackend paramName)
-		"specify key-value backend to use"
-	]
-	where
-		setforce v = Annex.changeState $ \s -> s { Annex.force = v }
-		setfast v = Annex.changeState $ \s -> s { Annex.fast = v }
-		setauto v = Annex.changeState $ \s -> s { Annex.auto = v }
-		setoutput v = Annex.changeState $ \s -> s { Annex.output = v }
-		setforcebackend v = Annex.changeState $ \s -> s { Annex.forcebackend = Just v }
-		setdebug = liftIO $ updateGlobalLogger rootLoggerName $
-			setLevel DEBUG
-	
-matcherOptions :: [Option]
-matcherOptions =
-	[ longopt "not" "negate next option"
-	, longopt "and" "both previous and next option must match"
-	, longopt "or" "either previous or next option must match"
-	, shortopt "(" "open group of options"
-	, shortopt ")" "close group of options"
-	]
-	where
-		longopt o = Option [] [o] $ NoArg $ addToken o
-		shortopt o = Option o [] $ NoArg $ addToken o
-
-{- Descriptions of params used in usage messages. -}
-paramPaths :: String
-paramPaths = paramOptional $ paramRepeating paramPath -- most often used
-paramPath :: String
-paramPath = "PATH"
-paramKey :: String
-paramKey = "KEY"
-paramDesc :: String
-paramDesc = "DESC"
-paramUrl :: String
-paramUrl = "URL"
-paramNumber :: String
-paramNumber = "NUMBER"
-paramRemote :: String
-paramRemote = "REMOTE"
-paramGlob :: String
-paramGlob = "GLOB"
-paramName :: String
-paramName = "NAME"
-paramUUID :: String
-paramUUID = "UUID"
-paramType :: String
-paramType = "TYPE"
-paramFormat :: String
-paramFormat = "FORMAT"
-paramKeyValue :: String
-paramKeyValue = "K=V"
-paramNothing :: String
-paramNothing = ""
-paramRepeating :: String -> String
-paramRepeating s = s ++ " ..."
-paramOptional :: String -> String
-paramOptional s = "[" ++ s ++ "]"
-paramPair :: String -> String -> String
-paramPair a b = a ++ " " ++ b
diff --git a/Remote.hs b/Remote.hs
--- a/Remote.hs
+++ b/Remote.hs
@@ -19,10 +19,12 @@
 	remoteList,
 	enabledRemoteList,
 	remoteMap,
+	uuidDescriptions,
 	byName,
 	prettyPrintUUIDs,
 	remotesWithUUID,
 	remotesWithoutUUID,
+	keyLocations,
 	keyPossibilities,
 	keyPossibilitiesTrusted,
 	nameToUUID,
@@ -39,69 +41,38 @@
 import Common.Annex
 import Types.Remote
 import qualified Annex
-import Config
 import Annex.UUID
 import Logs.UUID
 import Logs.Trust
 import Logs.Location
-import Logs.Remote
-
-import qualified Remote.Git
-import qualified Remote.S3
-import qualified Remote.Bup
-import qualified Remote.Directory
-import qualified Remote.Rsync
-import qualified Remote.Web
-import qualified Remote.Hook
-
-remoteTypes :: [RemoteType]
-remoteTypes =
-	[ Remote.Git.remote
-	, Remote.S3.remote
-	, Remote.Bup.remote
-	, Remote.Directory.remote
-	, Remote.Rsync.remote
-	, Remote.Web.remote
-	, Remote.Hook.remote
-	]
-
-{- Builds a list of all available Remotes.
- - Since doing so can be expensive, the list is cached. -}
-remoteList :: Annex [Remote]
-remoteList = do
-	rs <- Annex.getState Annex.remotes
-	if null rs
-		then do
-			m <- readRemoteLog
-			l <- mapM (process m) remoteTypes
-			let rs' = concat l
-			Annex.changeState $ \s -> s { Annex.remotes = rs' }
-			return rs'
-		else return rs
-	where
-		process m t = 
-			enumerate t >>=
-			mapM (gen m t)
-		gen m t r = do
-			u <- getRepoUUID r
-			generate t r u (M.lookup u m)
-
-{- All remotes that are not ignored. -}
-enabledRemoteList :: Annex [Remote]
-enabledRemoteList = filterM (repoNotIgnored . repo) =<< remoteList
+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)) <$> remoteList
+remoteMap = M.fromList . map (\r -> (uuid r, name r)) .
+	filter (\r -> uuid r /= NoUUID) <$> remoteList
 
-{- Looks up a remote by name. (Or by UUID.) Only finds currently configured
- - git remotes. -}
-byName :: String -> Annex (Remote)
-byName n = do
+{- 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
+
+addName :: String -> String -> String
+addName desc n
+	| desc == n = desc
+	| null desc = n
+	| otherwise = n ++ " (" ++ desc ++ ")"
+
+{- When a name is specified, looks up the remote matching that name.
+ - (Or it can be a UUID.) Only finds currently configured git remotes. -}
+byName :: Maybe String -> Annex (Maybe Remote)
+byName Nothing = return Nothing
+byName (Just n) = do
 	res <- byName' n
 	case res of
 		Left e -> error e
-		Right r -> return r
+		Right r -> return $ Just r
 byName' :: String -> Annex (Either String Remote)
 byName' "" = return $ Left "no remote specified"
 byName' n = do
@@ -142,28 +113,24 @@
 prettyPrintUUIDs :: String -> [UUID] -> Annex String
 prettyPrintUUIDs desc uuids = do
 	hereu <- getUUID
-	m <- M.unionWith addname <$> uuidMap <*> remoteMap
+	m <- uuidDescriptions
 	maybeShowJSON [(desc, map (jsonify m hereu) uuids)]
 	return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids
 	where
-		addname d n
-			| d == n = d
-			| null d = n
-			| otherwise = n ++ " (" ++ d ++ ")"
-		findlog m u = M.findWithDefault "" u m
+		finddescription m u = M.findWithDefault "" u m
 		prettify m hereu u
 			| not (null d) = fromUUID u ++ " -- " ++ d
 			| otherwise = fromUUID u
 			where
 				ishere = hereu == u
-				n = findlog m u
+				n = finddescription m u
 				d
 					| null n && ishere = "here"
-					| ishere = addname n "here"
+					| ishere = addName n "here"
 					| otherwise = n
 		jsonify m hereu u = toJSObject
 			[ ("uuid", toJSON $ fromUUID u)
-			, ("description", toJSON $ findlog m u)
+			, ("description", toJSON $ finddescription m u)
 			, ("here", toJSON $ hereu == u)
 			]
 
@@ -175,27 +142,32 @@
 remotesWithoutUUID :: [Remote] -> [UUID] -> [Remote]
 remotesWithoutUUID rs us = filter (\r -> uuid r `notElem` us) rs
 
-{- Cost ordered lists of remotes that the Logs.Location indicate may have a key.
+{- List of repository UUIDs that the location log indicates may have a key.
+ - Dead repositories are excluded. -}
+keyLocations :: Key -> Annex [UUID]
+keyLocations key = snd <$> (trustPartition DeadTrusted =<< loggedLocations key)
+
+{- Cost ordered lists of remotes that the location log indicates
+ - may have a key.
  -}
 keyPossibilities :: Key -> Annex [Remote]
-keyPossibilities key = fst <$> keyPossibilities' False key
+keyPossibilities key = fst <$> keyPossibilities' key []
 
-{- Cost ordered lists of remotes that the Logs.Location indicate may have a key.
+{- Cost ordered lists of remotes that the location log indicates
+ - may have a key.
  -
  - Also returns a list of UUIDs that are trusted to have the key
  - (some may not have configured remotes).
  -}
 keyPossibilitiesTrusted :: Key -> Annex ([Remote], [UUID])
-keyPossibilitiesTrusted = keyPossibilities' True
+keyPossibilitiesTrusted key = keyPossibilities' key =<< trustGet Trusted
 
-keyPossibilities' :: Bool -> Key -> Annex ([Remote], [UUID])
-keyPossibilities' withtrusted key = do
+keyPossibilities' :: Key -> [UUID] -> Annex ([Remote], [UUID])
+keyPossibilities' key trusted = do
 	u <- getUUID
-	trusted <- if withtrusted then trustGet Trusted else return []
 
-	-- get uuids of all remotes that are recorded to have the key
-	uuids <- keyLocations key
-	let validuuids = filter (/= u) uuids
+	-- uuids of all remotes that are recorded to have the key
+	validuuids <- filter (/= u) <$> keyLocations key
 
 	-- note that validuuids is assumed to not have dups
 	let validtrusteduuids = validuuids `intersect` trusted
@@ -228,13 +200,13 @@
 showTriedRemotes [] = return ()	
 showTriedRemotes remotes =
 	showLongNote $ "Unable to access these remotes: " ++
-		(join ", " $ map name remotes)
+		join ", " (map name remotes)
 
 forceTrust :: TrustLevel -> String -> Annex ()
 forceTrust level remotename = do
-	r <- nameToUUID remotename
+	u <- nameToUUID remotename
 	Annex.changeState $ \s ->
-		s { Annex.forcetrust = (r, level):Annex.forcetrust s }
+		s { Annex.forcetrust = M.insert u level (Annex.forcetrust s) }
 
 {- Used to log a change in a remote's having a key. The change is logged
  - in the local repo, not on the remote. The process of transferring the
diff --git a/Remote/Bup.hs b/Remote/Bup.hs
--- a/Remote/Bup.hs
+++ b/Remote/Bup.hs
@@ -19,7 +19,7 @@
 import qualified Git.Config
 import qualified Git.Construct
 import Config
-import Annex.Ssh
+import Remote.Helper.Ssh
 import Remote.Helper.Special
 import Remote.Helper.Encryptable
 import Crypto
diff --git a/Remote/Git.hs b/Remote/Git.hs
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -13,7 +13,7 @@
 import Common.Annex
 import Utility.CopyFile
 import Utility.RsyncFile
-import Annex.Ssh
+import Remote.Helper.Ssh
 import Types.Remote
 import qualified Git
 import qualified Git.Command
@@ -115,13 +115,11 @@
 				pOpen ReadFromPipe "git" ["config", "--null", "--list", "--file", tmpfile] $
 					Git.Config.hRead r
 
-		store a = do
-			r' <- a
+		store = observe $ \r' -> do
 			g <- gitRepo
 			let l = Git.remotes g
 			let g' = g { Git.remotes = exchange l r' }
 			Annex.changeState $ \s -> s { Annex.repo = g' }
-			return r'
 
 		exchange [] _ = []
 		exchange (old:ls) new =
@@ -184,9 +182,7 @@
 		-- No need to update the branch; its data is not used
 		-- for anything onLocal is used to do.
 		Annex.BranchState.disableUpdate
-		ret <- a
-		liftIO Git.Command.reap
-		return ret
+		liftIO Git.Command.reap `after` a
 
 keyUrls :: Git.Repo -> Key -> [String]
 keyUrls r key = map tourl (annexLocations key)
@@ -209,10 +205,8 @@
 		loc <- liftIO $ gitAnnexLocation key r
 		rsyncOrCopyFile params loc file
 	| Git.repoIsSsh r = rsyncHelper =<< rsyncParamsRemote r True key file
-	| Git.repoIsHttp r = liftIO $ downloadurls $ keyUrls r key
+	| Git.repoIsHttp r = Annex.Content.downloadUrl (keyUrls r key) file
 	| otherwise = error "copying from non-ssh, non-http repo not supported"
-	where
-		downloadurls us = untilTrue us $ \u -> Url.download u file
 
 {- Tries to copy a key's content to a remote's annex. -}
 copyToRemote :: Git.Repo -> Key -> Annex Bool
@@ -223,10 +217,9 @@
 		-- run copy from perspective of remote
 		liftIO $ onLocal r $ do
 			ensureInitialized
-			ok <- Annex.Content.getViaTmp key $
-				rsyncOrCopyFile params keysrc
-			Annex.Content.saveState
-			return ok
+			Annex.Content.saveState `after`
+				Annex.Content.getViaTmp key
+					(rsyncOrCopyFile params keysrc)
 	| Git.repoIsSsh r = do
 		keysrc <- inRepo $ gitAnnexLocation key
 		rsyncHelper =<< rsyncParamsRemote r False key keysrc
diff --git a/Remote/Helper/Ssh.hs b/Remote/Helper/Ssh.hs
new file mode 100644
--- /dev/null
+++ b/Remote/Helper/Ssh.hs
@@ -0,0 +1,65 @@
+{- git-annex remote access with ssh
+ -
+ - Copyright 2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Remote.Helper.Ssh where
+
+import Common
+import qualified Git
+import qualified Git.Url
+import Types
+import Config
+import Annex.UUID
+
+{- Generates parameters to ssh to a repository's host and run a command.
+ - Caller is responsible for doing any neccessary shellEscaping of the
+ - passed command. -}
+sshToRepo :: Git.Repo -> [CommandParam] -> Annex [CommandParam]
+sshToRepo repo sshcmd = do
+	s <- getConfig repo "ssh-options" ""
+	let sshoptions = map Param (words s)
+	let sshport = case Git.Url.port repo of
+		Nothing -> []
+		Just p -> [Param "-p", Param (show p)]
+	let sshhost = Param $ Git.Url.hostuser repo
+	return $ sshoptions ++ sshport ++ [sshhost] ++ sshcmd
+
+{- Generates parameters to run a git-annex-shell command on a remote
+ - repository. -}
+git_annex_shell :: Git.Repo -> String -> [CommandParam] -> Annex (Maybe (FilePath, [CommandParam]))
+git_annex_shell r command params
+	| not $ Git.repoIsUrl r = return $ Just (shellcmd, shellopts)
+	| Git.repoIsSsh r = do
+		uuid <- getRepoUUID r
+		sshparams <- sshToRepo r [Param $ sshcmd uuid ]
+		return $ Just ("ssh", sshparams)
+	| otherwise = return Nothing
+	where
+		dir = Git.workTree r
+		shellcmd = "git-annex-shell"
+		shellopts = Param command : File dir : params
+		sshcmd uuid = unwords $
+			shellcmd : map shellEscape (toCommand shellopts) ++
+			uuidcheck uuid
+		uuidcheck NoUUID = []
+		uuidcheck (UUID u) = ["--uuid", u]
+
+{- Uses a supplied function (such as boolSystem) to run a git-annex-shell
+ - command on a remote.
+ -
+ - Or, if the remote does not support running remote commands, returns
+ - a specified error value. -}
+onRemote 
+	:: Git.Repo
+	-> (FilePath -> [CommandParam] -> IO a, a)
+	-> String
+	-> [CommandParam]
+	-> Annex a
+onRemote r (with, errorval) command params = do
+	s <- git_annex_shell r command params
+	case s of
+		Just (c, ps) -> liftIO $ with c ps
+		Nothing -> return errorval
diff --git a/Remote/List.hs b/Remote/List.hs
new file mode 100644
--- /dev/null
+++ b/Remote/List.hs
@@ -0,0 +1,58 @@
+{- git-annex remote list
+ -
+ - Copyright 2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Remote.List where
+
+import qualified Data.Map as M
+
+import Common.Annex
+import qualified Annex
+import Logs.Remote
+import Types.Remote
+import Annex.UUID
+import Config
+
+import qualified Remote.Git
+import qualified Remote.S3
+import qualified Remote.Bup
+import qualified Remote.Directory
+import qualified Remote.Rsync
+import qualified Remote.Web
+import qualified Remote.Hook
+
+remoteTypes :: [RemoteType]
+remoteTypes =
+	[ Remote.Git.remote
+	, Remote.S3.remote
+	, Remote.Bup.remote
+	, Remote.Directory.remote
+	, Remote.Rsync.remote
+	, Remote.Web.remote
+	, Remote.Hook.remote
+	]
+
+{- Builds a list of all available Remotes.
+ - Since doing so can be expensive, the list is cached. -}
+remoteList :: Annex [Remote]
+remoteList = do
+	rs <- Annex.getState Annex.remotes
+	if null rs
+		then do
+			m <- readRemoteLog
+			rs' <- concat <$> mapM (process m) remoteTypes
+			Annex.changeState $ \s -> s { Annex.remotes = rs' }
+			return rs'
+		else return rs
+	where
+		process m t = enumerate t >>= mapM (gen m t)
+		gen m t r = do
+			u <- getRepoUUID r
+			generate t r u (M.lookup u m)
+
+{- All remotes that are not ignored. -}
+enabledRemoteList :: Annex [Remote]
+enabledRemoteList = filterM (repoNotIgnored . repo) =<< remoteList
diff --git a/Remote/Rsync.hs b/Remote/Rsync.hs
--- a/Remote/Rsync.hs
+++ b/Remote/Rsync.hs
@@ -172,9 +172,7 @@
 	let tmp = t </> "rsynctmp" </> show pid
 	nuke tmp
 	liftIO $ createDirectoryIfMissing True tmp
-	res <- a tmp
-	nuke tmp
-	return res
+	nuke tmp `after` a tmp
 	where
 		nuke d = liftIO $ 
 			doesDirectoryExist d >>? removeDirectoryRecursive d
diff --git a/Remote/S3.hs b/Remote/S3.hs
new file mode 100644
--- /dev/null
+++ b/Remote/S3.hs
@@ -0,0 +1,311 @@
+{- Amazon S3 remotes.
+ -
+ - Copyright 2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Remote.S3 (remote) where
+
+import Network.AWS.AWSConnection
+import Network.AWS.S3Object
+import Network.AWS.S3Bucket hiding (size)
+import Network.AWS.AWSResult
+import qualified Data.ByteString.Lazy.Char8 as L
+import qualified Data.Map as M
+import Data.Char
+import System.Environment
+import System.Posix.Env (setEnv)
+
+import Common.Annex
+import Types.Remote
+import Types.Key
+import qualified Git
+import Config
+import Remote.Helper.Special
+import Remote.Helper.Encryptable
+import Crypto
+import Annex.Content
+import Utility.Base64
+
+remote :: RemoteType
+remote = RemoteType {
+	typename = "S3",
+	enumerate = findSpecialRemotes "s3",
+	generate = gen,
+	setup = s3Setup
+}
+
+gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex Remote
+gen r u c = do
+	cst <- remoteCost r expensiveRemoteCost
+	return $ gen' r u c cst
+gen' :: Git.Repo -> UUID -> Maybe RemoteConfig -> Int -> Remote
+gen' r u c cst =
+	encryptableRemote c
+		(storeEncrypted this)
+		(retrieveEncrypted this)
+		this
+	where
+		this = Remote {
+			uuid = u,
+			cost = cst,
+			name = Git.repoDescribe r,
+	 		storeKey = store this,
+			retrieveKeyFile = retrieve this,
+			removeKey = remove this,
+			hasKey = checkPresent this,
+			hasKeyCheap = False,
+			config = c,
+			repo = r,
+			remotetype = remote
+		}
+
+s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
+s3Setup u c = handlehost $ M.lookup "host" c
+	where
+		remotename = fromJust (M.lookup "name" c)
+		defbucket = remotename ++ "-" ++ fromUUID u
+		defaults = M.fromList
+			[ ("datacenter", "US")
+			, ("storageclass", "STANDARD")
+			, ("host", defaultAmazonS3Host)
+			, ("port", show defaultAmazonS3Port)
+			, ("bucket", defbucket)
+			]
+		
+		handlehost Nothing = defaulthost
+		handlehost (Just h)
+			| ".archive.org" `isSuffixOf` map toLower h = archiveorg
+			| otherwise = defaulthost
+
+		use fullconfig = do
+			gitConfigSpecialRemote u fullconfig "s3" "true"
+			s3SetCreds fullconfig	
+
+		defaulthost = do
+			c' <- encryptionSetup c
+			let fullconfig = c' `M.union` defaults
+			genBucket fullconfig
+			use fullconfig
+
+		archiveorg = do
+			showNote "Internet Archive mode"
+			maybe (error "specify bucket=") (const $ return ()) $
+				M.lookup "bucket" archiveconfig
+			use archiveconfig
+			where
+				archiveconfig =
+					-- hS3 does not pass through
+					-- x-archive-* headers
+					M.mapKeys (replace "x-archive-" "x-amz-") $
+					-- encryption does not make sense here
+					M.insert "encryption" "none" $
+					M.union c $
+					-- special constraints on key names
+					M.insert "mungekeys" "ia" $
+					-- bucket created only when files
+					-- are uploaded
+					M.insert "x-amz-auto-make-bucket" "1" $
+					-- no default bucket name; should
+					-- be human-readable
+					M.delete "bucket" defaults
+
+store :: Remote -> Key -> Annex Bool
+store r k = s3Action r False $ \(conn, bucket) -> do
+	dest <- inRepo $ gitAnnexLocation k
+	res <- liftIO $ storeHelper (conn, bucket) r k dest
+	s3Bool res
+
+storeEncrypted :: Remote -> (Cipher, Key) -> Key -> Annex Bool
+storeEncrypted r (cipher, enck) k = s3Action r False $ \(conn, bucket) -> 
+	-- To get file size of the encrypted content, have to use a temp file.
+	-- (An alternative would be chunking to to a constant size.)
+	withTmp enck $ \tmp -> do
+		f <- inRepo $ gitAnnexLocation k
+		liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s
+		res <- liftIO $ storeHelper (conn, bucket) r enck tmp
+		s3Bool res
+
+storeHelper :: (AWSConnection, String) -> Remote -> Key -> FilePath -> IO (AWSResult ())
+storeHelper (conn, bucket) r k file = do
+	content <- liftIO $ L.readFile file
+	-- size is provided to S3 so the whole content does not need to be
+	-- buffered to calculate it
+	size <- maybe getsize (return . fromIntegral) $ keySize k
+	let object = setStorageClass storageclass $ 
+		S3Object bucket (bucketFile r k) ""
+			(("Content-Length", show size) : xheaders) content
+	sendObject conn object
+	where
+		storageclass =
+			case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
+				"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
+				_ -> STANDARD
+		getsize = do
+			s <- liftIO $ getFileStatus file
+			return $ fileSize s
+		
+		xheaders = filter isxheader $ M.assocs $ fromJust $ config r
+		isxheader (h, _) = "x-amz-" `isPrefixOf` h
+
+retrieve :: Remote -> Key -> FilePath -> Annex Bool
+retrieve r k f = s3Action r False $ \(conn, bucket) -> do
+	res <- liftIO $ getObject conn $ bucketKey r bucket k
+	case res of
+		Right o -> do
+			liftIO $ L.writeFile f $ obj_data o
+			return True
+		Left e -> s3Warning e
+
+retrieveEncrypted :: Remote -> (Cipher, Key) -> FilePath -> Annex Bool
+retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
+	res <- liftIO $ getObject conn $ bucketKey r bucket enck
+	case res of
+		Right o -> liftIO $ 
+			withDecryptedContent cipher (return $ obj_data o) $ \content -> do
+				L.writeFile f content
+				return True
+		Left e -> s3Warning e
+
+remove :: Remote -> Key -> Annex Bool
+remove r k = s3Action r False $ \(conn, bucket) -> do
+	res <- liftIO $ deleteObject conn $ bucketKey r bucket k
+	s3Bool res
+
+checkPresent :: Remote -> Key -> Annex (Either String Bool)
+checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
+	showAction $ "checking " ++ name r
+	res <- liftIO $ getObjectInfo conn $ bucketKey r bucket k
+	case res of
+		Right _ -> return $ Right True
+		Left (AWSError _ _) -> return $ Right False
+		Left e -> return $ Left (s3Error e)
+	where
+		noconn = Left $ error "S3 not configured"
+			
+s3Warning :: ReqError -> Annex Bool
+s3Warning e = do
+	warning $ prettyReqError e
+	return False
+
+s3Error :: ReqError -> a
+s3Error e = error $ prettyReqError e
+
+s3Bool :: AWSResult () -> Annex Bool
+s3Bool (Right _) = return True
+s3Bool (Left e) = s3Warning e
+
+s3Action :: Remote -> a -> ((AWSConnection, String) -> Annex a) -> Annex a
+s3Action r noconn action = do
+	when (isNothing $ config r) $
+		error $ "Missing configuration for special remote " ++ name r
+	let bucket = M.lookup "bucket" $ fromJust $ config r
+	conn <- s3Connection $ fromJust $ config r
+	case (bucket, conn) of
+		(Just b, Just c) -> action (c, b)
+		_ -> return noconn
+
+bucketFile :: Remote -> Key -> FilePath
+bucketFile r = munge . show
+	where
+		munge s = case M.lookup "mungekeys" $ fromJust $ config r of
+			Just "ia" -> iaMunge s
+			_ -> s
+
+bucketKey :: Remote -> String -> Key -> S3Object
+bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
+
+{- Internet Archive limits filenames to a subset of ascii,
+ - with no whitespace. Other characters are xml entity
+ - encoded. -}
+iaMunge :: String -> String
+iaMunge = (>>= munge)
+	where
+		munge c
+			| isAsciiUpper c || isAsciiLower c || isNumber c = [c]
+			| c `elem` "_-.\"" = [c]
+			| isSpace c = []
+			| otherwise = "&" ++ show (ord c) ++ ";"
+
+genBucket :: RemoteConfig -> Annex ()
+genBucket c = do
+	conn <- s3ConnectionRequired c
+	showAction "checking bucket"
+	loc <- liftIO $ getBucketLocation conn bucket 
+	case loc of
+		Right _ -> return ()
+		Left err@(NetworkError _) -> s3Error err
+		Left (AWSError _ _) -> do
+			showAction $ "creating bucket in " ++ datacenter
+			res <- liftIO $ createBucketIn conn bucket datacenter
+			case res of
+				Right _ -> return ()
+				Left err -> s3Error err
+	where
+		bucket = fromJust $ M.lookup "bucket" c
+		datacenter = fromJust $ M.lookup "datacenter" c
+
+s3ConnectionRequired :: RemoteConfig -> Annex AWSConnection
+s3ConnectionRequired c =
+	maybe (error "Cannot connect to S3") return =<< s3Connection c
+
+s3Connection :: RemoteConfig -> Annex (Maybe AWSConnection)
+s3Connection c = do
+	creds <- s3GetCreds c
+	case creds of
+		Just (ak, sk) -> return $ Just $ AWSConnection host port ak sk
+		_ -> do
+			warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey  ++ " to use S3"
+			return Nothing
+	where
+		host = fromJust $ M.lookup "host" c
+		port = let s = fromJust $ M.lookup "port" c in
+			case reads s of
+			[(p, _)] -> p
+			_ -> error $ "bad S3 port value: " ++ s
+
+{- 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
+			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')
+				_ -> return Nothing
+		else return $ Just (ak, sk)
+	where
+		getEnvKey s = liftIO $ catchDefaultIO (getEnv s) ""
+
+{- Stores S3 creds encrypted in the remote's config if possible. -}
+s3SetCreds :: RemoteConfig -> Annex RemoteConfig
+s3SetCreds c = do
+	creds <- s3GetCreds c
+	case creds of
+		Just (ak, sk) -> do
+			mcipher <- remoteCipher c
+			case mcipher of
+				Just cipher -> do
+					s <- liftIO $ withEncryptedContent cipher
+						(return $ L.pack $ unlines [ak, sk])
+						(return . L.unpack)
+					return $ M.insert "s3creds" (toB64 s) c
+				Nothing -> return c
+		_ -> return c
+
+s3AccessKey :: String
+s3AccessKey = "AWS_ACCESS_KEY_ID"
+s3SecretKey :: String
+s3SecretKey = "AWS_SECRET_ACCESS_KEY"
diff --git a/Remote/S3real.hs b/Remote/S3real.hs
deleted file mode 100644
--- a/Remote/S3real.hs
+++ /dev/null
@@ -1,311 +0,0 @@
-{- Amazon S3 remotes.
- -
- - Copyright 2011 Joey Hess <joey@kitenet.net>
- -
- - Licensed under the GNU GPL version 3 or higher.
- -}
-
-module Remote.S3 (remote) where
-
-import Network.AWS.AWSConnection
-import Network.AWS.S3Object
-import Network.AWS.S3Bucket hiding (size)
-import Network.AWS.AWSResult
-import qualified Data.ByteString.Lazy.Char8 as L
-import qualified Data.Map as M
-import Data.Char
-import System.Environment
-import System.Posix.Env (setEnv)
-
-import Common.Annex
-import Types.Remote
-import Types.Key
-import qualified Git
-import Config
-import Remote.Helper.Special
-import Remote.Helper.Encryptable
-import Crypto
-import Annex.Content
-import Utility.Base64
-
-remote :: RemoteType
-remote = RemoteType {
-	typename = "S3",
-	enumerate = findSpecialRemotes "s3",
-	generate = gen,
-	setup = s3Setup
-}
-
-gen :: Git.Repo -> UUID -> Maybe RemoteConfig -> Annex Remote
-gen r u c = do
-	cst <- remoteCost r expensiveRemoteCost
-	return $ gen' r u c cst
-gen' :: Git.Repo -> UUID -> Maybe RemoteConfig -> Int -> Remote
-gen' r u c cst =
-	encryptableRemote c
-		(storeEncrypted this)
-		(retrieveEncrypted this)
-		this
-	where
-		this = Remote {
-			uuid = u,
-			cost = cst,
-			name = Git.repoDescribe r,
-	 		storeKey = store this,
-			retrieveKeyFile = retrieve this,
-			removeKey = remove this,
-			hasKey = checkPresent this,
-			hasKeyCheap = False,
-			config = c,
-			repo = r,
-			remotetype = remote
-		}
-
-s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
-s3Setup u c = handlehost $ M.lookup "host" c
-	where
-		remotename = fromJust (M.lookup "name" c)
-		defbucket = remotename ++ "-" ++ fromUUID u
-		defaults = M.fromList
-			[ ("datacenter", "US")
-			, ("storageclass", "STANDARD")
-			, ("host", defaultAmazonS3Host)
-			, ("port", show defaultAmazonS3Port)
-			, ("bucket", defbucket)
-			]
-		
-		handlehost Nothing = defaulthost
-		handlehost (Just h)
-			| ".archive.org" `isSuffixOf` map toLower h = archiveorg
-			| otherwise = defaulthost
-
-		use fullconfig = do
-			gitConfigSpecialRemote u fullconfig "s3" "true"
-			s3SetCreds fullconfig	
-
-		defaulthost = do
-			c' <- encryptionSetup c
-			let fullconfig = c' `M.union` defaults
-			genBucket fullconfig
-			use fullconfig
-
-		archiveorg = do
-			showNote "Internet Archive mode"
-			maybe (error "specify bucket=") (const $ return ()) $
-				M.lookup "bucket" archiveconfig
-			use archiveconfig
-			where
-				archiveconfig =
-					-- hS3 does not pass through
-					-- x-archive-* headers
-					M.mapKeys (replace "x-archive-" "x-amz-") $
-					-- encryption does not make sense here
-					M.insert "encryption" "none" $
-					M.union c $
-					-- special constraints on key names
-					M.insert "mungekeys" "ia" $
-					-- bucket created only when files
-					-- are uploaded
-					M.insert "x-amz-auto-make-bucket" "1" $
-					-- no default bucket name; should
-					-- be human-readable
-					M.delete "bucket" defaults
-
-store :: Remote -> Key -> Annex Bool
-store r k = s3Action r False $ \(conn, bucket) -> do
-	dest <- inRepo $ gitAnnexLocation k
-	res <- liftIO $ storeHelper (conn, bucket) r k dest
-	s3Bool res
-
-storeEncrypted :: Remote -> (Cipher, Key) -> Key -> Annex Bool
-storeEncrypted r (cipher, enck) k = s3Action r False $ \(conn, bucket) -> 
-	-- To get file size of the encrypted content, have to use a temp file.
-	-- (An alternative would be chunking to to a constant size.)
-	withTmp enck $ \tmp -> do
-		f <- inRepo $ gitAnnexLocation k
-		liftIO $ withEncryptedContent cipher (L.readFile f) $ \s -> L.writeFile tmp s
-		res <- liftIO $ storeHelper (conn, bucket) r enck tmp
-		s3Bool res
-
-storeHelper :: (AWSConnection, String) -> Remote -> Key -> FilePath -> IO (AWSResult ())
-storeHelper (conn, bucket) r k file = do
-	content <- liftIO $ L.readFile file
-	-- size is provided to S3 so the whole content does not need to be
-	-- buffered to calculate it
-	size <- maybe getsize (return . fromIntegral) $ keySize k
-	let object = setStorageClass storageclass $ 
-		S3Object bucket (bucketFile r k) ""
-			(("Content-Length", show size) : xheaders) content
-	sendObject conn object
-	where
-		storageclass =
-			case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
-				"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
-				_ -> STANDARD
-		getsize = do
-			s <- liftIO $ getFileStatus file
-			return $ fileSize s
-		
-		xheaders = filter isxheader $ M.assocs $ fromJust $ config r
-		isxheader (h, _) = "x-amz-" `isPrefixOf` h
-
-retrieve :: Remote -> Key -> FilePath -> Annex Bool
-retrieve r k f = s3Action r False $ \(conn, bucket) -> do
-	res <- liftIO $ getObject conn $ bucketKey r bucket k
-	case res of
-		Right o -> do
-			liftIO $ L.writeFile f $ obj_data o
-			return True
-		Left e -> s3Warning e
-
-retrieveEncrypted :: Remote -> (Cipher, Key) -> FilePath -> Annex Bool
-retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
-	res <- liftIO $ getObject conn $ bucketKey r bucket enck
-	case res of
-		Right o -> liftIO $ 
-			withDecryptedContent cipher (return $ obj_data o) $ \content -> do
-				L.writeFile f content
-				return True
-		Left e -> s3Warning e
-
-remove :: Remote -> Key -> Annex Bool
-remove r k = s3Action r False $ \(conn, bucket) -> do
-	res <- liftIO $ deleteObject conn $ bucketKey r bucket k
-	s3Bool res
-
-checkPresent :: Remote -> Key -> Annex (Either String Bool)
-checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
-	showAction $ "checking " ++ name r
-	res <- liftIO $ getObjectInfo conn $ bucketKey r bucket k
-	case res of
-		Right _ -> return $ Right True
-		Left (AWSError _ _) -> return $ Right False
-		Left e -> return $ Left (s3Error e)
-	where
-		noconn = Left $ error "S3 not configured"
-			
-s3Warning :: ReqError -> Annex Bool
-s3Warning e = do
-	warning $ prettyReqError e
-	return False
-
-s3Error :: ReqError -> a
-s3Error e = error $ prettyReqError e
-
-s3Bool :: AWSResult () -> Annex Bool
-s3Bool (Right _) = return True
-s3Bool (Left e) = s3Warning e
-
-s3Action :: Remote -> a -> ((AWSConnection, String) -> Annex a) -> Annex a
-s3Action r noconn action = do
-	when (isNothing $ config r) $
-		error $ "Missing configuration for special remote " ++ name r
-	let bucket = M.lookup "bucket" $ fromJust $ config r
-	conn <- s3Connection $ fromJust $ config r
-	case (bucket, conn) of
-		(Just b, Just c) -> action (c, b)
-		_ -> return noconn
-
-bucketFile :: Remote -> Key -> FilePath
-bucketFile r = munge . show
-	where
-		munge s = case M.lookup "mungekeys" $ fromJust $ config r of
-			Just "ia" -> iaMunge s
-			_ -> s
-
-bucketKey :: Remote -> String -> Key -> S3Object
-bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
-
-{- Internet Archive limits filenames to a subset of ascii,
- - with no whitespace. Other characters are xml entity
- - encoded. -}
-iaMunge :: String -> String
-iaMunge = (>>= munge)
-	where
-		munge c
-			| isAsciiUpper c || isAsciiLower c || isNumber c = [c]
-			| c `elem` "_-.\"" = [c]
-			| isSpace c = []
-			| otherwise = "&" ++ show (ord c) ++ ";"
-
-genBucket :: RemoteConfig -> Annex ()
-genBucket c = do
-	conn <- s3ConnectionRequired c
-	showAction "checking bucket"
-	loc <- liftIO $ getBucketLocation conn bucket 
-	case loc of
-		Right _ -> return ()
-		Left err@(NetworkError _) -> s3Error err
-		Left (AWSError _ _) -> do
-			showAction $ "creating bucket in " ++ datacenter
-			res <- liftIO $ createBucketIn conn bucket datacenter
-			case res of
-				Right _ -> return ()
-				Left err -> s3Error err
-	where
-		bucket = fromJust $ M.lookup "bucket" c
-		datacenter = fromJust $ M.lookup "datacenter" c
-
-s3ConnectionRequired :: RemoteConfig -> Annex AWSConnection
-s3ConnectionRequired c =
-	maybe (error "Cannot connect to S3") return =<< s3Connection c
-
-s3Connection :: RemoteConfig -> Annex (Maybe AWSConnection)
-s3Connection c = do
-	creds <- s3GetCreds c
-	case creds of
-		Just (ak, sk) -> return $ Just $ AWSConnection host port ak sk
-		_ -> do
-			warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey  ++ " to use S3"
-			return Nothing
-	where
-		host = fromJust $ M.lookup "host" c
-		port = let s = fromJust $ M.lookup "port" c in
-			case reads s of
-			[(p, _)] -> p
-			_ -> error $ "bad S3 port value: " ++ s
-
-{- 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
-			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')
-				_ -> return Nothing
-		else return $ Just (ak, sk)
-	where
-		getEnvKey s = liftIO $ catchDefaultIO (getEnv s) ""
-
-{- Stores S3 creds encrypted in the remote's config if possible. -}
-s3SetCreds :: RemoteConfig -> Annex RemoteConfig
-s3SetCreds c = do
-	creds <- s3GetCreds c
-	case creds of
-		Just (ak, sk) -> do
-			mcipher <- remoteCipher c
-			case mcipher of
-				Just cipher -> do
-					s <- liftIO $ withEncryptedContent cipher
-						(return $ L.pack $ unlines [ak, sk])
-						(return . L.unpack)
-					return $ M.insert "s3creds" (toB64 s) c
-				Nothing -> return c
-		_ -> return c
-
-s3AccessKey :: String
-s3AccessKey = "AWS_ACCESS_KEY_ID"
-s3SecretKey :: String
-s3SecretKey = "AWS_SECRET_ACCESS_KEY"
diff --git a/Remote/S3stub.hs b/Remote/S3stub.hs
deleted file mode 100644
--- a/Remote/S3stub.hs
+++ /dev/null
@@ -1,13 +0,0 @@
--- stub for when hS3 is not available
-module Remote.S3 (remote) where
-
-import Types.Remote
-import Types
-
-remote :: RemoteType
-remote = RemoteType {
-	typename = "S3",
-	enumerate = return [],
-	generate = error "S3 not enabled",
-	setup = error "S3 not enabled"
-}
diff --git a/Remote/Web.hs b/Remote/Web.hs
--- a/Remote/Web.hs
+++ b/Remote/Web.hs
@@ -11,6 +11,7 @@
 import Types.Remote
 import qualified Git
 import qualified Git.Construct
+import Annex.Content
 import Config
 import Logs.Web
 import qualified Utility.Url as Url
@@ -55,7 +56,7 @@
 			return False
 		get urls = do
 			showOutput -- make way for download progress bar
-			liftIO $ anyM (`Url.download` file) urls
+			downloadUrl urls file
 
 uploadKey :: Key -> Annex Bool
 uploadKey _ = do
diff --git a/Seek.hs b/Seek.hs
--- a/Seek.hs
+++ b/Seek.hs
@@ -20,11 +20,10 @@
 import qualified Git.LsFiles as LsFiles
 import qualified Git.CheckAttr
 import qualified Limit
+import qualified Option
 
 seekHelper :: ([FilePath] -> Git.Repo -> IO [FilePath]) -> [FilePath] -> Annex [FilePath]
-seekHelper a params = do
-	g <- gitRepo
-	liftIO $ runPreserveOrder (`a` g) params
+seekHelper a params = inRepo $ \g -> runPreserveOrder (`a` g) params
 
 withFilesInGit :: (FilePath -> CommandStart) -> CommandSeek
 withFilesInGit a params = prepFiltered a $ seekHelper LsFiles.inRepo params
@@ -40,20 +39,21 @@
 		go (file, v) = a (readMaybe v) file
 
 withBackendFilesInGit :: (BackendFile -> CommandStart) -> CommandSeek
-withBackendFilesInGit a params = do
-	files <- seekHelper LsFiles.inRepo params
-	prepBackendPairs a files
-
-withFilesMissing :: (String -> CommandStart) -> CommandSeek
-withFilesMissing a params = prepFiltered a $ liftIO $ filterM missing params
-	where
-		missing = liftM not . doesFileExist
+withBackendFilesInGit a params =
+	prepBackendPairs a =<< seekHelper LsFiles.inRepo params
 
 withFilesNotInGit :: (BackendFile -> CommandStart) -> CommandSeek
 withFilesNotInGit a params = do
-	force <- Annex.getState Annex.force
-	newfiles <- seekHelper (LsFiles.notInRepo force) params
-	prepBackendPairs a newfiles
+	{- 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)
+	where
+		(dotps, ps) = partition dotfile params
+		seek l = do
+			force <- Annex.getState Annex.force
+			g <- gitRepo
+			liftIO $ (\p -> LsFiles.notInRepo force p g) l
 
 withWords :: ([String] -> CommandStart) -> CommandSeek
 withWords a params = return [a params]
@@ -85,6 +85,22 @@
 	where
 		parse p = fromMaybe (error "bad key") $ readKey p
 
+withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek
+withValue v a params = do
+	r <- v
+	a r params
+
+{- Modifies a seek action using the value of a field option, which is fed into
+ - a conversion function, and then is passed into the seek action.
+ - This ensures that the conversion function only runs once.
+ -}
+withField :: Option -> (Maybe String -> Annex a) -> (a -> CommandSeek) -> CommandSeek
+withField option converter = withValue $
+	converter =<< Annex.getField (Option.name option)
+
+withFlag :: Option -> (Bool -> CommandSeek) -> CommandSeek
+withFlag option = withValue $ Annex.getFlag (Option.name option)
+
 withNothing :: CommandStart -> CommandSeek
 withNothing a [] = return [a]
 withNothing _ _ = error "This command takes no parameters."
@@ -99,18 +115,12 @@
 prepFilteredGen :: (b -> CommandStart) -> (b -> FilePath) -> Annex [b] -> Annex [CommandStart]
 prepFilteredGen a d fs = do
 	matcher <- Limit.getMatcher
-	prepStart (proc matcher) fs
+	map (proc matcher) <$> fs
 	where
 		proc matcher v = do
 			let f = d v
 			ok <- matcher f
 			if ok then a v else return Nothing
-
-{- Generates a list of CommandStart actions that will be run to perform a
- - command, using a list (ie of files) coming from an action. The list
- - will be produced and consumed lazily. -}
-prepStart :: (b -> CommandStart) -> Annex [b] -> Annex [CommandStart]
-prepStart a = liftM (map a)
 
 notSymlink :: FilePath -> IO Bool
 notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
diff --git a/Types.hs b/Types.hs
--- a/Types.hs
+++ b/Types.hs
@@ -11,7 +11,8 @@
 	Key,
 	UUID(..),
 	Remote,
-	RemoteType
+	RemoteType,
+	Option
 ) where
 
 import Annex
@@ -19,6 +20,7 @@
 import Types.Key
 import Types.UUID
 import Types.Remote
+import Types.Option
 
 type Backend = BackendA Annex
 type Remote = RemoteA Annex
diff --git a/Types/Command.hs b/Types/Command.hs
--- a/Types/Command.hs
+++ b/Types/Command.hs
@@ -32,14 +32,15 @@
 type CommandCleanup = Annex Bool
 
 {- A command is defined by specifying these things. -}
-data Command = Command {
-	cmdnorepo :: Maybe (IO ()),
-	cmdcheck :: [CommandCheck],
-	cmdname :: String,
-	cmdparams :: String,
-	cmdseek :: [CommandSeek],
-	cmddesc :: String
-}
+data Command = Command
+	{ cmdoptions :: [Option]     -- command-specific options
+	, cmdnorepo :: Maybe (IO ()) -- an action to run when not in a repo
+	, cmdcheck :: [CommandCheck] -- check stage
+	, cmdname :: String
+	, cmdparamdesc :: String     -- description of params for usage
+	, cmdseek :: [CommandSeek]   -- seek stage
+	, cmddesc :: String          -- description of command for usage
+	}
 
 {- CommandCheck functions can be compared using their unique id. -}
 instance Eq CommandCheck where
diff --git a/Types/Option.hs b/Types/Option.hs
new file mode 100644
--- /dev/null
+++ b/Types/Option.hs
@@ -0,0 +1,17 @@
+{- git-annex command options
+ -
+ - Copyright 2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Types.Option where
+
+import System.Console.GetOpt
+
+import Annex
+
+{- Each dashed command-line option results in generation of an action
+ - in the Annex monad that performs the necessary setting.
+ -}
+type Option = OptDescr (Annex ())
diff --git a/Upgrade.hs b/Upgrade.hs
--- a/Upgrade.hs
+++ b/Upgrade.hs
@@ -13,12 +13,10 @@
 import qualified Upgrade.V1
 import qualified Upgrade.V2
 
-{- Uses the annex.version git config setting to automate upgrades. -}
 upgrade :: Annex Bool
-upgrade = do
-	version <- getVersion
-	case version of
-		Just "0" -> Upgrade.V0.upgrade
-		Just "1" -> Upgrade.V1.upgrade
-		Just "2" -> Upgrade.V2.upgrade
-		_ -> return True
+upgrade = go =<< getVersion
+	where
+		go (Just "0") = Upgrade.V0.upgrade
+		go (Just "1") = Upgrade.V1.upgrade
+		go (Just "2") = Upgrade.V2.upgrade
+		go _ = return True
diff --git a/Usage.hs b/Usage.hs
new file mode 100644
--- /dev/null
+++ b/Usage.hs
@@ -0,0 +1,88 @@
+{- git-annex usage messages
+ -
+ - Copyright 2010-2011 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Usage where
+
+import System.Console.GetOpt
+
+import Types.Command
+import Types.Option
+
+{- Usage message with lists of commands and options. -}
+usage :: String -> [Command] -> [Option] -> String
+usage header cmds commonoptions = unlines $ 
+	[ header
+	, ""
+	, "Options:"
+	] ++ optlines ++
+	[ ""
+	, "Commands:"
+	, ""
+	] ++ cmdlines
+	where
+		-- To get consistent indentation of options, generate the
+		-- usage for all options at once. A command's options will
+		-- be displayed after the command.
+		alloptlines = filter (not . null) $
+			lines $ usageInfo "" $
+				concatMap cmdoptions cmds ++ commonoptions
+		(cmdlines, optlines) = go cmds alloptlines []
+		go [] os ls = (ls, os)
+		go (c:cs) os ls = go cs os' (ls++(l:o))
+			where
+				(o, os') = splitAt (length $ cmdoptions c) os
+				l = concat 
+					[ cmdname c
+					, namepad (cmdname c)
+					, cmdparamdesc c
+					, descpad (cmdparamdesc c)
+					, cmddesc c
+					]
+		pad n s = replicate (n - length s) ' '
+		namepad = pad $ longest cmdname + 1
+		descpad = pad $ longest cmdparamdesc + 2
+		longest f = foldl max 0 $ map (length . f) cmds
+
+{- Descriptions of params used in usage messages. -}
+paramPaths :: String
+paramPaths = paramOptional $ paramRepeating paramPath -- most often used
+paramPath :: String
+paramPath = "PATH"
+paramKey :: String
+paramKey = "KEY"
+paramDesc :: String
+paramDesc = "DESC"
+paramUrl :: String
+paramUrl = "URL"
+paramNumber :: String
+paramNumber = "NUMBER"
+paramRemote :: String
+paramRemote = "REMOTE"
+paramGlob :: String
+paramGlob = "GLOB"
+paramName :: String
+paramName = "NAME"
+paramValue :: String
+paramValue = "VALUE"
+paramUUID :: String
+paramUUID = "UUID"
+paramType :: String
+paramType = "TYPE"
+paramDate :: String
+paramDate = "DATE"
+paramFormat :: String
+paramFormat = "FORMAT"
+paramKeyValue :: String
+paramKeyValue = "K=V"
+paramNothing :: String
+paramNothing = ""
+paramRepeating :: String -> String
+paramRepeating s = s ++ " ..."
+paramOptional :: String -> String
+paramOptional s = "[" ++ s ++ "]"
+paramPair :: String -> String -> String
+paramPair a b = a ++ " " ++ b
diff --git a/Utility/Gpg.hs b/Utility/Gpg.hs
--- a/Utility/Gpg.hs
+++ b/Utility/Gpg.hs
@@ -23,9 +23,13 @@
 stdParams :: [CommandParam] -> IO [String]
 stdParams params = do
 	-- Enable batch mode if GPG_AGENT_INFO is set, to avoid extraneous
-	-- gpg output about password prompts.
+	-- gpg output about password prompts. GPG_BATCH is set by the test
+	-- suite for a similar reason.
 	e <- getEnv "GPG_AGENT_INFO"
-	let batch = if isNothing e then [] else ["--batch"]
+	b <- getEnv "GPG_BATCH"
+	let batch = if isNothing e && isNothing b
+		then []
+		else ["--batch", "--no-tty"]
 	return $ batch ++ defaults ++ toCommand params
 	where
 		-- be quiet, even about checking the trustdb
diff --git a/Utility/Monad.hs b/Utility/Monad.hs
--- a/Utility/Monad.hs
+++ b/Utility/Monad.hs
@@ -20,7 +20,7 @@
 		then return (Just x)
 		else firstM p xs
 
-{- Returns true if any value in the list satisfies the preducate,
+{- Returns true if any value in the list satisfies the predicate,
  - stopping once one is found. -}
 anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
 anyM p = liftM isJust . firstM p
@@ -29,10 +29,13 @@
 untilTrue :: (Monad m) => [a] -> (a -> m Bool) -> m Bool
 untilTrue = flip anyM
 
-{- Runs a monadic action, passing its value to an observer
- - before returning it. -}
+{- Runs an action, passing its value to an observer before returning it. -}
 observe :: (Monad m) => (a -> m b) -> m a -> m a
 observe observer a = do
 	r <- a
 	_ <- observer r
 	return r
+
+{- b `after` a runs first a, then b, and returns the value of a -}
+after :: (Monad m) => m b -> m a -> m a
+after = observe . const
diff --git a/Utility/Path.hs b/Utility/Path.hs
--- a/Utility/Path.hs
+++ b/Utility/Path.hs
@@ -134,3 +134,14 @@
 inPath command = getSearchPath >>= anyM indir
 	where
 		indir d = doesFileExist $ d </> command
+
+{- Checks if a filename is a unix dotfile. All files inside dotdirs
+ - count as dotfiles. -}
+dotfile :: FilePath -> Bool
+dotfile file
+	| f == "." = False
+	| f == ".." = False
+	| f == "" = False
+	| otherwise = "." `isPrefixOf` f || dotfile (takeDirectory file)
+	where
+		f = takeFileName file
diff --git a/Utility/Url.hs b/Utility/Url.hs
--- a/Utility/Url.hs
+++ b/Utility/Url.hs
@@ -6,6 +6,7 @@
  -}
 
 module Utility.Url (
+	URLString,
 	exists,
 	canDownload,
 	download,
@@ -43,21 +44,21 @@
  - would not be appropriate to test at configure time and build support
  - for only one in.
  -}
-download :: URLString -> FilePath -> IO Bool
-download url file = do
+download :: URLString -> [CommandParam] -> FilePath -> IO Bool
+download url options file = do
 	e <- inPath "wget"
 	if e
 		then
-			boolSystem "wget"
-				[Params "-c -O", File file, File url]
+			go "wget" [Params "-c -O", File file, File url]
 		else
 			-- Uses the -# progress display, because the normal
 			-- one is very confusing when resuming, showing
 			-- the remainder to download as the whole file,
 			-- and not indicating how much percent was
 			-- downloaded before the resume.
-			boolSystem "curl" 
-				[Params "-L -C - -# -o", File file, File url]
+			go "curl" [Params "-L -C - -# -o", File file, File url]
+	where
+		go cmd opts = boolSystem cmd (options++opts)
 
 {- Downloads a small file. -}
 get :: URLString -> IO String
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,40 @@
+git-annex (3.20120113) unstable; urgency=low
+
+  * log: Add --gource mode, which generates output usable by gource.
+  * map: Fix display of remote repos
+  * Add annex-trustlevel configuration settings, which can be used to 
+    override the trust level of a remote.
+  * git-annex, git-union-merge: Support GIT_DIR and GIT_WORK_TREE.
+  * Add libghc-testpack-dev to build depends on all arches.
+
+ -- Joey Hess <joeyh@debian.org>  Fri, 13 Jan 2012 15:35:17 -0400
+
+git-annex (3.20120106) unstable; urgency=low
+
+  * 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.
+
+ -- Joey Hess <joeyh@debian.org>  Sat, 07 Jan 2012 13:16:23 -0400
+
+git-annex (3.20120105) unstable; urgency=low
+
+  * Added annex-web-options configuration settings, which can be
+    used to provide parameters to whichever of wget or curl git-annex uses
+    (depends on which is available, but most of their important options
+    suitable for use here are the same).
+  * Dotfiles, and files inside dotdirs are not added by "git annex add"
+    unless the dotfile or directory is explicitly listed. So "git annex add ."
+    will add all untracked files in the current directory except for those in
+    dotdirs.
+  * Added quickcheck to build dependencies, and fail if test suite cannot be
+    built.
+  * fsck: Do backend-specific check before checking numcopies is satisfied.
+  * Run gpg with --no-tty. Closes: #654721
+
+ -- Joey Hess <joeyh@debian.org>  Thu, 05 Jan 2012 13:44:12 -0400
+
 git-annex (3.20111231) unstable; urgency=low
 
   * sync: Improved to work well without a central bare repository.
diff --git a/debian/control b/debian/control
--- a/debian/control
+++ b/debian/control
@@ -12,7 +12,8 @@
 	libghc-http-dev,
 	libghc-utf8-string-dev,
 	libghc-hs3-dev (>= 0.5.6),
-	libghc-testpack-dev [any-i386 any-amd64],
+	libghc-testpack-dev,
+	libghc-quickcheck2-dev,
 	libghc-monad-control-dev (>= 0.3),
 	libghc-lifted-base-dev,
 	libghc-json-dev,
diff --git a/debian/copyright b/debian/copyright
--- a/debian/copyright
+++ b/debian/copyright
@@ -2,7 +2,7 @@
 Source: native package
 
 Files: *
-Copyright: © 2010-2011 Joey Hess <joey@kitenet.net>
+Copyright: © 2010-2012 Joey Hess <joey@kitenet.net>
 License: GPL-3+
  The full text of version 3 of the GPL is distributed as doc/GPL in
  this package's source, or in /usr/share/common-licenses/GPL-3 on
diff --git a/doc/NixOS.mdwn b/doc/NixOS.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/NixOS.mdwn
@@ -0,0 +1,5 @@
+Users of the [Nix package manager](http://nixos.org/) can install it by running:
+
+    nix-env -i git-annex
+
+The build status of the package within Nix can be seen on the [Hydra Build Farm](http://hydra.nixos.org/job/nixpkgs/trunk/gitAndTools.gitAnnex).
diff --git a/doc/bugs/Lost_S3_Remote.mdwn b/doc/bugs/Lost_S3_Remote.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Lost_S3_Remote.mdwn
@@ -0,0 +1,59 @@
+Somehow I've lost my S3 remote... git-annex knows it's there, but its not associating it with the git remote in .git/config
+
+    $ git-annex whereis pebuilder.iso 
+    whereis pebuilder.iso (3 copies) 
+      	3b6fc6f6-3025-11e1-b496-33bffbc0f3ed -- housebackup (external seagate drive on /mnt/back/RemoteStore)
+   	6b1326d8-2abb-11e1-8f43-979159a7f900 -- synology
+   	9b297772-2ab2-11e1-a86f-2fd669cb2417 -- Amazon S3
+    ok
+
+Amazon S3 is the description from the remote.  My .git/config file contains this block:
+
+    [remote "cloud"]
+      annex-s3 = true
+      annex-uuid = 9b297772-2ab2-11e1-a86f-2fd669cb2417
+      annex-cost = 70
+
+The UUID matches... But I cannot access it... see below:
+
+    [39532:39531 - 0:626] 08:20:38 [vivitron@tronlap:o +3] ~/annex/ISO 
+    $ git-annex get pebuilder.iso --from=cloud
+    git-annex: there is no git remote named "cloud"
+    
+    [39532:39531 - 0:627] 08:20:56 [vivitron@tronlap:o +3] ~/annex/ISO 
+    $ git-annex get pebuilder.iso --from="Amazon S3"
+    git-annex: there is no git remote named "Amazon S3"
+    
+    [39532:39531 - 0:628] 08:21:01 [vivitron@tronlap:o +3] ~/annex/ISO 
+    $ git-annex get pebuilder.iso --from=9b297772-2ab2-11e1-a86f-2fd669cb2417
+    git-annex: there is no git remote named "9b297772-2ab2-11e1-a86f-2fd669cb2417"
+
+    [39532:39531 - 0:629] 08:21:08 [vivitron@tronlap:o +3] ~/annex/ISO 
+    $ 
+
+git remote lists "cloud" as a remote:
+
+    $ git remote
+    all
+    cloud
+    cs
+    es3
+    origin
+
+git-annex status lists S3 support:
+
+    $ git-annex status
+    supported backends: SHA256 SHA1 SHA512 SHA224 SHA384 SHA256E SHA1E SHA512E SHA224E SHA384E WORM URL
+    supported remote types: git S3 bup directory rsync web hook
+
+
+
+I appreciate any help....  I've tested versions 3.20111211, 3.20111231, and 3.20120105
+
+    $ git --version
+    git version 1.7.8.1
+
+
+
+> [[done]]; I've fixed the build system so this confusing thing cannot
+> happen anymore. --[[Joey]]
diff --git a/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment b/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-06T03:04:35Z"
+ content="""
+Despite `status` listing S3 support, your git-annex is actually built with S3stub, probably because it failed to find the necessary S3 module at build time. Rebuild git-annex and watch closely, you'll see \"** building without S3 support\". Look above that for the error and fix it.
+
+It was certianly a bug that it showed S3 as supported when built without it. I've fixed that.
+"""]]
diff --git a/doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment b/doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 2"
+ date="2012-01-06T03:08:28Z"
+ content="""
+BTW, you'll want to \"make clean\", since the S3stub hack symlinks a file into place and it will continue building with S3stub even if you fix the problem until you clean.
+"""]]
diff --git a/doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment b/doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkey8WuXUh_x5JC2c9_it1CYRnVTgdGu1M"
+ nickname="Dustin"
+ subject="Thank you!"
+ date="2012-01-06T03:38:27Z"
+ content="""
+make clean and rebuild worked...  Thank you
+"""]]
diff --git a/doc/bugs/__34__make_test__34___fails_silently.mdwn b/doc/bugs/__34__make_test__34___fails_silently.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/__34__make_test__34___fails_silently.mdwn
@@ -0,0 +1,4 @@
+`make test` fails silently when the test program cannot be built. This happens, for example, when attempting to compile git-annex with `QuickCheck-2.4.2`.
+
+> I've made "make test" exit nonzero if the test suite cannot be built.
+> [[done]] --[[Joey]]
diff --git a/doc/bugs/conq:_invalid_command_syntax.mdwn b/doc/bugs/conq:_invalid_command_syntax.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/conq:_invalid_command_syntax.mdwn
@@ -0,0 +1,30 @@
+I've been getting an occasional error from git-annex.
+
+The error is:   'conq: invalid command syntax.'
+
+For example, the last two commands I ran are:
+
+    $ git annex unused
+    unused . (checking for unused data...) (checking master...) (checking origin/master...) 
+      Some annexed data is no longer used by any files:
+        NUMBER  KEY
+        1       SHA256-s.....
+      (To see where data was previously used, try: git log --stat -S'KEY')
+      
+      To remove unwanted data: git-annex dropunused NUMBER
+      
+    ok
+    
+    $ git annex dropunused 1
+    dropunused 1 conq: invalid command syntax.
+    ok
+
+
+
+*OS:*  OSX + port installs of the GNU tools
+
+*git-annex-version:*  3.20111211
+
+*git-version:*  1.7.7.4
+
+> [[done]], apparently not a git-annex bug --[[Joey]] 
diff --git a/doc/bugs/conq:_invalid_command_syntax/comment_1_f33b83025ce974e496f83f248275a66a._comment b/doc/bugs/conq:_invalid_command_syntax/comment_1_f33b83025ce974e496f83f248275a66a._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/conq:_invalid_command_syntax/comment_1_f33b83025ce974e496f83f248275a66a._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-03T00:34:59Z"
+ content="""
+AFAICs, you probably just have a \"conq\" program that is running in the background and emitted this error.
+
+The error message is not part of git-annex; it does not run any \"conq\" thing itself. Although you could try passing the --debug parameter to check the commands it does run to see if one of them somehow causes this conq thing.
+"""]]
diff --git a/doc/bugs/conq:_invalid_command_syntax/comment_2_195106ca8dedad5f4d755f625e38e8af._comment b/doc/bugs/conq:_invalid_command_syntax/comment_2_195106ca8dedad5f4d755f625e38e8af._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/conq:_invalid_command_syntax/comment_2_195106ca8dedad5f4d755f625e38e8af._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 2"
+ date="2012-01-03T00:41:08Z"
+ content="""
+A google search <http://www.google.com/search?hl=en&sclient=psy-ab&q=conq%3A+invalid+command+syntax&btnG=>
+finds other examples of this error message related to ssh, mercurial, and bitbucket. What that has to do with git is anyone's guess, but I'm pretty sure git-annex is not related to it at all.
+"""]]
diff --git a/doc/bugs/conq:_invalid_command_syntax/comment_3_55af43e2f43a4c373f7a0a33678d0b1c._comment b/doc/bugs/conq:_invalid_command_syntax/comment_3_55af43e2f43a4c373f7a0a33678d0b1c._comment
new file mode 100644
--- /dev/null
+++ b/doc/bugs/conq:_invalid_command_syntax/comment_3_55af43e2f43a4c373f7a0a33678d0b1c._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="http://a-or-b.myopenid.com/"
+ ip="203.45.2.230"
+ subject="comment 3"
+ date="2012-01-03T00:49:41Z"
+ content="""
+Yeah,  I saw those google links myself, but couldn't see why the bitbucket/ssh would be relevant.
+
+The strange thing is that I *only* get this message when running git-annex.
+
+I also don't have a conq in my path so I don't know where it is running from.
+ 
+
+Oh well, if I ever sort it out I'll post back here.
+"""]]
diff --git a/doc/bugs/on--git-dir_and_--work-tree_options.mdwn b/doc/bugs/on--git-dir_and_--work-tree_options.mdwn
--- a/doc/bugs/on--git-dir_and_--work-tree_options.mdwn
+++ b/doc/bugs/on--git-dir_and_--work-tree_options.mdwn
@@ -27,3 +27,5 @@
 
 git-annex version: 3.20110702
 
+> [[done]], git-annex now honors `GIT_DIR` and `GIT_WORK_TREE` like other
+> git commands do. --[[Joey]]
diff --git a/doc/bugs/signal_weirdness.mdwn b/doc/bugs/signal_weirdness.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/signal_weirdness.mdwn
@@ -0,0 +1,38 @@
+For the record, there is a slight weirdness with how git-annex
+handles a signal like ctrl-c.
+
+For example:
+
+	joey@gnu:~/tmp/b>git annex copy a b --to origin
+	copy a (checking origin...) (to origin...) 
+	SHA256-s104857600--20492a4d0d84f8beb1767f6616229f85d44c2827b64bdbfb260ee12fa1109e0e
+	        3272   0%    0.00kB/s    0:00:00  ^C
+	zsh: interrupt  git annex copy a --to origin
+	joey@gnu:~/tmp/b>
+	rsync error: unexplained error (code 130) at rsync.c(549) [sender=3.0.9]
+
+Here git-annex exits before rsync has fully exited. Not a large problem
+but sorta weird.
+
+The culprit is `safeSystemEnv` in Utility.SafeCommand, which installs
+a default signal handler for SIGINT, which causes it to immediatly
+terminate git-annex. rsync, in turn, has its own SIGINT handler, which 
+prints the message, typically later.
+
+(Why it prints that message and not its more usual message about having
+received a signal, I'm not sure?)
+
+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.
+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,
+it will be left running in the background after git exits, and continuing
+with further actions too. (Probably this is a bug in git.)
+
+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]] 
diff --git a/doc/feeds.mdwn b/doc/feeds.mdwn
--- a/doc/feeds.mdwn
+++ b/doc/feeds.mdwn
@@ -1,3 +1,5 @@
 Aggregating git-annex mentions from elsewhere on the net..
 
 * [[!aggregate expirecount=25 name="identica" feedurl="http://identi.ca/api/statusnet/tags/timeline/gitannex.rss" url="http://identi.ca/tag/gitannex"]]
+* [[!aggregate expirecount=25 name="twitter" feedurl="http://search.twitter.com/search.atom?q=git-annex" url="http://twitter.com/"]]
+
diff --git a/doc/forum/Auto_archiving.mdwn b/doc/forum/Auto_archiving.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Auto_archiving.mdwn
@@ -0,0 +1,17 @@
+I've been toying with the idea of auto archiving files (that is - removing them from .) based on a set of rules.
+
+Git already provides attribute management for files and I put together a simple script that tries to achieve the following:
+
+* Look for files with X copies (including a local one)
+* Verify that the file is not recent (defined as not having been dropped or getted within X days )
+* Verify that the file has an annex.archive attribute
+* Archive if the above is met
+
+Here is the script:
+http://pastebin.com/53iLqyPd
+
+You just add the annex.archive attribute to files via .gitattributes to use
+
+The script runs in preview mode... exec with autoArchive.sh commit to drop the files.
+
+Open to thoughts / suggestions
diff --git a/doc/forum/Handling_web_special_remote_when_content_changes__63__.mdwn b/doc/forum/Handling_web_special_remote_when_content_changes__63__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Handling_web_special_remote_when_content_changes__63__.mdwn
@@ -0,0 +1,19 @@
+I am in the process of organising all of my documents (pdf/ps/etc.) and putting them into an annex.  It seems like the easiest (and smartest?) way for me to manage my 15,000+ document library...  (Yeah, I've not read them all!)
+
+However, one of the issues I have run into is when I want to include something like a software manual...
+
+I'm not even sure if git-annex is the correct way to handle these sort of document, but...
+
+What would you do if the URL stays the same, but the file content changes over time?
+
+For example, the administration manual for R gets updated and re-released for each release of R, but the URL stays the same.
+
+    http://cran.r-project.org/doc/manuals/R-admin.pdf
+
+I'm not particularly worried about whether my annex version is the most recent, just that if I want to 'annex get' it, it will pull *a* version from somewhere.
+
+Any thoughts?
+
+I'd bet that the SHA-hash would change between releases(!) so would a WORM backend be the best approach?  It would mess up my one-file-in-multiple-directories (i.e. multiple simlinks to the one SHA-...-.....) approach.
+
+ 
diff --git a/doc/forum/git-subtree_support__63__.mdwn b/doc/forum/git-subtree_support__63__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__.mdwn
@@ -0,0 +1,9 @@
+Hi,
+
+I am a happy user of [git-subtree](http://github.com/apenwarr/git-subtree), and I wonder whether it integrates nicely with git-annex?
+
+My use-case looks like this: I have two annex repositories -- one at home and one at work. The annex at work is a strict subset of the one at home, i.e. all files that I have at work ought to be part of the annex at home, but not the other way round. Now, I realize that I could have one annex and selectively copy files to the checked out copy at work, but I don't want to do that because I don't want to have (broken) symlinks for all kinds of stuff visible on my machine at work that is not supposed to be there (such as MP3 files, etc.). Instead, I would like to use git-subtree to import the work annex into a sub-directory of the one at home, so that both annex are logically separate, but still the one at home always contains everything that the one at work contains.
+
+Is that possible?
+
+And if not, is there maybe another way to accomplish this kind of thing?
diff --git a/doc/forum/git-subtree_support__63__/comment_1_4f333cb71ed1ff259bbfd86704806aa6._comment b/doc/forum/git-subtree_support__63__/comment_1_4f333cb71ed1ff259bbfd86704806aa6._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_1_4f333cb71ed1ff259bbfd86704806aa6._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-03T17:00:53Z"
+ content="""
+I have no experience using git-subtree, but as long as the home repository has the work one as a git remote, it will automatically merge work's git-annex branch with its own git-annex branch, and so will know what files are present at work, and will be able to get them.
+
+Probably you won't want to make work have home as a remote, so work's git-annex will not know which files home has, nor will it be able to copy files to home (but home will be able to copy files to work).
+"""]]
diff --git a/doc/forum/git-subtree_support__63__/comment_2_73d2a015b1ac79ec99e071a8b1e29034._comment b/doc/forum/git-subtree_support__63__/comment_2_73d2a015b1ac79ec99e071a8b1e29034._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_2_73d2a015b1ac79ec99e071a8b1e29034._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://peter-simons.myopenid.com/"
+ ip="77.188.44.113"
+ subject="comment 2"
+ date="2012-01-03T18:11:37Z"
+ content="""
+The point of git-subtree is that I can import another repository into sub-directory, i.e. I can have a directory called \"work\" that contains all files from the annex I have at work. If I make the other annex a remote and merge its contents, then all contents is going to be merged at the top-level, which is somewhat undesirable in my particular case.
+"""]]
diff --git a/doc/forum/git-subtree_support__63__/comment_3_c533400e22c306c033fcd56e64761b0b._comment b/doc/forum/git-subtree_support__63__/comment_3_c533400e22c306c033fcd56e64761b0b._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_3_c533400e22c306c033fcd56e64761b0b._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 3"
+ date="2012-01-03T18:42:08Z"
+ content="""
+You can make it a remote without merging its contents. Git will not merge its contents by default unless it's named \"origin\". git-annex will be prefectly happy with that.
+"""]]
diff --git a/doc/forum/git-subtree_support__63__/comment_4_75b0e072e668aa46ff0a8d62a6620306._comment b/doc/forum/git-subtree_support__63__/comment_4_75b0e072e668aa46ff0a8d62a6620306._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_4_75b0e072e668aa46ff0a8d62a6620306._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://peter-simons.myopenid.com/"
+ ip="77.188.44.113"
+ subject="comment 4"
+ date="2012-01-03T19:17:36Z"
+ content="""
+Okay, I see, but is `git annex get --auto .` going to import all those files from the work remote into my home if the `master` branch of that remote isn't merged?
+"""]]
diff --git a/doc/forum/git-subtree_support__63__/comment_5_f5ec9649d9f1dc122e715de5533bc674._comment b/doc/forum/git-subtree_support__63__/comment_5_f5ec9649d9f1dc122e715de5533bc674._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_5_f5ec9649d9f1dc122e715de5533bc674._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 5"
+ date="2012-01-03T19:31:45Z"
+ content="""
+Yes; git-annex uses the git-annex branch independently of the branch you have checked out. You may find [[internals]] interesting reading, but the short answer is it will work.
+"""]]
diff --git a/doc/forum/git-subtree_support__63__/comment_6_85df530f7b6d76b74ac8017c6034f95e._comment b/doc/forum/git-subtree_support__63__/comment_6_85df530f7b6d76b74ac8017c6034f95e._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/git-subtree_support__63__/comment_6_85df530f7b6d76b74ac8017c6034f95e._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://peter-simons.myopenid.com/"
+ ip="77.188.44.113"
+ subject="comment 6"
+ date="2012-01-03T19:47:11Z"
+ content="""
+Very cool! Thank you for the explanation.
+"""]]
diff --git a/doc/forum/unlock__47__lock_always_gets_me.mdwn b/doc/forum/unlock__47__lock_always_gets_me.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/unlock__47__lock_always_gets_me.mdwn
@@ -0,0 +1,11 @@
+Several times now I've done something like:
+
+    $ git annex unlock movie.avi
+    $ mv /tmp/fixed.avi movie.avi
+    $ git annex lock movie.avi
+
+Oops, I just lost my fixed.avi!  That really feels like the right sequence of operations to me, so I'm always surprised when I make that mistake.  I would like to see the current `lock` renamed to something like `undo-unlock`, or have the behavior changed to be the same as `add`, or maybe warn and require a `--force` when the file has been changed.
+
+If changing current behavior is undesirable, maybe `unlock` could just print a reminder that `git annex add` is the correct next step after making changes?
+
+Failing that, I suppose I could slowly start to learn from my mistakes.
diff --git a/doc/forum/unlock__47__lock_always_gets_me/comment_1_dee73a7ea3e1a5154601adb59782831f._comment b/doc/forum/unlock__47__lock_always_gets_me/comment_1_dee73a7ea3e1a5154601adb59782831f._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/unlock__47__lock_always_gets_me/comment_1_dee73a7ea3e1a5154601adb59782831f._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 1"
+ date="2012-01-07T17:15:31Z"
+ content="""
+Well, lock could check for modifications and require --force to lose them. But the check could be expensive for large files.
+
+But `git annex lock` is just a convenient way to run `git checkout`. And running `git checkout` or `git reset --hard` will lose your uncommitted file the same way obviously. 
+
+Perhaps the best fix would be to get rid of `lock` entirely, and let the user use the underlying git commands same as they would to drop modifications to other files. It would then also make sense to remove `unlock`, leaving only `edit`.
+"""]]
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -64,7 +64,8 @@
 
   Adds files in the path to the annex. Files that are already checked into
   git, or that git has been configured to ignore will be silently skipped.
-  (Use --force to add ignored files.)
+  (Use --force to add ignored files.) Dotfiles are skipped unless explicitly
+  listed.
 
 * get [path ...]
 
@@ -127,8 +128,8 @@
   the default is to sync with all remotes. Or specify --fast to sync with
   the remotes with the lowest annex-cost value.
 
-  The sync process involves first committing all local changes, then
-  fetching and merging the `synced/master` and the `git-annex` branch
+  The sync process involves first committing all local changes (git commit -a),
+  then fetching and merging the `synced/master` and the `git-annex` branch
   from the remote repositories and finally pushing the changes back to
   those branches on the remote repositories. You can use standard git
   commands to do each of those steps by hand, or if you don't want to
@@ -272,6 +273,18 @@
   Displays a list of repositories known to contain the content of the
   specified file or files.
 
+* log [path ...]
+
+  Displays the location log for the specified file or files,
+  showing each repository they were added to ("+") and removed from ("-").
+
+  To limit how far back to seach for location log changes, the options
+  --since, --after, --until, --before, and --max-count can be specified.
+  They are passed through to git log. For example, --since "1 month ago"
+
+  To generate output suitable for the gource visualisation program,
+  specify --gource.
+
 * status
 
   Displays some statistics and other information, including how much data
@@ -593,6 +606,12 @@
 
   git-annex caches UUIDs of remote repositories here.
 
+* `remote.<name>.annex-trustlevel`
+
+  Configures a local trust level for the remote. This overrides the value
+  configured by the trust and untrust commands. The value can be any of
+  "trusted", "semitrusted" or "untrusted".
+
 * `remote.<name>.annex-ssh-options`
 
   Options to use when using ssh to talk to this remote.
@@ -603,16 +622,22 @@
   to or from this remote. For example, to force ipv6, and limit
   the bandwidth to 100Kbyte/s, set it to "-6 --bwlimit 100"
 
+* `remote.<name>.annex-web-options`
+
+  Options to use when using wget or curl to download a file from the web.
+  (wget is always used in preference to curl if available).
+  For example, to force ipv4 only, set it to "-4"
+
 * `remote.<name>.annex-bup-split-options`
 
   Options to pass to bup split when storing content in this remote.
   For example, to limit the bandwidth to 100Kbye/s, set it to "--bwlimit 100k"
   (There is no corresponding option for bup join.)
 
-* `annex.ssh-options`, `annex.rsync-options`, `annex.bup-split-options`
+* `annex.ssh-options`, `annex.rsync-options`, `annex.web-options, `annex.bup-split-options`
 
-  Default ssh, rsync, and bup options to use if a remote does not have
-  specific options.
+  Default ssh, rsync, wget/curl, and bup options to use if a remote does not
+  have specific options.
 
 * `remote.<name>.buprepo`
 
diff --git a/doc/how_it_works.mdwn b/doc/how_it_works.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/how_it_works.mdwn
@@ -0,0 +1,41 @@
+This page gives a high-level view of git-annex. For a detailed
+low-level view, see [[the_man_page|git-annex]] and [[internals]].
+
+You do not need to read this page to get started with using git-annex. The
+[[walkthrough]] provides step-by-step instructions.
+
+Still reading? Ok. Git's man page calls it "a stupid content
+tracker". With git-annex, git is instead "a stupid filename and metadata"
+tracker. The contents of large files are not stored in git, only the
+names of the files and some other metadata remain there.
+
+The contents of the files are kept by git-annex in a distributed key/value
+store consisting of every clone of a given git repository. That's a fancy
+way to say that git-annex stores the actual file content somewhere under
+`.git/annex/`. (See [[internals]] for details.)
+
+That was the values; what about the keys? Well, a key is calculated for a
+given file when it's first added into git-annex. Normally this uses a hash
+of its contents, but various [[backends]] can produce different sorts of
+keys. The file that gets checked into git is just a symlink to the key
+under `.git/annex/`. If the content of a file is modified, that produces
+a different key (and the symlink is changed).
+
+A file's content can be [[transferred|transferring_data]] from one
+repository to another by git-annex. Which repositories contain a given
+value is tracked by git-annex (see [[location_tracking]]). It stores this
+tracking information in a separate branch, named "git-annex". All you ever
+do with the "git-annex" branch is push/pull it around between repositories,
+to [[sync]] up git-annex's view of the world.
+
+That's really all there is to it. Oh, there are [[special_remotes]] that
+let values be stored other places than git repositories (anything from
+Amazon S3 to a USB key), and there's a pile of commands listed in
+[[the_man_page|git-annex]] to handle moving the values around and managing
+them. But if you grok the description above, you can see through all that.
+It's really just symlinks, keys, values, and a git-annex branch to store
+additional metadata.
+
+---
+
+Next: [[install]] or [[walkthrough]]
diff --git a/doc/index.mdwn b/doc/index.mdwn
--- a/doc/index.mdwn
+++ b/doc/index.mdwn
@@ -1,6 +1,6 @@
 [[!inline raw=yes pages="summary"]]
 
-To get a feel for it, see the [[walkthrough]].
+To get a feel for it, see the [[walkthrough]] or read about [[how_it_works]].
 
 [[!sidebar content="""
 [[!img logo_small.png link=no]]
@@ -51,11 +51,13 @@
 * [[internals]]
 * [[design]]
 * [[what git annex is not|not]]
-* git-annex is Free Software, licensed under the [[GPL]].
+* [[sitemap]]
 
 <br clear="all" />
 
 ----
+
+git-annex is Free Software, licensed under the [[GPL]].
 
 git-annex's wiki is powered by [Ikiwiki](http://ikiwiki.info/) and
 hosted by [Branchable](http://branchable.com/).
diff --git a/doc/install.mdwn b/doc/install.mdwn
--- a/doc/install.mdwn
+++ b/doc/install.mdwn
@@ -6,6 +6,9 @@
 * [[Fedora]]
 * [[FreeBSD]]
 * [[openSUSE]]
+* [[ArchLinux]]
+* [[NixOS]]
+* Windows: [[sorry, not possible yet|todo/windows_support]]
 
 ## Using cabal
 
@@ -29,7 +32,7 @@
   * [TestPack](http://hackage.haskell.org/cgi-bin/hackage-scripts/package/testpack)
   * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck)
   * [HTTP](http://hackage.haskell.org/package/HTTP)
-  * [hS3](http://hackage.haskell.org/package/hS3) (optional, but recommended)
+  * [hS3](http://hackage.haskell.org/package/hS3)
   * [json](http://hackage.haskell.org/package/json)
 * Shell commands
   * [git](http://git-scm.com/)
diff --git a/doc/install/ArchLinux.mdwn b/doc/install/ArchLinux.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/install/ArchLinux.mdwn
@@ -0,0 +1,9 @@
+There is a non-official source package for git-annex in
+[AUR](https://aur.archlinux.org/packages.php?ID=44272).
+
+You can then build it yourself or use a wrapper for AUR
+such as yaourt:
+
+<pre>
+$ yaourt -Sy git-annex
+</pre>
diff --git a/doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment b/doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment
deleted file mode 100644
--- a/doc/install/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawmByD9tmR48HuYgS4qWEGDDaoVTTC3m4kc"
- nickname="Jonas"
- subject="Any chance to get git-annex going on windows?"
- date="2011-06-10T18:08:36Z"
- content="""
-Would be great! :-)
-
-Jonas
-"""]]
diff --git a/doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment b/doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment
deleted file mode 100644
--- a/doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment
+++ /dev/null
@@ -1,69 +0,0 @@
-[[!comment format=mdwn
- username="http://joey.kitenet.net/"
- nickname="joey"
- subject="short answer: no"
- date="2011-06-10T19:55:38Z"
- content="""
-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>
-
-(There are probably also still some places where it assumes / as a path
-separator, although I fixed some.)
-
-FWIW, git-annex works fine on OS X and other fine proprietary unixen. ;P
-
-----
-
-Alternatively, 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
-challenge.
-
-<pre>
-addSignal
-blockSignals
-changeWorkingDirectory
-createLink
-createSymbolicLink
-emptySignalSet
-executeFile
-fileMode
-fileSize
-forkProcess
-getAnyProcessStatus
-getEffectiveUserID
-getEnvDefault
-getFileStatus
-getProcessID
-getProcessStatus
-getSignalMask
-getSymbolicLinkStatus
-getUserEntryForID
-getUserEntryForName
-groupWriteMode
-homeDirectory
-installHandler
-intersectFileModes
-isRegularFile
-isSymbolicLink
-modificationTime
-otherWriteMode
-ownerWriteMode
-readSymbolicLink
-setEnv
-setFileMode
-setSignalMask
-sigCHLD
-sigINT
-unionFileModes
-</pre>
-"""]]
diff --git a/doc/news.mdwn b/doc/news.mdwn
--- a/doc/news.mdwn
+++ b/doc/news.mdwn
@@ -3,7 +3,7 @@
 posted. git-annex users are recommended to subscribe to this page's RSS
 feed.
 
-[[!inline pages="./news/* and !*/Discussion" rootpage="news" show="30"]]
+[[!inline pages="./news/* and !./news/*/* and !*/Discussion" rootpage="news" show="30"]]
 
 """
 else="""
diff --git a/doc/news/version_3.20111107.mdwn b/doc/news/version_3.20111107.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111107.mdwn
+++ /dev/null
@@ -1,8 +0,0 @@
-git-annex 3.20111107 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * merge: Use fast-forward merges when possible.
-     Thanks Valentin Haenel for a test case showing how non-fast-forward
-     merges could result in an ongoing pull/merge/push cycle.
-   * Don't try to read config from repos with annex-ignore set.
-   * Bugfix: In the past two releases, git-annex init has written the uuid.log
-     in the wrong format, with the UUID and description flipped."""]]
diff --git a/doc/news/version_3.20111111.mdwn b/doc/news/version_3.20111111.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111111.mdwn
+++ /dev/null
@@ -1,10 +0,0 @@
-git-annex 3.20111111 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Handle a case where an annexed file is moved into a gitignored directory,
-     by having fix --force add its change.
-   * Avoid cyclic drop problems.
-   * Optimized copy --from and get --from to avoid checking the location log
-     for files that are already present.
-   * Automatically fix up badly formatted uuid.log entries produced by
-     3.20111105, whenever the uuid.log is changed (ie, by init or describe).
-   * map: Support remotes with /~/ and /~user/"""]]
diff --git a/doc/news/version_3.20111122.mdwn b/doc/news/version_3.20111122.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111122.mdwn
+++ /dev/null
@@ -1,22 +0,0 @@
-git-annex 3.20111122 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * merge: Improve commit messages to mention what was merged.
-   * Avoid doing auto-merging in commands that don't need fully current
-     information from the git-annex branch. In particular, git annex add
-     no longer needs to auto-merge.
-   * init: When run in an already initalized repository, and without
-     a description specified, don't delete the old description.
-   * Optimised union merging; now only runs git cat-file once, and runs
-     in constant space.
-   * status: Now displays trusted, untrusted, and semitrusted repositories
-     separately.
-   * status: Include all special remotes in the list of repositories.
-   * status: Fix --json mode.
-   * status: --fast is back
-   * Fix support for insteadOf url remapping. Closes: #[644278](http://bugs.debian.org/644278)
-   * When not run in a git repository, git-annex can still display a usage
-     message, and "git annex version" even works.
-   * migrate: Don't fall over a stale temp file.
-   * Avoid excessive escaping for rsync special remotes that are not accessed
-     over ssh.
-   * find: Support --print0"""]]
diff --git a/doc/news/version_3.20111122~bpo60+2.mdwn b/doc/news/version_3.20111122~bpo60+2.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111122~bpo60+2.mdwn
+++ /dev/null
@@ -1,22 +0,0 @@
-git-annex 3.20111122~bpo60+2 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * merge: Improve commit messages to mention what was merged.
-   * Avoid doing auto-merging in commands that don't need fully current
-     information from the git-annex branch. In particular, git annex add
-     no longer needs to auto-merge.
-   * init: When run in an already initalized repository, and without
-     a description specified, don't delete the old description.
-   * Optimised union merging; now only runs git cat-file once, and runs
-     in constant space.
-   * status: Now displays trusted, untrusted, and semitrusted repositories
-     separately.
-   * status: Include all special remotes in the list of repositories.
-   * status: Fix --json mode.
-   * status: --fast is back
-   * Fix support for insteadOf url remapping. Closes: #[644278](http://bugs.debian.org/644278)
-   * When not run in a git repository, git-annex can still display a usage
-     message, and "git annex version" even works.
-   * migrate: Don't fall over a stale temp file.
-   * Avoid excessive escaping for rsync special remotes that are not accessed
-     over ssh.
-   * find: Support --print0"""]]
diff --git a/doc/news/version_3.20111203.mdwn b/doc/news/version_3.20111203.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111203.mdwn
+++ /dev/null
@@ -1,19 +0,0 @@
-git-annex 3.20111203 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * The VFAT filesystem on recent versions of Linux, when mounted with
-     shortname=mixed, does not get along well with git-annex's mixed case
-     .git/annex/objects hash directories. To avoid this problem, new content
-     is now stored in all-lowercase hash directories. Except for non-bare
-     repositories which would be a pain to transition and cannot be put on FAT.
-     (Old mixed-case hash directories are still tried for backwards
-     compatibility.)
-   * Flush json output, avoiding a buffering problem that could result in
-     doubled output.
-   * Avoid needing haskell98 and other fixes for new ghc. Thanks, Mark Wright.
-   * Bugfix: dropunused did not drop keys with two spaces in their name.
-   * Support for storing .git/annex on a different device than the rest of the
-     git repository.
-   * --inbackend can be used to make git-annex only operate on files
-     whose content is stored using a specified key-value backend.
-   * dead: A command which says that a repository is gone for good
-     and you don't want git-annex to mention it again."""]]
diff --git a/doc/news/version_3.20111211.mdwn b/doc/news/version_3.20111211.mdwn
deleted file mode 100644
--- a/doc/news/version_3.20111211.mdwn
+++ /dev/null
@@ -1,20 +0,0 @@
-git-annex 3.20111211 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Fix bug in last version in getting contents from bare repositories.
-   * Ensure that git-annex branch changes are merged into git-annex's index,
-     which fixes a bug that could cause changes that were pushed to the
-     git-annex branch to get reverted. As a side effect, it's now safe
-     for users to check out and commit changes directly to the git-annex
-     branch.
-   * map: Fix a failure to detect a loop when both repositories are local
-     and refer to each other with relative paths.
-   * Prevent key names from containing newlines.
-   * add: If interrupted, add can leave files converted to symlinks but not
-     yet added to git. Running the add again will now clean up this situtation.
-   * Fix caching of decrypted ciphers, which failed when drop had to check
-     multiple different encrypted special remotes.
-   * unannex: Can be run on files that have been added to the annex, but not
-     yet committed.
-   * sync: New command that synchronises the local repository and default
-     remote, by running git commit, pull, and push for you.
-   * Version monad-control dependency in cabal file."""]]
diff --git a/doc/news/version_3.20120105.mdwn b/doc/news/version_3.20120105.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120105.mdwn
@@ -0,0 +1,14 @@
+git-annex 3.20120105 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Added annex-web-options configuration settings, which can be
+     used to provide parameters to whichever of wget or curl git-annex uses
+     (depends on which is available, but most of their important options
+     suitable for use here are the same).
+   * Dotfiles, and files inside dotdirs are not added by "git annex add"
+     unless the dotfile or directory is explicitly listed. So "git annex add ."
+     will add all untracked files in the current directory except for those in
+     dotdirs.
+   * Added quickcheck to build dependencies, and fail if test suite cannot be
+     built.
+   * fsck: Do backend-specific check before checking numcopies is satisfied.
+   * Run gpg with --no-tty. Closes: #[654721](http://bugs.debian.org/654721)"""]]
diff --git a/doc/news/version_3.20120106.mdwn b/doc/news/version_3.20120106.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120106.mdwn
@@ -0,0 +1,6 @@
+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."""]]
diff --git a/doc/news/version_3.20120106/comment_1_fb1a3135e2d9f39f2c372ccc2c50c85a._comment b/doc/news/version_3.20120106/comment_1_fb1a3135e2d9f39f2c372ccc2c50c85a._comment
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120106/comment_1_fb1a3135e2d9f39f2c372ccc2c50c85a._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://peter-simons.myopenid.com/"
+ ip="77.186.134.113"
+ subject="Not announced on Hackage?"
+ date="2012-01-13T17:37:11Z"
+ content="""
+We have the [latest version in NixOS](http://hydra.nixos.org/job/nixpkgs/trunk/gitAndTools.gitAnnex), but we cannot advertise that fact on Hackage because it seems the corresponding Cabal file hasn't been uploaded to <http://hackage.haskell.org/package/git-annex>. Is there any particular reason why Hackage doesn't know about this release?
+"""]]
diff --git a/doc/news/version_3.20120106/comment_2_ae292ca7294b6790233e545086c3ac2f._comment b/doc/news/version_3.20120106/comment_2_ae292ca7294b6790233e545086c3ac2f._comment
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120106/comment_2_ae292ca7294b6790233e545086c3ac2f._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 2"
+ date="2012-01-13T17:52:46Z"
+ content="""
+Uploading to hackage is a PITA (manual password entry and I am often on a slow link besides) and is not integrated with my regular release process, so I often forget to do it. I will try to upload the next release there again.
+
+You might add a page under [[install]] for your git-annex packages.
+"""]]
diff --git a/doc/news/version_3.20120106/comment_3_29ccda9ac458fd5cc9ec5508c62df6ea._comment b/doc/news/version_3.20120106/comment_3_29ccda9ac458fd5cc9ec5508c62df6ea._comment
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120106/comment_3_29ccda9ac458fd5cc9ec5508c62df6ea._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="http://peter-simons.myopenid.com/"
+ ip="77.186.134.113"
+ subject="comment 3"
+ date="2012-01-13T18:48:28Z"
+ content="""
+For what it's worth, the package `cabal-install` is pretty good for uploading packages to Hackages, among other things. It allows users to configure their username/password, and then making a release to Hackage is as simple as running:
+
+    cabal upload foo-version.tar.gz
+
+I use that to do releases of my stuff, too, and I'm quite happy. `cabal-install` has other features, too, of course.
+"""]]
diff --git a/doc/news/version_3.20120113.mdwn b/doc/news/version_3.20120113.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20120113.mdwn
@@ -0,0 +1,8 @@
+git-annex 3.20120113 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * log: Add --gource mode, which generates output usable by gource.
+   * map: Fix display of remote repos
+   * Add annex-trustlevel configuration settings, which can be used to
+     override the trust level of a remote.
+   * git-annex, git-union-merge: Support GIT\_DIR and GIT\_WORK\_TREE.
+   * Add libghc-testpack-dev to build depends on all arches."""]]
diff --git a/doc/news/version_backport.mdwn b/doc/news/version_backport.mdwn
deleted file mode 100644
--- a/doc/news/version_backport.mdwn
+++ /dev/null
@@ -1,6 +0,0 @@
-git-annex backport released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Removed conflict on newer version of git, this backport can now be used
-     with either stable's git, or the git backport.
-   * This backport continues to be modified to not build without monad-control,
-     which is not available in stable."""]]
diff --git a/doc/sitemap.mdwn b/doc/sitemap.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/sitemap.mdwn
@@ -0,0 +1,3 @@
+[[!map pages="page(*) and !*/discussion and !recentchanges
+and !bugs/* and !examples/*/* and !news/* and !tips/*
+and !forum/* and !todo/* and !users/* and !ikiwiki/*"]]
diff --git a/doc/tips/using_gitolite_with_git-annex/comment_9_28418635a6ed7231b89e02211cd3c236._comment b/doc/tips/using_gitolite_with_git-annex/comment_9_28418635a6ed7231b89e02211cd3c236._comment
new file mode 100644
--- /dev/null
+++ b/doc/tips/using_gitolite_with_git-annex/comment_9_28418635a6ed7231b89e02211cd3c236._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://joey.kitenet.net/"
+ nickname="joey"
+ subject="comment 9"
+ date="2012-01-02T16:27:55Z"
+ content="""
+Ah right. git-annex normalizes all git ssh style user@host:dir to valid uris, which is where the `/~/` comes from. I don't anticipate this changing on the git-annex side.
+"""]]
diff --git a/doc/tips/visualizing_repositories_with_gource.mdwn b/doc/tips/visualizing_repositories_with_gource.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/tips/visualizing_repositories_with_gource.mdwn
@@ -0,0 +1,22 @@
+[Gource](http://code.google.com/p/gource/) is an amazing animated
+visualisation of a git repository.
+
+Normally, gource shows files being added, removed, and changed in
+the repository, and the user(s) making the changes. Of course it can be
+used in this way in a repository using git-annex too; just run `gource`.
+
+The other way to use gource with git-annex is to visualise the movement of
+annexed file contents between repositories. In this view, the "users" are
+repositories, and they move around the file contents that are being added
+or removed from them with git-annex.
+
+[[!img screenshot.jpg]]
+
+To use gource this way, first go into the directory you want to visualize,
+and use `git annex log` to make an input file for `gource`:
+
+	git annex log --gource | tee gorce.log
+	sort gource.log | gource --log-format custom -
+
+The `git annex log` can take a while, to speed it up you can use something
+like `--after "4 monts ago"` to limit how far back it goes.
diff --git a/doc/tips/visualizing_repositories_with_gource/screenshot.jpg b/doc/tips/visualizing_repositories_with_gource/screenshot.jpg
new file mode 100644
Binary files /dev/null and b/doc/tips/visualizing_repositories_with_gource/screenshot.jpg differ
diff --git a/doc/todo/windows_support.mdwn b/doc/todo/windows_support.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/todo/windows_support.mdwn
@@ -0,0 +1,65 @@
+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>
+
+(There are probably also still some places where it assumes / as a path
+separator, although I fixed some. Probably almost all are fixed now.)
+
+FWIW, git-annex works fine on OS X and other fine proprietary unixen. ;P
+--[[Joey]]
+
+----
+
+Alternatively, 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
+challenge.
+
+<pre>
+addSignal
+blockSignals
+changeWorkingDirectory
+createLink
+createSymbolicLink
+emptySignalSet
+executeFile
+fileMode
+fileSize
+forkProcess
+getAnyProcessStatus
+getEffectiveUserID
+getEnvDefault
+getFileStatus
+getProcessID
+getProcessStatus
+getSignalMask
+getSymbolicLinkStatus
+getUserEntryForID
+getUserEntryForName
+groupWriteMode
+homeDirectory
+installHandler
+intersectFileModes
+isRegularFile
+isSymbolicLink
+modificationTime
+otherWriteMode
+ownerWriteMode
+readSymbolicLink
+setEnv
+setFileMode
+setSignalMask
+sigCHLD
+sigINT
+unionFileModes
+</pre>
diff --git a/doc/transferring_data.mdwn b/doc/transferring_data.mdwn
--- a/doc/transferring_data.mdwn
+++ b/doc/transferring_data.mdwn
@@ -10,7 +10,7 @@
 
 It's equally easy to transfer a single file to or from a repository,
 or to launch a retrievel of a massive pile of files from whatever
-repositories they are scattered amoung.
+repositories they are scattered amongst.
 
 git-annex automatically uses whatever remotes are currently accessible,
 preferring ones that are less expensive to talk to.
diff --git a/git-annex-shell.hs b/git-annex-shell.hs
--- a/git-annex-shell.hs
+++ b/git-annex-shell.hs
@@ -13,6 +13,7 @@
 import CmdLine
 import Command
 import Annex.UUID
+import qualified Option
 
 import qualified Command.ConfigList
 import qualified Command.InAnnex
@@ -36,10 +37,12 @@
 cmds :: [Command]
 cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
 	where
-		adddirparam c = c { cmdparams = "DIRECTORY " ++ cmdparams c }
+		adddirparam c = c
+			{ cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c
+			}
 
 options :: [OptDescr (Annex ())]
-options = commonOptions ++
+options = Option.common ++
 	[ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "repository uuid"
 	]
 	where
diff --git a/git-annex.cabal b/git-annex.cabal
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -1,5 +1,5 @@
 Name: git-annex
-Version: 3.20111231
+Version: 3.20120113
 Cabal-Version: >= 1.6
 License: GPL
 Maintainer: Joey Hess <joey@kitenet.net>
@@ -7,7 +7,7 @@
 Stability: Stable
 Copyright: 2010-2011 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 ./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/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/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/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/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/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/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/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/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/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/No_version_information_from_cli.mdwn ./doc/bugs/Error_while_adding_a_file___34__createSymbolicLink:_already_exists__34__.mdwn ./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/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/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.20111122.mdwn ./doc/news/version_3.20111203.mdwn ./doc/news/version_3.20111107.mdwn ./doc/news/version_backport.mdwn ./doc/news/version_3.20111122~bpo60+2.mdwn ./doc/news/version_3.20111231.mdwn ./doc/news/LWN_article.mdwn ./doc/news/version_3.20111211.mdwn ./doc/news/version_3.20111111.mdwn ./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/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/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/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/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/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_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/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/comment_3_cff163ea3e7cad926f4ed9e78b896598._comment ./doc/install/Debian/comment_2_648e3467e260cdf233acdb0b53313ce0._comment ./doc/install/Debian/comment_1_029486088d098c2d4f1099f2f0e701a9._comment ./doc/install/Fedora.mdwn ./doc/install/comment_4_82a17eee4a076c6c79fddeda347e0c9a._comment ./doc/walkthrough.mdwn ./CmdLine.hs ./Messages.hs ./Setup.hs ./GPL ./mdwn2man ./Remote.hs ./git-annex.hs ./Locations.hs ./Backend/WORM.hs ./Backend/URL.hs ./Backend/SHA.hs ./Options.hs ./git-annex-shell.hs ./test.hs ./INSTALL ./Remote/Web.hs ./Remote/S3stub.hs ./Remote/Helper/Encryptable.hs ./Remote/Helper/Special.hs ./Remote/Directory.hs ./Remote/S3real.hs ./Remote/Bup.hs ./Remote/Hook.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/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/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/manpages ./debian/changelog ./debian/doc-base ./debian/copyright ./debian/compat ./Messages/JSON.hs ./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/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.20120105.mdwn ./doc/news/version_3.20111231.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/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/Queue.hs ./Annex/Branch.hs ./Annex/UUID.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
 Homepage: http://git-annex.branchable.com/
 Build-type: Custom
 Category: Utility
@@ -31,7 +31,7 @@
   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
+   base < 5, monad-control, transformers-base, lifted-base, quickcheck >= 2.1
 
 Executable git-annex-shell
   Main-Is: git-annex-shell.hs
diff --git a/git-union-merge.hs b/git-union-merge.hs
--- a/git-union-merge.hs
+++ b/git-union-merge.hs
@@ -28,9 +28,7 @@
 setup = cleanup -- idempotency
 
 cleanup :: Git.Repo -> IO ()
-cleanup g = do
-	e' <- doesFileExist (tmpIndex g)
-	when e' $ removeFile (tmpIndex g)
+cleanup g = whenM (doesFileExist $ tmpIndex g) $ removeFile $ tmpIndex g
 
 parseArgs :: IO [String]
 parseArgs = do
@@ -42,8 +40,8 @@
 main :: IO ()
 main = do
 	[aref, bref, newref] <- map Git.Ref <$> parseArgs
-	g <- Git.Config.read =<< Git.Construct.fromCwd
-	_ <- Git.Index.override (tmpIndex g)
+	g <- Git.Config.read =<< Git.Construct.fromCurrent
+	_ <- Git.Index.override $ tmpIndex g
 	setup g
 	Git.UnionMerge.merge aref bref g
 	_ <- Git.Branch.commit "union merge" newref [aref, bref] g
diff --git a/test.hs b/test.hs
--- a/test.hs
+++ b/test.hs
@@ -32,7 +32,6 @@
 import qualified Types.Backend
 import qualified Types
 import qualified GitAnnex
-import qualified Logs.Location
 import qualified Logs.UUIDBased
 import qualified Logs.Trust
 import qualified Logs.Remote
@@ -663,6 +662,8 @@
 -- gpg is not a build dependency, so only test when it's available
 test_crypto :: Test
 test_crypto = "git-annex crypto" ~: intmpclonerepo $ when Build.SysConfig.gpg $ do
+	-- force gpg into batch mode for the tests
+	setEnv "GPG_BATCH" "1" True
 	Utility.Gpg.testTestHarness @? "test harness self-test failed"
 	Utility.Gpg.testHarness $ do
 		createDirectory "dir"
@@ -726,7 +727,7 @@
 -- are not run; this should only be used for actions that query state.
 annexeval :: Types.Annex a -> IO a
 annexeval a = do
-	g <- Git.Construct.fromCwd
+	g <- Git.Construct.fromCurrent
 	g' <- Git.Config.read g
 	s <- Annex.new g'
 	Annex.eval s { Annex.output = Annex.QuietOutput } a
@@ -845,7 +846,7 @@
 	r <- annexeval $ Backend.lookupFile f
 	case r of
 		Just (k, _) -> do
-			uuids <- annexeval $ Logs.Location.keyLocations k
+			uuids <- annexeval $ Remote.keyLocations k
 			assertEqual ("bad content in location log for " ++ f ++ " key " ++ (show k) ++ " uuid " ++ show thisuuid)
 				expected (thisuuid `elem` uuids)
 		_ -> assertFailure $ f ++ " failed to look up key"
