diff --git a/.git-annex.cabal.swp b/.git-annex.cabal.swp
new file mode 100644
Binary files /dev/null and b/.git-annex.cabal.swp differ
diff --git a/Annex.hs b/Annex.hs
--- a/Annex.hs
+++ b/Annex.hs
@@ -108,7 +108,6 @@
 	, uuidmap :: Maybe UUIDMap
 	, preferredcontentmap :: Maybe PreferredContentMap
 	, shared :: Maybe SharedRepository
-	, direct :: Maybe Bool
 	, forcetrust :: TrustMap
 	, trustmap :: Maybe TrustMap
 	, groupmap :: Maybe GroupMap
@@ -138,7 +137,6 @@
 	, uuidmap = Nothing
 	, preferredcontentmap = Nothing
 	, shared = Nothing
-	, direct = Nothing
 	, forcetrust = M.empty
 	, trustmap = Nothing
 	, groupmap = Nothing
diff --git a/Annex/Content.hs b/Annex/Content.hs
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -50,6 +50,7 @@
 import Git.SharedRepository
 import Annex.Perms
 import Annex.Content.Direct
+import Backend
 
 {- Checks if a given key's content is currently present. -}
 inAnnex :: Key -> Annex Bool
@@ -80,7 +81,7 @@
 {- A safer check; the key's content must not only be present, but
  - is not in the process of being removed. -}
 inAnnexSafe :: Key -> Annex (Maybe Bool)
-inAnnexSafe = inAnnex' (maybe False id) (Just False) go
+inAnnexSafe = inAnnex' (fromMaybe False) (Just False) go
   where
 	go f = liftIO $ openforlock f >>= check
 	openforlock f = catchMaybeIO $
@@ -239,42 +240,35 @@
 moveAnnex :: Key -> FilePath -> Annex ()
 moveAnnex key src = withObjectLoc key storeobject storedirect
   where
-	storeobject dest = do
-		ifM (liftIO $ doesFileExist dest)
-			( liftIO $ removeFile src
-			, do
-				createContentDir dest
-				liftIO $ moveFile src dest
-				freezeContent dest
-				freezeContentDir dest
-			)
-	storedirect fs = storedirect' =<< liftIO (filterM validsymlink fs)
-
-	validsymlink f = do
-		tl <- tryIO $ readSymbolicLink f
-		return $ case tl of
-			Right l
-				| isLinkToAnnex l ->
-					Just key == fileKey (takeFileName l)
-			_ -> False
+	storeobject dest = ifM (liftIO $ doesFileExist dest)
+		( liftIO $ removeFile src
+		, do
+			createContentDir dest
+			liftIO $ moveFile src dest
+			freezeContent dest
+			freezeContentDir dest
+		)
+	storedirect fs = storedirect' =<< filterM validsymlink fs
+	validsymlink f = (==) (Just key) <$> isAnnexLink f
 
 	storedirect' [] = storeobject =<< inRepo (gitAnnexLocation key)
 	storedirect' (dest:fs) = do
-		updateCache key src
+		updateInodeCache key src
 		thawContent src
-		liftIO $ replaceFile dest $ moveFile src
-		liftIO $ forM_ fs $ \f -> replaceFile f $
-			void . copyFileExternal dest
+		replaceFile dest $ liftIO . moveFile src
+		forM_ fs $ \f -> replaceFile f $
+			void . liftIO . copyFileExternal dest
 
 {- Replaces any existing file with a new version, by running an action.
  - First, makes sure the file is deleted. Or, if it didn't already exist,
  - makes sure the parent directory exists. -}
-replaceFile :: FilePath -> (FilePath -> IO ()) -> IO ()
+replaceFile :: FilePath -> (FilePath -> Annex ()) -> Annex ()
 replaceFile file a = do
-	r <- tryIO $ removeFile file
-	case r of
-		Left _ -> createDirectoryIfMissing True (parentDir file)
-		_ -> noop
+	liftIO $ do
+		r <- tryIO $ removeFile file
+		case r of
+			Left _ -> createDirectoryIfMissing True $ parentDir file
+			_ -> noop
 	a file
 
 {- Runs an action to transfer an object's content.
@@ -283,7 +277,7 @@
  - If this happens, runs the rollback action and returns False. The
  - rollback action should remove the data that was transferred.
  -}
-sendAnnex :: Key -> (Annex ()) -> (FilePath -> Annex Bool) -> Annex Bool
+sendAnnex :: Key -> Annex () -> (FilePath -> Annex Bool) -> Annex Bool
 sendAnnex key rollback sendobject = go =<< prepSendAnnex key
   where
 	go Nothing = return False
@@ -308,10 +302,10 @@
 	indirect f = return $ Just (f, return True)
 	direct [] = return Nothing
 	direct (f:fs) = do
-		cache <- recordedCache key
+		cache <- recordedInodeCache key
 		-- check that we have a good file
-		ifM (compareCache f cache)
-			( return $ Just (f, compareCache f cache)
+		ifM (liftIO $ compareInodeCache f cache)
+			( return $ Just (f, liftIO $ compareInodeCache f cache)
 			, direct fs
 			)
 
@@ -335,12 +329,9 @@
 cleanObjectLoc :: Key -> Annex ()
 cleanObjectLoc key = do
 	file <- inRepo $ gitAnnexLocation key
-	liftIO $ do
-		let dir = parentDir file
-		void $ catchMaybeIO $ do
-			allowWrite dir
-			removeDirectoryRecursive dir
-		removeparents dir (2 :: Int)
+	unlessM crippledFileSystem $
+		void $ liftIO $ catchMaybeIO $ allowWrite $ parentDir file
+	liftIO $ removeparents file (3 :: Int)
   where
 	removeparents _ 0 = noop
 	removeparents file n = do
@@ -356,28 +347,30 @@
 removeAnnex key = withObjectLoc key remove removedirect
   where
 	remove file = do
-		liftIO $ do
-			allowWrite $ parentDir file
-			removeFile file
+		unlessM crippledFileSystem $
+			liftIO $ allowWrite $ parentDir file
+		liftIO $ nukeFile file
+		removeInodeCache key
 		cleanObjectLoc key
 	removedirect fs = do
-		cache <- recordedCache key
+		cache <- recordedInodeCache key
+		removeInodeCache key
 		mapM_ (resetfile cache) fs
-		cleanObjectLoc key
-	resetfile cache f = whenM (compareCache f cache) $ do
+	resetfile cache f = whenM (liftIO $ compareInodeCache f cache) $ do
 		l <- calcGitLink f key
 		top <- fromRepo Git.repoPath
 		cwd <- liftIO getCurrentDirectory
 		let top' = fromMaybe top $ absNormPath cwd top
 		let l' = relPathDirToFile top' (fromMaybe l $ absNormPath top' l)
-		liftIO $ replaceFile f $ const $
-			createSymbolicLink l' f
+		replaceFile f $ const $
+			makeAnnexLink l' f
 
 {- Moves a key's file out of .git/annex/objects/ -}
 fromAnnex :: Key -> FilePath -> Annex ()
 fromAnnex key dest = do
 	file <- inRepo $ gitAnnexLocation key
-	liftIO $ allowWrite $ parentDir file
+	unlessM crippledFileSystem $
+		liftIO $ allowWrite $ parentDir file
 	thawContent file
 	liftIO $ moveFile file dest
 	cleanObjectLoc key
@@ -390,23 +383,29 @@
 	bad <- fromRepo gitAnnexBadDir
 	let dest = bad </> takeFileName src
 	createAnnexDirectory (parentDir dest)
-	liftIO $ do
-		allowWrite (parentDir src)
-		moveFile src dest
+	unlessM crippledFileSystem $
+		liftIO $ allowWrite (parentDir src)
+	liftIO $ moveFile src dest
 	cleanObjectLoc key
 	logStatus key InfoMissing
 	return dest
 
-{- List of keys whose content exists in .git/annex/objects/ -}
+{- List of keys whose content exists in the annex. -}
 getKeysPresent :: Annex [Key]
-getKeysPresent = liftIO . traverse (2 :: Int) =<< fromRepo gitAnnexObjectDir
+getKeysPresent = do
+	direct <- isDirect
+	dir <- fromRepo gitAnnexObjectDir
+	liftIO $ traverse direct (2 :: Int) dir
   where
-	traverse depth dir = do
+	traverse direct depth dir = do
 		contents <- catchDefaultIO [] (dirContents dir)
 		if depth == 0
-			then continue (mapMaybe (fileKey . takeFileName) contents) []
+			then do
+				contents' <- filterM (present direct) contents
+				let keys = mapMaybe (fileKey . takeFileName) contents'
+				continue keys []
 			else do
-				let deeper = traverse (depth - 1)
+				let deeper = traverse direct (depth - 1)
 				continue [] (map deeper contents)
 	continue keys [] = return keys
 	continue keys (a:as) = do
@@ -414,6 +413,13 @@
 		morekeys <- unsafeInterleaveIO a
 		continue (morekeys++keys) as
 
+	{- In indirect mode, look for the key. In direct mode,
+	 - the inode cache file is only present when a key's content
+	 - is present. -}
+	present False d = doesFileExist $ contentfile d
+	present True d = doesFileExist $ contentfile d ++ ".cache"
+	contentfile d = d </> takeFileName d
+
 {- Things to do to record changes to content when shutting down.
  -
  - It's acceptable to avoid committing changes to the branch,
@@ -444,17 +450,18 @@
 		when ok $ thawContent file
 		return ok
 	copy = ifM (liftIO $ doesFileExist file)
-			( return True
-			, do
-				s <- inRepo $ gitAnnexLocation key
-				liftIO $ copyFileExternal s file
-			)
+		( return True
+		, do
+			s <- inRepo $ gitAnnexLocation key
+			liftIO $ copyFileExternal s file
+		)
 
 {- Blocks writing to an annexed file. The file is made unwritable
  - to avoid accidental edits. core.sharedRepository may change
  - who can read it. -}
 freezeContent :: FilePath -> Annex ()
-freezeContent file = liftIO . go =<< fromRepo getSharedRepository
+freezeContent file = unlessM crippledFileSystem $
+	liftIO . go =<< fromRepo getSharedRepository
   where
 	go GroupShared = modifyFileMode file $
 		removeModes writeModes .
@@ -467,7 +474,8 @@
 {- Allows writing to an annexed file that freezeContent was called on
  - before. -}
 thawContent :: FilePath -> Annex ()
-thawContent file = liftIO . go =<< fromRepo getSharedRepository
+thawContent file = unlessM crippledFileSystem $
+	liftIO . go =<< fromRepo getSharedRepository
   where
 	go GroupShared = groupWriteRead file
 	go AllShared = groupWriteRead file
diff --git a/Annex/Content/Direct.hs b/Annex/Content/Direct.hs
--- a/Annex/Content/Direct.hs
+++ b/Annex/Content/Direct.hs
@@ -11,14 +11,12 @@
 	addAssociatedFile,
 	goodContent,
 	changedFileStatus,
-	updateCache,
-	recordedCache,
-	compareCache,
-	writeCache,
-	genCache,
-	toCache,
-	Cache(..),
-	prop_read_show_direct
+	recordedInodeCache,
+	updateInodeCache,
+	writeInodeCache,
+	compareInodeCache,
+	removeInodeCache,
+	toInodeCache,
 ) where
 
 import Common.Annex
@@ -26,8 +24,7 @@
 import qualified Git
 import Utility.TempFile
 import Logs.Location
-
-import System.Posix.Types
+import Utility.InodeCache
 
 {- Absolute FilePaths of Files in the tree that are associated with a key. -}
 associatedFiles :: Key -> Annex [FilePath]
@@ -78,7 +75,7 @@
 addAssociatedFile :: Key -> FilePath -> Annex [FilePath]
 addAssociatedFile key file = do
 	file' <- normaliseAssociatedFile file
-	changeAssociatedFiles key $ \files -> do
+	changeAssociatedFiles key $ \files ->
 		if file' `elem` files
 			then files
 			else file':files
@@ -98,70 +95,36 @@
  -}
 goodContent :: Key -> FilePath -> Annex Bool
 goodContent key file = do
-	old <- recordedCache key
-	compareCache file old
+	old <- recordedInodeCache key
+	liftIO $ compareInodeCache file old
 
 changedFileStatus :: Key -> FileStatus -> Annex Bool
 changedFileStatus key status = do
-	old <- recordedCache key
-	let curr = toCache status
+	old <- recordedInodeCache key
+	let curr = toInodeCache status
 	return $ curr /= old
 
-{- Gets the recorded cache for a key. -}
-recordedCache :: Key -> Annex (Maybe Cache)
-recordedCache key = withCacheFile key $ \cachefile ->
-	liftIO $ catchDefaultIO Nothing $ readCache <$> readFile cachefile
-
-{- Compares a cache with the current cache for a file. -}
-compareCache :: FilePath -> Maybe Cache -> Annex Bool
-compareCache file old = do
-	curr <- liftIO $ genCache file
-	return $ isJust curr && curr == old
+{- Gets the recorded inode cache for a key. -}
+recordedInodeCache :: Key -> Annex (Maybe InodeCache)
+recordedInodeCache key = withInodeCacheFile key $ \f ->
+	liftIO $ catchDefaultIO Nothing $ readInodeCache <$> readFile f
 
 {- Stores a cache of attributes for a file that is associated with a key. -}
-updateCache :: Key -> FilePath -> Annex ()
-updateCache key file = maybe noop (writeCache key) =<< liftIO (genCache file)
+updateInodeCache :: Key -> FilePath -> Annex ()
+updateInodeCache key file = maybe noop (writeInodeCache key)
+	=<< liftIO (genInodeCache file)
 
 {- Writes a cache for a key. -}
-writeCache :: Key -> Cache -> Annex ()
-writeCache key cache = withCacheFile key $ \cachefile -> do
-	createContentDir cachefile
-	liftIO $ writeFile cachefile $ showCache cache
-
-{- Cache a file's inode, size, and modification time to determine if it's
- - been changed. -}
-data Cache = Cache FileID FileOffset EpochTime
-	deriving (Eq, Show)
-
-showCache :: Cache -> String
-showCache (Cache inode size mtime) = unwords
-	[ show inode
-	, show size
-	, show mtime
-	]
-
-readCache :: String -> Maybe Cache
-readCache s = case words s of
-	(inode:size:mtime:_) -> Cache
-		<$> readish inode
-		<*> readish size
-		<*> readish mtime
-	_ -> Nothing
-
--- for quickcheck
-prop_read_show_direct :: Cache -> Bool
-prop_read_show_direct c = readCache (showCache c) == Just c
-
-genCache :: FilePath -> IO (Maybe Cache)
-genCache f = catchDefaultIO Nothing $ toCache <$> getFileStatus f
+writeInodeCache :: Key -> InodeCache -> Annex ()
+writeInodeCache key cache = withInodeCacheFile key $ \f -> do
+	createContentDir f
+	liftIO $ writeFile f $ showInodeCache cache
 
-toCache :: FileStatus -> Maybe Cache
-toCache s
-	| isRegularFile s = Just $ Cache
-		(fileID s)
-		(fileSize s)
-		(modificationTime s)
-	| otherwise = Nothing
+{- Removes an inode cache. -}
+removeInodeCache :: Key -> Annex ()
+removeInodeCache key = withInodeCacheFile key $ \f -> do
+	createContentDir f -- also thaws directory
+	liftIO $ nukeFile f
 
-withCacheFile :: Key -> (FilePath -> Annex a) -> Annex a
-withCacheFile key a = a =<< inRepo (gitAnnexCache key)
+withInodeCacheFile :: Key -> (FilePath -> Annex a) -> Annex a
+withInodeCacheFile key a = a =<< inRepo (gitAnnexInodeCache key)
diff --git a/Annex/Direct.hs b/Annex/Direct.hs
--- a/Annex/Direct.hs
+++ b/Annex/Direct.hs
@@ -10,8 +10,6 @@
 import Common.Annex
 import qualified Git
 import qualified Git.LsFiles
-import qualified Git.UpdateIndex
-import qualified Git.HashObject
 import qualified Git.Merge
 import qualified Git.DiffTree as DiffTree
 import Git.Sha
@@ -24,6 +22,8 @@
 import Types.KeySource
 import Annex.Content
 import Annex.Content.Direct
+import Annex.Link
+import Utility.InodeCache
 import Utility.CopyFile
 
 {- Uses git ls-files to find files that need to be committed, and stages
@@ -45,12 +45,12 @@
 	go (file, Just sha) = do
 		mkey <- catKey sha
 		mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus file
-		case (mkey, mstat, toCache =<< mstat) of
+		case (mkey, mstat, toInodeCache =<< mstat) of
 			(Just key, _, Just cache) -> do
 				{- All direct mode files will show as
 				 - modified, so compare the cache to see if
 				 - it really was. -}
-				oldcache <- recordedCache key
+				oldcache <- recordedInodeCache key
 				when (oldcache /= Just cache) $
 					modifiedannexed file key cache
 			(Just key, Nothing, _) -> deletedannexed file key
@@ -72,25 +72,23 @@
 
 {- Adds a file to the annex in direct mode. Can fail, if the file is
  - modified or deleted while it's being added. -}
-addDirect :: FilePath -> Cache -> Annex Bool
+addDirect :: FilePath -> InodeCache -> Annex Bool
 addDirect file cache = do
 	showStart "add" file
 	let source = KeySource
 		{ keyFilename = file
 		, contentLocation = file
+		, inodeCache = Just cache
 		}
 	got =<< genKey source =<< chooseBackend file
   where
 	got Nothing = do
 		showEndFail
 		return False
-	got (Just (key, _)) = ifM (compareCache file $ Just cache)
+	got (Just (key, _)) = ifM (liftIO $ compareInodeCache file $ Just cache)
 		( do
-			link <- calcGitLink file key
-			sha <- inRepo $ Git.HashObject.hashObject BlobObject link
-			Annex.Queue.addUpdateIndex =<<
-				inRepo (Git.UpdateIndex.stageSymlink file sha)
-			writeCache key cache
+			stageSymlink file =<< hashSymlink =<< calcGitLink file key
+			writeInodeCache key cache
 			void $ addAssociatedFile key file
 			logStatus key InfoPresent
 			showEndOk
@@ -137,13 +135,13 @@
 			| otherwise = araw f
 		f = DiffTree.file item
 
-	moveout k f = removeDirect k f
+	moveout = removeDirect
 
 	{- Files deleted by the merge are removed from the work tree.
 	 - Empty work tree directories are removed, per git behavior. -}
 	moveout_raw f = liftIO $ do
 		nukeFile f
-		void $ catchMaybeIO $ removeDirectory $ parentDir f
+		void $ tryIO $ removeDirectory $ parentDir f
 	
 	{- The symlink is created from the key, rather than moving in the
 	 - symlink created in the temp directory by the merge. This because
@@ -153,20 +151,20 @@
  	 - Symlinks are replaced with their content, if it's available. -}
 	movein k f = do
 		l <- calcGitLink f k
-		liftIO $ replaceFile f $ const $
-			createSymbolicLink l f
+		replaceFile f $
+			makeAnnexLink l
 		toDirect k f
 	
 	{- Any new, modified, or renamed files were written to the temp
 	 - directory by the merge, and are moved to the real work tree. -}
 	movein_raw f = liftIO $ do
 		createDirectoryIfMissing True $ parentDir f
-		void $ catchMaybeIO $ rename (d </> f) f
+		void $ tryIO $ rename (d </> f) f
 
 {- If possible, converts a symlink in the working tree into a direct
  - mode file. -}
 toDirect :: Key -> FilePath -> Annex ()
-toDirect k f = maybe noop id =<< toDirectGen k f
+toDirect k f = fromMaybe noop =<< toDirectGen k f
 
 toDirectGen :: Key -> FilePath -> Annex (Maybe (Annex ()))
 toDirectGen k f = do
@@ -177,16 +175,17 @@
 		[] -> ifM (liftIO $ doesFileExist loc)
 			( return $ Just $ do
 				{- Move content from annex to direct file. -}
-				updateCache k loc
+				updateInodeCache k loc
 				thawContent loc
-				liftIO $ replaceFile f $ moveFile loc
+				replaceFile f $
+					liftIO . moveFile loc
 			, return Nothing
 			)
-		(loc':_) -> ifM (liftIO $ catchBoolIO $ not . isSymbolicLink <$> getSymbolicLinkStatus loc')
+		(loc':_) -> ifM (isNothing <$> getAnnexLinkTarget loc')
 			{- Another direct file has the content; copy it. -}
-			( return $ Just $ do
-				liftIO $ replaceFile f $
-					void . copyFileExternal loc'
+			( return $ Just $
+				replaceFile f $
+					void . liftIO . copyFileExternal loc'
 			, return Nothing
 			)
 
@@ -194,16 +193,12 @@
 removeDirect :: Key -> FilePath -> Annex ()
 removeDirect k f = do
 	locs <- removeAssociatedFile k f
-	when (null locs) $ do
-		r <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus f
-		case r of
-			Just s
-				| not (isSymbolicLink s) ->
-					moveAnnex k f
-			_ -> noop
+	when (null locs) $
+		whenM (isNothing <$> getAnnexLinkTarget f) $
+			moveAnnex k f
 	liftIO $ do
 		nukeFile f
-		void $ catchMaybeIO $ removeDirectory $ parentDir f
+		void $ tryIO $ removeDirectory $ parentDir f
 
 {- Called when a direct mode file has been changed. Its old content may be
  - lost. -}
diff --git a/Annex/Link.hs b/Annex/Link.hs
new file mode 100644
--- /dev/null
+++ b/Annex/Link.hs
@@ -0,0 +1,76 @@
+{- git-annex links to content
+ -
+ - On file systems that support them, symlinks are used.
+ -
+ - On other filesystems, git instead stores the symlink target in a regular
+ - file.
+ -
+ - Copyright 2013 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Annex.Link where
+
+import Common.Annex
+import qualified Annex
+import qualified Git.HashObject
+import qualified Git.UpdateIndex
+import qualified Annex.Queue
+import Git.Types
+
+type LinkTarget = String
+
+{- Checks if a file is a link to a key. -}
+isAnnexLink :: FilePath -> Annex (Maybe Key)
+isAnnexLink file = maybe Nothing (fileKey . takeFileName) <$> getAnnexLinkTarget file
+
+{- Gets the link target of a symlink.
+ -
+ - On a filesystem that does not support symlinks, get the link
+ - target by looking inside the file. (Only return at first 8k of the file,
+ - more than enough for any symlink target.)
+ -
+ - Returns Nothing if the file is not a symlink, or not a link to annex
+ - content.
+ -}
+getAnnexLinkTarget :: FilePath -> Annex (Maybe LinkTarget)
+getAnnexLinkTarget file = do
+	v <- ifM (coreSymlinks <$> Annex.getGitConfig)
+		( liftIO $ catchMaybeIO $ readSymbolicLink file
+		, liftIO $ catchMaybeIO $ take 8192 <$> readFile file
+		)
+	case v of
+		Nothing -> return Nothing
+		Just l
+			| isLinkToAnnex l -> return v
+			| otherwise -> return Nothing
+
+{- Creates a link on disk.
+ -
+ - On a filesystem that does not support symlinks, writes the link target
+ - to a file. Note that git will only treat the file as a symlink if
+ - it's staged as such, so use addAnnexLink when adding a new file or
+ - modified link to git.
+ -}
+makeAnnexLink :: LinkTarget -> FilePath -> Annex ()
+makeAnnexLink linktarget file = ifM (coreSymlinks <$> Annex.getGitConfig)
+	( liftIO $ createSymbolicLink linktarget file
+	, liftIO $ writeFile file linktarget
+	)
+
+{- Creates a link on disk, and additionally stages it in git. -}
+addAnnexLink :: LinkTarget -> FilePath -> Annex ()
+addAnnexLink linktarget file = do
+	makeAnnexLink linktarget file
+	stageSymlink file =<< hashSymlink linktarget
+
+{- Injects a symlink target into git, returning its Sha. -}
+hashSymlink :: LinkTarget -> Annex Sha
+hashSymlink linktarget = inRepo $ Git.HashObject.hashObject BlobObject linktarget
+
+{- Stages a symlink to the annex, using a Sha of its target. -}
+stageSymlink :: FilePath -> Sha -> Annex ()
+stageSymlink file sha =
+	Annex.Queue.addUpdateIndex =<<
+		inRepo (Git.UpdateIndex.stageSymlink file sha)
diff --git a/Annex/Perms.hs b/Annex/Perms.hs
--- a/Annex/Perms.hs
+++ b/Annex/Perms.hs
@@ -18,6 +18,7 @@
 import Utility.FileMode
 import Git.SharedRepository
 import qualified Annex
+import Config
 
 import System.Posix.Types
 
@@ -34,7 +35,8 @@
  - use the default mode, but with core.sharedRepository set,
  - allow the group to write, etc. -}
 setAnnexPerm :: FilePath -> Annex ()
-setAnnexPerm file = withShared $ liftIO . go
+setAnnexPerm file = unlessM crippledFileSystem $
+	withShared $ liftIO . go
   where
 	go GroupShared = groupWriteRead file
 	go AllShared = modifyFileMode file $ addModes $
@@ -77,7 +79,8 @@
  - file.
  -}
 freezeContentDir :: FilePath -> Annex ()
-freezeContentDir file = liftIO . go =<< fromRepo getSharedRepository
+freezeContentDir file = unlessM crippledFileSystem $
+	liftIO . go =<< fromRepo getSharedRepository
   where
 	dir = parentDir file
 	go GroupShared = groupWriteRead dir
@@ -91,6 +94,7 @@
 	unlessM (liftIO $ doesDirectoryExist dir) $
 		createAnnexDirectory dir 
 	-- might have already existed with restricted perms
-	liftIO $ allowWrite dir
+	unlessM crippledFileSystem $
+		liftIO $ allowWrite dir
   where
 	dir = parentDir dest
diff --git a/Annex/UUID.hs b/Annex/UUID.hs
--- a/Annex/UUID.hs
+++ b/Annex/UUID.hs
@@ -6,7 +6,7 @@
  - UUIDs of remotes are cached in git config, using keys named
  - remote.<name>.annex-uuid
  -
- - Copyright 2010-2011 Joey Hess <joey@kitenet.net>
+ - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -24,20 +24,17 @@
 import Common.Annex
 import qualified Git
 import qualified Git.Config
-import qualified Build.SysConfig as SysConfig
 import Config
 
+import qualified Data.UUID as U
+import System.Random
+
 configkey :: ConfigKey
 configkey = annexConfig "uuid"
 
-{- Generates a UUID. There is a library for this, but it's not packaged,
- - so use the command line tool. -}
+{- Generates a random UUID, that does not include the MAC address. -}
 genUUID :: IO UUID
-genUUID = gen . lines <$> readProcess command params
-  where
-	gen [] = error $ "no output from " ++ command
-	gen (l:_) = toUUID l
-	(command:params) = words SysConfig.uuid
+genUUID = UUID . show <$> (randomIO :: IO U.UUID)
 
 {- Get current repository's UUID. -}
 getUUID :: Annex UUID
diff --git a/Assistant/Install.hs b/Assistant/Install.hs
--- a/Assistant/Install.hs
+++ b/Assistant/Install.hs
@@ -14,6 +14,7 @@
 import Assistant.Ssh
 import Locations.UserConfig
 import Utility.FileMode
+import Utility.Shell
 
 #ifdef darwin_HOST_OS
 import Utility.OSX
@@ -58,7 +59,7 @@
 		sshdir <- sshDir
 		let shim = sshdir </> "git-annex-shell"
 		let content = unlines
-			[ "#!/bin/sh"
+			[ shebang
 			, "set -e"
 			, "exec", base </> "runshell" ++ 
 			  " git-annex-shell -c \"$SSH_ORIGINAL_COMMAND\""
diff --git a/Assistant/Pairing.hs b/Assistant/Pairing.hs
--- a/Assistant/Pairing.hs
+++ b/Assistant/Pairing.hs
@@ -5,6 +5,8 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Assistant.Pairing where
 
 import Common.Annex
@@ -70,7 +72,12 @@
 	}
 	deriving (Show)
 
-data SomeAddr = IPv4Addr HostAddress | IPv6Addr HostAddress6
+data SomeAddr = IPv4Addr HostAddress
+{- My Android build of the Network library does not currently have IPV6
+ - support. -}
+#ifndef WITH_ANDROID
+	| IPv6Addr HostAddress6
+#endif
 	deriving (Ord, Eq, Read, Show)
 
 {- This contains the whole secret, just lightly obfuscated to make it not
diff --git a/Assistant/Pairing/Network.hs b/Assistant/Pairing/Network.hs
--- a/Assistant/Pairing/Network.hs
+++ b/Assistant/Pairing/Network.hs
@@ -60,7 +60,7 @@
 		go cache' $ pred <$> n
 	{- The multicast library currently chokes on ipv6 addresses. -}
 	sendinterface _ (IPv6Addr _) = noop
-	sendinterface cache i = void $ catchMaybeIO $
+	sendinterface cache i = void $ tryIO $
 		withSocketsDo $ bracket setup cleanup use
 	  where
 		setup = multicastSender (multicastAddress i) pairingPort
diff --git a/Assistant/Ssh.hs b/Assistant/Ssh.hs
--- a/Assistant/Ssh.hs
+++ b/Assistant/Ssh.hs
@@ -10,6 +10,7 @@
 import Common.Annex
 import Utility.TempFile
 import Utility.UserInfo
+import Utility.Shell
 import Git.Remote
 
 import Data.Text (Text)
@@ -155,7 +156,7 @@
 	echoval v = "echo " ++ shellEscape v
 	wrapper = "~/.ssh/git-annex-shell"
 	script =
-		[ "#!/bin/sh"
+		[ shebang
 		, "set -e"
 		, "exec git-annex-shell -c \"$SSH_ORIGINAL_COMMAND\""
 		]
diff --git a/Assistant/Threads/Committer.hs b/Assistant/Threads/Committer.hs
--- a/Assistant/Threads/Committer.hs
+++ b/Assistant/Threads/Committer.hs
@@ -15,16 +15,13 @@
 import Assistant.Commits
 import Assistant.Alert
 import Assistant.DaemonStatus
-import Assistant.Threads.Watcher
 import Assistant.TransferQueue
 import Logs.Transfer
 import Logs.Location
 import qualified Annex.Queue
 import qualified Git.Command
-import qualified Git.HashObject
 import qualified Git.LsFiles
 import qualified Git.Version
-import Git.Types
 import qualified Command.Add
 import Utility.ThreadScheduler
 import qualified Utility.Lsof as Lsof
@@ -33,6 +30,7 @@
 import Config
 import Annex.Exception
 import Annex.Content
+import Annex.Link
 import qualified Annex
 
 import Data.Time.Clock
@@ -216,9 +214,7 @@
 				, Command.Add.link file key True
 				)
 			whenM (pure DirWatcher.eventsCoalesce <||> isDirect) $ do
-				sha <- inRepo $
-					Git.HashObject.hashObject BlobObject link
-				stageSymlink file sha
+				stageSymlink file =<< hashSymlink link
 				showEndOk
 		queueTransfers Next key (Just file) Upload
 		return $ Just change
@@ -233,14 +229,14 @@
 			then a
 			else do
 				-- remove the hard link
-				void $ liftIO $ tryIO $ removeFile $ contentLocation keysource
+				when (contentLocation keysource /= keyFilename keysource) $
+					void $ liftIO $ tryIO $ removeFile $ contentLocation keysource
 				return Nothing
 
 {- Files can Either be Right to be added now,
  - or are unsafe, and must be Left for later.
  -
- - Check by running lsof on the temp directory, which
- - the KeySources are locked down in.
+ - Check by running lsof on the repository.
  -}
 safeToAdd :: Maybe Seconds -> [Change] -> [Change] -> Assistant [Either Change Change]
 safeToAdd _ [] [] = return []
@@ -248,12 +244,12 @@
 	maybe noop (liftIO . threadDelaySeconds) delayadd
 	liftAnnex $ do
 		keysources <- mapM Command.Add.lockDown (map changeFile pending)
-		let inprocess' = catMaybes $ 
-			map mkinprocess (zip pending keysources)
-		tmpdir <- fromRepo gitAnnexTmpDir
+		let inprocess' = inprocess ++ catMaybes (map mkinprocess $ zip pending keysources)
 		openfiles <- S.fromList . map fst3 . filter openwrite <$>
-			liftIO (Lsof.queryDir tmpdir)
-		let checked = map (check openfiles) $ inprocess ++ inprocess'
+			findopenfiles (map keySource inprocess')
+		liftIO $ print openfiles
+		let checked = map (check openfiles) inprocess'
+		liftIO $ print checked
 
 		{- If new events are received when files are closed,
 		 - there's no need to retry any changes that cannot
@@ -278,10 +274,29 @@
 		warning $ keyFilename ks
 			++ " still has writers, not adding"
 		-- remove the hard link
-		void $ liftIO $ tryIO $ removeFile $ contentLocation ks
+		when (contentLocation ks /= keyFilename ks) $
+			void $ liftIO $ tryIO $ removeFile $ contentLocation ks
 	canceladd _ = noop
 
-	openwrite (_file, mode, _pid) =
-		mode == Lsof.OpenWriteOnly || mode == Lsof.OpenReadWrite
+	openwrite (_file, mode, _pid)
+		| mode == Lsof.OpenWriteOnly = True
+		| mode == Lsof.OpenReadWrite = True
+		| mode == Lsof.OpenUnknown = True
+		| otherwise = False
 
 	allRight = return . map Right
+
+	{- Normally the KeySources are locked down inside the temp directory,
+	 - so can just lsof that, which is quite efficient.
+	 -
+	 - In crippled filesystem mode, there is no lock down, so must run lsof
+	 - on each individual file.
+	 -}
+	findopenfiles keysources = ifM crippledFileSystem
+		( liftIO $ do
+			let segments = segmentXargs $ map keyFilename keysources
+			concat <$> forM segments (\fs -> Lsof.query $ "--" : fs)
+		, do
+			tmpdir <- fromRepo gitAnnexTmpDir
+			liftIO $ Lsof.queryDir tmpdir
+		)
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -12,7 +12,6 @@
 	WatcherException(..),
 	checkCanWatch,
 	needLsof,
-	stageSymlink,
 	onAddSymlink,
 	runHandler,
 ) where
@@ -32,13 +31,13 @@
 import qualified Annex.Queue
 import qualified Git
 import qualified Git.UpdateIndex
-import qualified Git.HashObject
 import qualified Git.LsFiles as LsFiles
 import qualified Backend
 import Annex.Content
 import Annex.Direct
 import Annex.Content.Direct
 import Annex.CatFile
+import Annex.Link
 import Git.Types
 import Config
 import Utility.ThreadScheduler
@@ -206,7 +205,7 @@
 				ensurestaged (Just link) s
 			, do
 				liftIO $ removeFile file
-				liftIO $ createSymbolicLink link file
+				liftAnnex $ Backend.makeAnnexLink link file
 				checkcontent key =<< getDaemonStatus
 				addlink link
 			)
@@ -242,10 +241,7 @@
 				Just (currlink, sha)
 					| s2w8 link == L.unpack currlink ->
 						stageSymlink file sha
-				_ -> do
-					sha <- inRepo $
-						Git.HashObject.hashObject BlobObject link
-					stageSymlink file sha
+				_ -> stageSymlink file =<< hashSymlink link
 		madeChange file LinkChange
 
 	{- When a new link appears, or a link is changed, after the startup
@@ -289,13 +285,3 @@
 	liftAnnex $ warning msg
 	void $ addAlert $ warningAlert "watcher" msg
 	noChange
-
-{- Adds a symlink to the index, without ever accessing the actual symlink
- - on disk. This avoids a race if git add is used, where the symlink is
- - changed to something else immediately after creation. It also allows
- - direct mode to work.
- -}
-stageSymlink :: FilePath -> Sha -> Annex ()
-stageSymlink file sha =
-	Annex.Queue.addUpdateIndex =<<
-		inRepo (Git.UpdateIndex.stageSymlink file sha)
diff --git a/Assistant/WebApp/Configurators/Local.hs b/Assistant/WebApp/Configurators/Local.hs
--- a/Assistant/WebApp/Configurators/Local.hs
+++ b/Assistant/WebApp/Configurators/Local.hs
@@ -82,7 +82,6 @@
 		, (return $ path == home, "Sorry, using git-annex for your whole home directory is not currently supported.")
 		, (not <$> doesDirectoryExist parent, "Parent directory does not exist.")
 		, (not <$> canWrite path, "Cannot write a repository there.")
-		, (not <$> canMakeSymlink path, "That directory is on a filesystem that does not support symlinks. Try a different location.")
 		]
 	return $ 
 		case headMaybe problems of
@@ -306,16 +305,3 @@
 	tocheck <- ifM (doesDirectoryExist dir)
 		(return dir, return $ parentDir dir)
 	catchBoolIO $ fileAccess tocheck False True False
-
-{- Checks if a directory is on a filesystem that supports symlinks. -}
-canMakeSymlink :: FilePath -> IO Bool
-canMakeSymlink dir = ifM (doesDirectoryExist dir)
-	( catchBoolIO $ test dir
-	, canMakeSymlink (parentDir dir)
-	)
-  where
-	test d = do
-		let link = d </> "delete.me"
-		createSymbolicLink link link
-		removeLink link
-		return True
diff --git a/Assistant/WebApp/Utility.hs b/Assistant/WebApp/Utility.hs
--- a/Assistant/WebApp/Utility.hs
+++ b/Assistant/WebApp/Utility.hs
@@ -97,7 +97,7 @@
 		| otherwise = killThread tid
 	{- In order to stop helper processes like rsync,
 	 - kill the whole process group of the process running the transfer. -}
-	killproc pid = void $ catchMaybeIO $ do
+	killproc pid = void $ tryIO $ do
 		g <- getProcessGroupIDOf pid
 		void $ tryIO $ signalProcessGroup sigTERM g
 		threadDelay 50000 -- 0.05 second grace period
diff --git a/Assistant/XMPP/Git.hs b/Assistant/XMPP/Git.hs
--- a/Assistant/XMPP/Git.hs
+++ b/Assistant/XMPP/Git.hs
@@ -25,6 +25,7 @@
 import Locations.UserConfig
 import qualified Types.Remote as Remote
 import Utility.FileMode
+import Utility.Shell
 
 import Network.Protocol.XMPP
 import qualified Data.Text as T
@@ -141,7 +142,7 @@
 		let wrapper = tmpdir </> "git-remote-xmpp"
 		program <- readProgramFile
 		writeFile wrapper $ unlines
-			[ "#!/bin/sh"
+			[ shebang
 			, "exec " ++ program ++ " xmppgit"
 			]
 		modifyFileMode wrapper $ addModes executeModes
diff --git a/Backend.hs b/Backend.hs
--- a/Backend.hs
+++ b/Backend.hs
@@ -1,6 +1,6 @@
 {- git-annex key/value backends
  -
- - Copyright 2010 Joey Hess <joey@kitenet.net>
+ - Copyright 2010,2013 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -10,17 +10,18 @@
 	orderedList,
 	genKey,
 	lookupFile,
+	isAnnexLink,
+	makeAnnexLink,
 	chooseBackend,
 	lookupBackendName,
 	maybeLookupBackendName
 ) where
 
-import System.Posix.Files
-
 import Common.Annex
 import qualified Annex
 import Annex.CheckAttr
 import Annex.CatFile
+import Annex.Link
 import Types.Key
 import Types.KeySource
 import qualified Types.Backend as B
@@ -75,26 +76,23 @@
 		| otherwise = c
 
 {- Looks up the key and backend corresponding to an annexed file,
- - by examining what the file symlinks to.
+ - by examining what the file links to.
  -
- - In direct mode, there is often no symlink on disk, in which case
- - the symlink is looked up in git instead. However, a real symlink
+ - In direct mode, there is often no link on disk, in which case
+ - the symlink is looked up in git instead. However, a real link
  - on disk still takes precedence over what was committed to git in direct
  - mode.
  -}
 lookupFile :: FilePath -> Annex (Maybe (Key, Backend))
 lookupFile file = do
-	tl <- liftIO $ tryIO $ readSymbolicLink file
-	case tl of
-		Right l
-			| isLinkToAnnex l -> makekey l
-			| otherwise -> return Nothing
-		Left _ -> ifM isDirect
+	mkey <- isAnnexLink file
+	case mkey of
+		Just key -> makeret key
+		Nothing -> ifM isDirect
 			( maybe (return Nothing) makeret =<< catKeyFile file
 			, return Nothing
 			)
   where
-	makekey l = maybe (return Nothing) makeret (fileKey $ takeFileName l)
 	makeret k = let bname = keyBackendName k in
 		case maybeLookupBackendName bname of
 			Just backend -> do
diff --git a/Build/Configure.hs b/Build/Configure.hs
--- a/Build/Configure.hs
+++ b/Build/Configure.hs
@@ -7,6 +7,7 @@
 import System.Process
 import Control.Applicative
 import System.FilePath
+import System.Environment
 
 import Build.TestConfig
 import Utility.SafeCommand
@@ -19,7 +20,6 @@
 	, testCp "cp_a" "-a"
 	, testCp "cp_p" "-p"
 	, testCp "cp_reflink_auto" "--reflink=auto"
-	, TestCase "uuid generator" $ selectCmd "uuid" [("uuid -m", ""), ("uuid", ""), ("uuidgen", "")]
 	, TestCase "xargs -0" $ requireCmd "xargs_0" "xargs -0 </dev/null"
 	, TestCase "rsync" $ requireCmd "rsync" "rsync --version >/dev/null"
 	, TestCase "curl" $ testCmd "curl" "curl --version >/dev/null"
@@ -122,8 +122,26 @@
 
 run :: [TestCase] -> IO ()
 run ts = do
+	args <- getArgs
 	setup
 	config <- runTests ts
-	writeSysConfig config
+	if args == ["Android"]
+		then writeSysConfig $ androidConfig config
+		else writeSysConfig config
 	cleanup
 	cabalSetup
+
+{- Hard codes some settings to cross-compile for Android. -}
+androidConfig :: [Config] -> [Config]
+androidConfig c = overrides ++ filter (not . overridden) c
+  where
+	overrides = 
+		[ Config "cp_reflink_auto" $ BoolConfig False
+		, Config "curl" $ BoolConfig False
+		, Config "sshconnectioncaching" $ BoolConfig False
+		, Config "sha224" $ MaybeStringConfig Nothing
+		, Config "sha384" $ MaybeStringConfig Nothing
+		]
+	overridden (Config k _) = k `elem` overridekeys
+	overridekeys = map (\(Config k _) -> k) overrides
+
diff --git a/Build/Standalone.hs b/Build/Standalone.hs
--- a/Build/Standalone.hs
+++ b/Build/Standalone.hs
@@ -39,7 +39,6 @@
 	, Just "rsync"
 	, Just "ssh"
 	, Just "sh"
-	, headMaybe $ words SysConfig.uuid -- may include parameters
 	, ifset SysConfig.curl "curl"
 	, ifset SysConfig.wget "wget"
 	, ifset SysConfig.bup "bup"
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,32 @@
+git-annex (3.20130217) UNRELEASED; urgency=low
+
+  * Should now fully support git repositories with core.symlinks=false;
+    always using git's pseudosymlink files in such repositories.
+  * webapp: Allow creating repositories on filesystems that lack support for
+    symlinks.
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 17 Feb 2013 16:42:16 -0400
+
+git-annex (3.20130216) unstable; urgency=low
+
+  * Now uses the Haskell uuid library, rather than needing a uuid program.
+  * Now uses the Haskell Glob library, rather than pcre-light, avoiding
+    the need to install libpcre. Currently done only for Cabal or when
+    the Makefile is made to use -DWITH_GLOB
+  * Android port now available (command-line only).
+  * New annex.crippledfilesystem setting, allows use of git-annex
+    repositories on FAT and even worse filesystems; avoiding use of
+    hard links and locked down permissions settings. (Support is incomplete.)
+  * init: Detect when the repository is on a filesystem that does not
+    support hard links, or symlinks, or unix permissions, and set
+    annex.crippledfilesystem, as well as annex.direct.
+  * add: Improved detection of files that are modified while being added.
+  * Fix a bug in direct mode, introduced in the previous release, where
+    if a file was dropped and then got back, it would be stored in indirect
+    mode.
+
+ -- Joey Hess <joeyh@debian.org>  Sat, 16 Feb 2013 10:03:26 -0400
+
 git-annex (3.20130207) unstable; urgency=low
 
   * webapp: Now allows restarting any threads that crash.
diff --git a/Command/Add.hs b/Command/Add.hs
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -5,25 +5,28 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Command.Add where
 
 import Common.Annex
 import Annex.Exception
 import Command
-import qualified Annex
-import qualified Annex.Queue
 import Types.KeySource
 import Backend
 import Logs.Location
 import Annex.Content
 import Annex.Content.Direct
 import Annex.Perms
+import Annex.Link
+import qualified Annex
+import qualified Annex.Queue
+#ifndef WITH_ANDROID
 import Utility.Touch
+#endif
 import Utility.FileMode
 import Config
-import qualified Git.HashObject
-import qualified Git.UpdateIndex
-import Git.Types
+import Utility.InodeCache
 
 def :: [Command]
 def = [notBareRepo $ command "add" paramPaths seek "add files to annex"]
@@ -63,18 +66,32 @@
  - Lockdown can fail if a file gets deleted, and Nothing will be returned.
  -}
 lockDown :: FilePath -> Annex (Maybe KeySource)
-lockDown file = do
-	tmp <- fromRepo gitAnnexTmpDir
-	createAnnexDirectory tmp
-	liftIO $ catchMaybeIO $ do
-		preventWrite file
-		(tmpfile, h) <- openTempFile tmp (takeFileName file)
-		hClose h
-		nukeFile tmpfile
-		createLink file tmpfile
-		return $ KeySource { keyFilename = file , contentLocation = tmpfile }
+lockDown file = ifM (crippledFileSystem)
+	( liftIO $ catchMaybeIO $ do
+		cache <- genInodeCache file
+		return $ KeySource
+			{ keyFilename = file
+			, contentLocation = file
+			, inodeCache = cache
+			}
+	, do
+		tmp <- fromRepo gitAnnexTmpDir
+		createAnnexDirectory tmp
+		liftIO $ catchMaybeIO $ do
+			preventWrite file
+			(tmpfile, h) <- openTempFile tmp (takeFileName file)
+			hClose h
+			nukeFile tmpfile
+			createLink file tmpfile
+			cache <- genInodeCache tmpfile
+			return $ KeySource
+				{ keyFilename = file
+				, contentLocation = tmpfile
+				, inodeCache = cache
+				}
+	)
 
-{- Moves a locked down file into the annex.
+{- Ingests a locked down file into the annex.
  -
  - In direct mode, leaves the file alone, and just updates bookkeeping
  - information.
@@ -83,35 +100,36 @@
 ingest Nothing = return Nothing
 ingest (Just source) = do
 	backend <- chooseBackend $ keyFilename source
-	ifM isDirect
-		( do
-			mstat <- liftIO $ catchMaybeIO $ getSymbolicLinkStatus $ keyFilename source
-			k <- genKey source backend
-			godirect k (toCache =<< mstat)
-		, go =<< genKey source backend
-		)
+	k <- genKey source backend
+	cache <- liftIO $ genInodeCache $ contentLocation source
+	case inodeCache source of
+		Nothing -> go k cache
+		Just c
+			| (Just c == cache) -> go k cache
+			| otherwise -> failure
   where
-	go (Just (key, _)) = do
+	go k cache = ifM isDirect ( godirect k cache , goindirect k cache )
+
+	goindirect (Just (key, _)) _ = do
 		handle (undo (keyFilename source) key) $
 			moveAnnex key $ contentLocation source
 		liftIO $ nukeFile $ keyFilename source
 		return $ Just key
-	go Nothing = failure
+	goindirect Nothing _ = failure
 
-	godirect (Just (key, _)) (Just cache) =
-		ifM (compareCache (keyFilename source) $ Just cache)
-			( do
-				writeCache key cache
-				void $ addAssociatedFile key $ keyFilename source
-				liftIO $ allowWrite $ keyFilename source
-				liftIO $ nukeFile $ contentLocation source
-				return $ Just key
-			, failure
-			)
+	godirect (Just (key, _)) (Just cache) = do
+		writeInodeCache key cache
+		void $ addAssociatedFile key $ keyFilename source
+		unlessM crippledFileSystem $
+			liftIO $ allowWrite $ keyFilename source
+		when (contentLocation source /= keyFilename source) $
+			liftIO $ nukeFile $ contentLocation source
+		return $ Just key
 	godirect _ _ = failure
 
 	failure = do
-		liftIO $ nukeFile $ contentLocation source
+		when (contentLocation source /= keyFilename source) $
+			liftIO $ nukeFile $ contentLocation source
 		return Nothing		
 
 perform :: FilePath -> CommandPerform
@@ -139,35 +157,49 @@
 link :: FilePath -> Key -> Bool -> Annex String
 link file key hascontent = handle (undo file key) $ do
 	l <- calcGitLink file key
-	liftIO $ createSymbolicLink l file
+	makeAnnexLink l file
 
+#ifndef WITH_ANDROID
 	when hascontent $ do
 		-- touch the symlink to have the same mtime as the
 		-- file it points to
 		liftIO $ do
 			mtime <- modificationTime <$> getFileStatus file
 			touch file (TimeSpec mtime) False
+#endif
 
 	return l
 
 {- Note: Several other commands call this, and expect it to 
- - create the symlink and add it. -}
+ - create the link and add it.
+ - 
+ - In direct mode, when we have the content of the file, it's left as-is,
+ - and we just stage a symlink to git.
+ - 
+ - Otherwise, as long as the filesystem supports symlinks, we use
+ - git add, rather than directly staging the symlink to git.
+ - Using git add is best because it allows the queuing to work
+ - and is faster (staging the symlink runs hash-object commands each time).
+ - Also, using git add allows it to skip gitignored files, unless forced
+ - to include them.
+ -}
 cleanup :: FilePath -> Key -> Bool -> CommandCleanup
 cleanup file key hascontent = do
 	when hascontent $
 		logStatus key InfoPresent
 	ifM (isDirect <&&> pure hascontent)
-		( do
-			l <- calcGitLink file key
-			sha <- inRepo $ Git.HashObject.hashObject BlobObject l
-			Annex.Queue.addUpdateIndex =<<
-				inRepo (Git.UpdateIndex.stageSymlink file sha)
-		, do
-			_ <- link file key hascontent
-			params <- ifM (Annex.getState Annex.force)
-				( return [Param "-f"]
-				, return []
-				)
-			Annex.Queue.addCommand "add" (params++[Param "--"]) [file]
+		( stageSymlink file =<< hashSymlink =<< calcGitLink file key
+		, ifM (coreSymlinks <$> Annex.getGitConfig)
+			( do
+				_ <- link file key hascontent
+				params <- ifM (Annex.getState Annex.force)
+					( return [Param "-f"]
+					, return []
+					)
+				Annex.Queue.addCommand "add" (params++[Param "--"]) [file]
+			, do
+				l <- link file key hascontent
+				addAnnexLink l file
+			)
 		)
 	return True
diff --git a/Command/AddUrl.hs b/Command/AddUrl.hs
--- a/Command/AddUrl.hs
+++ b/Command/AddUrl.hs
@@ -75,7 +75,11 @@
 	liftIO $ createDirectoryIfMissing True (parentDir tmp)
 	stopUnless (downloadUrl [url] tmp) $ do
 		backend <- chooseBackend file
-		let source = KeySource { keyFilename = file, contentLocation = tmp }
+		let source = KeySource
+			{ keyFilename = file
+			, contentLocation = tmp
+			, inodeCache = Nothing
+			}
 		k <- genKey source backend
 		case k of
 			Nothing -> stop
diff --git a/Command/Drop.hs b/Command/Drop.hs
--- a/Command/Drop.hs
+++ b/Command/Drop.hs
@@ -60,10 +60,7 @@
 	untrusteduuids <- trustGet UnTrusted
 	let tocheck = Remote.remotesWithoutUUID remotes (trusteduuids'++untrusteduuids)
 	stopUnless (canDropKey key numcopies trusteduuids' tocheck []) $ do
-		whenM (inAnnex key) $
-			removeAnnex key
-		{- Clean up stale direct mode files that may exist. -}
-		cleanObjectLoc key
+		removeAnnex key
 		next $ cleanupLocal key
 
 performRemote :: Key -> Maybe Int -> Remote -> CommandPerform
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -10,7 +10,6 @@
 import Common.Annex
 import Command
 import qualified Annex
-import qualified Annex.Queue
 import qualified Remote
 import qualified Types.Backend
 import qualified Types.Key
@@ -18,6 +17,7 @@
 import Annex.Content
 import Annex.Content.Direct
 import Annex.Perms
+import Annex.Link
 import Logs.Location
 import Logs.Trust
 import Annex.UUID
@@ -182,14 +182,14 @@
 check :: [Annex Bool] -> Annex Bool
 check cs = all id <$> sequence cs
 
-{- Checks that the file's symlink points correctly to the content.
+{- Checks that the file's link points correctly to the content.
  -
- - In direct mode, there is only a symlink when the content is not present.
+ - In direct mode, there is only a link when the content is not present.
  -}
 fixLink :: Key -> FilePath -> Annex Bool
 fixLink key file = do
 	want <- calcGitLink file key
-	have <- liftIO $ catchMaybeIO $ readSymbolicLink file
+	have <- getAnnexLinkTarget file
 	maybe noop (go want) have
 	return True
   where
@@ -203,14 +203,14 @@
 				showNote "fixing content location"
 				dir <- liftIO $ parentDir <$> absPath file
 				let content = absPathFrom dir have
-				liftIO $ allowWrite (parentDir content)
+				unlessM crippledFileSystem $
+					liftIO $ allowWrite (parentDir content)
 				moveAnnex key content
 
 		showNote "fixing link"
 		liftIO $ createDirectoryIfMissing True (parentDir file)
 		liftIO $ removeFile file
-		liftIO $ createSymbolicLink want file
-		Annex.Queue.addCommand "add" [Param "--force", Param "--"] [file]
+		addAnnexLink want file
 
 {- Checks that the location log reflects the current status of the key,
  - in this repository only. -}
diff --git a/Command/Indirect.hs b/Command/Indirect.hs
--- a/Command/Indirect.hs
+++ b/Command/Indirect.hs
@@ -13,9 +13,11 @@
 import qualified Git.Command
 import qualified Git.LsFiles
 import Config
+import qualified Annex
 import Annex.Direct
 import Annex.Content
 import Annex.CatFile
+import Init
 
 def :: [Command]
 def = [notBareRepo $ command "indirect" paramNothing seek
@@ -25,7 +27,15 @@
 seek = [withNothing start]
 
 start :: CommandStart
-start = ifM isDirect ( next perform, stop )
+start = ifM isDirect
+	( do
+		unlessM (coreSymlinks <$> Annex.getGitConfig) $
+			error "Git is configured to not use symlinks, so you must use direct mode."
+		whenM probeCrippledFileSystem $
+			error "This repository seems to be on a crippled filesystem, you must use direct mode."
+		next perform
+	, stop
+	)
 
 perform :: CommandPerform
 perform = do
@@ -73,7 +83,7 @@
 		showEndOk
 
 	cleandirect k = do
-		liftIO . nukeFile =<< inRepo (gitAnnexCache k)
+		liftIO . nukeFile =<< inRepo (gitAnnexInodeCache k)
 		liftIO . nukeFile =<< inRepo (gitAnnexMapping k)
 
 cleanup :: CommandCleanup
diff --git a/Command/Migrate.hs b/Command/Migrate.hs
--- a/Command/Migrate.hs
+++ b/Command/Migrate.hs
@@ -63,5 +63,9 @@
 		next $ Command.ReKey.cleanup file oldkey newkey
 	genkey = do
 		content <- inRepo $ gitAnnexLocation oldkey
-		let source = KeySource { keyFilename = file, contentLocation = content }
+		let source = KeySource
+			{ keyFilename = file
+			, contentLocation = content
+			, inodeCache = Nothing
+			}
 		liftM fst <$> genKey source (Just newbackend)
diff --git a/Command/Move.hs b/Command/Move.hs
--- a/Command/Move.hs
+++ b/Command/Move.hs
@@ -104,7 +104,7 @@
 			Remote.logStatus dest key InfoPresent
 		if move
 			then do
-				whenM (inAnnex key) $ removeAnnex key
+				removeAnnex key
 				next $ Command.Drop.cleanupLocal key
 			else next $ return True
 
diff --git a/Command/ReKey.hs b/Command/ReKey.hs
--- a/Command/ReKey.hs
+++ b/Command/ReKey.hs
@@ -14,6 +14,8 @@
 import Annex.Content
 import qualified Command.Add
 import Logs.Web
+import Config
+import Utility.CopyFile
 
 def :: [Command]
 def = [notDirect $ command "rekey"
@@ -48,8 +50,15 @@
 linkKey :: Key -> Key -> Annex Bool
 linkKey oldkey newkey = getViaTmpUnchecked newkey $ \tmp -> do
 	src <- inRepo $ gitAnnexLocation oldkey
-	liftIO $ unlessM (doesFileExist tmp) $ createLink src tmp
-	return True
+	ifM (liftIO $ doesFileExist tmp)
+		( return True
+		, ifM crippledFileSystem
+			( liftIO $ copyFileExternal src tmp
+			, do
+				liftIO $ createLink src tmp
+				return True
+			)
+		)
 
 cleanup :: FilePath -> Key -> Key -> CommandCleanup
 cleanup file oldkey newkey = do
diff --git a/Command/Sync.hs b/Command/Sync.hs
--- a/Command/Sync.hs
+++ b/Command/Sync.hs
@@ -17,6 +17,7 @@
 import Annex.Content
 import Annex.Direct
 import Annex.CatFile
+import Annex.Link
 import qualified Git.Command
 import qualified Git.LsFiles as LsFiles
 import qualified Git.Merge
@@ -263,10 +264,8 @@
 	makelink (Just key) = do
 		let dest = mergeFile file key
 		l <- calcGitLink dest key
-		liftIO $ do
-			nukeFile dest
-			createSymbolicLink l dest
-		Annex.Queue.addCommand "add" [Param "--force", Param "--"] [dest]
+		liftIO $ nukeFile dest
+		addAnnexLink l dest
 		whenM (isDirect) $
 			toDirect key dest
 	makelink _ = noop
diff --git a/Config.hs b/Config.hs
--- a/Config.hs
+++ b/Config.hs
@@ -86,6 +86,14 @@
 	setConfig (annexConfig "direct") (Git.Config.boolConfig b)
 	Annex.changeGitConfig $ \c -> c { annexDirect = b }
 
+crippledFileSystem :: Annex Bool
+crippledFileSystem = annexCrippledFileSystem <$> Annex.getGitConfig
+
+setCrippledFileSystem :: Bool -> Annex ()
+setCrippledFileSystem b = do
+	setConfig (annexConfig "crippledfilesystem") (Git.Config.boolConfig b)
+	Annex.changeGitConfig $ \c -> c { annexCrippledFileSystem = b }
+
 {- Gets the http headers to use. -}
 getHttpHeaders :: Annex [String]
 getHttpHeaders = do
diff --git a/INSTALL b/INSTALL
--- a/INSTALL
+++ b/INSTALL
@@ -14,6 +14,7 @@
 [[ScientificLinux5]]  | (and other RHEL5 clones like CentOS5)
 [[openSUSE]]          | 
 Windows               | [[sorry, Windows not supported yet|todo/windows_support]]
+[[Android]]           | 
 """]]
 
 ## Using cabal
diff --git a/Init.hs b/Init.hs
--- a/Init.hs
+++ b/Init.hs
@@ -9,18 +9,25 @@
 	ensureInitialized,
 	isInitialized,
 	initialize,
-	uninitialize
+	uninitialize,
+	probeCrippledFileSystem
 ) where
 
 import Common.Annex
 import Utility.TempFile
 import Utility.Network
 import qualified Git
+import qualified Git.LsFiles
 import qualified Annex.Branch
 import Logs.UUID
 import Annex.Version
 import Annex.UUID
 import Utility.UserInfo
+import Utility.Shell
+import Utility.FileMode
+import Config
+import Annex.Direct
+import Backend
 
 genDescription :: Maybe String -> Annex String
 genDescription (Just d) = return d
@@ -34,6 +41,7 @@
 initialize :: Maybe String -> Annex ()
 initialize mdescription = do
 	prepUUID
+	checkCrippledFileSystem
 	Annex.Branch.create
 	setVersion
 	gitPreCommitHookWrite
@@ -92,7 +100,43 @@
 preCommitHook = (</>) <$> fromRepo Git.localGitDir <*> pure "hooks/pre-commit"
 
 preCommitScript :: String
-preCommitScript = 
-	"#!/bin/sh\n" ++
-	"# automatically configured by git-annex\n" ++ 
-	"git annex pre-commit .\n"
+preCommitScript = unlines
+	[ shebang
+	, "# automatically configured by git-annex"
+	, "git annex pre-commit ."
+	]
+
+probeCrippledFileSystem :: Annex Bool
+probeCrippledFileSystem = do
+	tmp <- fromRepo gitAnnexTmpDir
+	let f = tmp </> "gaprobe"
+	liftIO $ do
+		createDirectoryIfMissing True tmp
+		writeFile f ""
+	uncrippled <- liftIO $ probe f
+	liftIO $ removeFile f
+	return $ not uncrippled
+  where
+	probe f = catchBoolIO $ do
+		let f2 = f ++ "2"
+		nukeFile f2
+		createLink f f2
+		nukeFile f2
+		createSymbolicLink f f2
+		nukeFile f2
+		preventWrite f
+		allowWrite f
+		return True
+
+checkCrippledFileSystem :: Annex ()
+checkCrippledFileSystem = whenM (probeCrippledFileSystem) $ do
+	warning "Detected a crippled filesystem."
+	setCrippledFileSystem True
+	unlessM isDirect $ do
+		warning "Enabling direct mode."
+		top <- fromRepo Git.repoPath
+		(l, clean) <- inRepo $ Git.LsFiles.inRepo [top]
+		forM_ l $ \f ->
+			maybe noop (`toDirect` f) =<< isAnnexLink f
+		void $ liftIO clean
+		setDirect True
diff --git a/Limit.hs b/Limit.hs
--- a/Limit.hs
+++ b/Limit.hs
@@ -5,13 +5,19 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE PackageImports, CPP #-}
+
 module Limit where
 
-import Text.Regex.PCRE.Light.Char8
-import System.Path.WildMatch
 import Data.Time.Clock.POSIX
 import qualified Data.Set as S
 import qualified Data.Map as M
+#ifdef WITH_GLOB
+import "Glob" System.FilePath.Glob (simplify, compile, match)
+#else
+import Text.Regex.PCRE.Light.Char8
+import System.Path.WildMatch
+#endif
 
 import Common.Annex
 import qualified Annex
@@ -82,10 +88,16 @@
 
 matchglob :: String -> Annex.FileInfo -> Bool
 matchglob glob (Annex.FileInfo { Annex.matchFile = f }) =
+#ifdef WITH_GLOB
+	match pattern f
+  where
+	pattern = simplify $ compile glob
+#else
 	isJust $ match cregex f []
   where
 	cregex = compile regex []
 	regex = '^':wildToRegex glob
+#endif
 
 {- Adds a limit to skip files not believed to be present
  - in a specfied repository. -}
diff --git a/Locations.hs b/Locations.hs
--- a/Locations.hs
+++ b/Locations.hs
@@ -12,7 +12,7 @@
 	keyPath,
 	gitAnnexLocation,
 	gitAnnexMapping,
-	gitAnnexCache,
+	gitAnnexInodeCache,
 	annexLocations,
 	annexLocation,
 	gitAnnexDir,
@@ -123,8 +123,8 @@
 {- File that caches information about a key's content, used to determine
  - if a file has changed.
  - Used in direct mode. -}
-gitAnnexCache :: Key -> Git.Repo -> IO FilePath
-gitAnnexCache key r  = do
+gitAnnexInodeCache :: Key -> Git.Repo -> IO FilePath
+gitAnnexInodeCache key r  = do
 	loc <- gitAnnexLocation key r 
 	return $ loc ++ ".cache"
 
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -6,14 +6,20 @@
 # you can turn off some of these features.
 #
 # If you're using an old version of yesod, enable -DWITH_OLD_YESOD
-FEATURES?=$(GIT_ANNEX_LOCAL_FEATURES) -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBDAV -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS
+FEATURES?=$(GIT_ANNEX_LOCAL_FEATURES) -DWITH_ASSISTANT -DWITH_S3 -DWITH_WEBDAV -DWITH_WEBAPP -DWITH_PAIRING -DWITH_XMPP -DWITH_DNS -DWITH_GLOB
 
 bins=git-annex
 mans=git-annex.1 git-annex-shell.1
 sources=Build/SysConfig.hs Utility/Touch.hs Utility/Mounts.hs
 all=$(bins) $(mans) docs
 
-OS:=$(shell uname | sed 's/[-_].*//')
+OS?=$(shell uname | sed 's/[-_].*//')
+ifeq ($(OS),Android)
+OPTFLAGS?=-DWITH_INOTIFY -DWITH_ANDROID
+clibs=Utility/libdiskfree.o Utility/libmounts.o
+CFLAGS:=-Wall -DWITH_ANDROID
+THREADFLAGS=-threaded
+else
 ifeq ($(OS),Linux)
 OPTFLAGS?=-DWITH_INOTIFY -DWITH_DBUS
 clibs=Utility/libdiskfree.o Utility/libmounts.o
@@ -41,6 +47,7 @@
 endif
 endif
 endif
+endif
 
 ALLFLAGS = $(BASEFLAGS) $(FEATURES) $(OPTFLAGS) $(THREADFLAGS)
 
@@ -51,7 +58,8 @@
 GHCFLAGS=-prof -auto-all -rtsopts -caf-all -fforce-recomp $(ALLFLAGS)
 endif
 
-GHCMAKE=ghc $(GHCFLAGS) --make
+GHC?=ghc
+GHCMAKE=$(GHC) $(GHCFLAGS) --make
 
 # Am I typing :make in vim? Do a fast build.
 ifdef VIM
@@ -69,7 +77,7 @@
 
 Build/SysConfig.hs: configure.hs Build/TestConfig.hs Build/Configure.hs
 	$(GHCMAKE) configure
-	./configure
+	./configure $(OS)
 
 %.hs: %.hsc
 	hsc2hs $<
@@ -113,7 +121,7 @@
 
 testcoverage:
 	rm -f test.tix test
-	ghc $(GHCFLAGS) -outputdir $(GIT_ANNEX_TMP_BUILD_DIR)/testcoverage --make -fhpc test
+	$(GHC) $(GHCFLAGS) -outputdir $(GIT_ANNEX_TMP_BUILD_DIR)/testcoverage --make -fhpc test
 	./test
 	@echo ""
 	@hpc report test --exclude=Main --exclude=QC
@@ -215,6 +223,40 @@
 		-volname git-annex -o tmp/git-annex.dmg
 	rm -f tmp/git-annex.dmg.bz2
 	bzip2 --fast tmp/git-annex.dmg
+
+# Cross compile for Android binary.
+# Uses https://github.com/neurocyte/ghc-android
+#
+# configure is run, probing the local system.
+# So the Android should have all the same stuff that configure probes for,
+# including the same version of git.
+android:
+	OS=Android $(MAKE) Build/SysConfig.hs
+	GHC=$$HOME/.ghc/android-14/arm-linux-androideabi-4.7/bin/arm-unknown-linux-androideabi-ghc \
+	CC=$$HOME/.ghc/android-14/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-gcc \
+	FEATURES="-DWITH_ANDROID -DWITH_ASSISTANT -DWITH_GLOB -DWITH_DNS" \
+	OS=Android $(MAKE) fast
+
+ANDROIDAPP_DEST=$(GIT_ANNEX_TMP_BUILD_DIR)/git-annex.android
+androidapp:
+	$(MAKE) android
+	$(MAKE) -C standalone/android
+
+	rm -rf "$(ANDROIDAPP_DEST)"
+	install -d "$(ANDROIDAPP_DEST)"
+
+	cp -aR standalone/android/git-annex-bundle "$(ANDROIDAPP_DEST)"
+	cp -a standalone/android/runshell "$(ANDROIDAPP_DEST)"
+	install -d "$(ANDROIDAPP_DEST)/git-annex-bundle/bin"
+	cp git-annex "$(ANDROIDAPP_DEST)/git-annex-bundle/bin/"
+
+	$$HOME/.ghc/android-14/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-strip "$(ANDROIDAPP_DEST)"/git-annex-bundle/bin/*
+
+	ln -sf git-annex "$(ANDROIDAPP_DEST)/git-annex-bundle/bin/git-annex-shell"
+	zcat standalone/licences.gz > $(ANDROIDAPP_DEST)/git-annex-bundle/LICENSE
+	install -d "$(ANDROIDAPP_DEST)/git-annex-bundle/templates"
+	
+	cd $(ANDROIDAPP_DEST) && tar czf ../git-annex-android.tar.gz .
 
 # used by ./ghci
 getflags:
diff --git a/Remote/Directory.hs b/Remote/Directory.hs
--- a/Remote/Directory.hs
+++ b/Remote/Directory.hs
@@ -183,12 +183,14 @@
 		void $ tryIO $ removeDirectoryRecursive dest -- or not exist
 		createDirectoryIfMissing True (parentDir dest)
 		renameDirectory tmp dest
-		mapM_ preventWrite =<< dirContents dest
-		preventWrite dest
+		-- may fail on some filesystems
+		void $ tryIO $ do
+			mapM_ preventWrite =<< dirContents dest
+			preventWrite dest
 	recorder f s = do
 		void $ tryIO $ allowWrite f
 		writeFile f s
-		preventWrite f
+		void $ tryIO $ preventWrite f
 
 retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
@@ -215,10 +217,11 @@
 	go _files = return False
 
 remove :: FilePath -> Key -> Annex Bool
-remove d k = liftIO $ catchBoolIO $ do
-	allowWrite dir
-	removeDirectoryRecursive dir
-	return True
+remove d k = liftIO $ do
+	void $ tryIO $ allowWrite dir
+	catchBoolIO $ do
+		removeDirectoryRecursive dir
+		return True
   where
 	dir = storeDir d k
 
diff --git a/Remote/Rsync.hs b/Remote/Rsync.hs
--- a/Remote/Rsync.hs
+++ b/Remote/Rsync.hs
@@ -20,6 +20,7 @@
 import Remote.Helper.Encryptable
 import Crypto
 import Utility.Rsync
+import Utility.CopyFile
 import Annex.Perms
 
 type RsyncUrl = String
@@ -101,14 +102,14 @@
 	f = keyFile k
 
 store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
-store o k _f p = sendAnnex k (void $ remove o k) $ rsyncSend o p k
+store o k _f p = sendAnnex k (void $ remove o k) $ rsyncSend o p k False
 
 storeEncrypted :: RsyncOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
 storeEncrypted o (cipher, enck) k p = withTmp enck $ \tmp ->
 	sendAnnex k (void $ remove o enck) $ \src -> do
 		liftIO $ encrypt cipher (feedFile src) $
 			readBytes $ L.writeFile tmp
-		rsyncSend o p enck tmp
+		rsyncSend o p enck True tmp
 
 retrieve :: RsyncOpts -> Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieve o k _ f = untilTrue (rsyncUrls o k) $ \u -> rsyncRemote o Nothing
@@ -207,16 +208,34 @@
 
 {- To send a single key is slightly tricky; need to build up a temporary
  - directory structure to pass to rsync so it can create the hash
- - directories. -}
-rsyncSend :: RsyncOpts -> MeterUpdate -> Key -> FilePath -> Annex Bool
-rsyncSend o callback k src = withRsyncScratchDir $ \tmp -> do
+ - directories.
+ -
+ - This would not be necessary if the hash directory structure used locally
+ - was always the same as that used on the rsync remote. So if that's ever
+ - unified, this gets nicer. Especially in the crippled filesystem case.
+ - (When we have the right hash directory structure, we can just
+ - pass --include=X --include=X/Y --include=X/Y/file --exclude=*)
+ -}
+rsyncSend :: RsyncOpts -> MeterUpdate -> Key -> Bool -> FilePath -> Annex Bool
+rsyncSend o callback k canrename src = withRsyncScratchDir $ \tmp -> do
 	let dest = tmp </> Prelude.head (keyPaths k)
 	liftIO $ createDirectoryIfMissing True $ parentDir dest
-	liftIO $ createLink src dest
-	rsyncRemote o (Just callback)
-		[ Param "--recursive"
-		, partialParams
-		-- tmp/ to send contents of tmp dir
-		, Param $ addTrailingPathSeparator tmp
-		, Param $ rsyncUrl o
-		]
+	ok <- if canrename
+		then do
+			liftIO $ renameFile src dest
+			return True
+		else ifM crippledFileSystem
+			( liftIO $ copyFileExternal src dest
+			, do
+				liftIO $ createLink src dest
+				return True
+			)
+	if ok
+		then rsyncRemote o (Just callback)
+			[ Param "--recursive"
+			, partialParams
+			-- tmp/ to send contents of tmp dir
+			, Param $ addTrailingPathSeparator tmp
+			, Param $ rsyncUrl o
+			]
+		else return False
diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs
--- a/Types/GitConfig.hs
+++ b/Types/GitConfig.hs
@@ -35,37 +35,41 @@
 	, annexHttpHeadersCommand :: Maybe String
 	, annexAutoCommit :: Bool
 	, annexWebOptions :: [String]
+	, annexCrippledFileSystem :: Bool
+	, coreSymlinks :: Bool
 	}
 
 extractGitConfig :: Git.Repo -> GitConfig
 extractGitConfig r = GitConfig
-	{ annexVersion = notempty $ getmaybe "version"
-	, annexNumCopies = get "numcopies" 1
+	{ annexVersion = notempty $ getmaybe (annex "version")
+	, annexNumCopies = get (annex "numcopies") 1
 	, annexDiskReserve = fromMaybe onemegabyte $
-		readSize dataUnits =<< getmaybe "diskreserve"
-	, annexDirect = getbool "direct" False
-	, annexBackends = getwords "backends"
-	, annexQueueSize = getmayberead "queuesize"
-	, annexBloomCapacity = getmayberead "bloomcapacity"
-	, annexBloomAccuracy = getmayberead "bloomaccuracy"
-	, annexSshCaching = getmaybebool "sshcaching"
-	, annexAlwaysCommit = getbool "alwayscommit" True
-	, annexDelayAdd = getmayberead "delayadd"
-	, annexHttpHeaders = getlist "http-headers"
-	, annexHttpHeadersCommand = getmaybe "http-headers-command"
-	, annexAutoCommit = getbool "autocommit" True
-	, annexWebOptions = getwords "web-options"
+		readSize dataUnits =<< getmaybe (annex "diskreserve")
+	, annexDirect = getbool (annex "direct") False
+	, annexBackends = getwords (annex "backends")
+	, annexQueueSize = getmayberead (annex "queuesize")
+	, annexBloomCapacity = getmayberead (annex "bloomcapacity")
+	, annexBloomAccuracy = getmayberead (annex "bloomaccuracy")
+	, annexSshCaching = getmaybebool (annex "sshcaching")
+	, annexAlwaysCommit = getbool (annex "alwayscommit") True
+	, annexDelayAdd = getmayberead (annex "delayadd")
+	, annexHttpHeaders = getlist (annex "http-headers")
+	, annexHttpHeadersCommand = getmaybe (annex "http-headers-command")
+	, annexAutoCommit = getbool (annex "autocommit") True
+	, annexWebOptions = getwords (annex "web-options")
+	, annexCrippledFileSystem = getbool (annex "crippledfilesystem") False
+	, coreSymlinks = getbool "core.symlinks" True
 	}
   where
 	get k def = fromMaybe def $ getmayberead k
 	getbool k def = fromMaybe def $ getmaybebool k
 	getmaybebool k = Git.Config.isTrue =<< getmaybe k
 	getmayberead k = readish =<< getmaybe k
-	getmaybe k = Git.Config.getMaybe (key k) r
-	getlist k = Git.Config.getList (key k) r
+	getmaybe k = Git.Config.getMaybe k r
+	getlist k = Git.Config.getList k r
 	getwords k = fromMaybe [] $ words <$> getmaybe k
 
-	key k = "annex." ++ k
+	annex k = "annex." ++ k
 			
 	onemegabyte = 1000000
 
diff --git a/Types/KeySource.hs b/Types/KeySource.hs
--- a/Types/KeySource.hs
+++ b/Types/KeySource.hs
@@ -7,16 +7,23 @@
 
 module Types.KeySource where
 
+import Utility.InodeCache
+
 {- When content is in the process of being added to the annex,
  - and a Key generated from it, this data type is used. 
  -
  - The contentLocation may be different from the filename
  - associated with the key. For example, the add command
- - temporarily puts the content into a lockdown directory
+ - may temporarily hard link the content into a lockdown directory
  - for checking. The migrate command uses the content
- - of a different Key. -}
+ - of a different Key.
+ -
+ - The inodeCache can be used to detect some types of modifications to
+ - files that may be made while they're in the process of being added.
+ -}
 data KeySource = KeySource
 	{ keyFilename :: FilePath
 	, contentLocation :: FilePath
+	, inodeCache :: Maybe InodeCache
 	}
 	deriving (Show)
diff --git a/Utility/InodeCache.hs b/Utility/InodeCache.hs
new file mode 100644
--- /dev/null
+++ b/Utility/InodeCache.hs
@@ -0,0 +1,50 @@
+{- Caching a file's inode, size, and modification time to see when it's changed.
+ -
+ - Copyright 2013 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Utility.InodeCache where
+
+import Common
+import System.Posix.Types
+
+data InodeCache = InodeCache FileID FileOffset EpochTime
+	deriving (Eq, Show)
+
+showInodeCache :: InodeCache -> String
+showInodeCache (InodeCache inode size mtime) = unwords
+	[ show inode
+	, show size
+	, show mtime
+	]
+
+readInodeCache :: String -> Maybe InodeCache
+readInodeCache s = case words s of
+	(inode:size:mtime:_) -> InodeCache
+		<$> readish inode
+		<*> readish size
+		<*> readish mtime
+	_ -> Nothing
+
+-- for quickcheck
+prop_read_show_inodecache :: InodeCache -> Bool
+prop_read_show_inodecache c = readInodeCache (showInodeCache c) == Just c
+
+genInodeCache :: FilePath -> IO (Maybe InodeCache)
+genInodeCache f = catchDefaultIO Nothing $ toInodeCache <$> getFileStatus f
+
+toInodeCache :: FileStatus -> Maybe InodeCache
+toInodeCache s
+	| isRegularFile s = Just $ InodeCache
+		(fileID s)
+		(fileSize s)
+		(modificationTime s)
+	| otherwise = Nothing
+
+{- Compares an inode cache with the current inode of file. -}
+compareInodeCache :: FilePath -> Maybe InodeCache -> IO Bool
+compareInodeCache file old = do
+	curr <- genInodeCache file
+	return $ isJust curr && curr == old
diff --git a/Utility/Lsof.hs b/Utility/Lsof.hs
--- a/Utility/Lsof.hs
+++ b/Utility/Lsof.hs
@@ -5,7 +5,7 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns, CPP #-}
 
 module Utility.Lsof where
 
@@ -52,6 +52,15 @@
   where
 	p = proc "lsof" ("-F0can" : opts)
 
+type LsofParser = String -> [(FilePath, LsofOpenMode, ProcessInfo)]
+
+parse :: LsofParser
+#ifdef WITH_ANDROID
+parse = parseDefault
+#else
+parse = parseFormatted
+#endif
+
 {- Parsing null-delimited output like:
  -
  - pPID\0cCMDLINE\0
@@ -62,8 +71,8 @@
  - Where each new process block is started by a pid, and a process can
  - have multiple files open.
  -}
-parse :: String -> [(FilePath, LsofOpenMode, ProcessInfo)]
-parse s = bundle $ go [] $ lines s
+parseFormatted :: LsofParser
+parseFormatted s = bundle $ go [] $ lines s
   where
 	bundle = concatMap (\(fs, p) -> map (\(f, m) -> (f, m, p)) fs)
 
@@ -97,3 +106,14 @@
 	splitnull = split "\0"
 
 	parsefail = error $ "failed to parse lsof output: " ++ show s
+
+{- Parses lsof's default output format. -}
+parseDefault :: LsofParser
+parseDefault = catMaybes . map parseline . drop 1 . lines
+  where
+	parseline l = case words l of
+		(command : spid : _user : _fd : _type : _device : _size : _node : rest) -> 
+			case readish spid of
+				Nothing -> Nothing
+				Just pid -> Just (unwords rest, OpenUnknown, ProcessInfo pid command)
+		_ -> Nothing
diff --git a/Utility/SafeCommand.hs b/Utility/SafeCommand.hs
--- a/Utility/SafeCommand.hs
+++ b/Utility/SafeCommand.hs
@@ -85,3 +85,20 @@
 prop_idempotent_shellEscape s = [s] == (shellUnEscape . shellEscape) s
 prop_idempotent_shellEscape_multiword :: [String] -> Bool
 prop_idempotent_shellEscape_multiword s = s == (shellUnEscape . unwords . map shellEscape) s
+
+{- Segements a list of filenames into groups that are all below the manximum
+ - command-line length limit. Does not preserve order. -}
+segmentXargs :: [FilePath] -> [[FilePath]]
+segmentXargs l = go l [] 0 []
+  where
+	go [] c _ r = c:r
+	go (f:fs) c accumlen r
+		| len < maxlen && newlen > maxlen = go (f:fs) [] 0 (c:r)
+		| otherwise = go fs (f:c) newlen r
+	  where
+		len = length f
+		newlen = accumlen + len
+
+	{- 10k of filenames per command, well under Linux's 20k limit;
+	 - allows room for other parameters etc. -}
+	maxlen = 10240
diff --git a/Utility/Shell.hs b/Utility/Shell.hs
new file mode 100644
--- /dev/null
+++ b/Utility/Shell.hs
@@ -0,0 +1,20 @@
+{- /bin/sh handling
+ -
+ - Copyright 2013 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module Utility.Shell where
+
+shellPath :: FilePath
+#ifndef WITH_ANDROID
+shellPath = "/bin/sh"
+#else
+shellPath = "/system/bin/sh"
+#endif
+
+shebang :: String
+shebang = "#!" ++ shellPath
diff --git a/Utility/ThreadScheduler.hs b/Utility/ThreadScheduler.hs
--- a/Utility/ThreadScheduler.hs
+++ b/Utility/ThreadScheduler.hs
@@ -6,13 +6,17 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Utility.ThreadScheduler where
 
 import Common
 
 import Control.Concurrent
-import System.Posix.Terminal
 import System.Posix.Signals
+#ifndef WITH_ANDROID
+import System.Posix.Terminal
+#endif
 
 newtype Seconds = Seconds { fromSeconds :: Int }
 	deriving (Eq, Ord, Show)
@@ -49,8 +53,10 @@
 waitForTermination = do
 	lock <- newEmptyMVar
 	check softwareTermination lock
+#ifndef WITH_ANDROID
 	whenM (queryTerminal stdInput) $
 		check keyboardSignal lock
+#endif
 	takeMVar lock
   where
 	check sig lock = void $
diff --git a/Utility/libdiskfree.c b/Utility/libdiskfree.c
--- a/Utility/libdiskfree.c
+++ b/Utility/libdiskfree.c
@@ -22,6 +22,10 @@
 # define STATCALL statfs /* statfs64 not yet tested on a real FreeBSD machine */
 # define STATSTRUCT statfs
 #else
+#if defined WITH_ANDROID
+# warning free space checking code not available for Android
+# define UNKNOWN
+#else
 #if defined (__linux__) || defined (__FreeBSD_kernel__)
 /* Linux or Debian kFreeBSD */
 /* This is a POSIX standard, so might also work elsewhere too. */
@@ -31,6 +35,7 @@
 #else
 # warning free space checking code not available for this OS
 # define UNKNOWN
+#endif
 #endif
 #endif
 #endif
diff --git a/Utility/libmounts.h b/Utility/libmounts.h
--- a/Utility/libmounts.h
+++ b/Utility/libmounts.h
@@ -5,6 +5,10 @@
 # include <sys/mount.h>
 # define GETMNTINFO
 #else
+#if defined WITH_ANDROID
+# warning mounts listing code not available for Android
+# define UNKNOWN
+#else
 #if defined (__linux__) || defined (__FreeBSD_kernel__)
 /* Linux or Debian kFreeBSD */
 #include <mntent.h>
@@ -12,6 +16,7 @@
 #else
 # warning mounts listing code not available for this OS
 # define UNKNOWN
+#endif
 #endif
 #endif
 
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,32 @@
+git-annex (3.20130217) UNRELEASED; urgency=low
+
+  * Should now fully support git repositories with core.symlinks=false;
+    always using git's pseudosymlink files in such repositories.
+  * webapp: Allow creating repositories on filesystems that lack support for
+    symlinks.
+
+ -- Joey Hess <joeyh@debian.org>  Sun, 17 Feb 2013 16:42:16 -0400
+
+git-annex (3.20130216) unstable; urgency=low
+
+  * Now uses the Haskell uuid library, rather than needing a uuid program.
+  * Now uses the Haskell Glob library, rather than pcre-light, avoiding
+    the need to install libpcre. Currently done only for Cabal or when
+    the Makefile is made to use -DWITH_GLOB
+  * Android port now available (command-line only).
+  * New annex.crippledfilesystem setting, allows use of git-annex
+    repositories on FAT and even worse filesystems; avoiding use of
+    hard links and locked down permissions settings. (Support is incomplete.)
+  * init: Detect when the repository is on a filesystem that does not
+    support hard links, or symlinks, or unix permissions, and set
+    annex.crippledfilesystem, as well as annex.direct.
+  * add: Improved detection of files that are modified while being added.
+  * Fix a bug in direct mode, introduced in the previous release, where
+    if a file was dropped and then got back, it would be stored in indirect
+    mode.
+
+ -- Joey Hess <joeyh@debian.org>  Sat, 16 Feb 2013 10:03:26 -0400
+
 git-annex (3.20130207) unstable; urgency=low
 
   * webapp: Now allows restarting any threads that crash.
diff --git a/debian/control b/debian/control
--- a/debian/control
+++ b/debian/control
@@ -17,6 +17,7 @@
 	libghc-quickcheck2-dev,
 	libghc-monad-control-dev (>= 0.3),
 	libghc-lifted-base-dev,
+	libghc-uuid-dev,
 	libghc-json-dev,
 	libghc-ifelse-dev,
 	libghc-bloomfilter-dev,
diff --git a/debian/rules b/debian/rules
--- a/debian/rules
+++ b/debian/rules
@@ -12,6 +12,12 @@
 %:
 	dh $@
 
+# Builds standalone tarball with the same FEATURES as debian package.
+standalone:
+	$(MAKE) linuxstandalone
+
 # Not intended for use by anyone except the author.
 announcedir:
 	@echo ${HOME}/src/git-annex/doc/news
+
+.PHONY: standalone
diff --git a/doc/assistant/release_notes.mdwn b/doc/assistant/release_notes.mdwn
--- a/doc/assistant/release_notes.mdwn
+++ b/doc/assistant/release_notes.mdwn
@@ -1,9 +1,24 @@
-## 3.20130124
+## version 3.20130216
 
-This is primarily a bugfix release.
+This adds a port to Android. Only usable at the command line so far;
+beta qualitty.
 
-## version 3.20130107, 3.20130114
+Also a bugfix release, and improves support for FAT.
 
+The following are known limitations of this release of the git-annex
+assistant:
+
+* No Android app yet.
+* On BSD operating systems (but not on OS X), the assistant uses kqueue to
+  watch files. Kqueue has to open every directory it watches, so too many
+  directories will run it out of the max number of open files (typically
+  1024), and fail. See [[this_bug|bugs/Issue_on_OSX_with_some_system_limits]]
+  for a workaround.
+* Also on systems with kqueue, modifications to existing files in direct
+  mode will not be noticed.
+
+## version 3.20130107, 3.20130114, 3.20130124, 3.20130207
+
 These are bugfix releases.
 
 ## version 3.20130102
@@ -16,20 +31,6 @@
 your changes. Direct mode is the default for new repositories created
 by the assistant. To convert your existing repository to use direct mode,
 manually run `git annex direct` inside the repository.
-
-The following are known limitations of this release of the git-annex
-assistant:
-
-* If a file in a direct mode repository is modified as it's being transferred,
-  the old version of the file can be lost, and fsck will later complain
-  about a corrupt object.
-* On BSD operating systems (but not on OS X), the assistant uses kqueue to
-  watch files. Kqueue has to open every directory it watches, so too many
-  directories will run it out of the max number of open files (typically
-  1024), and fail. See [[this_bug|bugs/Issue_on_OSX_with_some_system_limits]]
-  for a workaround.
-* Also on systems with kqueue, modifications to existing files in direct
-  mode will not be noticed.
 
 ## version 3.20121211
 
diff --git a/doc/bugs/3.20121113_build_error___39__not_in_scope_getAddBoxComR__39__.mdwn b/doc/bugs/3.20121113_build_error___39__not_in_scope_getAddBoxComR__39__.mdwn
deleted file mode 100644
--- a/doc/bugs/3.20121113_build_error___39__not_in_scope_getAddBoxComR__39__.mdwn
+++ /dev/null
@@ -1,33 +0,0 @@
-What steps will reproduce the problem?
-
-Building from latest source, Cabal update, cabal install --only dependencies, cabal configure, Cabal build
-
-What is the expected output? What do you see instead?
-
-Error message from build
-
-...
-
-Loading package DAV-0.2 ... linking ... done.
-
-Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libdiskfree.o ... done
-
-Loading object (static) dist/build/git-annex/git-annex-tmp/Utility/libmounts.o ... done
-
-final link ... done
-
-
-Assistant/Threads/WebApp.hs:47:1: Not in scope: `getAddBoxComR'
-
-Assistant/Threads/WebApp.hs:47:1: Not in scope: `getEnableWebDAVR'
-
-
-What version of git-annex are you using? On what operating system?
-
-Latest version via git from git-annex.branchable.com
-
-Debian Squeeze (6.0.6)
-
-Please provide any additional information below.
-
-> I noticed this earlier and fixed it. [[done]] --[[Joey]]
diff --git a/doc/bugs/Is_there_any_way_to_rate_limit_uploads_to_an_S3_backend__63__.mdwn b/doc/bugs/Is_there_any_way_to_rate_limit_uploads_to_an_S3_backend__63__.mdwn
deleted file mode 100644
--- a/doc/bugs/Is_there_any_way_to_rate_limit_uploads_to_an_S3_backend__63__.mdwn
+++ /dev/null
@@ -1,19 +0,0 @@
-What steps will reproduce the problem?
-
-Adding files to a local annex set up to sync to a remote S3 one
-
-
-What is the expected output? What do you see instead?
-
-It syncs, but maxes out the uplink
-
-
-What version of git-annex are you using? On what operating system?
-
-3.20121112 on Debian testing
-
-
-Please provide any additional information below.
-
-The man page lists how to configure rate limiting for rsync, not sure how to do it for this
-
diff --git a/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment b/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment
deleted file mode 100644
--- a/doc/bugs/Lost_S3_Remote/comment_1_6e80e6db6671581d471fc9a54181c04c._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!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
deleted file mode 100644
--- a/doc/bugs/Lost_S3_Remote/comment_2_c99c65882a3924f4890e500f9492b442._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!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
deleted file mode 100644
--- a/doc/bugs/Lost_S3_Remote/comment_3_1e434d5a20a692cd9dc7f6f8f20f30dd._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!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/More_sync__39__ing_weirdness_with_the_assistant_branch_on_OSX.mdwn b/doc/bugs/More_sync__39__ing_weirdness_with_the_assistant_branch_on_OSX.mdwn
deleted file mode 100644
--- a/doc/bugs/More_sync__39__ing_weirdness_with_the_assistant_branch_on_OSX.mdwn
+++ /dev/null
@@ -1,15 +0,0 @@
-Running the 'assistant' branch, I occassionally get
-
-To myhost1:/Users/jtang/annex
- ! [rejected]        master -> synced/master (non-fast-forward)
-error: failed to push some refs to 'myhost1:/Users/jtang/annex'
-hint: Updates were rejected because a pushed branch tip is behind its remote
-hint: counterpart. Check out this branch and merge the remote changes
-hint: (e.g. 'git pull') before pushing again.
-hint: See the 'Note about fast-forwards' in 'git push --help' for details.
-(Recording state in git...)
-
-manually running a 'git annex sync' usually fixes it, I guess once the sync command runs periodically this problem will go away, is this even OSX specific? I don't quite get the behaviour that is described in [[design/assistant/blog/day_15__its_aliiive]].
-
-> With my changes today, I've seen it successfully recover from this
-> situation. [[done]] --[[Joey]] 
diff --git a/doc/bugs/OSX_app_issues/comment_10_54d8f3e429df9a9958370635c890abf0._comment b/doc/bugs/OSX_app_issues/comment_10_54d8f3e429df9a9958370635c890abf0._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_10_54d8f3e429df9a9958370635c890abf0._comment
+++ /dev/null
@@ -1,11 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- ip="4.154.3.194"
- subject="comment 10"
- date="2013-01-19T16:13:20Z"
- content="""
-The app uses the version of git included in it, because using the system's installed version if there is one is likely to run into other version incompatabilities. 
-
-You can either install git-annex using cabal and homebrew, as documented, or you could go in and delete
-all the git programs out of the app, and then it'd use the system's git stuff instead.
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_11_bb2ceb95a844449795addee6986d0763._comment b/doc/bugs/OSX_app_issues/comment_11_bb2ceb95a844449795addee6986d0763._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_11_bb2ceb95a844449795addee6986d0763._comment
+++ /dev/null
@@ -1,26 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawlYy4BrJyV1PdfqzevCVziXRp89iUH6Xzw"
- nickname="Christopher"
- subject="Code signing errors in log on starting git-annex.app"
- date="2013-01-19T22:30:32Z"
- content="""
-When I run via the App and set up a fresh repo, i get some Console.app spam (looks like one per file added to the repo dir... or maybe one per git command?):
-
-    2013-01-19 2:44:55.000 PM kernel[0]: CODE SIGNING: cs_invalid_page(0x1008b4000): p=73995[git] clearing CS_VALID
-    2013-01-19 2:44:55.000 PM kernel[0]: CODE SIGNING: cs_invalid_page(0x10f99e000): p=73996[git] clearing CS_VALID
-    2013-01-19 2:44:55.000 PM kernel[0]: CODE SIGNING: cs_invalid_page(0x102b44000): p=73997[git] clearing CS_VALID
-    2013-01-19 2:44:55.000 PM kernel[0]: CODE SIGNING: cs_invalid_page(0x1029f4000): p=73998[git] clearing CS_VALID
-    ...
-
-and nothing seems to work. The page address and the pid increment steadily with each line.  I'm using 10.8.2 (12C60) on a Mac Pro, and grabbed:
-
-     /git-annex/OSX/current/10.8.2_Mountain_Lion/git-annex.dmg.bz2	
-
-(published 14-Jan-2013 15:19)
-
-It seems to be a code signing issue, perhaps with the vendored git binaries. While things are sort-of working, the web app shows the files flying by really fast. 
-
-Using a fresh repo via `git annex webapp` works great (I built that after much teeth-knashing, brew install/link cycles, and then cabal install git-annex). 
-
-I am very excited for this to work, this is exactly what I've been waiting for to replace dropbox. Came very close to writing it myself a few times (and in Haskell no less!!). 
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_12_f3bc5a4e4895ac9351786f0bdd8005ba._comment b/doc/bugs/OSX_app_issues/comment_12_f3bc5a4e4895ac9351786f0bdd8005ba._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_12_f3bc5a4e4895ac9351786f0bdd8005ba._comment
+++ /dev/null
@@ -1,11 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawmYiJgOvC4IDYkr2KIjMlfVD9r_1Sij_jY"
- nickname="Douglas"
- subject="Error creating remote repository using ssh on OSX"
- date="2013-01-25T13:18:40Z"
- content="""
-There is an issue with creating remote repositories using ssh (the problem may require using a different account name.) I filed the following bug:
-
-
-<http://git-annex.branchable.com/bugs/Error_creating_remote_repository_using_ssh_on_OSX/>
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_2_fd560811c57df5cbc3976639642b8b19._comment b/doc/bugs/OSX_app_issues/comment_2_fd560811c57df5cbc3976639642b8b19._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_2_fd560811c57df5cbc3976639642b8b19._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkN91jAhoesnVI9TtWANaBPaYjd1V9Pag8"
- nickname="Benjamin"
- subject="Package for older OS X"
- date="2012-11-17T12:36:45Z"
- content="""
-Is there an option to provide application bundle for older versions of OS X? The last time I tried the bundle wouldn't work under 10.5. If no specific features from newer OS X versions are required, it could be enough to add a simple switch when building.
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_4_4cda124b57ddc87645d5822f14ed5c59._comment b/doc/bugs/OSX_app_issues/comment_4_4cda124b57ddc87645d5822f14ed5c59._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_4_4cda124b57ddc87645d5822f14ed5c59._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkBTVYS5lTecuenAB01eHgfUxE20vWVpU4"
- nickname="Peng"
- subject="Large Mountain Loin package size"
- date="2012-12-28T22:17:43Z"
- content="""
-I see that git-annex package file is 489.9MB on Mac Mountain Loin (git-annex.dmg.bz2). The file git-annex.dmg.bz2 is only of size 24M. Is there any way to reduce the package size?
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_5_0d1df34f83a8dac9c438d93806236818._comment b/doc/bugs/OSX_app_issues/comment_5_0d1df34f83a8dac9c438d93806236818._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_5_0d1df34f83a8dac9c438d93806236818._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- ip="2001:4978:f:21a::2"
- subject="comment 5"
- date="2013-01-02T19:52:28Z"
- content="""
-The build for today's release is 24 mb.
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_6_bc44d5aea5f77e331a32913ada293730._comment b/doc/bugs/OSX_app_issues/comment_6_bc44d5aea5f77e331a32913ada293730._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_6_bc44d5aea5f77e331a32913ada293730._comment
+++ /dev/null
@@ -1,27 +0,0 @@
-[[!comment format=mdwn
- username="https://www.jsilence.org/"
- nickname="jsilence"
- subject="Setting up a repository fails on OSX 10.6"
- date="2013-01-05T11:17:18Z"
- content="""
-I installed Haskell via MacPorts and managed to compile git-annex as described via cabal.
-
-On the initial start, git-annex webapp starts just fine and the webapp opens in the browser. When I try to configure a local repository, the webapp crashes.
-
-    1 jsilence@zeo ~ % git-annex webapp
-    (Recording state in git...)
-    WebApp crashed: watch mode is not available on this system
-    
-    Launching web browser on file:///var/folders/6b/6bWnFAnbFXSPCLPvCnKNrE+++TI/-Tmp-/webapp1003.html
-    jsilence@zeo ~ % 
-
-\"watch mode is not suported\" suggests that lsof is not in the PATH. lsof resides in /usr/sbin and can be found just fine:
-
-    jsilence@zeo ~ % which lsof
-    /usr/sbin/lsof
-
-Any help would be appreciated.
-
--jsl
-
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_7_93e0bb53ac2d7daef53426fbdc5f92d9._comment b/doc/bugs/OSX_app_issues/comment_7_93e0bb53ac2d7daef53426fbdc5f92d9._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_7_93e0bb53ac2d7daef53426fbdc5f92d9._comment
+++ /dev/null
@@ -1,15 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkurjhi0CRJvgm7QNaZDWS9hitBtavqIpc"
- nickname="Bret"
- subject="git-annex.app Not working on 32 bit machines"
- date="2012-11-03T19:18:47Z"
- content="""
-I tried running the git-annex.app on my Core Duo Macbook pro, and it does not run at all.  I get an error on my system.log
-
-`Nov  3 12:13:26 Bret-Mac [0x0-0x15015].com.branchable.git-annex[155]: /Applications/git-annex.app/Contents/MacOS/runshell: line 52: /Applications/git-annex.app/Contents/MacOS/bin/git-annex: Bad CPU type in executable
-Nov  3 12:13:26 Bret-Mac com.apple.launchd.peruser.501[92] ([0x0-0x15015].com.branchable.git-annex[155]): Exited with exit code: 1`
-
-It works on my 64 bit machine, and this has become quite the problem for a while now, where people with newer macs dont compile back for a 32bit machine.  
-
-Is there any hope for a pre-compiled binary that works on a 32 bit machine?
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_7_acd73cc5c4caa88099e2d2f19947aadf._comment b/doc/bugs/OSX_app_issues/comment_7_acd73cc5c4caa88099e2d2f19947aadf._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_7_acd73cc5c4caa88099e2d2f19947aadf._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- ip="4.152.108.211"
- subject="comment 7"
- date="2013-01-05T17:48:52Z"
- content="""
-@jsilence, this problem does not involve lsof. There was a typo in the cabal file that prevented cabal building it with hfsevents. I'm releasing a new version to cabal with this typo fixed.
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_8_141eac2f3fb25fe18b4268786f00ad6a._comment b/doc/bugs/OSX_app_issues/comment_8_141eac2f3fb25fe18b4268786f00ad6a._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_8_141eac2f3fb25fe18b4268786f00ad6a._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkSq2FDpK2n66QRUxtqqdbyDuwgbQmUWus"
- nickname="Jimmy"
- subject="comment 8"
- date="2012-11-07T16:08:00Z"
- content="""
-I've been updating my haskell platform install recently, i used to try and get the builder to spit out 32/64bit binaries, but recently it's just become too messy, I've just migrated to a full 64bit build system. I'm afraid I won't be able to  provide 32bit builds any more.
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_8_f4d5b2645d7f29b80925159efb94a998._comment b/doc/bugs/OSX_app_issues/comment_8_f4d5b2645d7f29b80925159efb94a998._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_8_f4d5b2645d7f29b80925159efb94a998._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawmOsimKUgz6rxpmsS_nrBQGavEYyUpDlsE"
- nickname="Tim"
- subject="OS X 10.8.2"
- date="2013-01-11T00:07:54Z"
- content="""
-Double click on the app, give permission for it to run and ... nothing
-"""]]
diff --git a/doc/bugs/OSX_app_issues/comment_9_2e6dfca0fd8df04066769653724eae28._comment b/doc/bugs/OSX_app_issues/comment_9_2e6dfca0fd8df04066769653724eae28._comment
deleted file mode 100644
--- a/doc/bugs/OSX_app_issues/comment_9_2e6dfca0fd8df04066769653724eae28._comment
+++ /dev/null
@@ -1,17 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawmRFKwny4rArBaz-36xTcsJYqKIgdDaw5Q"
- nickname="Andrew"
- subject="Prefer the system/path git binaries if they're a newer version"
- date="2013-01-19T06:20:38Z"
- content="""
-I have used homebrew to install git v1.8.0.2 but git-annex.app packages git v1.7.10.2. git 1.7 crashes due to some newer directives in my global git config.
-
-    error: Malformed value for push.default: simple
-    error: Must be one of nothing, matching, tracking or current.
-    fatal: bad config file line 38 in /Users/akraut/.gitconfig
-    
-    git-annex: fd:13: hGetLine: end of file
-    failed
-    git-annex: webapp: 1 failed
-
-"""]]
diff --git a/doc/bugs/Switching_from_indirect_mode_to_direct_mode_breaks_duplicates.mdwn b/doc/bugs/Switching_from_indirect_mode_to_direct_mode_breaks_duplicates.mdwn
deleted file mode 100644
--- a/doc/bugs/Switching_from_indirect_mode_to_direct_mode_breaks_duplicates.mdwn
+++ /dev/null
@@ -1,30 +0,0 @@
-#What steps will reproduce the problem?
-
-1. Create a new repository in indirect mode.
-
-2. Add the same file twice under a different name. Now you have two symlinks pointing to the same file under .git/annex/objects/
-
-3. Switch to direct mode. The first symlink gets replaced by the actual file. The second stays unchanged, pointing to nowhere. But git annex whereis still reports it has a copy.
-
-4. Delete the first file. Git annex whereis still thinks it has a copy of file 2, which is not true -> data loss.
-
-#What is the expected output? What do you see instead?
-
-When switching to direct mode, both symlinks should be replaced by a copy (or at least a hardlink) of the actual file.
-
-> The typo that caused this bug is fixed. --[[Joey]] 
-
-#What version of git-annex are you using? On what operating system?
-
-3.20130107 on Arch Linux x64
-
-#Please provide any additional information below.
-
-The deduplication performed by git-annex is very dangerous in itself
-because files with identical content become replaced by references to the
-same file without the user necessarily being aware. Think of the user
-making a copy of a file, than modifying it. He would expect to end up with
-two files, the unchanged original and the modified copy. But what he really
-gets is two symlinks pointing to the same modified file.
-
-> I agree, it now copies rather than hard linking.  [[done]] --[[Joey]] 
diff --git a/doc/bugs/Unknown_remote_type_webdav.mdwn b/doc/bugs/Unknown_remote_type_webdav.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Unknown_remote_type_webdav.mdwn
@@ -0,0 +1,8 @@
+When I attempt to setup a [box.com special remote](http://git-annex.branchable.com/tips/using_box.com_as_a_special_remote/) I get the following error:
+
+    git-annex: Unknown remote type webdav
+
+I'm using the Linux prebuilt tarball. Does it not include webdav support?
+
+> The amd64 standalone tarball was indeed built without it for the last
+> release. Fixed that. [[done]] --[[Joey]]
diff --git a/doc/bugs/WebDAV_HandshakeFailed_.mdwn b/doc/bugs/WebDAV_HandshakeFailed_.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/WebDAV_HandshakeFailed_.mdwn
@@ -0,0 +1,5 @@
+When I attempt to add a Box.com special remote to my annex I get the following error:
+
+    git-annex: HandshakeFailed (Error_Protocol ("certificate rejected: certificate is not allowed to sign another certificate",True,CertificateUnknown))
+
+Running git-annex version: 3.20130207
diff --git a/doc/bugs/assistant_does_not_list_remote___39__origin__39__.mdwn b/doc/bugs/assistant_does_not_list_remote___39__origin__39__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/assistant_does_not_list_remote___39__origin__39__.mdwn
@@ -0,0 +1,22 @@
+What steps will reproduce the problem?
+
+1. create a git annex repo on a server
+2. clone it on workstation
+3. open the webapp on the workstation
+
+
+What is the expected output? What do you see instead?
+
+The webapp should show the 'origin' remote and the assistant should ensure syncing.
+Instead the remote does not show up in the webapp.
+I checked with `git annex status` and the remote is there.
+
+What version of git-annex are you using? On what operating system?
+
+3.20130207 on latest Ubuntu
+
+Please provide any additional information below.
+
+I tried both with direct and indirect mode for the local annex repo.
+
+I am sorry if I am missing the point. I checked the docs, however without much success.
diff --git a/doc/bugs/dropunused_doesn__39__t_work_in_my_case__63__.mdwn b/doc/bugs/dropunused_doesn__39__t_work_in_my_case__63__.mdwn
--- a/doc/bugs/dropunused_doesn__39__t_work_in_my_case__63__.mdwn
+++ b/doc/bugs/dropunused_doesn__39__t_work_in_my_case__63__.mdwn
@@ -57,5 +57,14 @@
 git-annex: 3.20130124
 Debian: sid 2013-02-01
 
-> unused being confused by stale data left when switching from direct mode.
-> I've made this be cleaned up. [[done]] --[[Joey]]
+> I put a fix in for this in 57780cb3a4dfe1292b72e1412ec4d2a70b6d04ce 
+> but it was buggy and I had to revert it.
+> 
+> The bug is caused by direct mode cache and mapping info.
+> This makes getKeysPresent find keys that are not present.
+> It would be expensive to make getKeysPresent check that the
+> actual key files are present (it just lists the directories). 
+> But this seems to be needed, since direct mode can leave
+> cache and mapping files behind. --[[Joey]]
+
+>> Now fixed properly. [[done]] --[[Joey]]
diff --git a/doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn b/doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn
deleted file mode 100644
--- a/doc/bugs/git-annex_has_issues_with_git_when_staging__47__commiting_logs.mdwn
+++ /dev/null
@@ -1,34 +0,0 @@
-After a series of pretty convoluted copying files around between annex'd repos and pulling changes around between repos. I noticed that occassionally when git-annex tries to stage files (the `.git-annex/*/*/*logs`) git some times gets wedged and doing a "git commit -a" doesn't seem to work or files might not get added thus leaving a bunch of untracked files or modified files that aren't staged for a commit.
-
-I tried running a *`git rm --cached -f -r *`* then *git add -u .git-annex/* or the usual *git add* then a commit fixes things for me. If I don't do that then my subsequent merges/pulls will fail and result in *no known copies of files* I suspect git-annex might have just touched some file modes and git picked up the changes but got confused since there was no content change. It might also just be a git on OSX thing and it doesn't affect linux/bsd users.
-
-For now it's just a bit of extra work for me when it does occur but it does not seem to occur often.
-
-> What do you mean when you say that git "got wedged"? It hung somehow?
->
-> If git-annex runs concurrently with another git command that locks
-> the repository, its git add of log files can fail.
-> 
-> Update: Also, of course, if you are running a "got annex get" or
-> similar, and ctrl-c it after it has gotten some files, it can
-> end up with unstaged or in some cases un-added log files that git-annex
-> wrote -- since git-annex only stages log files in git on shutdown, and
-> ctrl-c bypasses that.
-> --[[Joey]] 
-
->> It "got wedged" as in git doesn't let me commit anything, even though it tells me that there is stuff to be committed in the staging area.
-
->>> I've never seen git refuse to commit staged files. There would have to
->>> be some error message? --[[Joey]] 
-
->>>> there were no error messages at all
-
->>>>> Can I see a transcript? I'm having difficulty getting my head around
->>>>> what git is doing. Sounds like the files could just not be `git
->>>>> added` yet, but I get the impression from other things that you say
->>>>> that it's not so simple. --[[Joey]] 
-
-This turns out to be a bug in git, and I have posted a bug report on the mailing list.
-The git-annex behavior that causes this situation is being handled as
-another bug, [[git-annex directory hashing problems on osx]].
-So, closing this bug report. [[done]] --[[Joey]]
diff --git a/doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn b/doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn
deleted file mode 100644
--- a/doc/bugs/git_annex_gets_confused_about_remotes_with_dots_in_their_names.mdwn
+++ /dev/null
@@ -1,34 +0,0 @@
-For test.com//test, I get this:
-
-    % git annex copy . --to test.com//test
-    (getting UUID for test...) git-annex: there is no git remote named "test.com//test"
-
-And my .git/config changes from
-
-    [remote "test.com//test"]
-    	url = richih@test.com:/test
-    	fetch = +refs/heads/*:refs/remotes/test.com//test/*
-
-to
-
-    [remote "test.com//test"]
-    	url = richih@test.com:/test
-    	fetch = +refs/heads/*:refs/remotes/test.com//test/*
-    	annex-uuid = xyz
-    [remote "test"]
-    	annex-uuid = xyz
-
-
-Unless I am misunderstanding something, git annex gets confused about what the name of the remote it supposed to be, truncates at the dot for some operations and uses the full name for others.
-
-> I've fixed this bug. [[done]]
-> 
-> However, using "/" in a remote name seems likely to me to confuse 
-> git's own remote branch handling. Although I've never tried it.
-> --[[Joey]] 
-
->> From what I can see, git handles / just fine, but would get upset about : which is why it's not allowed in a remote's name.
->> My naming scheme is host//path/to/annex. It sorts nicely and gives all important information left to right with the most specific parts at the beginning and end.
->> If you have any other ideas or scheme, I am all ears :)
->> Either way, thanks for fixing this so quickly.
->> -- RichiH
diff --git a/doc/bugs/git_defunct_processes___40__child_of_git-annex_assistant__41__.mdwn b/doc/bugs/git_defunct_processes___40__child_of_git-annex_assistant__41__.mdwn
deleted file mode 100644
--- a/doc/bugs/git_defunct_processes___40__child_of_git-annex_assistant__41__.mdwn
+++ /dev/null
@@ -1,34 +0,0 @@
-What steps will reproduce the problem?
-
-run git annex assistant, add a file, which is picked up and pushed by the assistant.  
-
-What is the expected output? What do you see instead?
-
-a ps -ef shows a large number of defunct git processes.. for example:
-<pre>
-nelg      9622     1  0 02:01 ?        00:00:01 git-annex assistant
-nelg      9637  9622  0 02:01 ?        00:00:00 git --git-dir=/home/nelg/Downloads/test2/.git --work-tree=/home/nelg/Downloads/test2 cat-file --batch
-nelg     12080  9622  0 02:19 ?        00:00:00 [git] &lt;defunct&gt;
-nelg     12082  9622  0 02:19 ?        00:00:00 [git] &lt;defunct&gt;
-nelg     12083  9622  0 02:19 ?        00:00:00 [git] &lt;defunct&gt;
-nelg     12084  9622  0 02:19 ?        00:00:00 [git] &lt;defunct&gt;
------
-</pre>
-
-What version of git-annex are you using? On what operating system?
-
-Compiled git annex from git (cbcd208d158f8e42dda03a5eeaf1bac21045a140), on Mandriva 2010.2, 32 bit, using ghc-7.4.1.
- git version 1.7.1
-
-
-Please provide any additional information below.
-
-I also found that the version of git I have does not support the option: --allow-empty-message
-So, suggest that if the version of git installed is an older version, that the params in  Assistant/Threads/Committer.hs
-are changed to  [ Param "-m", Param "git assistant".... or something like that.
-
-I have done this on my copy for testing it.
-
-For testing, I am also using two repositories on the same computer.  I set this up from the command line, as the web app does not seem to support syncing to two different git folders on the same computer.
-
-> [[done]]; all zombies are squelched now in the assistant. --[[Joey]]
diff --git a/doc/bugs/missing_dependency_in_git-annex-3.20130216.mdwn b/doc/bugs/missing_dependency_in_git-annex-3.20130216.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/missing_dependency_in_git-annex-3.20130216.mdwn
@@ -0,0 +1,29 @@
+What steps will reproduce the problem?
+
+build git-annex-3.20130216 on OS X lion
+
+
+What is the expected output? What do you see instead?
+
+successful compile; error was:
+
+```
+Annex/UUID.hs:30:8:
+    Could not find module `System.Random'
+    It is a member of the hidden package `random-1.0.1.1'.
+    Perhaps you need to add `random' to the build-depends in your .cabal file.
+    Use -v to see a list of the files searched for.
+```
+
+
+What version of git-annex are you using? On what operating system?
+
+building git-annex-3.20130216 on OS X Lion
+
+
+Please provide any additional information below.
+
+
+adding 'random' to the BuildDepends in git-annex.cabal does indeed fix the error.
+
+> [[done]] --[[Joey]] 
diff --git a/doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn b/doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn
deleted file mode 100644
--- a/doc/bugs/wishlist:_more_descriptive_commit_messages_in_git-annex_branch.mdwn
+++ /dev/null
@@ -1,39 +0,0 @@
-as of git-annex version 3.20110719, all git-annex commits only contain the word "update" as a commit message. given that the contents of the commit are pretty non-descriptive (SHA1 hashes for file names, uuids for repository names), i suggest to have more descriptive commit messages, as shown here:
-
-    /mnt/usb_disk/photos/2011$ git annex get
-    /mnt/usb_disk/photos/2011$ git show git-annex
-    [...]
-    usb-disk-photos: get 2011
-    
-    * 10 files retrieved from 2 sources (9 from local-harddisk, 1 from my-server)
-    * 120 files were already present
-    * 2 files could not be retrieved
-    /mnt/usb_disk/photos/2011$ cd ~/photos/2011/07
-    ~/photos/2011/07$ git copy --to my-server
-    ~/photos/2011/07$ git show git-annex
-    [...]
-    local-harddisk: copy 2011/07 to my-server
-    
-    * 20 files pushed
-    ~/photos/2011/07$
-
-in my opinion, the messages should at least contain
-
-* what command was used
-* in which repository they were executed
-* which files or directories they affected (not necessarily all files, but what was given on command line or implicitly from the working directory)
-
---[[chrysn]]
-
-> The implementation of the git-annex branch precludes more descriptive
-> commit messages, since a single commit can include changes that were
-> previously staged to the branch's index file, or spooled to its journal
-> by other git-annex commands (either concurrently running or
-> interrupted commands, or even changes needed to automatically merge
-> other git-annex branches).
-> 
-> It would be possible to make it *less* verbose, with an empty commit
-> message. :) --[[Joey]] 
-
->> Closing as this is literally impossible to do without making
->> git-annex worse. [[done]] --[[Joey]] 
diff --git a/doc/design/assistant.mdwn b/doc/design/assistant.mdwn
--- a/doc/design/assistant.mdwn
+++ b/doc/design/assistant.mdwn
@@ -11,11 +11,11 @@
 * Month 4 "cloud": [[!traillink cloud]] [[!traillink transfer_control]]
 * Month 5 "cloud continued": [[!traillink xmpp]] [[!traillink more_cloud_providers]]
 * Month 6 "9k bonus round": [[!traillink desymlink]]
+* Month 7: user-driven features and polishing; 
+  [presentation at LCA2013](http://mirror.linux.org.au/linux.conf.au/2013/mp4/gitannex.mp4)
 
 We are, approximately, here:
 
-* Month 7: user-driven features and polishing; 
-  [presentation at LCA2013](https://lca2013.linux.org.au/schedule/30059/view_talk)
 * Month 8: [[!traillink Android]]
 * Months 9-11: more user-driven features and polishing (see remaining TODO items in all pages above)
 * Month 12: "Windows purgatory" [[Windows]]
@@ -23,6 +23,8 @@
 ## porting
 
 * [[OSX]] port is in fairly good shape, but still has some room for improvement
+* [[android]] port is just getting started
+* Windows port does not exist yet
 
 ## not yet on the map:
 
diff --git a/doc/design/assistant/OSX/comment_1_9290f6e6f265e906b08631224392b7bf._comment b/doc/design/assistant/OSX/comment_1_9290f6e6f265e906b08631224392b7bf._comment
deleted file mode 100644
--- a/doc/design/assistant/OSX/comment_1_9290f6e6f265e906b08631224392b7bf._comment
+++ /dev/null
@@ -1,14 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawlu-fdXIt_RF9ggvg4zP0yBbtjWQwHAMS4"
- nickname="Jörn"
- subject="Mount detection"
- date="2012-09-21T09:23:34Z"
- content="""
-regarding the current mount polling on OSX: why not use the NSNotificationCenter for being notified on mount events on OSX?
-
-Details see:
-
-1. <http://stackoverflow.com/questions/12409458/detect-when-a-volume-is-mounted-on-os-x>
-2. <http://stackoverflow.com/questions/6062299/how-to-add-an-observer-to-nsnotificationcenter-in-a-c-class-using-objective-c>
-3. <https://developer.apple.com/library/mac/#documentation/CoreFoundation/Reference/CFNotificationCenterRef/Reference/reference.html#//apple_ref/doc/uid/20001438>
-"""]]
diff --git a/doc/design/assistant/android.mdwn b/doc/design/assistant/android.mdwn
--- a/doc/design/assistant/android.mdwn
+++ b/doc/design/assistant/android.mdwn
@@ -1,36 +1,10 @@
-Porting git-annex to Android will use the Android native SDK.
-
-A hopefully small Java app will be developed, which runs the webapp
-daemon, and a web browser to display it.
-
-### programs to port
-
-These will probably need to be bundled into the Android app, unless already
-available in the App Store.
-
-* ssh (native ssh needed for scp, not a client like ConnectBot)
-* rsync
-* gpg
-* git (not all git commands are needed,
-  but core plumbing and a few like `git-add` are.)
-
-## GHC Android?
-
-Android's native SDK does not use glibc. GHC's runtime links with glibc.
-This could be an enormous problem. Other people want to see GHC able to
-target Android, of course, so someone may solve it before I get stuck on
-it.
-
-References:
-
-* <http://stackoverflow.com/questions/5151858/running-a-haskell-program-on-the-android-os>
-* <http://www.reddit.com/r/haskell/comments/ful84/haskell_on_android/>
-
-I've heard anecdoally that ipwnstudios not only has an IPhone GHC port,
-but also Android. Need to get in touch with them.
-<http://ipwnstudios.com/>
+### goals
 
-Update: This looks likely: <https://github.com/neurocyte/ghc-android>
+1. Get git-annex working at the command line in Android,
+   along with all the programs it needs, and the assistant. **done**
+2. Get the webapp working. Needs Template Haskell, or a workaround.
+3. A hopefully small Java app will be developed, which runs the
+   webapp daemon, and a web browser to display it.
 
 ### Android specific features
 
@@ -40,7 +14,9 @@
 The app should be aware of network status, and avoid expensive data
 transfers when not on wifi. This may need to be configurable.
 
-## FAT
+### FAT
 
 Due to use of the FAT filesystem, which doesn't do symlinks, [[desymlink]]
 is probably needed for at least older Android devices that have SD cards.
+Additionally, cripped filesystem mode is needed, to avoid hard links,
+file modes, etc.
diff --git a/doc/design/assistant/blog/day_183__plan_b.mdwn b/doc/design/assistant/blog/day_183__plan_b.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_183__plan_b.mdwn
@@ -0,0 +1,19 @@
+Have not tried to run my static binary on Android yet, but I'm already
+working on a plan B in case that doesn't work. Yesterday I stumbled upon
+<https://github.com/neurocyte/ghc-android>, a ghc cross-compiler for
+Android that uses the Android native development kit. 
+It first appeared on February 4th. Good timing!
+
+I've gotten it to build and it emits arm executables, that seem to use the
+Android linker. So that's very promising indeed. 
+
+I've also gotten cabal working with it, and have it chewing through
+installing git-annex's build dependencies.
+
+----
+
+Also made a release today, this is another release that's mostly bugfixes,
+and a few minor features. Including one bug fixed at 6 am this morning, urk.
+
+I think I will probably split my days between working on Android porting
+and other git-annex development.
diff --git a/doc/design/assistant/blog/day_184__just_wanna_run_something.mdwn b/doc/design/assistant/blog/day_184__just_wanna_run_something.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_184__just_wanna_run_something.mdwn
@@ -0,0 +1,46 @@
+Have been working on getting all the haskell libraries git-annex uses
+built with the android cross compiler. Difficulties so far are
+libraries that need tweaks to work with the new version of ghc, and some
+that use cabal in ways that break cross compilation. Haskell's network
+library was the last and most challenging of those.
+
+At this point, I'm able to start trying to build git-annex for android.
+Here's the first try!
+
+<pre>
+joey@gnu:~/src/git-annex>cabal install -w $HOME/.ghc-android-14-arm-linux-androideabi-4.7/bin/arm-unknown-linux-androideabi-ghc --with-ghc-pkg=$HOME/.ghc-android-14-arm-linux-androideabi-4.7/bin/arm-unknown-linux-androideabi-ghc-pkg --with-ld=$HOME/.ghc-android-14-arm-linux-androideabi-4.7/bin/arm-linux-androideabi-ld --flags="-Webapp -WebDAV -XMPP -S3 -Dbus"
+Resolving dependencies...
+Configuring git-annex-3.20130207...
+Building git-annex-3.20130207...
+Preprocessing executable 'git-annex' for git-annex-3.20130207...
+on the commandline: Warning:
+    -package-conf is deprecated: Use -package-db instead
+
+Utility/libdiskfree.c:28:26:
+     fatal error: sys/statvfs.h: No such file or directory
+compilation terminated.
+</pre>
+
+Should not be far from a first android build now..
+
+----
+
+While I already have Android "hello world" executables to try, I have not yet
+been able to run them. Can't seem to find a directory I can write to on the
+Asus Transformer, with a filesystem that supports the +x bit. Do you really
+have to root Android just to run simple binaries? I'm crying inside.
+
+It seems that the blessed Android NDK way would involve making a Java app,
+that pulls in a shared library that contains the native code. For haskell,
+the library will need to contain a C shim that, probably, calls an entry
+point to the Haskell runtime system. Once running, it can use the FFI to
+communicate back to the Java side, probably. The good news is that CJ van
+den Berg, who already saved my bacon once by developing ghc-android, tells
+me he's hard at work on that very thing.
+
+----
+
+In the meantime, downloaded the Android SDK. Have gotten it to build a
+`.apk` package from just javascript code, and managed to do it without
+using eclipse (thank god). Will need this later, but for now want to wash
+my brain out with soap after using it.
diff --git a/doc/design/assistant/blog/day_185__android_liftoff.mdwn b/doc/design/assistant/blog/day_185__android_liftoff.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_185__android_liftoff.mdwn
@@ -0,0 +1,20 @@
+Thanks to hhm, who pointed me at [KBOX](http://kevinboone.net/kbox.html),
+I have verified that I can build haskell programs that work on Android.
+
+After hacking on it all day, I've succeeded in making an initial build of
+git-annex for Android. It links! It runs!
+
+Which is not to say it's usable yet; for one thing I need to get a port
+of git before it can do anything useful. (Some of the other things git-annex
+needs, like ssh and sha256sum, are helpfully provided by KBOX.)
+
+Next step will be to find or built a git port for Android. I know there's
+one in the "Terminal IDE" app. Once I can use git-annex at the command line
+on Android, I'll be able to test it out some (I can also port the test
+suite program and run it on Android), and get a feeling for what is needed
+to get the port to a usable command-line state.
+
+And then on to the webapp, and an Android app, I suppose. So far, the port
+doesn't include the webapp, but does include the assistant. The webapp
+needs ghci/template haskell for arm. A few people have been reporting they
+have that working, but I don't yet.
diff --git a/doc/design/assistant/blog/day_186__Android_success.mdwn b/doc/design/assistant/blog/day_186__Android_success.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_186__Android_success.mdwn
@@ -0,0 +1,33 @@
+I'm now successfully using git-annex at the command line on Android.
+`git annex watch` works too.
+
+For now, I'm using a git repository under `/data`, which is on a real,
+non-cripped filesystem, so symlinks work there.
+
+There's still the issue of running without any symlinks on `/mnt/sdcard`.
+While direct mode gets most of the way, it still uses symlinks in a few
+places, so some more work will be needed there. Also, git-annex uses hard
+links to lock down files, which won't work on cripped filesystems.
+
+Besides that, there's lots of minor porting, but no big show-stoppers
+currently.. Some of today's porting work:
+
+* Cross-compiled git for Android. While the Terminal IDE app has some git
+  stuff, it's not complete and misses a lot of plumbing commands git-annex
+  uses. My git build needs some tweaks to be relocatable without setting
+  `GIT_EXEC_PATH`, but it works.
+
+* Switched git-annex to use the Haskell glob library, rather than PCRE. This
+  avoids needing libpcre, which simplifies installation on several platforms
+  (including Android).
+
+* Made git-annex's `configure` hardcode some settings when cross-compiling
+  for Android, rather than probing the build system.
+
+* Android's built-in `lsof` doesn't support the -F option to use a
+  machine-readable output format. So wrote a separate lsof output parser for
+  the standard lsof output format. Unfortunatly, Android's lsof does not
+  provide any information about where a file is open for read or write, so
+  for safety, git-annex has to assume any file that's open might be written
+  to, and avoid annexing it. It might be better to provide my own lsof
+  eventually.
diff --git a/doc/design/assistant/blog/day_187__porting_utilities.mdwn b/doc/design/assistant/blog/day_187__porting_utilities.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_187__porting_utilities.mdwn
@@ -0,0 +1,22 @@
+Ported all the utilities git-annex needs to run on Android: 
+git, rsync, gnupg, dropbear (ssh client), busybox. Built a
+Makefile that can download, patch, and cross build these from source.
+
+While all the utilities work, dropbear doesn't allow git-annex to use ssh
+connection caching, which is rather annoying especially since these systems
+tend to be rather slow and take a while to start up ssh connections.
+I'd sort of like to try to get openssh's client working on Android instead.
+Don't know how realistic that is.
+
+Dealt with several parts of git-annex that assumed `/bin/sh` exists,
+so it instead uses `/system/bin/sh` on Android. Also adapted `runshell`
+for Android.
+
+Now I have a 8 mb compressed tarball for Android.
+Uncompressed it's 25 mb. This includes a lot of git and busybox
+commands that won't be used, so it could be trimmed down further.
+16 mb of it is git-annex itself.
+
+[[Instructions for using the Android tarball|install/Android]]  
+This is for users who are rather brave, not afraid of command line and
+keyboard usage. Good first step.
diff --git a/doc/design/assistant/blog/day_188__crippled_filesystem_support.mdwn b/doc/design/assistant/blog/day_188__crippled_filesystem_support.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_188__crippled_filesystem_support.mdwn
@@ -0,0 +1,37 @@
+There are at least three problems with using git-annex
+on `/sdcard` on Android, or on a FAT filesystem, or on (to a first
+approximation) Windows:
+
+1. symlinks
+2. hard links
+3. unix permissions
+
+So, I've added an `annex.crippledfilesystem` setting. `git annex init` now
+probes to see if all three are supported, and if not, enables that, as well
+as direct mode.
+
+In crippled filesystem mode, all the permissions settings are skipped.
+Most of them are only used to lock down content in the annex in indirect
+mode anyway, so no great loss.
+
+There are several uses of hard links, most of which can be dealt with by
+making copies. The one use of permissions and hard links I absolutely
+needed to deal with was that they're used to lock down a file as it's being
+ingested into the annex. That can't be done on crippled filesystems, so I
+made it instead check the metadata of the file before and after to detect
+if it changed, the same way direct mode detects when files are modified.
+This is likely better than the old method anyway.
+
+The other reason files are hardlinked while they're being ingested is that
+this allows running lsof on a single directory of files that are in the
+process of being added, to detect if anything has them open for write.
+I still need to adjust the lsof check to work in crippled filesystem mode.
+It seems this won't make it much slower to run lsof on the whole repository.
+
+At this point, I can use git-annex with a repository on `/sdcard` or a FAT
+filesystem, and at least `git annex add` works. 
+
+Still several things on the TODO list before crippled filesystem mode is
+complete. The only one I'm scared about is making `git merge` do something
+sane when it wants to merge two trees full of symlinks, and the filesystem
+doesn't let it create a symlink..
diff --git a/doc/design/assistant/blog/day_189__more_crippling.mdwn b/doc/design/assistant/blog/day_189__more_crippling.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_189__more_crippling.mdwn
@@ -0,0 +1,44 @@
+Finished crippled filesystem support, except for symlink handling.
+This was straightforward, just got lsof working in that mode, made
+`migrate` copy key contents, and adapted the rsync special remote to
+support it. Encrypted rsync special remotes have no more overhead on
+crippled filesystems than normally. Un-encrypted rsync special remotes
+have some added overhead, but less than encrypted remotes. Acceptable
+for now.
+
+I've now successfully run the assistant on a FAT filesystem.
+
+----
+
+Git handles symlinks on crippled filesystems by setting
+`core.symlinks=false` and checking them out as files containing the link
+text. So to finish up crippled filesystem support, git-annex needs to
+do the same whenever it creates a symlink, and needs to read file contents
+when it normally reads a symlink target.
+
+There are rather a lot of calls to `createSymbolicLink`,
+`readSymbolicLink`, `getSymbolicLinkStatus`, `isSymbolicLink`, and `isSymLink`
+in the tree; only ones that are used in direct mode
+need to be converted. This will take a while.
+
+Checking whether something is a symlink, or where it points is especially
+tricky. How to tell if a small file in a git working tree is intended to be
+a symlink or not? Well, it can look at the content and see if it makes
+sense as a link text pointing at a git-annex key. As long as the
+possibility of false positives is ok. It might be possible, in some cases,
+to query git to verify if the object stored for that file is really a
+symlink, but that won't work if the file has been renamed, for example.
+
+Converted some of the most commonly used symlink code to handle this.
+Much more to do, but it basically works; I can `git annex get` and `git
+annex drop` on FAT, and it works.
+
+-----
+
+Unfortunately, got side-tracked when I discovered that the last release
+introduced a bug in direct mode. Due to the bug, "git annex get file; git annex
+drop file; git annex get file" would end up with the file being an indirect
+mode symlink to the content, rather than a direct mode file. No data loss,
+but not right. So, spent several hours fixing that reversion, which was
+caused by me stupidly fixing another bug at 5 am in the morning last week.. 
+and I'll probably be pushing out another release tomorrow with the fix.
diff --git a/doc/design/assistant/blog/day_190-191__weekend.mdwn b/doc/design/assistant/blog/day_190-191__weekend.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_190-191__weekend.mdwn
@@ -0,0 +1,28 @@
+Pushed out a release yesterday mostly for a bug fix. I have to build
+git-annex 5 times now when releasing. Am wondering if I could get rid of
+the Linux 64 bit standalone build. The 32 bit build should run ok on 64 bit
+Linux systems, since it has all its own 32 bit libraries. What I really
+need to do is set up autobuilders for Linux and Android, like we have for OSX.
+
+Today, dealt with all code that creates or looks at symlinks. Audited every
+bit of it, and converted all relevant parts to use a new abstraction layer
+that handles the pseudolink files git uses when core.symlinks=false.
+This is untested, but I'm quite happy with how it turned out.
+
+----
+
+Where next for Android? I want to spend a while testing command-line
+git-annex. After I'm sure it's really solid, I should try to get the webapp
+working, if possible.
+
+I've heard rumors that Ubuntu's version of ghc somehow supports template
+haskell on arm, so I need to investigate that. If I am unable to get
+template haskell on arm, I would need to either wait for further
+developments, or try to expand yesod's template haskell to regular haskell
+and then build it on arm, or I could of course switch away from hamlet
+(using blaze-html instead is appealing in some ways) and
+use yesod in non-template-haskell mode entirely. One of these will work,
+for sure, only question is how much pain.
+
+After getting the webapp working, there's still the issue of bundling it
+all up in an Android app that regular users can install.
diff --git a/doc/design/assistant/blog/day_81__enabling_pre-existing_special_remotes.mdwn b/doc/design/assistant/blog/day_81__enabling_pre-existing_special_remotes.mdwn
deleted file mode 100644
--- a/doc/design/assistant/blog/day_81__enabling_pre-existing_special_remotes.mdwn
+++ /dev/null
@@ -1,34 +0,0 @@
-It's possible for one git annex repository to configure a special remote
-that it makes sense for other repositories to also be able to use. Today I
-added the UI to support that; in the list of repositories, such
-repositories have a "enable" link.
-
-To enable pre-existing rsync special remotes, the webapp has to do the same
-probing and ssh key setup that it does when initially creating them.
-Rsync.net is also handled as a special case in that code. There was one
-ugly part to this.. When a rsync remote is configured in the webapp,
-it uses a mangled hostname like "git-annex-example.com-user", to
-make ssh use the key it sets up. That gets stored in the `remote.log`, and so
-the enabling code has to unmangle it to get back to the real hostname.
-
----
-
-Based on the still-running [[prioritizing_special_remotes]] poll, a lot
-of people want special remote support for their phone or mp3 player.
-(As opposed to running git-annex on an Android phone, which comes later..)
-It'd be easy enough to make the webapp set up a directory special remote
-on such a device, but that makes consuming some types of content on the
-phone difficult (mp3 players seem to handle them ok based on what people tell
-me). I need to think more about some of the ideas mentioned in [[android]]
-for more suitable ways of storing files.
-
-One thing's for sure: You won't want the assistant to sync all your files
-to your phone! So I also need to start coming up with partial syncing
-controls. One idea is for each remote to have a configurable matcher for files
-it likes to receive. That could be only mp3 files, or all files inside a
-given subdirectory, or all files *not* in a given subdirectory. That means
-that when the assistant detects a file has been moved, it'll need to add
-(or remove) a queued transfer. Lots of other things could be matched on,
-like file size, number of copies, etc. Oh look, I have a
-[beautiful library I wrote earlier](http://joeyh.name/blog/entry/happy_haskell_hacker)
-that I can reuse!
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 15 "Amazon S3 (done)" 12 "Amazon Glacier (done)" 9 "Box.com (done)" 68 "My phone (or MP3 player)" 18 "Tahoe-LAFS" 6 "OpenStack SWIFT" 25 "Google Drive"]]
+[[!poll open=yes 15 "Amazon S3 (done)" 12 "Amazon Glacier (done)" 9 "Box.com (done)" 69 "My phone (or MP3 player)" 18 "Tahoe-LAFS" 6 "OpenStack SWIFT" 27 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they
diff --git a/doc/direct_mode.mdwn b/doc/direct_mode.mdwn
--- a/doc/direct_mode.mdwn
+++ b/doc/direct_mode.mdwn
@@ -7,7 +7,13 @@
 commands cannot safely be used, and only a subset of git-annex commands
 can be used.
 
-Repositories created by the [[assistant]] use direct mode by default.
+Normally, git-annex repositories start off in indirect mode. With some
+exceptions:
+
+* Repositories created by the [[assistant]] use direct mode by default.
+* Repositories on FAT and other less than stellar filesystems
+  that don't support things like symlinks will be automatically put
+  into direct mode.
 
 ## enabling (and disabling) direct mode
 
diff --git a/doc/forum/Different_annexes_pointing_to_same_special_remote__63__.mdwn b/doc/forum/Different_annexes_pointing_to_same_special_remote__63__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Different_annexes_pointing_to_same_special_remote__63__.mdwn
@@ -0,0 +1,6 @@
+Is there likely to be any problem in pointing different annexes to the same special remote (i.e. rsync/box.com/etc.) ?
+
+As the objects are stored based on their SHA256 key the expectation is that the chance of collision is is small.
+
+The only problem I can foresee is where the same content is stored in more than one annex and it is deleted in the remote in one annex, but not the other - there won't be any protection against that, but for non-overlapping content this risk should be negligible.
+
diff --git a/doc/forum/Does_Jabber_syncing_work_when_the_buddy_is_offline__63__.mdwn b/doc/forum/Does_Jabber_syncing_work_when_the_buddy_is_offline__63__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Does_Jabber_syncing_work_when_the_buddy_is_offline__63__.mdwn
@@ -0,0 +1,1 @@
+Do both friends need to be online in order for Jabber syncing to work? Or will the pushed changes be stored on the Jabber server, so that when the remote machine does come online it can pull them even if the original machine is now offline?
diff --git a/doc/forum/Feature_request:_Multiple_concurrent_transfers.mdwn b/doc/forum/Feature_request:_Multiple_concurrent_transfers.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Feature_request:_Multiple_concurrent_transfers.mdwn
@@ -0,0 +1,19 @@
+I'm sitting on a pretty fast connection, and often git-annex isn't using my entire upstream to fill the remotes.
+
+Lets say i have an annex with 10 x 100mb files. and my git-annex has 5 special remotes that needs to be filled.
+
+I would like to have git-annex do 5 concurrent uploads(one to each remote). Currently git-annex only uploads to one special-remote at a time (meaning the 4 remaining servers are just waiting).
+
+It would also be nice to have a per remote number of threads. Especially if adding a directory with 1000 small files in it(say 100bytes each), having 5 transfer threads to each remote would make the sync complete much faster.
+
+
+For now i've made a shell script that i call:
+
+# for j in `seq -w 0 10`; do echo DOING $j; for i in `curl "http://127.0.0.1:$1/?auth=$2" | grep "continue" | gawk -F\"  ' { print $8 } '`; do curl "http://127.0.0.1:$1$i"; sleep 0; done; done
+
+But it is very rough, and basically just starts all transfers on the page. Which means i currently have 315 active transfers running. whoops.
+
+I could improve the shell script. But it really would be quite a bit niftier to have it as settings in git-annex.
+
+Sincerely
+Tobias
diff --git a/doc/forum/Feature_request:_git_annex_copy_--auto_does_the_right_thing.mdwn b/doc/forum/Feature_request:_git_annex_copy_--auto_does_the_right_thing.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Feature_request:_git_annex_copy_--auto_does_the_right_thing.mdwn
@@ -0,0 +1,5 @@
+I'm not sure of the right place for feature requests; sorry if this is incorrect.
+
+It occurred to me that it would be neat if running git annex copy --auto copied enough copies of the file to connected remotes that don't have it to fulfil numcopies (if possible). Further coolness could come from choosing the remotes smartly (based, for example, on availability, access cost, remote disk space, trust level... you get the idea)
+
+Having an option like this would allow the user to use numcopies to backup files to wherever possible, if they don't really care about where those copies go.
diff --git a/doc/forum/Feature_request:_webapp_support_for_centralized_bare_repos.mdwn b/doc/forum/Feature_request:_webapp_support_for_centralized_bare_repos.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Feature_request:_webapp_support_for_centralized_bare_repos.mdwn
@@ -0,0 +1,1 @@
+I would like it if the webapp could support setting up [bare remotes](http://git-annex.branchable.com/tips/centralized_git_repository_tutorial/) to assist with syncing between two machines that are never online at the same time.
diff --git a/doc/forum/How_to_prevent_the_assistant_from_downloading_all_data__63__.mdwn b/doc/forum/How_to_prevent_the_assistant_from_downloading_all_data__63__.mdwn
deleted file mode 100644
--- a/doc/forum/How_to_prevent_the_assistant_from_downloading_all_data__63__.mdwn
+++ /dev/null
@@ -1,9 +0,0 @@
-I have a repository on my laptop with a single remote on a USB drive. Both were created without the assistant.
-
-The remote holds all the content. My understanding is that this would be a "backup" repository, using the group definitions.
-
-The local repository generally holds no content. When I want a file, I make sure the remote is available, and then I `git annex get` it.
-
-This works fine in git annex, but I'm now experimenting with the assistant. As soon as I fire up the assistant, it starts downloading all of the data from the remote. I don't ever want it to automatically get any content from the remote. If it sees new content in the repository, I would like it to push that out to the remote. But never get anything from the remote, and never drop anything that is in the repository.
-
-How can I setup this behaviour in the assistant? I have set the remote's repository group to "backup". The local repository is not in any group, since none of the groups fit my model for this repo.
diff --git a/doc/forum/Make_whereis_output_more_compact.mdwn b/doc/forum/Make_whereis_output_more_compact.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Make_whereis_output_more_compact.mdwn
@@ -0,0 +1,13 @@
+Hi there,
+
+first of all, great job done on annex! I think I finally found what I was looking for to organize all my data.
+
+My question: when I do git annex whereis Music, I get a detailed list of every single song and its remote locations. I have tens of thousands of songs, so just typing git annex whereis Music gives me way too much information. 
+Here is my proposition: in my case, every file of the Music folder resides in the same remote. So, wouldn't it be better to show just one line of output telling me that Music is on remote bla and blub, and then I know that all files in Music are in remotes bla and blub?
+
+Is that possible now already or would that be a feature request? What do you think?
+
+Cheers,
+Moritz.
+
+PS: I know that I can specify filters, e.g. git annex whereis --in bla --and --not --in blub. That is great!
diff --git a/doc/forum/Syncronisation_of_syncronisation_between_3_repositories__63__.mdwn b/doc/forum/Syncronisation_of_syncronisation_between_3_repositories__63__.mdwn
deleted file mode 100644
--- a/doc/forum/Syncronisation_of_syncronisation_between_3_repositories__63__.mdwn
+++ /dev/null
@@ -1,11 +0,0 @@
-Hello Joey,
-
-I just want to know if file transfers between three inter-connected repositories somehow gets syncronized. I have a laptop, a local file server in my home and a virtual server on the internet.
-
-Both my laptop and my file server are configured with a full git repository and connected through pairing and xmpp. The server on the internet is configured as a rsync special remote.
-
-I want the local file server to hold a copy of everything in my annex, the rsync remote should get everything except the folder "media" (too large to upload and not that important) and the laptop whatever I manually decide (preferred content is set to "present").
-
-I have added some files to the repository on the local file server and transferred their content to my laptop by using "git annex get". But when I started the web interface, I saw that those files who are present on both the file server and on the laptop get uploaded to the remote server from both computers, often the same file at the same time. Previously I though the two assistants would somehow talk to each other via xmpp so that doubled effort like that would be avoided. I'm also worried about data consistency because two apparently separate processes rsync to the same remote repository at the same time.
-
-So my question would be: Is your xmpp protocol designed to deal with situations like this and I did not set it up correctly or is what I'm trying to accomplish simply not (yet) possible?
diff --git a/doc/forum/mistakenly_checked___42__files__42___into_an_annex.__bummer..mdwn b/doc/forum/mistakenly_checked___42__files__42___into_an_annex.__bummer..mdwn
deleted file mode 100644
--- a/doc/forum/mistakenly_checked___42__files__42___into_an_annex.__bummer..mdwn
+++ /dev/null
@@ -1,3 +0,0 @@
-A couple times working in an annex manually, I accidentally did "git add" instead of "git annex add" and ended up with files checked into my repo instead of symlinks.  So now there is some file content in git, rather than in the annex.  Is there any way to root that out, or is it there forever?  (My limited knowledge of git says: it's there forever, unless maybe you go into the branches where it lives, do an interactive rebase to the time before it was added, remove that commit, replay history, and then garbage collect, but I have no idea if that would suddenly break git annex, and it sounds painful anyway.)
-
-If say I were a neat freak and wanted to just start over with a clean annex, I imagine the thing to do would be to just start the hell over -- and if I wanted to do that, the way to go would be to get into a full repository, do a "git annex uninit" and then throw away the .git directory, then do "git init" and "git annex init" and then "git annex add ." ?
diff --git a/doc/forum/switching_to__47__from_direct_mode_while_assistant_is_running.mdwn b/doc/forum/switching_to__47__from_direct_mode_while_assistant_is_running.mdwn
deleted file mode 100644
--- a/doc/forum/switching_to__47__from_direct_mode_while_assistant_is_running.mdwn
+++ /dev/null
@@ -1,2 +0,0 @@
-Is that really unsafe?  Because I did that and the switch to direct mode seemed to leave a lot of files still as links, and when I panicked and switched back to indirect mode, a whole lot of stuff seemed to have become unannexed and I reannexed it.
-
diff --git a/doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn b/doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn
deleted file mode 100644
--- a/doc/forum/wishlist:_git_annex_put_--_same_as_get__44___but_for_defaults.mdwn
+++ /dev/null
@@ -1,17 +0,0 @@
-I am running centralized git-annex exclusively.
-
-Similar to
-
-    git annex get
-
-I'd like to have a
-
-    git annex put
-
-which would put all files on the default remote(s).
-
-My main reason for not wanting to use copy --to is that I need to specify the remote's name in this case which makes writing a wrapper unnecessarily hard. Also, this would allow
-
-    mr push
-
-to do the right thing all by itself.
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -796,16 +796,22 @@
   to close it. On Mac OSX, when not using direct mode this defaults to
   1 second, to work around a bad interaction with software there.
 
+* `annex.autocommit`
+
+  Set to false to prevent the git-annex assistant from automatically
+  committing changes to files in the repository.
+
 * `annex.direct`
 
   Set to true to enable an (experimental) mode where files in the repository
   are accessed directly, rather than through symlinks. Note that many git
   and git-annex commands will not work with such a repository.
 
-* `annex.autocommit`
+* `annex.crippledfilesystem`
 
-  Set to false to prevent the git-annex assistant from automatically
-  committing changes to files in the repository.
+  Set to true if the repository is on a crippled filesystem, such as FAT,
+  which does not support symbolic links, or hard links, or unix permissions.
+  This is automatically probed by "git annex init".
 
 * `remote.<name>.annex-cost`
 
diff --git a/doc/index.mdwn b/doc/index.mdwn
--- a/doc/index.mdwn
+++ b/doc/index.mdwn
@@ -59,8 +59,22 @@
 
 ## talks
 
-<video controls src="http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm" width="500"></video>  
-A [15 minute introduction to git-annex](http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm), presented by Richard Hartmann.
+<table>
+<tr>
+<td width="50%" valign="top">
+<video controls
+src="http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm" width="100%"></video><br>
+A <a href="http://video.fosdem.org/2012/lightningtalks/git_annex___manage_files_with_git,_without_checking_their_contents_into_git.webm">15 minute introduction to git-annex</a>, presented by Richard Hartmann at Fosdem 2012.
+</td>
+<td width="50%" valign="top">
+<video controls width="100%">
+<source type="video/mp4" src="http://mirror.linux.org.au/linux.conf.au/2013/mp4/gitannex.mp4">
+<source type="video/ogg" src="http://mirror.linux.org.au/linux.conf.au/2013/ogv/gitannex.ogv">
+</video><br>
+A <a href="http://mirror.linux.org.au/linux.conf.au/2013/mp4/gitannex.mp4">45 minute demo of git-annex and the assistant</a>), presented by Joey Hess at LCA 2013.
+</td>
+</tr>
+</table>
 
 <br clear="all" />
 
diff --git a/doc/install.mdwn b/doc/install.mdwn
--- a/doc/install.mdwn
+++ b/doc/install.mdwn
@@ -14,6 +14,7 @@
 [[ScientificLinux5]]  | (and other RHEL5 clones like CentOS5)
 [[openSUSE]]          | 
 Windows               | [[sorry, Windows not supported yet|todo/windows_support]]
+[[Android]]           | 
 """]]
 
 ## Using cabal
diff --git a/doc/install/Android.mdwn b/doc/install/Android.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/install/Android.mdwn
@@ -0,0 +1,37 @@
+git-annex can be used on Android, however you need to know your way around
+the command line to install and use it. (An Android app may be developed
+eventually.)
+
+## prebuilt tarball
+
+Download the [prebuilt tarball](http://downloads.kitenet.net/git-annex/android/).
+Instructions below assume it was downloaded to `/sdcard/Download`, which
+is the default if you use the web browser for the download.
+
+To use this tarball, you need to install either
+[KBOX](http://kevinboone.net/kbox.html) or
+[Terminal IDE](https://play.google.com/store/apps/details?id=com.spartacusrex.spartacuside)
+(available in Google Play).
+This is both to get a shell console, as well as a location under
+`/data` where git-annex can be installed.
+
+Open the console app you installed, and enter this command:
+
+	cd $(which sh)/..; tar xf /sdcard/Download/git-annex-android.tar.gz
+
+Now git-annex is installed, but to use it you need to enter a special
+shell environment:
+
+	runshell
+
+Now you have git-annex, git, and some other utilities available, and can
+do everything in the [[walkthrough]] and more.
+
+## building it yourself
+
+git-annex can be built for Android, with `make android`.
+You need <https://github.com/neurocyte/ghc-android> installed first,
+and also have to `cabal install` all necessary dependencies. This is not
+yet an easy process. 
+
+You also need to install git and all the utilities listed on [[fromscratch]].
diff --git a/doc/install/Fedora.mdwn b/doc/install/Fedora.mdwn
--- a/doc/install/Fedora.mdwn
+++ b/doc/install/Fedora.mdwn
@@ -9,7 +9,7 @@
 Older version? Here's an installation recipe for Fedora 14 through 15.
 
 <pre>
-sudo yum install ghc cabal-install pcre-devel
+sudo yum install ghc cabal-install
 git clone git://git-annex.branchable.com/ git-annex
 cd git-annex
 git checkout ghc7.0
diff --git a/doc/install/Linux_standalone.mdwn b/doc/install/Linux_standalone.mdwn
--- a/doc/install/Linux_standalone.mdwn
+++ b/doc/install/Linux_standalone.mdwn
@@ -6,6 +6,9 @@
 on anything except for glibc.
 
 [download tarball](http://downloads.kitenet.net/git-annex/linux/)
- 
+
+To use, just unpack the tarball, `cd git-annex.linux` and run `./runshell`
+-- this sets up an environment where you can use `git annex`
+
 Warning: This is a last resort. Most Linux users should instead
 [[install]] git-annex from their distribution of choice.
diff --git a/doc/install/OSX.mdwn b/doc/install/OSX.mdwn
--- a/doc/install/OSX.mdwn
+++ b/doc/install/OSX.mdwn
@@ -24,7 +24,7 @@
 
 <pre>
 brew update
-brew install haskell-platform git ossp-uuid md5sha1sum coreutils pcre libgsasl gnutls libidn libgsasl pkg-config libxml2
+brew install haskell-platform git ossp-uuid md5sha1sum coreutils libgsasl gnutls libidn libgsasl pkg-config libxml2
 brew link libxml2
 cabal update
 PATH=$HOME/bin:$PATH
@@ -38,10 +38,7 @@
 Then execute
 
 <pre>
-sudo port install git-core ossp-uuid md5sha1sum coreutils pcre gnutls libxml2 libgsasl pkgconfig
-
-sudo ln -s /opt/local/include/pcre.h  /usr/include/pcre.h # This is hack that allows pcre-light to find pcre
-
+sudo port install git-core ossp-uuid md5sha1sum coreutils gnutls libxml2 libgsasl pkgconfig
 sudo cabal update
 PATH=$HOME/bin:$PATH
 cabal install c2hs git-annex --bindir=$HOME/bin
diff --git a/doc/install/OSX/comment_14_035f856923276b0edad879e196e94097._comment b/doc/install/OSX/comment_14_035f856923276b0edad879e196e94097._comment
new file mode 100644
--- /dev/null
+++ b/doc/install/OSX/comment_14_035f856923276b0edad879e196e94097._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawmCmNS-oUgYfNg85-LPuxzTZJUp0sIgprM"
+ nickname="Jonas"
+ subject="more homebrew"
+ date="2013-02-16T19:46:47Z"
+ content="""
+i had macports installed. then i installed brew, instaled haskell via brew. i needed to set 
+    PATH=$HOME/bin:/usr/local/bin:$PATH
+"""]]
diff --git a/doc/install/ScientificLinux5.mdwn b/doc/install/ScientificLinux5.mdwn
--- a/doc/install/ScientificLinux5.mdwn
+++ b/doc/install/ScientificLinux5.mdwn
@@ -29,23 +29,11 @@
 
     $ export PATH=/usr/hs/bin:$PATH
 
-On SL5 pcre is at version 6.6 which is far too old for one of the
-dependancies that git-annex requires. Therefore the user must install
-an updated version of _pcre_ either from source or another method, I
-chose to install it from source and by hand into /usr/local
-
-    $ wget http://sourceforge.net/projects/pcre/files/pcre/8.30/pcre-8.30.tar.gz/download
-    $ tar zxvf pcre-8.30.tar.gz
-    $ cd pcre-8.30
-    $ ./configure
-    $ make && make install
-
 Once the packages are installed and are in your execution path, using
 cabal to configure and build git-annex just makes life easier, it
 should install all the needed dependancies.
 
     $ cabal update
-    $ cabal install pcre-light --extra-include-dirs=/usr/local/include
     $ git clone git://git.kitenet.net/git-annex
     $ cd git-annex
     $ make git-annex.1
diff --git a/doc/install/fromscratch.mdwn b/doc/install/fromscratch.mdwn
--- a/doc/install/fromscratch.mdwn
+++ b/doc/install/fromscratch.mdwn
@@ -5,7 +5,6 @@
   * [The Haskell Platform](http://haskell.org/platform/) (GHC 7.4 or newer)
   * [mtl](http://hackage.haskell.org.package/mtl) (2.1.1 or newer)
   * [MissingH](http://github.com/jgoerzen/missingh/wiki)
-  * [pcre-light](http://hackage.haskell.org/package/pcre-light)
   * [utf8-string](http://hackage.haskell.org/package/utf8-string)
   * [SHA](http://hackage.haskell.org/package/SHA)
   * [dataenc](http://hackage.haskell.org/package/dataenc)
@@ -20,6 +19,8 @@
   * [hS3](http://hackage.haskell.org/package/hS3) (optional)
   * [DAV](http://hackage.haskell.org/package/DAV) (optional)
   * [SafeSemaphore](http://hackage.haskell.org/package/SafeSemaphore)
+  * [UUID](http://hackage.haskell.org/package/uuid)
+  * [Glob](http://hackage.haskell.org/package/Glob)
 * Optional haskell stuff, used by the [[assistant]] and its webapp (edit Makefile to disable)
   * [stm](http://hackage.haskell.org/package/stm)
     (version 2.3 or newer)
@@ -48,8 +49,6 @@
   * [async](http://hackage.haskell.org/package/async)
 * Shell commands
   * [git](http://git-scm.com/)
-  * [uuid](http://www.ossp.org/pkg/lib/uuid/)
-    (or `uuidgen` from util-linux)
   * [xargs](http://savannah.gnu.org/projects/findutils/)
   * [rsync](http://rsync.samba.org/)
   * [curl](http://http://curl.haxx.se/) (optional, but recommended)
diff --git a/doc/news/version_3.20130216.mdwn b/doc/news/version_3.20130216.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/news/version_3.20130216.mdwn
@@ -0,0 +1,17 @@
+git-annex 3.20130216 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Now uses the Haskell uuid library, rather than needing a uuid program.
+   * Now uses the Haskell Glob library, rather than pcre-light, avoiding
+     the need to install libpcre. Currently done only for Cabal or when
+     the Makefile is made to use -DWITH\_GLOB
+   * Android port now available (command-line only).
+   * New annex.crippledfilesystem setting, allows use of git-annex
+     repositories on FAT and even worse filesystems; avoiding use of
+     hard links and locked down permissions settings. (Support is incomplete.)
+   * init: Detect when the repository is on a filesystem that does not
+     support hard links, or symlinks, or unix permissions, and set
+     annex.crippledfilesystem, as well as annex.direct.
+   * add: Improved detection of files that are modified while being added.
+   * Fix a bug in direct mode, introduced in the previous release, where
+     if a file was dropped and then got back, it would be stored in indirect
+     mode."""]]
diff --git a/doc/special_remotes/bup/comment_1_96179a003da4444f6fc08867872cda0a._comment b/doc/special_remotes/bup/comment_1_96179a003da4444f6fc08867872cda0a._comment
deleted file mode 100644
--- a/doc/special_remotes/bup/comment_1_96179a003da4444f6fc08867872cda0a._comment
+++ /dev/null
@@ -1,43 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkgbXwQtPQSG8igdS7U8l031N8sqDmuyvk"
- nickname="Albert"
- subject="Error with bup and gnupg"
- date="2012-10-22T20:56:56Z"
- content="""
-Hello,
-
-I get this error when trying to use git-annex with bup and gnupg:
-
-<pre>
-move importable_pilot_surveys.tar (gpg) (checking localaseebup...) (to localaseebup...) 
-Traceback (most recent call last):
-  File \"/usr/lib/bup/cmd/bup-split\", line 133, in <module>
-    progress=prog)
-  File \"/usr/lib/bup/bup/hashsplit.py\", line 167, in split_to_shalist
-    for (sha,size,bits) in sl:
-  File \"/usr/lib/bup/bup/hashsplit.py\", line 118, in _split_to_blobs
-    for (blob, bits) in hashsplit_iter(files, keep_boundaries, progress):
-  File \"/usr/lib/bup/bup/hashsplit.py\", line 86, in _hashsplit_iter
-    bnew = next(fi)
-  File \"/usr/lib/bup/bup/helpers.py\", line 86, in next
-    return it.next()
-  File \"/usr/lib/bup/bup/hashsplit.py\", line 49, in blobiter
-    for filenum,f in enumerate(files):
-  File \"/usr/lib/bup/cmd/bup-split\", line 128, in <genexpr>
-    files = extra and (open(fn) for fn in extra) or [sys.stdin]
-IOError: [Errno 2] No such file or directory: '-'
-</pre>
-
-
-I was able to work-around this issue by altering /usr/lib/bup/cmd/bup-split (though I don't think its a bup bug) to just pull from stdin:
-
-files = [sys.stdin]
-
-on ~ line 128.
-
-Any ideas? Also, do you think that bup's data-deduplication does anything when gnupg is enabled, i.e. is it just as well to use a directory remote with gnupg?
-
-Thanks! Git annex rules!
-
-Albert
-"""]]
diff --git a/doc/special_remotes/bup/comment_2_612b038c15206f9f3c2e23c7104ca627._comment b/doc/special_remotes/bup/comment_2_612b038c15206f9f3c2e23c7104ca627._comment
deleted file mode 100644
--- a/doc/special_remotes/bup/comment_2_612b038c15206f9f3c2e23c7104ca627._comment
+++ /dev/null
@@ -1,12 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- ip="4.154.0.23"
- subject="comment 2"
- date="2012-10-23T20:01:43Z"
- content="""
-@Albert, thanks for reporting this bug (but put them in [[bugs]] in future please).
-
-This is specific to using the bup special remote with encryption. Without encryption it works. And no, it won't manage to deduplicate anything that's encrypted, as far as I know. 
-
-I think bup-split must have used - for stdin in the past, but now, it just reads from stdin when no file is specified, so I've updated git-annex.
-"""]]
diff --git a/doc/special_remotes/hook/comment_1_6a74a25891974a28a8cb42b87cb53c26._comment b/doc/special_remotes/hook/comment_1_6a74a25891974a28a8cb42b87cb53c26._comment
deleted file mode 100644
--- a/doc/special_remotes/hook/comment_1_6a74a25891974a28a8cb42b87cb53c26._comment
+++ /dev/null
@@ -1,32 +0,0 @@
-[[!comment format=mdwn
- username="helmut"
- ip="89.0.176.236"
- subject="Asynchronous hooks?"
- date="2012-10-13T09:46:14Z"
- content="""
-Is there a way to use asynchronous remotes? Interaction with git annex would have to
-split the part of initiating some action from completing it.
-
-I imagine I could `git annex copy` a file to an asynchronous remote and the command
-would almost immediately complete. Later I would learn that the transfer is
-completed, so the hook must be able to record that information in the `git-annex`
-branch. An additional plumbing command seems required here as well as a way to
-indicate that even though the store-hook completed, the file is not transferred.
-
-Similarly `git annex get` would immediately return without actually fetching the
-file. This should already be possible by returning non-zero from the retrieve-hook.
-Later the hook could use plumbing level commands to actually stick the received file
-into the repository.
-
-The remove-hook should need no changes, but the checkpresent-hook would be more like
-a trigger without any actual result. The extension of the plumbing required for the
-extension to the receive-hook could update the location log. A downside here is that
-you never know when a fsck has completed.
-
-My proposal does not include a way to track the completion of actions, but relies on
-the hook to always complete them reliably. It is not clear that this is the best road
-for asynchronous hooks.
-
-One use case for this would be a remote that is only accessible via uucp. Are there
-other use cases? Is the drafted interface useful?
-"""]]
diff --git a/doc/tips/assume-unstaged/comment_1_44abd811ef79a85e557418e17a3927be._comment b/doc/tips/assume-unstaged/comment_1_44abd811ef79a85e557418e17a3927be._comment
deleted file mode 100644
--- a/doc/tips/assume-unstaged/comment_1_44abd811ef79a85e557418e17a3927be._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="https://me.yahoo.com/a/2djv2EYwk43rfJIAQXjYt_vfuOU-#a11a6"
- nickname="Olivier R"
- subject="It doesn't work 100%"
- date="2012-05-03T21:42:54Z"
- content="""
-When you remove tracked files... it doesn't show the new status. it's like if the file was ignored.
-
-
-"""]]
diff --git a/doc/tips/using_Amazon_S3/comment_1_666a26f95024760c99c627eed37b1966._comment b/doc/tips/using_Amazon_S3/comment_1_666a26f95024760c99c627eed37b1966._comment
deleted file mode 100644
--- a/doc/tips/using_Amazon_S3/comment_1_666a26f95024760c99c627eed37b1966._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawnoUOqs_lbuWyZBqyU6unHgUduJwDDgiKY"
- nickname="Matt"
- subject="ANNEX_S3 vs AWS for keys"
- date="2012-05-29T12:24:25Z"
- content="""
-The instructions state ANNEX_S3_ACCESS_KEY_ID and ANNEX_SECRET_ACCESS_KEY but git-annex cannot connect with those constants. git-annex tells me to set both \"AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY\" instead, which works. This is with Xubuntu 12.04.
-"""]]
diff --git a/doc/tips/using_Amazon_S3/comment_2_f5a0883be7dbb421b584c6dc0165f1ef._comment b/doc/tips/using_Amazon_S3/comment_2_f5a0883be7dbb421b584c6dc0165f1ef._comment
deleted file mode 100644
--- a/doc/tips/using_Amazon_S3/comment_2_f5a0883be7dbb421b584c6dc0165f1ef._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- ip="4.153.81.112"
- subject="comment 2"
- date="2012-05-29T19:10:42Z"
- content="""
-Thanks, I've fixed that. (You could have too.. this is a wiki ;)
-"""]]
diff --git a/doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment b/doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment
deleted file mode 100644
--- a/doc/todo/cache_key_info/comment_1_578df1b3b2cbfdc4aa1805378f35dc48._comment
+++ /dev/null
@@ -1,11 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawl9sYlePmv1xK-VvjBdN-5doOa_Xw-jH4U"
- nickname="Richard"
- subject="comment 1"
- date="2011-05-17T07:27:02Z"
- content="""
-Sounds like a good idea.
-
-* git annex fsck (or similar) should check/rebuild the caches
-* I would simply require a clean tree with a verbose error. 80/20 rule and defaulting to save actions.
-"""]]
diff --git a/doc/todo/windows_support/comment_1_3cc26ad8101a22e95a8c60cf0c4dedcc._comment b/doc/todo/windows_support/comment_1_3cc26ad8101a22e95a8c60cf0c4dedcc._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_1_3cc26ad8101a22e95a8c60cf0c4dedcc._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkRITTYYsN0TFKN7G5sZ6BWGZOTQ88Pz4s"
- nickname="Zoltán"
- subject="cygwin"
- date="2012-05-15T00:14:08Z"
- content="""
-What about [Cygwin](http://cygwin.com/)? It emulates POSIX fairly well under Windows (including signals, forking, fs (also things like /dev/null, /proc), unix file permissions), has all standard gnu utilities. It also emulates symlinks, but they are unfortunately incompatible with NTFS symlinks introduced in Vista [due to some stupid restrictions on Windows](http://cygwin.com/ml/cygwin/2009-10/msg00756.html).
-
-If git-annex could be modified to not require symlinks to work, the it would be a pretty neat solution (and you get a real shell, not some command.com on drugs (aka cmd.exe))
-"""]]
diff --git a/doc/todo/windows_support/comment_2_8acae818ce468967499050bbe3c532ea._comment b/doc/todo/windows_support/comment_2_8acae818ce468967499050bbe3c532ea._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_2_8acae818ce468967499050bbe3c532ea._comment
+++ /dev/null
@@ -1,12 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawk5cj-itfFHq_yhJHdzk3QOPp-PNW_MjPU"
- nickname="Michael"
- subject="+1 Cygwin"
- date="2012-05-23T19:30:21Z"
- content="""
-Windows support is a must. In my experience, binary file means proprietary editor, which means Windows.
-
-Unfortunately, there's not much overlap between people who use graphical editors in Windows all day vs. people who are willing to tolerate Cygwin's setup.exe, compile a Haskell program, learn git and git-annex's 90-odd subcommands, and use a mintty terminal to manage their repository, especially now that there's a sexy GitHub app for Windows.
-
-That aside, I think Windows-based content producers are still *the* audience for git-annex. First Windows support, then a GUI, then the world.
-"""]]
diff --git a/doc/todo/windows_support/comment_3_bd0a12f4c9b884ab8a06082842381a01._comment b/doc/todo/windows_support/comment_3_bd0a12f4c9b884ab8a06082842381a01._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_3_bd0a12f4c9b884ab8a06082842381a01._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="http://xolus.net/openid/max"
- nickname="B0FH"
- subject="What about NTFS support ?"
- date="2012-08-02T17:45:10Z"
- content="""
-Has git-annex been tested with an NTFS-formatted disk under Linux ? NTFS is supposed to be case-sensitive and to allow symlinks, and these are supposed to work with ntfs3g.
-"""]]
diff --git a/doc/todo/windows_support/comment_4_ad06b98b2ddac866ffee334e41fee6a8._comment b/doc/todo/windows_support/comment_4_ad06b98b2ddac866ffee334e41fee6a8._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_4_ad06b98b2ddac866ffee334e41fee6a8._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawlc1og3PqIGudOMkFNrCCNg66vB7s-jLpc"
- nickname="Paul"
- subject="Re: What about NTFS support?"
- date="2012-08-16T19:30:38Z"
- content="""
-I successfully use git-annex on an NTFS formatted external USB drive, so yes, it is possible and works well.
-"""]]
diff --git a/doc/todo/windows_support/comment_5_444fc7251f57db241b6e80abae41851c._comment b/doc/todo/windows_support/comment_5_444fc7251f57db241b6e80abae41851c._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_5_444fc7251f57db241b6e80abae41851c._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="https://me.yahoo.com/a/dASECLNzvckz4VwqUGYsvthiplY.#d2c27"
- nickname="A. D. Sicks"
- subject="comment 5"
- date="2012-09-09T23:48:21Z"
- content="""
-Haskell has C++ binding, so it should be possible to port it to .net/Mono with a VB GUI for Windows users.  Windows has a primitive form of symlinks called shortcuts.  Perhaps shortcut support in Windows could replace the use of symlinks.  I've used shortcuts since XP to put my home Windows directory on another partition and never had a hitch...
-
-If anyone is interested in working on this, hit me up.  I would like to use this in my XP vbox to have access to files on my host OS...I have a student edition of Visual Studio 2005 to do an open source port...
-"""]]
diff --git a/doc/todo/windows_support/comment_6_34f1f60b570c389bb1e741b990064a7e._comment b/doc/todo/windows_support/comment_6_34f1f60b570c389bb1e741b990064a7e._comment
deleted file mode 100644
--- a/doc/todo/windows_support/comment_6_34f1f60b570c389bb1e741b990064a7e._comment
+++ /dev/null
@@ -1,8 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawnlwEMhiNYv__mEUABW4scn83yMraC3hqE"
- nickname="Sean"
- subject="NTFS symlinks"
- date="2013-01-11T21:44:21Z"
- content="""
-It seems that NTFS (from Vista forward) has full POSIX support for symlinks. At least, Wikipedia [seems to think so.](http://en.wikipedia.org/wiki/NTFS_symbolic_link). What about doing like GitHub and using MinGW for compatibility? Cygwin absolutely blows in terms of installation size and compatability with the rest of Windows.
-"""]]
diff --git a/doc/todo/wishlist:_special_remote_Ubuntu_One.mdwn b/doc/todo/wishlist:_special_remote_Ubuntu_One.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/todo/wishlist:_special_remote_Ubuntu_One.mdwn
@@ -0,0 +1,1 @@
+Special remote support for [Ubuntu One](http://one.ubuntu.com) would be nice. They're [using propietary but open protocol](https://wiki.ubuntu.com/UbuntuOne/TechnicalDetails#ubuntuone-storageprotocol) based on [Google Protocol Buffers](http://code.google.com/p/protobuf/). There's [protobuf for Haskell](http://code.google.com/p/protobuf-haskell/) so it should be possible to compile [the protocol file](http://bazaar.launchpad.net/~ubuntuone-control-tower/ubuntuone-storage-protocol/trunk/view/head:/ubuntuone/storageprotocol/protocol.proto) to Haskell code and then use that to implement the native Ubuntu special remote.
diff --git a/git-annex.1 b/git-annex.1
--- a/git-annex.1
+++ b/git-annex.1
@@ -700,14 +700,19 @@
 to close it. On Mac OSX, when not using direct mode this defaults to
 1 second, to work around a bad interaction with software there.
 .IP
+.IP "annex.autocommit"
+Set to false to prevent the git\-annex assistant from automatically
+committing changes to files in the repository.
+.IP
 .IP "annex.direct"
 Set to true to enable an (experimental) mode where files in the repository
 are accessed directly, rather than through symlinks. Note that many git
 and git\-annex commands will not work with such a repository.
 .IP
-.IP "annex.autocommit"
-Set to false to prevent the git\-annex assistant from automatically
-committing changes to files in the repository.
+.IP "annex.crippledfilesystem"
+Set to true if the repository is on a crippled filesystem, such as FAT,
+which does not support symbolic links, or hard links, or unix permissions.
+This is automatically probed by "git annex init".
 .IP
 .IP "remote.<name>.annex\-cost"
 When determining which repository to
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.20130207
+Version: 3.20130216.1
 Cabal-Version: >= 1.8
 License: GPL
 Maintainer: Joey Hess <joey@kitenet.net>
@@ -57,16 +57,17 @@
   Build-Depends: MissingH, hslogger, directory, filepath,
    unix, containers, utf8-string, network (>= 2.0), mtl (>= 2.1.1),
    bytestring, old-locale, time,
-   pcre-light, extensible-exceptions, dataenc, SHA, process, json, HTTP,
-   base (>= 4.5 && < 4.7), monad-control, transformers-base, lifted-base,
+   extensible-exceptions, dataenc, SHA, process, json,
+   base (>= 4.5 && < 4.8), monad-control, transformers-base, lifted-base,
    IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance, process,
-   SafeSemaphore
+   SafeSemaphore, uuid, random, Glob
   -- Need to list these because they're generated from .hsc files.
   Other-Modules: Utility.Touch Utility.Mounts
   Include-Dirs: Utility
   C-Sources: Utility/libdiskfree.c Utility/libmounts.c
   Extensions: CPP
   GHC-Options: -threaded
+  CPP-Options: -DWITH_GLOB
 
   if flag(S3)
     Build-Depends: hS3
@@ -123,15 +124,15 @@
   Main-Is: test.hs
   Build-Depends: testpack, HUnit, MissingH, hslogger, directory, filepath,
    unix, containers, utf8-string, network, mtl (>= 2.1.1), bytestring,
-   old-locale, time, pcre-light, extensible-exceptions, dataenc, SHA,
-   process, json, HTTP, base (>= 4.5 && < 4.7), monad-control,
+   old-locale, time, extensible-exceptions, dataenc, SHA,
+   process, json, base (>= 4.5 && < 4.7), monad-control,
    transformers-base, lifted-base, IfElse, text, QuickCheck >= 2.1,
-   bloomfilter, edit-distance, process, SafeSemaphore
+   bloomfilter, edit-distance, process, SafeSemaphore, Glob
   Other-Modules: Utility.Touch
   Include-Dirs: Utility
   C-Sources: Utility/libdiskfree.c
   Extensions: CPP
-  GHC-Options: -threaded
+  GHC-Options: -threaded -DWITH_GLOB
 
 source-repository head
   type: git
diff --git a/standalone/android/Makefile b/standalone/android/Makefile
new file mode 100644
--- /dev/null
+++ b/standalone/android/Makefile
@@ -0,0 +1,53 @@
+# Cross-compiles utilities needed for git-annex on Android.
+
+# Add Android cross-compiler to PATH (as installed by ghc-android)
+PATH:=$(HOME)/.ghc/android-14/arm-linux-androideabi-4.7/bin:$(PATH)
+
+build: source
+	mkdir -p git-annex-bundle/bin
+	
+	cd source/git && $(MAKE) install NO_OPENSSL=1 NO_GETTEXT=1 NO_GECOS_IN_PWENT=1 NO_GETPASS=1 NO_NSEC=1 NO_MKDTEMP=1 NO_PTHREADS=1 NO_PERL=1 NO_CURL=1 NO_EXPAT=1 NO_TCLTK=1 NO_ICONV=1 prefix= DESTDIR=../../git-annex-bundle
+	rm -f git-annex-bundle/bin/git-cvsserver
+
+	cp source/busybox/configs/android2_defconfig source/busybox/.config
+	echo 'CONFIG_FEATURE_INSTALLER=y' >> source/busybox/.config
+	cd source/busybox && yes '' | $(MAKE) oldconfig
+	cd source/busybox && $(MAKE)
+	cp -a source/busybox/busybox git-annex-bundle/bin/
+
+	cd source/dropbear && git reset --hard origin/master && git am < ../../dropbear.patch
+	cp source/automake/lib/config.sub source/automake/lib/config.guess source/dropbear/
+	cd source/dropbear && ./configure --host=arm-linux-androideabi --disable-lastlog --disable-utmp --disable-utmpx --disable-wtmp --disable-wtmpx
+	cd source/dropbear && $(MAKE) dbclient
+	cp -a source/dropbear/dbclient git-annex-bundle/bin/ssh
+	
+	cd source/rsync && git reset --hard origin/master && git am < ../../rsync.patch
+	cp source/automake/lib/config.sub source/automake/lib/config.guess source/rsync/
+	cd source/rsync && ./configure --host=arm-linux-androideabi --disable-locale --disable-iconv-open --disable-iconv --disable-acl-support --disable-xattr-support
+	cd source/rsync && $(MAKE)
+	cp -a source/rsync/rsync git-annex-bundle/bin/
+
+	cd source/gnupg && git checkout gnupg-1.4.13
+	cd source/gnupg && ./autogen.sh
+	cd source/gnupg && ./configure --host=arm-linux-androideabi --disable-gnupg-iconv --enable-minimal --disable-card-support --disable-agent-support --disable-photo-viewers --disable-keyserver-helpers --disable-nls
+	cd source/gnupg; $(MAKE) || true # expected failure in doc build
+	cp -a source/gnupg/g10/gpg git-annex-bundle/bin/
+
+source:
+	mkdir -p source
+	git clone git://git.savannah.gnu.org/automake.git source/automake
+	git clone git://git.debian.org/git/d-i/busybox source/busybox
+	git clone git://github.com/android/platform_external_dropbear source/dropbear
+	git clone git://git.kernel.org/pub/scm/git/git.git source/git
+	git clone git://git.samba.org/rsync.git source/rsync
+	git clone git://git.gnupg.org/gnupg.git source/gnupg
+
+clean:
+	cd source/busybox && $(MAKE) clean
+	cd source/dropbear && $(MAKE) clean
+	cd source/git && $(MAKE) clean
+	cd source/rsync && $(MAKE) clean
+	cd source/gnupg && $(MAKE) clean
+
+reallyclean:
+	rm -rf source
diff --git a/standalone/android/dropbear.patch b/standalone/android/dropbear.patch
new file mode 100644
--- /dev/null
+++ b/standalone/android/dropbear.patch
@@ -0,0 +1,55 @@
+From 014dadb02fd984828a6232534c47dba8e2f7818a Mon Sep 17 00:00:00 2001
+From: Joey Hess <joey@kitenet.net>
+Date: Wed, 13 Feb 2013 15:29:52 -0400
+Subject: [PATCH] android patch for dropbear
+
+* Disable HOME override
+* Use urandom to avoid blocking on every ssh connection.
+* Enable use of netbsd_getpass.c
+---
+ cli-auth.c |    1 +
+ cli-main.c |    2 --
+ options.h  |    2 +-
+ 3 files changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/cli-auth.c b/cli-auth.c
+index 4c17a21..91dfdf8 100644
+--- a/cli-auth.c
++++ b/cli-auth.c
+@@ -31,6 +31,7 @@
+ #include "ssh.h"
+ #include "packet.h"
+ #include "runopts.h"
++#include "netbsd_getpass.c"
+ 
+ void cli_authinitialise() {
+ 
+diff --git a/cli-main.c b/cli-main.c
+index 106006b..68cf023 100644
+--- a/cli-main.c
++++ b/cli-main.c
+@@ -47,8 +47,6 @@ int main(int argc, char ** argv) {
+ 	_dropbear_exit = cli_dropbear_exit;
+ 	_dropbear_log = cli_dropbear_log;
+ 
+-	putenv("HOME=/data/local");
+-
+ 	disallow_core();
+ 
+ 	cli_getopts(argc, argv);
+diff --git a/options.h b/options.h
+index 7625151..48e404d 100644
+--- a/options.h
++++ b/options.h
+@@ -159,7 +159,7 @@ etc) slower (perhaps by 50%). Recommended for most small systems. */
+  * however significantly reduce the security of your ssh connections
+  * if the PRNG state becomes guessable - make sure you know what you are
+  * doing if you change this. */
+-#define DROPBEAR_RANDOM_DEV "/dev/random"
++#define DROPBEAR_RANDOM_DEV "/dev/urandom"
+ 
+ /* prngd must be manually set up to produce output */
+ /*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/
+-- 
+1.7.10.4
+
diff --git a/standalone/android/rsync.patch b/standalone/android/rsync.patch
new file mode 100644
--- /dev/null
+++ b/standalone/android/rsync.patch
@@ -0,0 +1,40 @@
+From f91df535053958600d57f9df78d9ce84c8718655 Mon Sep 17 00:00:00 2001
+From: Joey Hess <joey@kitenet.net>
+Date: Wed, 13 Feb 2013 15:51:40 -0400
+Subject: [PATCH] android portability
+
+---
+ authenticate.c |    3 ++-
+ batch.c        |    2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/authenticate.c b/authenticate.c
+index 7650377..626dec6 100644
+--- a/authenticate.c
++++ b/authenticate.c
+@@ -296,7 +296,8 @@ void auth_client(int fd, const char *user, const char *challenge)
+                  *
+                  * OpenBSD has a readpassphrase() that might be more suitable.
+                  */
+-		pass = getpass("Password: ");
++		/*pass = getpass("Password: "); */
++		exit(1);
+ 	}
+ 
+ 	if (!pass)
+diff --git a/batch.c b/batch.c
+index a3e9dca..ee31532 100644
+--- a/batch.c
++++ b/batch.c
+@@ -221,7 +221,7 @@ void write_batch_shell_file(int argc, char *argv[], int file_arg_cnt)
+ 	stringjoin(filename, sizeof filename,
+ 		   batch_name, ".sh", NULL);
+ 	fd = do_open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+-		     S_IRUSR | S_IWUSR | S_IEXEC);
++		     S_IRUSR | S_IWUSR);
+ 	if (fd < 0) {
+ 		rsyserr(FERROR, errno, "Batch file %s open error",
+ 			filename);
+-- 
+1.7.10.4
+
diff --git a/standalone/android/runshell b/standalone/android/runshell
new file mode 100644
--- /dev/null
+++ b/standalone/android/runshell
@@ -0,0 +1,62 @@
+#!/system/bin/sh
+# Runs a shell command (or interactive shell) using the binaries and
+# libraries bundled with this app.
+
+set -e
+
+base="$(dirname $0)"/git-annex-bundle
+
+if [ ! -d "$base" ]; then
+	echo "** cannot find base directory (I seem to be $0)" >&2
+	exit 1
+fi
+
+if [ ! -e "$base/bin/git-annex" ]; then
+	echo "** base directory $base does not contain bin/git-annex" >&2
+	exit 1
+fi
+if [ ! -e "$base/bin/git" ]; then
+	echo "** base directory $base does not contain bin/git" >&2
+	exit 1
+fi
+
+# Install busybox links.
+if [ ! -e "$base/bin/sh" ]; then
+	echo "(First run detected ... setting up busybox ...)"
+	"$base/bin/busybox" --install "$base/bin"
+fi
+
+# Get absolute path to base, to avoid breakage when things change directories.
+orig="$(pwd)"
+cd "$base"
+base="$(pwd)"
+cd "$orig"
+
+# Put our binaries first, to avoid issues with out of date or incompatable
+# system or App binaries.
+ORIG_PATH="$PATH"
+export ORIG_PATH
+PATH=$base/bin:$PATH
+export PATH
+
+ORIG_GIT_EXEC_PATH="$GIT_EXEC_PATH"
+export ORIG_GIT_EXEC_PATH
+GIT_EXEC_PATH=$base/libexec/git-core
+export GIT_EXEC_PATH
+
+ORIG_GIT_TEMPLATE_DIR="$GIT_TEMPLATE_DIR"
+export ORIG_GIT_TEMPLATE_DIR
+GIT_TEMPLATE_DIR="$base/templates"
+export GIT_TEMPLATE_DIR
+
+# Indicate which variables were exported above.
+GIT_ANNEX_STANDLONE_ENV="PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR"
+export GIT_ANNEX_STANDLONE_ENV
+
+if [ "$1" ]; then
+	cmd="$1"
+	shift 1
+	exec "$cmd" "$@"
+else
+	sh
+fi
diff --git a/standalone/licences.gz b/standalone/licences.gz
Binary files a/standalone/licences.gz and b/standalone/licences.gz differ
diff --git a/test.hs b/test.hs
--- a/test.hs
+++ b/test.hs
@@ -54,7 +54,7 @@
 import qualified Utility.Verifiable
 import qualified Utility.Process
 import qualified Utility.Misc
-import qualified Annex.Content.Direct
+import qualified Utility.InodeCache
 
 -- instances for quickcheck
 instance Arbitrary Types.Key.Key where
@@ -75,8 +75,8 @@
 		<*> arbitrary `suchThat` (/= Just "")
 		<*> arbitrary
 
-instance Arbitrary Annex.Content.Direct.Cache where
-	arbitrary = Annex.Content.Direct.Cache
+instance Arbitrary Utility.InodeCache.InodeCache where
+	arbitrary = Utility.InodeCache.InodeCache
 		<$> arbitrary
 		<*> arbitrary
 		<*> arbitrary
@@ -119,7 +119,7 @@
 	, qctest "prop_verifiable_sane" Utility.Verifiable.prop_verifiable_sane
 	, qctest "prop_segment_regressionTest" Utility.Misc.prop_segment_regressionTest
 	, qctest "prop_read_write_transferinfo" Logs.Transfer.prop_read_write_transferinfo
-	, qctest "prop_read_show_direct" Annex.Content.Direct.prop_read_show_direct
+	, qctest "prop_read_show_inodecache" Utility.InodeCache.prop_read_show_inodecache
 	, qctest "prop_parse_show_log" Logs.Presence.prop_parse_show_log
 	, qctest "prop_read_show_TrustLevel" Types.TrustLevel.prop_read_show_TrustLevel
 	, qctest "prop_parse_show_TrustLog" Logs.Trust.prop_parse_show_TrustLog
@@ -209,7 +209,7 @@
 	git_annex "drop" ["--force", sha1annexedfile] @? "drop failed"
 	writeFile tmp $ content sha1annexedfile
 	r <- annexeval $ Types.Backend.getKey backendSHA1 $
-		Types.KeySource.KeySource { Types.KeySource.keyFilename = tmp, Types.KeySource.contentLocation = tmp }
+		Types.KeySource.KeySource { Types.KeySource.keyFilename = tmp, Types.KeySource.contentLocation = tmp, Types.KeySource.inodeCache = Nothing }
 	let key = Types.Key.key2file $ fromJust r
 	git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed"
 	git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed"
