diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@
 .DS_Store
 .virthualenv
 tags
+Setup
diff --git a/Annex.hs b/Annex.hs
--- a/Annex.hs
+++ b/Annex.hs
@@ -1,6 +1,6 @@
 {- git-annex monad
  -
- - Copyright 2010-2012 Joey Hess <joey@kitenet.net>
+ - Copyright 2010-2013 Joey Hess <joey@kitenet.net>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -16,7 +16,6 @@
 	newState,
 	run,
 	eval,
-	exec,
 	getState,
 	changeState,
 	setFlag,
@@ -35,10 +34,10 @@
 	withCurrentState,
 ) where
 
-import "mtl" Control.Monad.State.Strict
-import Control.Monad.Trans.Control (StM, MonadBaseControl, liftBaseWith, restoreM)
-import Control.Monad.Base (liftBase, MonadBase)
+import "mtl" Control.Monad.Reader
+import "MonadCatchIO-transformers" Control.Monad.CatchIO
 import System.Posix.Types (Fd)
+import Control.Concurrent
 
 import Common
 import qualified Git
@@ -56,32 +55,24 @@
 import Types.Group
 import Types.Messages
 import Types.UUID
-import Utility.State
 import qualified Utility.Matcher
 import qualified Data.Map as M
 import qualified Data.Set as S
 
--- git-annex's monad
-newtype Annex a = Annex { runAnnex :: StateT AnnexState IO a }
+{- git-annex's monad is a ReaderT around an AnnexState stored in a MVar.
+ - This allows modifying the state in an exception-safe fashion.
+ - The MVar is not exposed outside this module.
+ -}
+newtype Annex a = Annex { runAnnex :: ReaderT (MVar AnnexState) IO a }
 	deriving (
 		Monad,
 		MonadIO,
-		MonadState AnnexState,
+		MonadReader (MVar AnnexState),
+		MonadCatchIO,
 		Functor,
 		Applicative
 	)
 
-instance MonadBase IO Annex where
-	liftBase = Annex . liftBase
-
-instance MonadBaseControl IO Annex where
-	newtype StM Annex a = StAnnex (StM (StateT AnnexState IO) a)
-	liftBaseWith f = Annex $ liftBaseWith $ \runInIO ->
-		f $ liftM StAnnex . runInIO . runAnnex
-	restoreM = Annex . restoreM . unStAnnex
-	  where
-		unStAnnex (StAnnex st) = st
-
 type Matcher a = Either [Utility.Matcher.Token a] (Utility.Matcher.Matcher a)
 
 data FileInfo = FileInfo
@@ -156,14 +147,33 @@
 new :: Git.Repo -> IO AnnexState
 new = newState <$$> Git.Config.read
 
-{- performs an action in the Annex monad -}
+{- Performs an action in the Annex monad from a starting state,
+ - returning a new state. -}
 run :: AnnexState -> Annex a -> IO (a, AnnexState)
-run s a = runStateT (runAnnex a) s
+run s a = do
+	mvar <- newMVar s
+	r <- runReaderT (runAnnex a) mvar
+	s' <- takeMVar mvar
+	return (r, s')
+
+{- Performs an action in the Annex monad from a starting state, 
+ - and throws away the new state. -}
 eval :: AnnexState -> Annex a -> IO a
-eval s a = evalStateT (runAnnex a) s
-exec :: AnnexState -> Annex a -> IO AnnexState
-exec s a = execStateT (runAnnex a) s
+eval s a = do
+	mvar <- newMVar s
+	runReaderT (runAnnex a) mvar
 
+getState :: (AnnexState -> v) -> Annex v
+getState selector = do
+	mvar <- ask
+	s <- liftIO $ readMVar mvar
+	return $ selector s
+
+changeState :: (AnnexState -> AnnexState) -> Annex ()
+changeState modifier = do
+	mvar <- ask
+	liftIO $ modifyMVar_ mvar $ return . modifier
+
 {- Sets a flag to True -}
 setFlag :: String -> Annex ()
 setFlag flag = changeState $ \s ->
@@ -204,6 +214,7 @@
 fromRepo :: (Git.Repo -> a) -> Annex a
 fromRepo a = a <$> gitRepo
 
+{- Calculates a value from an annex's git repository and its GitConfig. -}
 calcRepo :: (Git.Repo -> GitConfig -> IO a) -> Annex a
 calcRepo a = do
 	s <- getState id
diff --git a/Annex/Branch.hs b/Annex/Branch.hs
--- a/Annex/Branch.hs
+++ b/Annex/Branch.hs
@@ -151,33 +151,30 @@
 				(nub $ fullname:refs)
 		liftIO cleanjournal
 
-{- Gets the content of a file, which may be in the journal, or committed
- - to the branch. Due to limitatons of git cat-file, does *not* get content
- - that has only been staged to the index.
+{- Gets the content of a file, which may be in the journal, or in the index
+ - (and committed to the branch).
  - 
  - Updates the branch if necessary, to ensure the most up-to-date available
  - content is available.
  -
  - Returns an empty string if the file doesn't exist yet. -}
 get :: FilePath -> Annex String
-get = get' False
+get file = do
+	update
+	get' file
 
 {- Like get, but does not merge the branch, so the info returned may not
- - reflect changes in remotes. (Changing the value this returns, and then
- - merging is always the same as using get, and then changing its value.) -}
+ - reflect changes in remotes.
+ - (Changing the value this returns, and then merging is always the
+ - same as using get, and then changing its value.) -}
 getStale :: FilePath -> Annex String
-getStale = get' True
+getStale = get'
 
-get' :: Bool -> FilePath -> Annex String
-get' staleok file = fromjournal =<< getJournalFile file
+get' :: FilePath -> Annex String
+get' file = go =<< getJournalFile file
   where
-	fromjournal (Just content) = return content
-	fromjournal Nothing
-		| staleok = withIndex frombranch
-		| otherwise = do
-			update
-			frombranch
-	frombranch = withIndex $ L.unpack <$> catFile fullname file
+	go (Just journalcontent) = return journalcontent
+	go Nothing = withIndex $ L.unpack <$> catFile fullname file
 
 {- Applies a function to modifiy the content of a file.
  -
@@ -294,8 +291,7 @@
 	 - Use getEnv to get some key environment variables that
 	 - git expects to have. -}
 	let keyenv = words "USER PATH GIT_EXEC_PATH HOSTNAME HOME"
-	let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> 
-		catchMaybeIO (getEnv k)
+	let getEnvPair k = maybe Nothing (\v -> Just (k, v)) <$> getEnv k
 	e <- liftIO $ catMaybes <$> forM keyenv getEnvPair
 #else
 	e <- liftIO getEnvironment
diff --git a/Annex/Content.hs b/Annex/Content.hs
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -28,7 +28,6 @@
 	preseedTmp,
 	freezeContent,
 	thawContent,
-	replaceFile,
 	cleanObjectLoc,
 ) where
 
@@ -53,6 +52,7 @@
 import Annex.Perms
 import Annex.Link
 import Annex.Content.Direct
+import Annex.ReplaceFile
 
 {- Checks if a given key's content is currently present. -}
 inAnnex :: Key -> Annex Bool
@@ -245,49 +245,40 @@
 moveAnnex key src = withObjectLoc key storeobject storedirect
   where
 	storeobject dest = ifM (liftIO $ doesFileExist dest)
-		( liftIO $ removeFile src
+		( alreadyhave
 		, do
 			createContentDir dest
 			liftIO $ moveFile src dest
 			freezeContent dest
 			freezeContentDir dest
 		)
-	storedirect fs = storedirect' =<< filterM validsymlink fs
-	validsymlink f = (==) (Just key) <$> isAnnexLink f
+	storeindirect = storeobject =<< calcRepo (gitAnnexLocation key)
 
-	storedirect' [] = storeobject =<< calcRepo (gitAnnexLocation key)
-	storedirect' (dest:fs) = do
+	{- In direct mode, the associated file's content may be locally
+	 - modified. In that case, it's preserved. However, the content
+	 - we're moving into the annex may be the only extant copy, so
+	 - it's important we not lose it. So, when the key's content
+	 - cannot be moved to any associated file, it's stored in indirect
+	 - mode.
+	 -}
+	storedirect = storedirect' storeindirect
+	storedirect' fallback [] = fallback
+	storedirect' fallback (f:fs) = do
 		thawContentDir =<< calcRepo (gitAnnexLocation key)
-		updateInodeCache key src
 		thawContent src
-		replaceFile dest $ liftIO . moveFile src
-		{- Copy to any other locations. -}
-		forM_ fs $ \f -> replaceFile f $
-			liftIO . void . copyFileExternal dest
-
-{- Replaces a possibly already existing file with a new version, 
- - atomically, by running an action.
-
- - The action is passed a temp file, which it can write to, and once
- - done the temp file is moved into place.
- -}
-replaceFile :: FilePath -> (FilePath -> Annex ()) -> Annex ()
-replaceFile file a = do
-	tmpdir <- fromRepo gitAnnexTmpDir
-	createAnnexDirectory tmpdir
-	tmpfile <- liftIO $ do
-		(tmpfile, h) <- openTempFileWithDefaultPermissions tmpdir $
-			takeFileName file
-		hClose h
-		return tmpfile
-	a tmpfile
-	liftIO $ do
-		r <- tryIO $ rename tmpfile file
-		case r of
-			Left _ -> do
-				createDirectoryIfMissing True $ parentDir file
-				rename tmpfile file
-			_ -> noop
+		v <- isAnnexLink f
+		if (Just key == v)
+			then do
+				updateInodeCache key src
+				replaceFile f $ liftIO . moveFile src
+				forM_ fs $
+					addContentWhenNotPresent key f
+			else ifM (goodContent key f)
+				( storedirect' alreadyhave fs
+				, storedirect' fallback fs
+				)
+	
+	alreadyhave = liftIO $ removeFile src
 
 {- Runs an action to transfer an object's content.
  -
diff --git a/Annex/Content/Direct.hs b/Annex/Content/Direct.hs
--- a/Annex/Content/Direct.hs
+++ b/Annex/Content/Direct.hs
@@ -8,6 +8,7 @@
 module Annex.Content.Direct (
 	associatedFiles,
 	removeAssociatedFile,
+	removeAssociatedFileUnchecked,
 	addAssociatedFile,
 	goodContent,
 	recordedInodeCache,
@@ -23,6 +24,7 @@
 	toInodeCache,
 	inodesChanged,
 	createInodeSentinalFile,
+	addContentWhenNotPresent,
 ) where
 
 import Common.Annex
@@ -32,6 +34,9 @@
 import Utility.Tmp
 import Logs.Location
 import Utility.InodeCache
+import Utility.CopyFile
+import Annex.ReplaceFile
+import Annex.Link
 
 {- Absolute FilePaths of Files in the tree that are associated with a key. -}
 associatedFiles :: Key -> Annex [FilePath]
@@ -69,15 +74,22 @@
  		hPutStr h content
 		hClose h
 
-{- Removes an associated file. Returns new associatedFiles value. -}
+{- Removes an associated file. Returns new associatedFiles value.
+ - Checks if this was the last copy of the object, and updates location
+ - log. -}
 removeAssociatedFile :: Key -> FilePath -> Annex [FilePath]
 removeAssociatedFile key file = do
-	file' <- normaliseAssociatedFile file
-	fs <- changeAssociatedFiles key $ filter (/= file')
+	fs <- removeAssociatedFileUnchecked key file
 	when (null fs) $
 		logStatus key InfoMissing
 	return fs
 
+{- Removes an associated file. Returns new associatedFiles value. -}
+removeAssociatedFileUnchecked :: Key -> FilePath -> Annex [FilePath]
+removeAssociatedFileUnchecked key file = do
+	file' <- normaliseAssociatedFile file
+	changeAssociatedFiles key $ filter (/= file')
+
 {- Adds an associated file. Returns new associatedFiles value. -}
 addAssociatedFile :: Key -> FilePath -> Annex [FilePath]
 addAssociatedFile key file = do
@@ -179,6 +191,17 @@
 
 compareInodeCachesWith :: Annex InodeComparisonType
 compareInodeCachesWith = ifM inodesChanged ( return Weakly, return Strongly )
+
+{- Copies the contentfile to the associated file, if the associated
+ - file has not content. If the associated file does have content,
+ - even if the content differs, it's left unchanged. -}
+addContentWhenNotPresent :: Key -> FilePath -> FilePath -> Annex ()
+addContentWhenNotPresent key contentfile associatedfile = do
+	v <- isAnnexLink associatedfile
+	when (Just key == v) $ do
+		replaceFile associatedfile $
+			liftIO . void . copyFileExternal contentfile
+	updateInodeCache key associatedfile	
 
 {- Some filesystems get new inodes each time they are mounted.
  - In order to work on such a filesystem, a sentinal file is used to detect
diff --git a/Annex/Direct.hs b/Annex/Direct.hs
--- a/Annex/Direct.hs
+++ b/Annex/Direct.hs
@@ -26,6 +26,7 @@
 import Utility.InodeCache
 import Utility.CopyFile
 import Annex.Perms
+import Annex.ReplaceFile
 
 {- Uses git ls-files to find files that need to be committed, and stages
  - them into the index. Returns True if some changes were staged. -}
@@ -157,13 +158,10 @@
 		nukeFile 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
-	 - a conflicted merge will write to some other file in the temp
-	 - directory.
-	 -
- 	 - Symlinks are replaced with their content, if it's available. -}
-	movein k f = do
+	{- If the file is already present, with the right content for the
+	 - key, it's left alone. Otherwise, create the symlink and then
+	 - if possible, replace it with the content. -}
+	movein k f = unlessM (goodContent k f) $ do
 		l <- inRepo $ gitAnnexLink f k
 		replaceFile f $ makeAnnexLink l
 		toDirect k f
@@ -191,7 +189,7 @@
 		{- Move content from annex to direct file. -}
 		thawContentDir loc
 		updateInodeCache k loc
-		addAssociatedFile k f
+		void $ addAssociatedFile k f
 		thawContent loc
 		replaceFile f $ liftIO . moveFile loc
 	fromdirect = do
@@ -200,18 +198,22 @@
 		locs <- filterM (\loc -> isNothing <$> getAnnexLinkTarget loc) =<<
 			(filter (/= absf) <$> addAssociatedFile k f)
 		case locs of
-			(loc:_) -> return $ Just $
+			(loc:_) -> return $ Just $ do
 				replaceFile f $
 					liftIO . void . copyFileExternal loc
+				updateInodeCache k f
 			_ -> return Nothing
 
-{- Removes a direct mode file, while retaining its content. -}
+{- Removes a direct mode file, while retaining its content in the annex
+ - (unless its content has already been changed). -}
 removeDirect :: Key -> FilePath -> Annex ()
 removeDirect k f = do
-	locs <- removeAssociatedFile k f
-	when (null locs) $
-		whenM (isNothing <$> getAnnexLinkTarget f) $
-			moveAnnex k f
+	void $ removeAssociatedFileUnchecked k f
+	unlessM (inAnnex k) $
+		ifM (goodContent k f)
+			( moveAnnex k f
+			, logStatus k InfoMissing
+			)
 	liftIO $ do
 		nukeFile f
 		void $ tryIO $ removeDirectory $ parentDir f
diff --git a/Annex/Exception.hs b/Annex/Exception.hs
--- a/Annex/Exception.hs
+++ b/Annex/Exception.hs
@@ -1,32 +1,37 @@
 {- exception handling in the git-annex monad
  -
- - Copyright 2011 Joey Hess <joey@kitenet.net>
+ - Note that when an Annex action fails and the exception is handled
+ - by these functions, any changes the action has made to the
+ - AnnexState are retained. This works because the Annex monad
+ - internally stores the AnnexState in a MVar.
  -
+ - Copyright 2011-2013 Joey Hess <joey@kitenet.net>
+ -
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
 module Annex.Exception (
 	bracketIO,
-	handle,
 	tryAnnex,
 	throw,
+	catchAnnex,
 ) where
 
-import Control.Exception.Lifted (handle, try)
-import Control.Monad.Trans.Control (liftBaseOp)
-import Control.Exception hiding (handle, try, throw)
+import Prelude hiding (catch)
+import "MonadCatchIO-transformers" Control.Monad.CatchIO (bracket, try, throw, catch)
+import Control.Exception hiding (handle, try, throw, bracket, catch)
 
 import Common.Annex
 
 {- Runs an Annex action, with setup and cleanup both in the IO monad. -}
 bracketIO :: IO c -> (c -> IO b) -> Annex a -> Annex a
 bracketIO setup cleanup go =
-	liftBaseOp (Control.Exception.bracket setup cleanup) (const go)
+	bracket (liftIO setup) (liftIO . cleanup) (const go)
 
 {- try in the Annex monad -}
 tryAnnex :: Annex a -> Annex (Either SomeException a)
 tryAnnex = try
 
-{- Throws an exception in the Annex monad. -}
-throw :: Control.Exception.Exception e => e -> Annex a
-throw = liftIO . throwIO
+{- catch in the Annex monad -}
+catchAnnex :: Exception e => Annex a -> (e -> Annex a) -> Annex a
+catchAnnex = catch
diff --git a/Annex/ReplaceFile.hs b/Annex/ReplaceFile.hs
new file mode 100644
--- /dev/null
+++ b/Annex/ReplaceFile.hs
@@ -0,0 +1,35 @@
+{- git-annex file replacing
+ -
+ - Copyright 2013 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Annex.ReplaceFile where
+
+import Common.Annex
+import Annex.Perms
+
+{- Replaces a possibly already existing file with a new version, 
+ - atomically, by running an action.
+ - 
+ - The action is passed a temp file, which it can write to, and once
+ - done the temp file is moved into place.
+ -}
+replaceFile :: FilePath -> (FilePath -> Annex ()) -> Annex ()
+replaceFile file a = do
+	tmpdir <- fromRepo gitAnnexTmpDir
+	createAnnexDirectory tmpdir
+	tmpfile <- liftIO $ do
+		(tmpfile, h) <- openTempFileWithDefaultPermissions tmpdir $
+			takeFileName file
+		hClose h
+		return tmpfile
+	a tmpfile
+	liftIO $ do
+		r <- tryIO $ rename tmpfile file
+		case r of
+			Left _ -> do
+				createDirectoryIfMissing True $ parentDir file
+				rename tmpfile file
+			_ -> noop
diff --git a/Assistant/Install/AutoStart.o b/Assistant/Install/AutoStart.o
Binary files a/Assistant/Install/AutoStart.o and b/Assistant/Install/AutoStart.o differ
diff --git a/Assistant/Install/Menu.o b/Assistant/Install/Menu.o
Binary files a/Assistant/Install/Menu.o and b/Assistant/Install/Menu.o differ
diff --git a/Assistant/Monad.hs b/Assistant/Monad.hs
--- a/Assistant/Monad.hs
+++ b/Assistant/Monad.hs
@@ -26,7 +26,6 @@
 ) where
 
 import "mtl" Control.Monad.Reader
-import Control.Monad.Base (liftBase, MonadBase)
 import System.Log.Logger
 
 import Common.Annex
@@ -52,9 +51,6 @@
 		Functor,
 		Applicative
 	)
-
-instance MonadBase IO Assistant where
-	liftBase = Assistant . liftBase
 
 data AssistantData = AssistantData
 	{ threadName :: ThreadName
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -30,12 +30,12 @@
 import qualified Git.UpdateIndex
 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 Annex.FileMatcher
+import Annex.ReplaceFile
 import Git.Types
 import Config
 import Utility.ThreadScheduler
diff --git a/Build/BundledPrograms.hs b/Build/BundledPrograms.hs
--- a/Build/BundledPrograms.hs
+++ b/Build/BundledPrograms.hs
@@ -30,7 +30,7 @@
 #ifndef mingw32_HOST_OS
 	, Just "sh"
 #endif
-	, ifset SysConfig.gpg "gpg"
+	, SysConfig.gpg
 	, ifset SysConfig.curl "curl"
 	, ifset SysConfig.wget "wget"
 	, ifset SysConfig.bup "bup"
diff --git a/Build/BundledPrograms.o b/Build/BundledPrograms.o
new file mode 100644
Binary files /dev/null and b/Build/BundledPrograms.o differ
diff --git a/Build/Configure.hs b/Build/Configure.hs
--- a/Build/Configure.hs
+++ b/Build/Configure.hs
@@ -10,6 +10,7 @@
 import System.Environment
 import Data.Maybe
 import Control.Monad.IfElse
+import Data.Char
 
 import Build.TestConfig
 import Utility.SafeCommand
@@ -30,7 +31,9 @@
 	, TestCase "curl" $ testCmd "curl" "curl --version >/dev/null"
 	, TestCase "wget" $ testCmd "wget" "wget --version >/dev/null"
 	, TestCase "bup" $ testCmd "bup" "bup --version >/dev/null"
-	, TestCase "gpg" $ testCmd "gpg" "gpg --version >/dev/null"
+	, TestCase "gpg" $ maybeSelectCmd "gpg"
+		[ ("gpg", "--version >/dev/null")
+		, ("gpg2", "--version >/dev/null") ]
 	, TestCase "lsof" $ findCmdPath "lsof" "lsof"
 	, TestCase "ssh connection caching" getSshConnectionCaching
 	] ++ shaTestCases
@@ -129,7 +132,8 @@
 {- Set up cabal file with version. -}
 cabalSetup :: IO ()
 cabalSetup = do
-	version <- takeWhile (/= '~') <$> getChangelogVersion
+	version <- takeWhile (\c -> isDigit c || c == '.')
+		<$> getChangelogVersion
 	cabal <- readFile cabalfile
 	writeFile tmpcabalfile $ unlines $ 
 		map (setfield "Version" version) $
diff --git a/Build/Configure.o b/Build/Configure.o
Binary files a/Build/Configure.o and b/Build/Configure.o differ
diff --git a/Build/DesktopFile.o b/Build/DesktopFile.o
Binary files a/Build/DesktopFile.o and b/Build/DesktopFile.o differ
diff --git a/Build/EvilSplicer.o b/Build/EvilSplicer.o
Binary files a/Build/EvilSplicer.o and b/Build/EvilSplicer.o differ
diff --git a/Build/InstallDesktopFile.o b/Build/InstallDesktopFile.o
Binary files a/Build/InstallDesktopFile.o and b/Build/InstallDesktopFile.o differ
diff --git a/Build/NullSoftInstaller.hs b/Build/NullSoftInstaller.hs
--- a/Build/NullSoftInstaller.hs
+++ b/Build/NullSoftInstaller.hs
@@ -19,22 +19,32 @@
 {-# LANGUAGE OverloadedStrings #-}
 
 import Development.NSIS
+import System.Directory
 import System.FilePath
 import Control.Monad
-import System.Directory
 import Data.String
+import Data.Maybe
 
 import Utility.Tmp
+import Utility.Path
 import Utility.CopyFile
 import Utility.SafeCommand
 import Build.BundledPrograms
 
 main = do
 	withTmpDir "nsis-build" $ \tmpdir -> do
-		let gitannex = tmpdir </> "git-annex.exe"
+		let gitannex = tmpdir </> gitannexprogram
 		mustSucceed "ln" [File "dist/build/git-annex/git-annex.exe", File gitannex]
-		writeFile nsifile $ makeInstaller gitannex
-		mustSucceed "C:\\Program Files\\NSIS\\makensis" [File nsifile]
+		let license = tmpdir </> licensefile
+		mustSucceed "sh" [Param "-c", Param $ "zcat standalone/licences.gz > '" ++ license ++ "'"]
+		extrafiles <- forM (cygwinPrograms ++ cygwinDlls) $ \f -> do
+			p <- searchPath f
+			when (isNothing p) $
+				print ("unable to find in PATH", f)
+			return p
+		writeFile nsifile $ makeInstaller gitannex license $
+			catMaybes extrafiles
+		mustSucceed "makensis" [File nsifile]
 	removeFile nsifile -- left behind if makensis fails
   where
 	nsifile = "git-annex.nsi"
@@ -42,11 +52,20 @@
 		r <- boolSystem cmd params
 		case r of
 			True -> return ()
-			False -> error $ cmd ++ "failed"
+			False -> error $ cmd ++ " failed"
 
+gitannexprogram :: FilePath
+gitannexprogram = "git-annex.exe"
+
+licensefile :: FilePath
+licensefile = "git-annex-licenses.txt"
+
 installer :: FilePath
 installer = "git-annex-installer.exe"
 
+uninstaller :: FilePath
+uninstaller = "git-annex-uninstall.exe"
+
 gitInstallDir :: Exp FilePath
 gitInstallDir = fromString "$PROGRAMFILES\\Git\\cmd"
 
@@ -59,8 +78,8 @@
 	, fromString "You can install git from http:////git-scm.com//"
 	]
 
-makeInstaller :: FilePath -> String
-makeInstaller gitannex = nsis $ do
+makeInstaller :: FilePath -> FilePath -> [FilePath] -> String
+makeInstaller gitannex license extrafiles = nsis $ do
 	name "git-annex"
 	outFile $ str installer
 	{- Installing into the same directory as git avoids needing to modify
@@ -74,18 +93,23 @@
 	
 	-- Pages to display
 	page Directory                   -- Pick where to install
+	page (License license)
 	page InstFiles                   -- Give a progress bar while installing
 	-- Groups of files to install
-	section "programs" [] $ do
+	section "main" [] $ do
 		setOutPath "$INSTDIR"
 		addfile gitannex
-		mapM_ addcygfile cygwinPrograms
-	section "DLLS" [] $ do
-		setOutPath "$INSTDIR"
-		mapM_ addcygfile cygwinDlls
+		addfile license
+		mapM_ addfile extrafiles
+		writeUninstaller $ str uninstaller
+	uninstall $
+		mapM_ (\f -> delete [RebootOK] $ fromString $ "$INSTDIR/" ++ f) $
+			[ gitannexprogram
+			, licensefile
+			, uninstaller
+			] ++ cygwinPrograms ++ cygwinDlls
   where
 	addfile f = file [] (str f)
-	addcygfile f = addfile $ "C:\\cygwin\\bin" </> f
 
 cygwinPrograms :: [FilePath]
 cygwinPrograms = map (\p -> p ++ ".exe") bundledPrograms
diff --git a/Build/SysConfig.o b/Build/SysConfig.o
Binary files a/Build/SysConfig.o and b/Build/SysConfig.o differ
diff --git a/Build/TestConfig.o b/Build/TestConfig.o
Binary files a/Build/TestConfig.o and b/Build/TestConfig.o differ
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,30 @@
+git-annex (4.20130521) unstable; urgency=low
+
+  * Sanitize debian changelog version before putting it into cabal file.
+    Closes: #708619
+  * Switch to MonadCatchIO-transformers for better handling of state while
+    catching exceptions.
+  * Fix a zombie that could result when running a process like gpg to
+    read and write to it.
+  * Allow building with gpg2.
+  * Disable building with the haskell threaded runtime when the webapp
+    is not built. This may fix builds on mips, s390x and sparc, which are
+    failing to link -lHSrts_thr
+  * Temporarily build without webapp on kfreebsd-i386, until yesod is
+    installable there again.
+  * Direct mode bug fix: After a conflicted merge was automatically resolved,
+    the content of a file that was already present could incorrectly
+    be replaced with a symlink.
+  * Fix a bug in the git-annex branch handling code that could
+    cause info from a remote to not be merged and take effect immediately.
+  * Direct mode is now fully tested by the test suite.
+  * Detect bad content in ~/.config/git-annex/program and look in PATH instead.
+  * OSX: Fixed gpg included in dmg.
+  * Linux standalone: Back to being built with glibc 2.13 for maximum
+    portability.
+
+ -- Joey Hess <joeyh@debian.org>  Tue, 21 May 2013 13:10:26 -0400
+
 git-annex (4.20130516) unstable; urgency=low
 
   * Android: The webapp is ported and working.
diff --git a/Command/Add.hs b/Command/Add.hs
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -30,6 +30,7 @@
 import Config
 import Utility.InodeCache
 import Annex.FileMatcher
+import Annex.ReplaceFile
 
 def :: [Command]
 def = [notBareRepo $ command "add" paramPaths seek SectionCommon
@@ -129,8 +130,8 @@
 	go k cache = ifM isDirect ( godirect k cache , goindirect k cache )
 
 	goindirect (Just (key, _)) _ = do
-		handle (undo (keyFilename source) key) $
-			moveAnnex key $ contentLocation source
+		catchAnnex (moveAnnex key $ contentLocation source)
+			(undo (keyFilename source) key)
 		liftIO $ nukeFile $ keyFilename source
 		return $ Just key
 	goindirect Nothing _ = failure "failed to generate a key"
@@ -155,6 +156,11 @@
 	when (contentLocation source /= keyFilename source) $
 		liftIO $ nukeFile $ contentLocation source
 
+	{- Copy to any other locations using the same key. -}
+	otherfs <- filter (/= keyFilename source) <$> associatedFiles key
+	forM_ otherfs $
+		addContentWhenNotPresent key (keyFilename source)
+
 perform :: FilePath -> CommandPerform
 perform file = 
 	maybe stop (\key -> next $ cleanup file key True)
@@ -166,7 +172,7 @@
 undo file key e = do
 	whenM (inAnnex key) $ do
 		liftIO $ nukeFile file
-		handle tryharder $ fromAnnex key file
+		catchAnnex (fromAnnex key file) tryharder
 		logStatus key InfoMissing
 	throw e
   where
@@ -178,7 +184,7 @@
 
 {- Creates the symlink to the annexed content, returns the link target. -}
 link :: FilePath -> Key -> Bool -> Annex String
-link file key hascontent = handle (undo file key) $ do
+link file key hascontent = flip catchAnnex (undo file key) $ do
 	l <- inRepo $ gitAnnexLink file key
 	replaceFile file $ makeAnnexLink l
 
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -474,7 +474,7 @@
 			then Just $ modificationTime s
 			else Nothing
 
-{- Records the start time of an interactive fsck.
+{- Records the start time of an incremental fsck.
  -
  - To guard against time stamp damange (for example, if an annex directory
  - is copied without -a), the fsckstate file contains a time that should
diff --git a/Config/Files.hs b/Config/Files.hs
--- a/Config/Files.hs
+++ b/Config/Files.hs
@@ -46,7 +46,7 @@
 	filter (not . equalFilePath path)
 
 {- The path to git-annex is written here; which is useful when cabal
- - has installed it to some aweful non-PATH location. -}
+ - has installed it to some awful non-PATH location. -}
 programFile :: IO FilePath
 programFile = userConfigFile "program"
 
@@ -54,7 +54,14 @@
 readProgramFile :: IO FilePath
 readProgramFile = do
 	programfile <- programFile
-	catchDefaultIO cmd $ 
+	p <- catchDefaultIO cmd $ 
 		fromMaybe cmd . headMaybe . lines <$> readFile programfile
+	ifM (inPath p)
+		( return p
+		, ifM (inPath cmd)
+			( return cmd
+			, error $ "cannot find git-annex program in PATH or in the location listed in " ++ programfile
+			)
+		)
   where
   	cmd = "git-annex"
diff --git a/Config/Files.o b/Config/Files.o
Binary files a/Config/Files.o and b/Config/Files.o differ
diff --git a/Git/Construct.hs b/Git/Construct.hs
--- a/Git/Construct.hs
+++ b/Git/Construct.hs
@@ -27,6 +27,8 @@
 
 #ifndef __WINDOWS__
 import System.Posix.User
+#else
+import Git.FilePath
 #endif
 import qualified Data.Map as M hiding (map, split)
 import Network.URI
@@ -34,7 +36,6 @@
 import Common
 import Git.Types
 import Git
-import Git.FilePath
 import qualified Git.Url as Url
 import Utility.UserInfo
 
diff --git a/INSTALL b/INSTALL
--- a/INSTALL
+++ b/INSTALL
@@ -1,20 +1,20 @@
 ## Pick your OS
 
 [[!table format=dsv header=yes data="""
-detailed instructions | quick install
-[[OSX]]               | [download git-annex.app](http://downloads.kitenet.net/git-annex/OSX/current/)
-[[Android]]           | [download git-annex.apk](http://downloads.kitenet.net/git-annex/android/current/) **beta**
-[[Linux|linux_standalone]] | [download prebuilt linux tarball](http://downloads.kitenet.net/git-annex/linux/current/)
-[[Debian]]            | `apt-get install git-annex`
-[[Ubuntu]]            | `apt-get install git-annex`
-[[Fedora]]            | `yum install git-annex`
-[[FreeBSD]]           | `pkg_add -r hs-git-annex`
-[[ArchLinux]]         | `yaourt -Sy git-annex`
-[[NixOS]]             | `nix-env -i git-annex`
-[[Gentoo]]            | `emerge git-annex`
-[[ScientificLinux5]]  | (and other RHEL5 clones like CentOS5)
-[[openSUSE]]          | 
-[[Windows]]           | **alpha**
+detailed instructions             | quick install
+[[OSX]]                           | [download git-annex.app](http://downloads.kitenet.net/git-annex/OSX/current/)
+[[Android]]                       | [download git-annex.apk](http://downloads.kitenet.net/git-annex/android/current/) **beta**
+[[Linux|linux_standalone]]        | [download prebuilt linux tarball](http://downloads.kitenet.net/git-annex/linux/current/)
+&nbsp;&nbsp;[[Debian]]            | `apt-get install git-annex`
+&nbsp;&nbsp;[[Ubuntu]]            | `apt-get install git-annex`
+&nbsp;&nbsp;[[Fedora]]            | `yum install git-annex`
+&nbsp;&nbsp;[[FreeBSD]]           | `pkg_add -r hs-git-annex`
+&nbsp;&nbsp;[[ArchLinux]]         | `yaourt -Sy git-annex`
+&nbsp;&nbsp;[[NixOS]]             | `nix-env -i git-annex`
+&nbsp;&nbsp;[[Gentoo]]            | `emerge git-annex`
+&nbsp;&nbsp;[[ScientificLinux5]]  |
+&nbsp;&nbsp;[[openSUSE]]          | 
+[[Windows]]                       | [download installer](http://downloads.kitenet.net/git-annex/windows/current/) **alpha**
 """]]
 
 ## Using cabal
diff --git a/Init.hs b/Init.hs
--- a/Init.hs
+++ b/Init.hs
@@ -86,7 +86,10 @@
 gitPreCommitHookWrite = unlessBare $ do
 	hook <- preCommitHook
 	ifM (liftIO $ doesFileExist hook)
-		( warning $ "pre-commit hook (" ++ hook ++ ") already exists, not configuring"
+		( do
+			content <- liftIO $ readFile hook
+			when (content /= preCommitScript) $
+				warning $ "pre-commit hook (" ++ hook ++ ") already exists, not configuring"
 		, unlessM crippledFileSystem $
 			liftIO $ do
 				viaTmp writeFile hook preCommitScript
diff --git a/Remote/Git.hs b/Remote/Git.hs
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -321,7 +321,7 @@
 	 -}
 	feedprogressback a = ifM (isJust <$> sshCacheDir)
 		( feedprogressback' a
-		, bracketIO noop (const noop) (a $ const noop)
+		, a $ const noop
 		)
 	feedprogressback' a = do
 		u <- getUUID
diff --git a/Remote/Helper/Encryptable.hs b/Remote/Helper/Encryptable.hs
--- a/Remote/Helper/Encryptable.hs
+++ b/Remote/Helper/Encryptable.hs
@@ -35,12 +35,13 @@
 		=<< highRandomQuality
 	(Just keyid, Nothing) -> use "encryption setup" . genEncryptedCipher keyid
 		=<< highRandomQuality
-	(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
+	(Just keyid, Just v) -> use "encryption update" $ updateEncryptedCipher keyid v
   where
 	cannotchange = error "Cannot change encryption type of existing remote."
 	use m a = do
+		showNote m
 		cipher <- liftIO a
-		showNote $ m ++ " " ++ describeCipher cipher
+		showNote $ describeCipher cipher
 		return $ M.delete "encryption" $ M.delete "highRandomQuality" $
 				storeCipher c cipher
 	highRandomQuality = 
diff --git a/Setup.o b/Setup.o
Binary files a/Setup.o and b/Setup.o differ
diff --git a/Test.hs b/Test.hs
--- a/Test.hs
+++ b/Test.hs
@@ -5,13 +5,15 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Test where
 
 import Test.HUnit
 import Test.QuickCheck
 import Test.QuickCheck.Test
 
-import System.Posix.Files
+import System.PosixCompat.Files
 import Control.Exception.Extensible
 import qualified Data.Map as M
 import System.IO.HVFS (SystemFS(..))
@@ -31,7 +33,6 @@
 import qualified Types.Backend
 import qualified Types.TrustLevel
 import qualified Types
-import qualified GitAnnex
 import qualified Logs.UUIDBased
 import qualified Logs.Trust
 import qualified Logs.Remote
@@ -44,9 +45,9 @@
 import qualified Config
 import qualified Config.Cost
 import qualified Crypto
+import qualified Init
 import qualified Utility.Path
 import qualified Utility.FileMode
-import qualified Utility.Gpg
 import qualified Build.SysConfig
 import qualified Utility.Format
 import qualified Utility.Verifiable
@@ -54,6 +55,10 @@
 import qualified Utility.Misc
 import qualified Utility.InodeCache
 import qualified Utility.Env
+import qualified Utility.Gpg
+#ifndef __WINDOWS__
+import qualified GitAnnex
+#endif
 
 type TestEnv = M.Map String String
 
@@ -67,15 +72,18 @@
 	putStrLn "Now, some broader checks ..."
 	putStrLn "  (Do not be alarmed by odd output here; it's normal."
         putStrLn "   wait for the last line to see how it went.)"
-	env <- prepare
-	rs <- forM hunit $ \t -> do
-		divider
-		t env
-	cleanup tmpdir
+	rs <- runhunit =<< prepare False
+	directrs <- runhunit =<< prepare True
 	divider
-	propigate rs qcok
+	propigate (rs++directrs) qcok
   where
 	divider = putStrLn $ replicate 70 '-'
+	runhunit env = do
+		r <- forM hunit $ \t -> do
+			divider
+			t env
+		cleanup tmpdir
+		return r
 
 propigate :: [Counts] -> Bool -> IO ()
 propigate cs qcok
@@ -146,7 +154,8 @@
 	, check "status" test_status
 	, check "version" test_version
 	, check "sync" test_sync
-	, check "sync regression" test_sync_regression
+	, check "union merge regression" test_union_merge_regression
+	, check "conflict resolution" test_conflict_resolution
 	, check "map" test_map
 	, check "uninit" test_uninit
 	, check "upgrade" test_upgrade
@@ -165,6 +174,7 @@
 test_init :: TestEnv -> Test
 test_init env = "git-annex init" ~: TestCase $ innewrepo env $ do
 	git_annex env "init" [reponame] @? "init failed"
+	handleforcedirect env
   where
 	reponame = "test repo"
 
@@ -206,7 +216,7 @@
 		git_annex env "add" ["../dir2"] @? "add of ../subdir failed"
 
 test_reinject :: TestEnv -> Test
-test_reinject env = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo env $ do
+test_reinject env = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepoInDirect env $ do
 	git_annex env "drop" ["--force", sha1annexedfile] @? "drop failed"
 	writeFile tmp $ content sha1annexedfile
 	r <- annexeval $ Types.Backend.getKey backendSHA1 $
@@ -221,11 +231,11 @@
 test_unannex :: TestEnv -> Test
 test_unannex env = "git-annex unannex" ~: TestList [nocopy, withcopy]
   where
-	nocopy = "no content" ~: intmpclonerepo env $ do
+	nocopy = "no content" ~: intmpclonerepoInDirect env $ do
 		annexed_notpresent annexedfile
 		git_annex env "unannex" [annexedfile] @? "unannex failed with no copy"
 		annexed_notpresent annexedfile
-	withcopy = "with content" ~: intmpclonerepo env $ do
+	withcopy = "with content" ~: intmpclonerepoInDirect env $ do
 		git_annex env "get" [annexedfile] @? "get failed"
 		annexed_present annexedfile
 		git_annex env "unannex" [annexedfile, sha1annexedfile] @? "unannex failed"
@@ -330,7 +340,7 @@
 	checkcontent ingitfile
 
 test_lock :: TestEnv -> Test
-test_lock env = "git-annex unlock/lock" ~: intmpclonerepo env $ do
+test_lock env = "git-annex unlock/lock" ~: intmpclonerepoInDirect env $ do
 	-- regression test: unlock of not present file should skip it
 	annexed_notpresent annexedfile
 	not <$> git_annex env "unlock" [annexedfile] @? "unlock failed to fail with not present file"
@@ -358,7 +368,7 @@
 
 test_edit :: TestEnv -> Test
 test_edit env = "git-annex edit/commit" ~: TestList [t False, t True]
-  where t precommit = TestCase $ intmpclonerepo env $ do
+  where t precommit = TestCase $ intmpclonerepoInDirect env $ do
 	git_annex env "get" [annexedfile] @? "get of file failed"
 	annexed_present annexedfile
 	git_annex env "edit" [annexedfile] @? "edit failed"
@@ -377,7 +387,7 @@
 	not <$> git_annex env "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file"
 
 test_fix :: TestEnv -> Test
-test_fix env = "git-annex fix" ~: intmpclonerepo env $ do
+test_fix env = "git-annex fix" ~: intmpclonerepoInDirect env $ do
 	annexed_notpresent annexedfile
 	git_annex env "fix" [annexedfile] @? "fix of not present failed"
 	annexed_notpresent annexedfile
@@ -454,14 +464,17 @@
 		git_annex env "get" [f] @? "get of file failed"
 		Utility.FileMode.allowWrite f
 		writeFile f (changedcontent f)
-		not <$> git_annex env "fsck" [] @? "fsck failed to fail with corrupted file content"
+		ifM (annexeval Config.isDirect)
+			( git_annex env "fsck" [] @? "fsck failed in direct mode with changed file content"
+			, not <$> git_annex env "fsck" [] @? "fsck failed to fail with corrupted file content"
+			)
 		git_annex env "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f
 	fsck_should_fail m = do
 		not <$> git_annex env "fsck" [] @? "fsck failed to fail with " ++ m
 
 test_migrate :: TestEnv -> Test
 test_migrate env = "git-annex migrate" ~: TestList [t False, t True]
-  where t usegitattributes = TestCase $ intmpclonerepo env $ do
+  where t usegitattributes = TestCase $ intmpclonerepoInDirect env $ do
 	annexed_notpresent annexedfile
 	annexed_notpresent sha1annexedfile
 	git_annex env "migrate" [annexedfile] @? "migrate of not present failed"
@@ -499,21 +512,22 @@
 	checkbackend sha1annexedfile backendSHA256
 
 test_unused :: TestEnv -> Test
-test_unused env = "git-annex unused/dropunused" ~: intmpclonerepo env $ do
+-- This test is broken in direct mode
+test_unused env = "git-annex unused/dropunused" ~: intmpclonerepoInDirect env $ do
 	-- keys have to be looked up before files are removed
 	annexedfilekey <- annexeval $ findkey annexedfile
 	sha1annexedfilekey <- annexeval $ findkey sha1annexedfile
 	git_annex env "get" [annexedfile] @? "get of file failed"
 	git_annex env "get" [sha1annexedfile] @? "get of file failed"
 	checkunused [] "after get"
-	boolSystem "git" [Params "rm -q", File annexedfile] @? "git rm failed"
+	boolSystem "git" [Params "rm -fq", File annexedfile] @? "git rm failed"
 	checkunused [] "after rm"
 	boolSystem "git" [Params "commit -q -m foo"] @? "git commit failed"
 	checkunused [] "after commit"
 	-- unused checks origin/master; once it's gone it is really unused
 	boolSystem "git" [Params "remote rm origin"] @? "git remote rm origin failed"
 	checkunused [annexedfilekey] "after origin branches are gone"
-	boolSystem "git" [Params "rm -q", File sha1annexedfile] @? "git rm failed"
+	boolSystem "git" [Params "rm -fq", File sha1annexedfile] @? "git rm failed"
 	boolSystem "git" [Params "commit -q -m foo"] @? "git commit failed"
 	checkunused [annexedfilekey, sha1annexedfilekey] "after rm sha1annexedfile"
 
@@ -584,10 +598,10 @@
 test_sync env = "git-annex sync" ~: intmpclonerepo env $ do
 	git_annex env "sync" [] @? "sync failed"
 
-{- Regression test for sync merge bug fixed in
+{- Regression test for union merge bug fixed in
  - 0214e0fb175a608a49b812d81b4632c081f63027 -}
-test_sync_regression :: TestEnv -> Test
-test_sync_regression env = "git-annex sync_regression" ~:
+test_union_merge_regression :: TestEnv -> Test
+test_union_merge_regression env = "union merge regression" ~:
 	{- We need 3 repos to see this bug. -}
 	withtmpclonerepo env False $ \r1 -> do
 		withtmpclonerepo env False $ \r2 -> do
@@ -613,6 +627,47 @@
 					 - thought the file was still in r2 -}
 					git_annex_expectoutput env "find" ["--in", "r2"] []
 
+{- Regression test for the automatic conflict resolution bug fixed
+ - in f4ba19f2b8a76a1676da7bb5850baa40d9c388e2. -}
+test_conflict_resolution :: TestEnv -> Test
+test_conflict_resolution env = "automatic conflict resolution" ~:
+	withtmpclonerepo env False $ \r1 -> do
+		withtmpclonerepo env False $ \r2 -> do
+			let rname r = if r == r1 then "r1" else "r2"
+			forM_ [r1, r2] $ \r -> indir env r $ do
+				{- Get all files, see check below. -}
+				git_annex env "get" [] @? "get failed"
+				{- Set up repos as remotes of each other;
+				 - remove origin since we're going to sync
+				 - some changes to a file. -}
+				when (r /= r1) $
+					boolSystem "git" [Params "remote add r1", File ("../../" ++ r1)] @? "remote add"
+				when (r /= r2) $
+					boolSystem "git" [Params "remote add r2", File ("../../" ++ r2)] @? "remote add"
+				boolSystem "git" [Params "remote rm origin"] @? "remote rm"
+
+				{- Set up a conflict. -}
+				let newcontent = content annexedfile ++ rname r
+				ifM (annexeval Config.isDirect)
+					( writeFile annexedfile newcontent
+					, do
+						git_annex env "unlock" [annexedfile] @? "unlock failed"		
+						writeFile annexedfile newcontent
+					)
+			{- Sync twice in r1 so it gets the conflict resolution
+			 - update from r2 -}
+			forM_ [r1, r2, r1] $ \r -> indir env r $ do
+				git_annex env "sync" [] @? "sync failed in " ++ rname r
+			{- After the sync, it should be possible to get all
+			 - files. This includes both sides of the conflict,
+			 - although the filenames are not easily predictable.
+			 -
+			 - The bug caused, in direct mode, one repo to
+			 - be missing the content of the file that had
+			 - been put in it. -}
+			forM_ [r1, r2] $ \r -> indir env r $ do
+			 	git_annex env "get" [] @? "unable to get all files after merge conflict resolution in " ++ rname r
+
 test_map :: TestEnv -> Test
 test_map env = "git-annex map" ~: intmpclonerepo env $ do
 	-- set descriptions, that will be looked for in the map
@@ -622,7 +677,7 @@
 	git_annex env "map" ["--fast"] @? "map failed"
 
 test_uninit :: TestEnv -> Test
-test_uninit env = "git-annex uninit" ~: intmpclonerepo env $ do
+test_uninit env = "git-annex uninit" ~: intmpclonerepoInDirect env $ do
 	git_annex env "get" [] @? "get failed"
 	annexed_present annexedfile
 	boolSystem "git" [Params "checkout git-annex"] @? "git checkout git-annex"
@@ -723,7 +778,8 @@
 
 -- gpg is not a build dependency, so only test when it's available
 test_crypto :: TestEnv -> Test
-test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ when Build.SysConfig.gpg $ do
+test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path.inPath Utility.Gpg.gpgcmd) $ do
+#ifndef __WINDOWS__
 	Utility.Gpg.testTestHarness @? "test harness self-test failed"
 	Utility.Gpg.testHarness $ do
 		createDirectory "dir"
@@ -747,12 +803,16 @@
 		git_annex env "move" [annexedfile, "--from", "foo"] @? "move --from encrypted remote failed"
 		annexed_present annexedfile
 		not <$> git_annex env "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"
-		annexed_present annexedfile	
+		annexed_present annexedfile
+#else
+		putStrLn "gpg testing not implemented on Windows"
+#endif
 
 -- This is equivilant to running git-annex, but it's all run in-process
--- so test coverage collection works.
+-- (when the OS allows) so test coverage collection works.
 git_annex :: TestEnv -> String -> [String] -> IO Bool
 git_annex env command params = do
+#ifndef __WINDOWS__
 	forM_ (M.toList env) $ \(var, val) ->
 		Utility.Env.setEnv var val True
 
@@ -763,12 +823,17 @@
 		Left _ -> return False
   where
 	run = GitAnnex.run (command:"-q":params)
+#else
+	Utility.SafeCommand.boolSystemEnv "git-annex"
+		(map Param $ command : params)
+		(Just $ M.toList env)
+#endif
 
 {- Runs git-annex and returns its output. -}
 git_annex_output :: TestEnv -> String -> [String] -> IO String
 git_annex_output env command params = do
-	got <- Utility.Process.readProcessEnv "git-annex" (command:params) $
-		Just $ M.toList env
+	got <- Utility.Process.readProcessEnv "git-annex" (command:params)
+		(Just $ M.toList env)
 	-- XXX since the above is a separate process, code coverage stats are
 	-- not gathered for things run in it.
 	-- Run same command again, to get code coverage.
@@ -798,6 +863,17 @@
 intmpclonerepo :: TestEnv -> Assertion -> Assertion
 intmpclonerepo env a = withtmpclonerepo env False $ \r -> indir env r a
 
+intmpclonerepoInDirect :: TestEnv -> Assertion -> Assertion
+intmpclonerepoInDirect env a = intmpclonerepo env $
+	ifM isdirect
+		( putStrLn "not supported in direct mode; skipping"
+		, a
+		)
+  where
+  	isdirect = annexeval $ do
+		Init.initialize Nothing
+		Config.isDirect
+
 intmpbareclonerepo :: TestEnv -> Assertion -> Assertion
 intmpbareclonerepo env a = withtmpclonerepo env True $ \r -> indir env r a
 
@@ -840,7 +916,14 @@
 	boolSystem "git" [Params ("clone -q" ++ b), File old, File new] @? "git clone failed"
 	indir env new $
 		git_annex env "init" ["-q", new] @? "git annex init failed"
+	when (not bare) $
+		indir env new $
+			handleforcedirect env
 	return new
+
+handleforcedirect :: TestEnv -> IO ()
+handleforcedirect env = when (M.lookup "FORCEDIRECT" env == Just "1") $
+	git_annex env "direct" ["-q"] @? "git annex direct failed"
 	
 ensuretmpdir :: IO ()
 ensuretmpdir = do
@@ -946,8 +1029,8 @@
 unannexed :: FilePath -> Assertion
 unannexed = runchecks [checkregularfile, checkcontent, checkwritable]
 
-prepare :: IO TestEnv
-prepare = do
+prepare :: Bool -> IO TestEnv
+prepare forcedirect = do
 	whenM (doesDirectoryExist tmpdir) $
 		error $ "The temporary directory " ++ tmpdir ++ " already exists; cannot run test suite."
 
@@ -967,6 +1050,7 @@
 		, ("GIT_COMMITTER_NAME", "git-annex test")
 		-- force gpg into batch mode for the tests
 		, ("GPG_BATCH", "1")
+		, ("FORCEDIRECT", if forcedirect then "1" else "")
 		]
 
 	return $ M.fromList env
diff --git a/Utility/CopyFile.o b/Utility/CopyFile.o
new file mode 100644
Binary files /dev/null and b/Utility/CopyFile.o differ
diff --git a/Utility/Directory.hs b/Utility/Directory.hs
--- a/Utility/Directory.hs
+++ b/Utility/Directory.hs
@@ -5,6 +5,8 @@
  - Licensed under the GNU GPL version 3 or higher.
  -}
 
+{-# LANGUAGE CPP #-}
+
 module Utility.Directory where
 
 import System.IO.Error
@@ -85,9 +87,16 @@
 			(Left _) -> return False
 			(Right s) -> return $ isDirectory s
 
-{- Removes a file, which may or may not exist.
+{- Removes a file, which may or may not exist, and does not have to
+ - be a regular file.
  -
  - Note that an exception is thrown if the file exists but
  - cannot be removed. -}
 nukeFile :: FilePath -> IO ()
-nukeFile file = whenM (doesFileExist file) $ removeFile file
+nukeFile file = void $ tryWhenExists go
+  where
+#ifndef mingw32_HOST_OS
+	go = removeLink file
+#else
+	go = removeFile file
+#endif
diff --git a/Utility/Directory.o b/Utility/Directory.o
Binary files a/Utility/Directory.o and b/Utility/Directory.o differ
diff --git a/Utility/Exception.hs b/Utility/Exception.hs
--- a/Utility/Exception.hs
+++ b/Utility/Exception.hs
@@ -12,6 +12,8 @@
 import Prelude hiding (catch)
 import Control.Exception
 import Control.Applicative
+import Control.Monad
+import System.IO.Error (isDoesNotExistError)
 
 {- Catches IO errors and returns a Bool -}
 catchBoolIO :: IO Bool -> IO Bool
@@ -49,3 +51,8 @@
 
 tryNonAsync :: IO a -> IO (Either SomeException a)
 tryNonAsync a = (Right <$> a) `catchNonAsync` (return . Left)
+
+{- Catches only DoesNotExist exceptions, and lets all others through. -}
+tryWhenExists :: IO a -> IO (Maybe a)
+tryWhenExists a = either (const Nothing) Just <$>
+	tryJust (guard . isDoesNotExistError) a
diff --git a/Utility/Exception.o b/Utility/Exception.o
Binary files a/Utility/Exception.o and b/Utility/Exception.o differ
diff --git a/Utility/ExternalSHA.o b/Utility/ExternalSHA.o
Binary files a/Utility/ExternalSHA.o and b/Utility/ExternalSHA.o differ
diff --git a/Utility/FreeDesktop.o b/Utility/FreeDesktop.o
Binary files a/Utility/FreeDesktop.o and b/Utility/FreeDesktop.o differ
diff --git a/Utility/Gpg.hs b/Utility/Gpg.hs
--- a/Utility/Gpg.hs
+++ b/Utility/Gpg.hs
@@ -17,10 +17,16 @@
 
 import Common
 import Utility.Env
+import qualified Build.SysConfig as SysConfig
 
 newtype KeyIds = KeyIds [String]
 	deriving (Ord, Eq)
 
+{- If a specific gpg command was found at configure time, use it.
+ - Otherwise, try to run gpg. -}
+gpgcmd :: FilePath
+gpgcmd = fromMaybe "gpg" SysConfig.gpg
+
 stdParams :: [CommandParam] -> IO [String]
 stdParams params = do
 #ifndef __WINDOWS__
@@ -44,7 +50,7 @@
 readStrict :: [CommandParam] -> IO String
 readStrict params = do
 	params' <- stdParams params
-	withHandle StdoutHandle createProcessSuccess (proc "gpg" params') $ \h -> do
+	withHandle StdoutHandle createProcessSuccess (proc gpgcmd params') $ \h -> do
 		hSetBinaryMode h True
 		hGetContentsStrict h
 
@@ -53,7 +59,7 @@
 pipeStrict :: [CommandParam] -> String -> IO String
 pipeStrict params input = do
 	params' <- stdParams params
-	withBothHandles createProcessSuccess (proc "gpg" params') $ \(to, from) -> do
+	withBothHandles createProcessSuccess (proc gpgcmd params') $ \(to, from) -> do
 		hSetBinaryMode to True
 		hSetBinaryMode from True
 		hPutStr to input
@@ -84,7 +90,7 @@
 
 	params' <- stdParams $ [Param "--batch"] ++ passphrasefd ++ params
 	closeFd frompipe `after`
-		withBothHandles createProcessSuccess (proc "gpg" params') go
+		withBothHandles createProcessSuccess (proc gpgcmd params') go
   where
 	go (to, from) = do
 		void $ forkIO $ do
diff --git a/Utility/Misc.o b/Utility/Misc.o
Binary files a/Utility/Misc.o and b/Utility/Misc.o differ
diff --git a/Utility/Monad.o b/Utility/Monad.o
Binary files a/Utility/Monad.o and b/Utility/Monad.o differ
diff --git a/Utility/Path.o b/Utility/Path.o
Binary files a/Utility/Path.o and b/Utility/Path.o differ
diff --git a/Utility/Process.hs b/Utility/Process.hs
--- a/Utility/Process.hs
+++ b/Utility/Process.hs
@@ -47,6 +47,7 @@
 #endif
 
 import Utility.Misc
+import Utility.Exception
 
 type CreateProcessRunner = forall a. CreateProcess -> ((Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO a) -> IO a
 
@@ -141,13 +142,13 @@
 createProcessSuccess p a = createProcessChecked (forceSuccessProcess p) p a
 
 {- Runs createProcess, then an action on its handles, and then
- - an action on its exit code. -}
+ - a checker action on its exit code, which must wait for the process. -}
 createProcessChecked :: (ProcessHandle -> IO b) -> CreateProcessRunner
 createProcessChecked checker p a = do
 	t@(_, _, _, pid) <- createProcess p
-	r <- a t
+	r <- tryNonAsync $ a t
 	_ <- checker pid
-	return r
+	either E.throw return r
 
 {- Leaves the process running, suitable for lazy streaming.
  - Note: Zombies will result, and must be waited on. -}
diff --git a/Utility/Process.o b/Utility/Process.o
Binary files a/Utility/Process.o and b/Utility/Process.o differ
diff --git a/Utility/SafeCommand.o b/Utility/SafeCommand.o
Binary files a/Utility/SafeCommand.o and b/Utility/SafeCommand.o differ
diff --git a/Utility/State.hs b/Utility/State.hs
deleted file mode 100644
--- a/Utility/State.hs
+++ /dev/null
@@ -1,26 +0,0 @@
-{- state monad support
- -
- - Copyright 2012 Joey Hess <joey@kitenet.net>
- -
- - Licensed under the GNU GPL version 3 or higher.
- -}
-
-module Utility.State where
-
-import "mtl" Control.Monad.State.Strict
-
-{- Modifies Control.Monad.State's state, forcing a strict update.
- - This avoids building thunks in the state and leaking.
- - Why it's not the default, I don't know.
- -
- - Example: changeState $ \s -> s { foo = bar }
- -}
-changeState :: MonadState s m => (s -> s) -> m ()
-changeState f = do
-	x <- get
-	put $! f x
-
-{- Gets a value from the internal state, selected by the passed value
- - constructor. -}
-getState :: MonadState s m => (s -> a) -> m a
-getState = gets
diff --git a/Utility/Tmp.o b/Utility/Tmp.o
Binary files a/Utility/Tmp.o and b/Utility/Tmp.o differ
diff --git a/debian/changelog b/debian/changelog
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,30 @@
+git-annex (4.20130521) unstable; urgency=low
+
+  * Sanitize debian changelog version before putting it into cabal file.
+    Closes: #708619
+  * Switch to MonadCatchIO-transformers for better handling of state while
+    catching exceptions.
+  * Fix a zombie that could result when running a process like gpg to
+    read and write to it.
+  * Allow building with gpg2.
+  * Disable building with the haskell threaded runtime when the webapp
+    is not built. This may fix builds on mips, s390x and sparc, which are
+    failing to link -lHSrts_thr
+  * Temporarily build without webapp on kfreebsd-i386, until yesod is
+    installable there again.
+  * Direct mode bug fix: After a conflicted merge was automatically resolved,
+    the content of a file that was already present could incorrectly
+    be replaced with a symlink.
+  * Fix a bug in the git-annex branch handling code that could
+    cause info from a remote to not be merged and take effect immediately.
+  * Direct mode is now fully tested by the test suite.
+  * Detect bad content in ~/.config/git-annex/program and look in PATH instead.
+  * OSX: Fixed gpg included in dmg.
+  * Linux standalone: Back to being built with glibc 2.13 for maximum
+    portability.
+
+ -- Joey Hess <joeyh@debian.org>  Tue, 21 May 2013 13:10:26 -0400
+
 git-annex (4.20130516) unstable; urgency=low
 
   * Android: The webapp is ported and working.
diff --git a/debian/control b/debian/control
--- a/debian/control
+++ b/debian/control
@@ -16,7 +16,7 @@
 	libghc-dav-dev (>= 0.3) [amd64 i386 kfreebsd-amd64 kfreebsd-i386 sparc],
 	libghc-quickcheck2-dev,
 	libghc-monad-control-dev (>= 0.3),
-	libghc-lifted-base-dev,
+	libghc-monadcatchio-transformers-dev,
 	libghc-unix-compat-dev,
 	libghc-dlist-dev,
 	libghc-uuid-dev,
@@ -24,12 +24,13 @@
 	libghc-ifelse-dev,
 	libghc-bloomfilter-dev,
 	libghc-edit-distance-dev,
+	libghc-extensible-exceptions-dev,
 	libghc-hinotify-dev [linux-any],
 	libghc-stm-dev (>= 2.3),
 	libghc-dbus-dev (>= 0.10.3) [linux-any],
 	libghc-yesod-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
 	libghc-yesod-static-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
-	libghc-yesod-default-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
+	libghc-yesod-default-dev [i386 amd64 kfreebsd-amd64],
 	libghc-hamlet-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
 	libghc-clientsession-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
 	libghc-warp-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
@@ -37,7 +38,6 @@
 	libghc-wai-logger-dev [i386 amd64 kfreebsd-i386 kfreebsd-amd64],
 	libghc-case-insensitive-dev,
 	libghc-http-types-dev,
-	libghc-transformers-dev,
 	libghc-blaze-builder-dev,
 	libghc-crypto-api-dev,
 	libghc-network-multicast-dev,
diff --git a/doc/Android/comment_1_cc9caa5dd22dd67e5c1d22d697096dd2._comment b/doc/Android/comment_1_cc9caa5dd22dd67e5c1d22d697096dd2._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_1_cc9caa5dd22dd67e5c1d22d697096dd2._comment
@@ -0,0 +1,15 @@
+[[!comment format=txt
+ username="http://yarikoptic.myopenid.com/"
+ nickname="site-myopenid"
+ subject="Does it require the device to be rooted?"
+ date="2013-05-16T20:55:45Z"
+ content="""
+Following your news on kickstarter downloaded the .apk, and installed it.  Upn start I just got a terminal window with
+
+  link busybox: Read-only file system
+
+  [Terminal session finished]
+
+That is on Galaxy Note
+
+"""]]
diff --git a/doc/Android/comment_2_c2422b7dd9d526b3616e49f48cf178c2._comment b/doc/Android/comment_2_c2422b7dd9d526b3616e49f48cf178c2._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_2_c2422b7dd9d526b3616e49f48cf178c2._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="http://joeyh.name/"
+ nickname="joey"
+ subject="comment 2"
+ date="2013-05-17T22:28:34Z"
+ content="""
+The Android app works on many non-rooted Android systems.
+
+The \"link busybox: Read-only file system\" means that `/data/data/ga.androidterm/lib/lib.busybox.so` cannot be hard linked to `/data/data/ga.androidterm/busybox`. That's not normal. I'd appreciate if you could provide more information on your Android device, like Android version and model number.
+"""]]
diff --git a/doc/Android/comment_3_0e4980c27b13dbc28477c02a82898248._comment b/doc/Android/comment_3_0e4980c27b13dbc28477c02a82898248._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_3_0e4980c27b13dbc28477c02a82898248._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="http://yarikoptic.myopenid.com/"
+ nickname="site-myopenid"
+ subject="Follow-up information on my system"
+ date="2013-05-18T01:23:28Z"
+ content="""
+Sorry for the delay:  my android is stock Samsung-tuned Jelly beans.
+Android 4.1.2
+Baseband version N7000XXLSO
+
+not sure if that would be of any use :-/  nothing in the logs (aLogcat) if I filter by annex -- should there any debug output? what should be a key to search by?
+
+
+"""]]
diff --git a/doc/Android/comment_4_86f7b5444e2eaea7f8f7b9160f671a1d._comment b/doc/Android/comment_4_86f7b5444e2eaea7f8f7b9160f671a1d._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_4_86f7b5444e2eaea7f8f7b9160f671a1d._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawnu1NYw8UF-NoDbKu8YKVGxi8FoZLH7JPs"
+ nickname="Chris"
+ subject="Not starting browser on Nexus 7, Android 4.2.2"
+ date="2013-05-19T14:04:28Z"
+ content="""
+I just tried to run this on my Nexus 7 which has Android 4.2.2, and I received the following: <http://hodapple.com/files/Screenshot_2013-05-19-09-49-53.png> <http://hodapple.com/files/git-annex-error.txt>
+
+In spite of that, though, the URL provided still worked.
+"""]]
diff --git a/doc/Android/comment_5_9d78009435736a178d5a3f5a9bc0ed6a._comment b/doc/Android/comment_5_9d78009435736a178d5a3f5a9bc0ed6a._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_5_9d78009435736a178d5a3f5a9bc0ed6a._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="http://joeyh.name/"
+ nickname="joey"
+ subject="comment 5"
+ date="2013-05-19T19:46:14Z"
+ content="""
+@Chris, that is a known bug: [[bugs/Android_app_permission_denial_on_startup]]
+"""]]
diff --git a/doc/Android/comment_6_7b9523ddb20dc4a929e556c3ed0c7406._comment b/doc/Android/comment_6_7b9523ddb20dc4a929e556c3ed0c7406._comment
new file mode 100644
--- /dev/null
+++ b/doc/Android/comment_6_7b9523ddb20dc4a929e556c3ed0c7406._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="http://joeyh.name/"
+ nickname="joey"
+ subject="comment 6"
+ date="2013-05-19T20:06:56Z"
+ content="""
+@yarikoptic, there is a process you can perform that will help me determine what's going on.
+
+You should be able to get the git-annex app to let you into a shell. You can do this by starting the app, and then going into its configuration menu, to Preferences, selecting \"Command Line\", and changing it to run \"/system/bin/sh\"
+
+Then when you open a new window in the git-annex app, you'll be at a shell prompt. From there, you can run:
+
+ls -ld /data/data/ga.androidterm
+
+I'm interested to know a) whether the directory exists and b) what permissions and owner it has. On my tablet, I get back \"drwxr-x--x app_39 app_39\" .. and if I run `id` in the shell, it tells me it's running as `app_39`.
+
+My guess is the directory probably does exist, but cannot be written to by the app. If you're able to verify that, the next step will be to investigate if there is some other directory that the app can write to. It needs to be able to write to someplace that is not on the `/sdcard` to install itself.
+"""]]
diff --git a/doc/assistant.mdwn b/doc/assistant.mdwn
--- a/doc/assistant.mdwn
+++ b/doc/assistant.mdwn
@@ -1,15 +1,15 @@
-The git-annex assistant creates a folder on each of your computers,
-Android devices, removable drives, and cloud services, which
-it keeps synchronised, so its contents are the same everywhere.
+The git-annex assistant creates a synchronised folder on each of your
+OSX and Linux  computers, Android devices, removable drives, and
+cloud services. The contents of the folder are the same everywhere.
 It's very easy to use, and has all the power of git and git-annex.
 
 ## installation
 
-The git-annex assistant comes as part of git-annex, starting with version
-3.20120924. See [[install]] to get it installed.
+The git-annex assistant comes as part of git-annex. 
+See [[install]] to get it installed.
 
-Note that the git-annex assistant is still beta quality code. See
-the [[release_notes]] for known infelicities and upgrade instructions.
+See the [[release_notes]] for an overview of the status, and upgrade
+instructions.
 
 ## intro screencast
 
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,3 +1,20 @@
+## version 4.20130516
+
+This version contains numerous bug fixes, and improvements.
+
+This is the first release with a fully usable Android app. No command-line
+typing needed to set up syncing to your Android phone or tablet!
+A few of the more advanced features may not work (or not work reliably)
+on Android. The Android app is still beta quality.
+
+There is a known problem with android 4.2.2 that prevents the webapp
+from opening. See [[Android_app_permission_denial_on_startup]] for a
+workaround.
+
+This is also the first release with a Windows port! The Windows port
+is in an alpha quality state, and is missing many features.
+It does not yet include the assistant.
+
 ## version 4.20130501
 
 This version contains numerous bug fixes, and improvements.
diff --git a/doc/assistant/thanks.mdwn b/doc/assistant/thanks.mdwn
--- a/doc/assistant/thanks.mdwn
+++ b/doc/assistant/thanks.mdwn
@@ -50,8 +50,9 @@
 Sherif Abouseda, Ben Strawbridge, chee rabbits, Pedro Côrte-Real
 
 And special thanks to Kevin McKenzie, who also gave me a login to a Mac OSX
-machine, which has proven invaluable, and Jimmy Tang who has helped
-with Mac OSX autobuilding and packaging.
+machine, which has proven invaluable, Jimmy Tang who has helped
+with Mac OSX autobuilding and packaging, and Yury V. Zaytsev who
+provides the Windows autobuilder.
 
 ## Other Backers
 
diff --git a/doc/bugs/Adding_box.com_remote_on_Android_fails_for_me.mdwn b/doc/bugs/Adding_box.com_remote_on_Android_fails_for_me.mdwn
--- a/doc/bugs/Adding_box.com_remote_on_Android_fails_for_me.mdwn
+++ b/doc/bugs/Adding_box.com_remote_on_Android_fails_for_me.mdwn
@@ -15,3 +15,6 @@
 ### Please provide any additional information below.
 
 Didn't find a .git/annex/debug.log
+
+> This error seems entirely consistent with you entering the wrong password.
+> [[done]] --[[Joey]]
diff --git a/doc/bugs/Assistant_doesn__39__t_actually_sync_file_contents_by_default.mdwn b/doc/bugs/Assistant_doesn__39__t_actually_sync_file_contents_by_default.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Assistant_doesn__39__t_actually_sync_file_contents_by_default.mdwn
@@ -0,0 +1,16 @@
+### Please describe the problem.
+
+I'm trying to use the assistant to replicate the basic dropbox functionality of having a synced folder between two machines. Instead of actually syncing file contents by default the assistant just creates broken symlinks.
+
+### What steps will reproduce the problem?
+
+I've setup two repositories with each other as ssh remotes and ran the assistant on each after setting them in direct mode. I can create files on each of the repositories and have them show up on the other but all that shows up is a broken link. Actual contents don't get transferred. If I do a "git annex get" I can get the contents just fine. I tried setting annex.numcopies to 2 and that didn't work either.
+
+What I'm missing here is some setting to tell the assistant "sync the contents of every new file". What it's doing instead is unacceptable. If I have the file in both repositories and change it in one, the other repository gets it's previous version of the file replaced by a broken symlink. From my point of view for the assistant to be a dropbox replacement it should never, under any circumstances, create a broken symlink in the sync folder. I had understood that that's what direct mode was, but it's not behaving that way right now at least.
+
+### What version of git-annex are you using? On what operating system?
+
+My basic setup for testing is two Ubuntu 12.04 LTS machines running git-annex 4.20130501
+
+> [[done]], broken ~/.config/git-annex/program file, which is now detected
+> and worked around. --[[Joey]]
diff --git a/doc/bugs/Can__39__t_set_repositories_directory.mdwn b/doc/bugs/Can__39__t_set_repositories_directory.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/Can__39__t_set_repositories_directory.mdwn
@@ -0,0 +1,12 @@
+Can't set the repository directory
+
+
+At beginning during the webapp installation
+
+
+0.0.1 for OS X 10.8.2
+
+
+user error (git ["--git-dir=/Users/filippo/Desktop/annex/.git","--work-tree=/Users/filippo/Desktop/annex","commit-tree","4b825dc642cb6eb9a060e54bf8d69288fbee4904"] exited 128)
+
+[[!tag moreinfo assistant]]
diff --git a/doc/bugs/adding_an_rsync.net_repo_give_an_gpg_error.mdwn b/doc/bugs/adding_an_rsync.net_repo_give_an_gpg_error.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/adding_an_rsync.net_repo_give_an_gpg_error.mdwn
@@ -0,0 +1,20 @@
+### Please describe the problem.
+adding an rsync.net repo returns an internal server error "user error (gpg ["--quite","--trust-model","always","--gen-random","--armor","1","512"] exited 127)
+
+
+### What steps will reproduce the problem?
+add an rsync.net repo
+
+
+### What version of git-annex are you using? On what operating system?
+4.20130516-g8a26544 for OSX Mountain Lion
+
+### Please provide any additional information below.
+
+[[!format sh """
+# If you can, paste a complete transcript of the problem occurring here.
+# If the problem is with the git-annex assistant, paste in .git/annex/debug.log
+
+
+# End of transcript or log.
+"""]]
diff --git a/doc/bugs/git_annex_fork_bombs_on_gpg_file.mdwn b/doc/bugs/git_annex_fork_bombs_on_gpg_file.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/git_annex_fork_bombs_on_gpg_file.mdwn
@@ -0,0 +1,25 @@
+### Please describe the problem.
+git-annex goes into a loop of I think Haskell's createProcess function and causes the entire operating system to starve of process creation.
+
+### What steps will reproduce the problem?
+The last file git-annex was processing was tinco.gpg, my gpg key exported with
+  
+   gpg --export mail@tinco.nl --output tinco.gpg 
+
+### What version of git-annex are you using? On what operating system?
+4.20130516-g8a26544 on OSX
+
+I had a remote setup using bup.
+
+### Please provide any additional information below.
+Unfortunately to fix the problem I have deleted the entire git repository and made a new init in the same directory, this time without the gpg file. Everything seems to be working now.
+
+What I remember about the log file is that the last thing it said was something along the lines of 
+
+add tinco.gpg
+
+.. (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) (gpg) ..etc
+
+recv (resource unavailable or something) ..
+
+> [[done]]; fixed 3 bugs! --[[Joey]]
diff --git a/doc/bugs/git_annix_breaks_git_commit_after_uninstall.mdwn b/doc/bugs/git_annix_breaks_git_commit_after_uninstall.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/git_annix_breaks_git_commit_after_uninstall.mdwn
@@ -0,0 +1,42 @@
+Sorry to be reporting another vague bug, this one interferes with my work unfortunately.
+
+### Please describe the problem.
+After uninstalling git-annix, running git commit returns the following error:
+
+git: 'annex' is not a git command. See 'git --help'.
+
+### What steps will reproduce the problem?
+
+Install git-annex using the ubuntu ppa of fmarcier like so:
+
+    sudo apt-get install git-annex
+
+Then remove it:
+
+    sudo apt-get remove git-annex
+
+Then go to work in a git project, that is not in annex and has no relation to it. Add your changes and run commit:
+
+    git add my-new-file
+    git commit -m "added new file"
+
+I expect it to confirm the file is committed, instead I get the error message:
+  
+    git: 'annex' is not a git command. See 'git --help'.
+
+### What version of git-annex are you using? On what operating system?
+
+Ubuntu 13.04, using the PPA by marcier linked on the branchable website.
+
+> I don't think this is something I want to change.. `git-annex init`
+> installs a pre-commit hook that runs `git annex fix`. If git-annex
+> is removed that hook is left behind to fail. However, if you were really
+> using git-annex in the repo, that's the least of your troubles. If you were
+> using git-annex in the repo and stopped, then you should run `git annex uninit` to remove the hook.
+>
+> The only change I could make is to have the hook check if git-annex
+> is in PATH before trying to run it. But this adds time and complexity
+> to the usual case for a edge case. And keeps cruft around in the edge case
+> rather than informing you of the problem. 
+> 
+> [[done]] --[[Joey]] 
diff --git a/doc/bugs/gpg_error_on_android.mdwn b/doc/bugs/gpg_error_on_android.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/bugs/gpg_error_on_android.mdwn
@@ -0,0 +1,36 @@
+### Please describe the problem.
+
+Adding an existing cloud repo on box.com results in an gpg error:
+
+    user error (gpg ["--quiet","--trust-model","always","--batch","--passphrase-fd","86","--decrypt"] exited 2)
+
+### What steps will reproduce the problem?
+
+Enabling an existing cloud repository.
+
+### What version of git-annex are you using? On what operating system?
+
+Latest Android (4.20130516-g32 40006) on a rooted Samsung Galaxy Note (CyanogenMod 10.1)
+
+### Please provide any additional information below.
+
+[[!format sh """
+# If you can, paste a complete transcript of the problem occurring here.
+# If the problem is with the git-annex assistant, paste in .git/annex/debug.log
+
+(merging refs/synced/de8a8792-70de-48c3-a646-a168ce1d9d35/c25hdXRoQGphYmJlci5vcmc=/git-annex into git-annex...)
+(Recording state in git...)
+(gpg) gpg: can't open `/usr/local/share/gnupg/options.skel': No such file or directory
+gpg: DBG: locking for `/sdcard/git-annex.home/.gnupg/secring.gpg.lock' done via O_EXCL
+gpg: DBG: locking for `/sdcard/git-annex.home/.gnupg/pubring.gpg.lock' done via O_EXCL
+gpg: encrypted with unknown algorithm 3
+gpg: decryption failed: secret key not available
+(gpg) gpg: encrypted with unknown algorithm 3
+gpg: decryption failed: secret key not available
+(gpg) gpg: encrypted with unknown algorithm 3
+gpg: decryption failed: secret key not available
+(gpg) gpg: encrypted with unknown algorithm 3
+gpg: decryption failed: secret key not available
+
+# End of transcript or log.
+"""]]
diff --git a/doc/design/assistant/OSX.mdwn b/doc/design/assistant/OSX.mdwn
--- a/doc/design/assistant/OSX.mdwn
+++ b/doc/design/assistant/OSX.mdwn
@@ -5,6 +5,8 @@
 * use FSEvents to detect file changes (better than kqueue) **done**
 * Use OSX's "network reachability functionality" to detect when on a network
   <http://developer.apple.com/library/mac/#documentation/Networking/Conceptual/SystemConfigFrameworks/SC_Intro/SC_Intro.html#//apple_ref/doc/uid/TP40001065>
+* Switch from gpg to <https://gpgtools.org/>. According to a user,
+  this is better because it can show a dialog window for password prompts.
 
 Bugs:
 
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
@@ -6,6 +6,7 @@
 * [[bugs/Android_app_permission_denial_on_startup]]
 * S3 doesn't work (at least to Internet Archive: 
   "connect: does not exist (connection refused)")
+* Get app into Goole Play and/or FDroid
 
 ## TODO
 
@@ -25,7 +26,8 @@
   and a few places use it. I have some horrible workarounds in place.
 * Get local pairing to work. network-multicast and network-info don't
   currently install.
-* Get test suite to pass.
+* Get test suite to pass. `git clone` of a local repo fails on android
+  for some reason.
 * Make app autostart on boot, optionally. <http://stackoverflow.com/questions/1056570/how-to-autostart-an-android-application>
 * The app should be aware of power status, and avoid expensive background
   jobs when low on battery or run flat out when plugged in.
diff --git a/doc/design/assistant/blog/day_266__release_day.mdwn b/doc/design/assistant/blog/day_266__release_day.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_266__release_day.mdwn
@@ -0,0 +1,6 @@
+Made a release.
+
+I am firming up some ideas for post-kickstarter. More on that later.
+
+In the process of setting up a Windows autobuilder, using the same
+jenkins installation that is used to autobuild msysgit.
diff --git a/doc/design/assistant/blog/day_267__windows_autobuilder.mdwn b/doc/design/assistant/blog/day_267__windows_autobuilder.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_267__windows_autobuilder.mdwn
@@ -0,0 +1,9 @@
+git-annex is now autobuilt for Windows on the same Jenkins farm that
+builds msysgit. Thanks for Yury V. Zaytsev for providing that! Spent about
+half of today setting up the build.
+
+Got the test suite to pass in direct mode, and indeed in direct mode
+on a FAT file system. Had to fix one corner case in direct mode `git annex
+add`. Unfortunately it still doesn't work on Android; somehow `git clone` 
+of a local repository is broken there. Also got the test suite to build,
+and run on Windows, though it fails pretty miserably.
diff --git a/doc/design/assistant/blog/day_268__core_monad_change.mdwn b/doc/design/assistant/blog/day_268__core_monad_change.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_268__core_monad_change.mdwn
@@ -0,0 +1,9 @@
+Today I had to change the implementation of the Annex monad. The old one
+turned out to be buggy around exception handling -- changes to state
+recorded by code that ran in an exception handler were discarded when it
+threw an exception. Changed from a StateT monad to a ReaderT with
+a MVar. Really deep-level change, but it went off without a
+hitch!
+
+Other than that it was a bug catch up day. Almost entirely caught up once
+more.
diff --git a/doc/design/assistant/blog/day_269__bugfixes.mdwn b/doc/design/assistant/blog/day_269__bugfixes.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/design/assistant/blog/day_269__bugfixes.mdwn
@@ -0,0 +1,14 @@
+Worked on several important bug fixes today. One affects automatic merge
+confict resolution, and can case data loss in direct mode, so I will be
+making a release with the fix tomorrow.
+
+Practiced TDD today, and good thing too. The new improved test suite
+turned up a really subtle bug involving the git-annex branch vector
+clocks-ish code, which I also fixed.
+
+Also, fixes to the OSX autobuilds. One of them had a broken gpg, which is
+now fixed. The other one is successfully building again. And, I'm switching
+the Linux autobuilds to build against Debian stable, since testing has a
+new version of libc now, which would make the autobuilds not work on older
+systems. Getting an amd64 chroot into shape is needing rather a lot
+of backporting of build dependencies, which I already did for i386.
diff --git a/doc/design/assistant/more_cloud_providers.mdwn b/doc/design/assistant/more_cloud_providers.mdwn
--- a/doc/design/assistant/more_cloud_providers.mdwn
+++ b/doc/design/assistant/more_cloud_providers.mdwn
@@ -14,5 +14,10 @@
 * [nimbus.io](https://nimbus.io/) Fairly low prices ($0.06/GB);
   REST API; free software
 * Mediafire provides 50gb free and has a REST API.
+* Flickr provides 1 tb (!!!!) to free accounts, and can store at least
+  photos and videos.
+* mega.co.nz. Already supported via [[tips/megaannex]], would just need
+  webapp modifications to configure it. May want to use megaannex as-is to
+  build a non-hook special remote in haskell.
 
 See poll at [[polls/prioritizing_special_remotes]].
diff --git a/doc/design/assistant/polls/Android_default_directory.mdwn b/doc/design/assistant/polls/Android_default_directory.mdwn
--- a/doc/design/assistant/polls/Android_default_directory.mdwn
+++ b/doc/design/assistant/polls/Android_default_directory.mdwn
@@ -4,4 +4,4 @@
 want the first time they run it, but to save typing on android, anything
 that gets enough votes will be included in a list of choices as well.
 
-[[!poll open=yes expandable=yes 44 "/sdcard/annex" 3 "Whole /sdcard" 4 "DCIM directory (photos and videos only)" 1 "Same as for regular git-annex. ~/annex/"]]
+[[!poll open=yes expandable=yes 45 "/sdcard/annex" 3 "Whole /sdcard" 4 "DCIM directory (photos and videos only)" 1 "Same as for regular git-annex. ~/annex/"]]
diff --git a/doc/forum/Check_when_your_last_fsck_was__63__.mdwn b/doc/forum/Check_when_your_last_fsck_was__63__.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Check_when_your_last_fsck_was__63__.mdwn
@@ -0,0 +1,4 @@
+Hey Joey,
+    Is there a way to see when the last fsck was?  I know about --incremental and the related options, but sometimes I'd like to know when the last time I fsck'd a file (or even the whole repo) was.  There doesn't seem to be a command line option for it, but I know that info is saved somewhere...maybe this could be part of 'git annex status'?
+
+$(words of gratitude and encouragement)
diff --git a/doc/forum/Running_assistant_steps_manually.mdwn b/doc/forum/Running_assistant_steps_manually.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/Running_assistant_steps_manually.mdwn
@@ -0,0 +1,20 @@
+Unfortunately one of the machines that I am running git-annex on is too old to handle the watcher thread.
+
+The error message is:
+
+    Watcher crashed: Need at least OSX 10.7.0 for file-level FSEvents
+
+
+I am however interested in duplicating the assistant steps manually.  I'm happy to accept that I won't have the great, auto-magic, coverage, but, how I run the steps that the assistant runs?
+
+I'm guessing there is a:
+
+    git annex add .
+    git commit -m "[blah]"
+    git annex sync
+
+but how do I get the pushing to backup/transfer/whatever remotes to work?  And what order should I do these?
+
+What I would *like* to do is get a script that runs all of the needed annex steps and then kick it off in a cron job 2-5 times a day.
+
+Any thoughts?
diff --git a/doc/forum/Ubuntu_PPA/comment_6_bd99fb70399fc58d98781a89c6d38428._comment b/doc/forum/Ubuntu_PPA/comment_6_bd99fb70399fc58d98781a89c6d38428._comment
new file mode 100644
--- /dev/null
+++ b/doc/forum/Ubuntu_PPA/comment_6_bd99fb70399fc58d98781a89c6d38428._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkx5V3MTbzCXS3J7Mn9FEq8M9bPPYMkAHY"
+ nickname="Pedro"
+ subject="comment 6"
+ date="2013-05-20T15:52:40Z"
+ content="""
+Be careful that the fmarier ppa includes more than just git-annex.
+"""]]
diff --git a/doc/forum/exclude_files_from_annex.mdwn b/doc/forum/exclude_files_from_annex.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/forum/exclude_files_from_annex.mdwn
@@ -0,0 +1,10 @@
+I'm wanting to do essentially [[/tips/replacing_Sparkleshare_or_dvcs-autosync_with_the_assistant/]], that is prevent git-annex from annexing certain files (by extension), and rather manage them in git only.
+
+This seems to work fine, and the assistant auto-adds them to git, and commits when I make changes, when in indirect mode.
+
+However, in a direct mode repository, then the files are automatically annexed, and even if I create them in an indirect repo, and then change it, they are annexed.
+
+Is this expected behaviour? Does the annex.largefiles setting only make sense in indirect mode?
+
+
+--Walter
diff --git a/doc/install.mdwn b/doc/install.mdwn
--- a/doc/install.mdwn
+++ b/doc/install.mdwn
@@ -1,20 +1,20 @@
 ## Pick your OS
 
 [[!table format=dsv header=yes data="""
-detailed instructions | quick install
-[[OSX]]               | [download git-annex.app](http://downloads.kitenet.net/git-annex/OSX/current/)
-[[Android]]           | [download git-annex.apk](http://downloads.kitenet.net/git-annex/android/current/) **beta**
-[[Linux|linux_standalone]] | [download prebuilt linux tarball](http://downloads.kitenet.net/git-annex/linux/current/)
-[[Debian]]            | `apt-get install git-annex`
-[[Ubuntu]]            | `apt-get install git-annex`
-[[Fedora]]            | `yum install git-annex`
-[[FreeBSD]]           | `pkg_add -r hs-git-annex`
-[[ArchLinux]]         | `yaourt -Sy git-annex`
-[[NixOS]]             | `nix-env -i git-annex`
-[[Gentoo]]            | `emerge git-annex`
-[[ScientificLinux5]]  | (and other RHEL5 clones like CentOS5)
-[[openSUSE]]          | 
-[[Windows]]           | **alpha**
+detailed instructions             | quick install
+[[OSX]]                           | [download git-annex.app](http://downloads.kitenet.net/git-annex/OSX/current/)
+[[Android]]                       | [download git-annex.apk](http://downloads.kitenet.net/git-annex/android/current/) **beta**
+[[Linux|linux_standalone]]        | [download prebuilt linux tarball](http://downloads.kitenet.net/git-annex/linux/current/)
+&nbsp;&nbsp;[[Debian]]            | `apt-get install git-annex`
+&nbsp;&nbsp;[[Ubuntu]]            | `apt-get install git-annex`
+&nbsp;&nbsp;[[Fedora]]            | `yum install git-annex`
+&nbsp;&nbsp;[[FreeBSD]]           | `pkg_add -r hs-git-annex`
+&nbsp;&nbsp;[[ArchLinux]]         | `yaourt -Sy git-annex`
+&nbsp;&nbsp;[[NixOS]]             | `nix-env -i git-annex`
+&nbsp;&nbsp;[[Gentoo]]            | `emerge git-annex`
+&nbsp;&nbsp;[[ScientificLinux5]]  |
+&nbsp;&nbsp;[[openSUSE]]          | 
+[[Windows]]                       | [download installer](http://downloads.kitenet.net/git-annex/windows/current/) **alpha**
 """]]
 
 ## Using cabal
diff --git a/doc/install/Ubuntu.mdwn b/doc/install/Ubuntu.mdwn
--- a/doc/install/Ubuntu.mdwn
+++ b/doc/install/Ubuntu.mdwn
@@ -1,19 +1,26 @@
+## Saucy
+
+	sudo apt-get install git-annex
+
 ## Raring
 
 	sudo apt-get install git-annex
 
+Note: This version does not have the WebApp enabled (see [here](https://bugs.launchpad.net/ubuntu/+source/git-annex/+bug/1084693)), but is otherwise
+usable.
+
 ## Precise
 
 	sudo apt-get install git-annex
 
-Note: This version is too old to include the [[assistant]], but is otherwise
-usable.
+Note: This version is too old to include the [[assistant]] or its WebApp,
+but is otherwise usable.
 
 ## Precise PPA
 
 <https://launchpad.net/~fmarier/+archive/ppa>
 
-A newer version of git-annex, including the [[assistant]].
+A newer version of git-annex, including the [[assistant]] and WebApp.
 (Maintained by François Marier)
 
 	sudo add-apt-repository ppa:fmarier/ppa
diff --git a/doc/install/Ubuntu/comment_1_d1c511153fe94bf33e19a1281f1c92f2._comment b/doc/install/Ubuntu/comment_1_d1c511153fe94bf33e19a1281f1c92f2._comment
new file mode 100644
--- /dev/null
+++ b/doc/install/Ubuntu/comment_1_d1c511153fe94bf33e19a1281f1c92f2._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="https://www.google.com/accounts/o8/id?id=AItOawkx5V3MTbzCXS3J7Mn9FEq8M9bPPYMkAHY"
+ nickname="Pedro"
+ subject="comment 1"
+ date="2013-05-20T15:53:25Z"
+ content="""
+Note that the fmarier ppa includes more than just git-annex. I've asked the author if he could segregate git-annex into a separate ppa.
+"""]]
diff --git a/doc/install/Windows.mdwn b/doc/install/Windows.mdwn
--- a/doc/install/Windows.mdwn
+++ b/doc/install/Windows.mdwn
@@ -8,19 +8,19 @@
 current status. Note especially that git-annex always uses [[direct_mode]]
 on Windows.
 
-## building it yourself
+## autobuilds
 
-To build git-annex from source on Windows, you need to install
-the Haskell Platform and Cygwin. Use Cygwin to install gcc, rsync, git,
-ssh, and gpg.
+A daily build is also available, thanks to Yury V. Zaytsev and
+[NEST](http://nest-initiative.org/).
 
-Then, within Cygwin, git-annex can be compiled following the instructions
-for [[using cabal|cabal]].
+* [download](https://qa.nest-initiative.org/view/msysGit/job/msysgit-git-annex-assistant-test/lastSuccessfulBuild/artifact/git-annex-installer.exe) ([build logs](https://qa.nest-initiative.org/view/msysGit/job/msysgit-git-annex-assistant-test/))
 
-Once git-annex is built, the NullSoft installer can be built, as follows:
+## building it yourself
 
-<pre>
-	cabal install nsis
-	ghc --make Build/NullSoftInstaller.hs
-	Build/NullSoftInstaller.exe
-</pre>
+To build git-annex from source on Windows, you need to install
+the Haskell Platform, Mingw, and Cygwin. Use Cygwin to install
+gcc, rsync, git, wget, ssh, and gpg.
+
+There is a shell script `standalone/windows/build.sh` that can be
+used to build git-annex. Note that this shell script cannot be run
+in Cygwin; run it with the Mingw sh.
diff --git a/doc/install/Windows/comment_1_77e6b1023fee32277f1890def86b0106._comment b/doc/install/Windows/comment_1_77e6b1023fee32277f1890def86b0106._comment
deleted file mode 100644
--- a/doc/install/Windows/comment_1_77e6b1023fee32277f1890def86b0106._comment
+++ /dev/null
@@ -1,14 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkGCmVc5qIJaQQgG82Hc5zzBdAVdhe2JEM"
- nickname="Bruno"
- subject="comment 1"
- date="2013-05-15T18:29:19Z"
- content="""
-Did you have any problem installing unix-2.6.0.1?
-
-I got :
-
-    cabal.exe: Missing dependencies on foreign libraries:
-    * Missing (or bad) header file: HsUnix.h
-    * Missing C libraries: rt, dl
-"""]]
diff --git a/doc/install/Windows/comment_2_1b4eaffa46dfff0a5e20f7f2016bdf9a._comment b/doc/install/Windows/comment_2_1b4eaffa46dfff0a5e20f7f2016bdf9a._comment
deleted file mode 100644
--- a/doc/install/Windows/comment_2_1b4eaffa46dfff0a5e20f7f2016bdf9a._comment
+++ /dev/null
@@ -1,52 +0,0 @@
-[[!comment format=mdwn
- username="https://www.google.com/accounts/o8/id?id=AItOawkGCmVc5qIJaQQgG82Hc5zzBdAVdhe2JEM"
- nickname="Bruno"
- subject="comment 2"
- date="2013-05-16T02:22:26Z"
- content="""
-By doing `cabal install --only-dependencies -v3` I saw that the problem was that it didn't find `sys/times.h`:
-
-    In file included from C:\cygwin\tmp\36.c:1:0:
-    include/HsUnix.h:33:23: fatal error: sys/times.h: No such file or directory
-    compilation terminated.
-    (\"C:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\mingw\\bin\\gcc.exe\",[\"-Wl,--hash-size=31\",\"-Wl,--reduce-memory-overheads\",\"C:\\cygwin\\tmp\\36.c\",\"-o\",\"C:\\cygwin\\tmp\\36\",\"-E\",\"-D__GLASGOW_HASKELL__=704\",\"-Dmingw32_HOST_OS=1\",\"-Di386_HOST_ARCH=1\",\"-Idist\\build\\autogen\",\"-Iinclude\",\"-I.\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\time-1.4\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\Win32-2.2.2.0\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\bytestring-0.9.2.1\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\base-4.5.1.0\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib/include\"])
-    C:\Program Files (x86)\Haskell Platform\2012.4.0.0\mingw\bin\gcc.exe returned
-    ExitFailure 1 with error message:
-    In file included from C:\cygwin\tmp\36.c:1:0:
-    include/HsUnix.h:33:23: fatal error: sys/times.h: No such file or directory
-    compilation terminated.
-    cabal.exe: Missing dependencies on foreign libraries:
-    * Missing (or bad) header file: HsUnix.h
-    * Missing C libraries: rt, dl
-    This problem can usually be solved by installing the system packages that
-    provide these libraries (you may need the \"-dev\" versions). If the libraries
-    are already installed but in a non-standard location then you can use the
-    flags --extra-include-dirs= and --extra-lib-dirs= to specify where they are.
-    If the header file does exist, it may contain errors that are caught by the C
-    compiler at the preprocessing stage. In this case you can re-run configure
-    with the verbosity flag -v3 to see the error messages.
-    Failed to install unix-2.6.0.1
-
-Is it normal that cabal use mingw's gcc (from the Haskell Platform)?
-
-If so, should the following command work (I'm not sure if mixing mingw and cygwin stuff is ok) ?
-
-cabal install --only-dependencies -v3 --extra-include-dirs=C:\\cygwin\\usr\\include --extra-lib-dirs=c:\\cygwin\\lib
-
-    configure: WARNING: unrecognized options: --with-compiler, --with-gcc
-    Reading parameters from .\unix.buildinfo
-    (\"C:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\mingw\\bin\\gcc.exe\",[\"-Wl,--hash-size=31\",\"-Wl,--reduce-memory-overheads\",\"C:\\cygwin\\tmp\\3504.c\",\"-o\",\"C:\\cygwin\\tmp\\3504\",\"-D__GLASGOW_HASKELL__=704\",\"-Dmingw32_HOST_OS=1\",\"-Di386_HOST_ARCH=1\",\"-Idist\\build\\autogen\",\"-Iinclude\",\"-IC:\\cygwin\\usr\\include\",\"-I.\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\time-1.4\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\Win32-2.2.2.0\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\bytestring-0.9.2.1\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\base-4.5.1.0\\include\",\"-IC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib/include\",\"-lrt\",\"-ldl\",\"-Lc:\\cygwin\\lib\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\time-1.4\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\old-locale-1.0.0.4\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\deepseq-1.3.0.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\array-0.4.0.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\Win32-2.2.2.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\bytestring-0.9.2.1\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\base-4.5.1.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\integer-gmp-0.4.0.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\\ghc-prim-0.2.0.0\",\"-LC:\\Program Files (x86)\\Haskell Platform\\2012.4.0.0\\lib\"])
-    C:\Program Files (x86)\Haskell Platform\2012.4.0.0\mingw\bin\gcc.exe returned
-    ExitFailure 1 with error message:
-    In file included from include/HsUnix.h:39:0,
-    from C:\cygwin\tmp\3504.c:1:
-    C:\cygwin\usr\include/sys/resource.h:76:29: error: expected declaration
-    specifiers or '...' before 'id_t'
-    C:\cygwin\usr\include/sys/resource.h:77:29: error: expected declaration
-    specifiers or '...' before 'id_t'
-    In file included from C:\cygwin\usr\include/dirent.h:6:0,
-    from include/HsUnix.h:75,
-    from C:\cygwin\tmp\3504.c:1:
-    C:\cygwin\usr\include/sys/dirent.h:24:3: error: expected
-    specifier-qualifier-list before '__ino64_t'
-"""]]
diff --git a/doc/install/Windows/comment_3_b0c4c6e77246b1b4a81f6940e11b67d3._comment b/doc/install/Windows/comment_3_b0c4c6e77246b1b4a81f6940e11b67d3._comment
deleted file mode 100644
--- a/doc/install/Windows/comment_3_b0c4c6e77246b1b4a81f6940e11b67d3._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- nickname="joey"
- subject="comment 3"
- date="2013-05-16T02:29:10Z"
- content="""
-You need to build from git master, which is fixed to not use unix on windows, or wait for tomorrow's release.
-
-(Cabal will also try to upgrade network as part of installing yesod. Since the webapp is not ported yet, you need to `cabal configure -f-Webapp` for now.)
-"""]]
diff --git a/doc/install/fromscratch.mdwn b/doc/install/fromscratch.mdwn
--- a/doc/install/fromscratch.mdwn
+++ b/doc/install/fromscratch.mdwn
@@ -9,7 +9,6 @@
   * [SHA](http://hackage.haskell.org/package/SHA)
   * [dataenc](http://hackage.haskell.org/package/dataenc)
   * [monad-control](http://hackage.haskell.org/package/monad-control)
-  * [lifted-base](http://hackage.haskell.org/package/lifted-base)
   * [QuickCheck 2](http://hackage.haskell.org/package/QuickCheck)
   * [json](http://hackage.haskell.org/package/json)
   * [IfElse](http://hackage.haskell.org/package/IfElse)
@@ -21,6 +20,7 @@
   * [SafeSemaphore](http://hackage.haskell.org/package/SafeSemaphore)
   * [UUID](http://hackage.haskell.org/package/uuid)
   * [regex-tdfa](http://hackage.haskell.org/package/regex-tdfa)
+  * [extensible-exceptions](http://hackage.haskell.org/package/extensible-exceptions)
 * Optional haskell stuff, used by the [[assistant]] and its webapp
   * [stm](http://hackage.haskell.org/package/stm)
     (version 2.3 or newer)
@@ -33,7 +33,6 @@
   * [data-default](http://hackage.haskell.org/package/data-default)
   * [case-insensitive](http://hackage.haskell.org/package/case-insensitive)
   * [http-types](http://hackage.haskell.org/package/http-types)
-  * [transformers](http://hackage.haskell.org/package/transformers)
   * [wai](http://hackage.haskell.org/package/wai)
   * [wai-logger](http://hackage.haskell.org/package/wai-logger)
   * [warp](http://hackage.haskell.org/package/warp)
@@ -49,6 +48,7 @@
   * [async](http://hackage.haskell.org/package/async)
   * [HTTP](http://hackage.haskell.org/package/HTTP)
   * [unix-compat](http://hackage.haskell.org/package/unix-compat)
+  * [MonadCatchIO-transformers](http://hackage.haskell.org/package/MonadCatchIO-transformers)
 * Shell commands
   * [git](http://git-scm.com/)
   * [xargs](http://savannah.gnu.org/projects/findutils/)
diff --git a/doc/news/version_4.20130323.mdwn b/doc/news/version_4.20130323.mdwn
deleted file mode 100644
--- a/doc/news/version_4.20130323.mdwn
+++ /dev/null
@@ -1,37 +0,0 @@
-git-annex 4.20130323 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * webapp: Repository list is now included in the dashboard, and other
-     UI tweaks.
-   * webapp: Improved UI for pairing your own devices together using XMPP.
-   * webapp: Display an alert when there are XMPP remotes, and a cloud
-     transfer repository needs to be configured.
-   * Add incrementalbackup repository group.
-   * webapp: Encourage user to install git-annex on a server when adding
-     a ssh server, rather than just funneling them through to rsync.
-   * xmpp: --debug now enables a sanitized dump of the XMPP protocol
-   * xmpp: Try harder to detect presence of clients when there's a git push
-     to send.
-   * xmpp: Re-enable XA flag, since disabling it did not turn out to help
-     with the problems Google Talk has with not always sending presence
-     messages to clients.
-   * map: Combine duplicate repositories, for a nicer looking map.
-   * Fix several bugs caused by a bad Ord instance for Remote.
-   * webapp: Switch all forms to POST.
-   * assistant: Avoid syncing with annex-ignored remotes when reconnecting
-     to the network, or connecting a drive.
-   * assistant: Fix OSX bug that prevented committing changed files to a
-     repository when in indirect mode.
-   * webapp: Improved alerts displayed when syncing with remotes, and
-     when syncing with a remote fails.
-   * webapp: Force wrap long filenames in transfer display.
-   * assistant: The ConfigMonitor left one zombie behind each time
-     it checked for changes, now fixed.
-   * get, copy, move: Display an error message when an identical transfer
-     is already in progress, rather than failing with no indication why.
-   * assistant: Several optimisations to file transfers.
-   * OSX app and standalone Linux tarball now both support being added to
-     PATH; no need to use runshell to start git-annex.
-   * webapp: When adding a removable drive, you can now specify the
-     directory inside it to use.
-   * webapp: Confirm whether user wants to combine repositories when
-     adding a removable drive that already has a repository on it."""]]
diff --git a/doc/news/version_4.20130521.mdwn b/doc/news/version_4.20130521.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/news/version_4.20130521.mdwn
@@ -0,0 +1,24 @@
+git-annex 4.20130521 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * Sanitize debian changelog version before putting it into cabal file.
+     Closes: #[708619](http://bugs.debian.org/708619)
+   * Switch to MonadCatchIO-transformers for better handling of state while
+     catching exceptions.
+   * Fix a zombie that could result when running a process like gpg to
+     read and write to it.
+   * Allow building with gpg2.
+   * Disable building with the haskell threaded runtime when the webapp
+     is not built. This may fix builds on mips, s390x and sparc, which are
+     failing to link -lHSrts\_thr
+   * Temporarily build without webapp on kfreebsd-i386, until yesod is
+     installable there again.
+   * Direct mode bug fix: After a conflicted merge was automatically resolved,
+     the content of a file that was already present could incorrectly
+     be replaced with a symlink.
+   * Fix a bug in the git-annex branch handling code that could
+     cause info from a remote to not be merged and take effect immediately.
+   * Direct mode is now fully tested by the test suite.
+   * Detect bad content in ~/.config/git-annex/program and look in PATH instead.
+   * OSX: Fixed gpg included in dmg.
+   * Linux standalone: Back to being built with glibc 2.13 for maximum
+     portability."""]]
diff --git a/doc/special_remotes.mdwn b/doc/special_remotes.mdwn
--- a/doc/special_remotes.mdwn
+++ b/doc/special_remotes.mdwn
@@ -26,7 +26,9 @@
 * [[tips/Internet_Archive_via_S3]]
 * [[tahoe-lafs|forum/tips:_special__95__remotes__47__hook_with_tahoe-lafs]]
 * [[tips/using_box.com_as_a_special_remote]]
+* [[tips/using_mega.co.nz_as_a_special_remote|tips/megaannex]]
 * [[forum/special_remote_for_IMAP]]
+* [[forum/nntp__47__usenet special remote]]
 
 ## Unused content on special remotes
 
diff --git a/doc/special_remotes/bup/comment_5_61b32f9ee00e6016443a1cf10273959c._comment b/doc/special_remotes/bup/comment_5_61b32f9ee00e6016443a1cf10273959c._comment
deleted file mode 100644
--- a/doc/special_remotes/bup/comment_5_61b32f9ee00e6016443a1cf10273959c._comment
+++ /dev/null
@@ -1,10 +0,0 @@
-[[!comment format=mdwn
- username="http://joeyh.name/"
- nickname="joey"
- subject="comment 5"
- date="2013-03-13T16:05:57Z"
- content="""
-I don't plan to support creating bup spefial remotes in the assistant, currently. Of course the assistant can use bup special remotes you set up.
-
-Your two bup repos would be synced using bup-split.
-"""]]
diff --git a/doc/tips/megaannex.mdwn b/doc/tips/megaannex.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/tips/megaannex.mdwn
@@ -0,0 +1,45 @@
+[Megaannex](https://github.com/TobiasTheViking/megaannex)
+is a hook program for git-annex to use mega.co.nz as backend
+
+# Requirements:
+
+    requests>=0.10
+    pycrypto
+
+Credit for the mega api interface goes to:
+<https://github.com/richardasaurus/mega.py>
+
+## Install
+
+Clone the git repository in your home folder.
+
+    git clone git://github.com/TobiasTheViking/megaannex.git 
+
+This should make a ~/megannex folder
+
+## Setup
+
+Run the program once to make an empty config file.
+
+    cd ~/megaannex; python2 megaannex.py
+
+Edit the megaannex.conf file. Add your mega.co.nz username and password
+
+Note: The folder option in the megaannex.conf file isn't yet used. 
+
+## Configuring git-annex
+
+	git config annex.mega-store-hook 'python2 ~/megaannex/megaannex.py store --subject $ANNEX_KEY --file $ANNEX_FILE'
+	git config annex.mega-retrieve-hook 'python2 ~/megaannex/megaannex.py  getfile --subject $ANNEX_KEY --file $ANNEX_FILE'
+	git config annex.mega-checkpresent-hook 'python2 ~/megaannex/megaannex.py fileexists --subject $ANNEX_KEY'
+	git config annex.mega-remove-hook 'python2 ~/megaannex/megaannex.py delete --subject $ANNEX_KEY'
+
+	git annex initremote mega type=hook hooktype=mega encryption=shared
+	git annex describe mega "the mega.co.nz library"
+
+## Notes
+
+You may need to use a different command than "python2", depending
+on your python installation.
+
+-- Tobias
diff --git a/doc/todo/wishlist:_Advanced_settings_for_xmpp_and_webdav.mdwn b/doc/todo/wishlist:_Advanced_settings_for_xmpp_and_webdav.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/todo/wishlist:_Advanced_settings_for_xmpp_and_webdav.mdwn
@@ -0,0 +1,5 @@
+It would be very nice with an "advanced settings" for jabber and webdav support.
+
+Currently XMPP fails if you use a google apps account. Since the domain provided in the email is not the same as the XMPP server.
+
+Same goes for webdav support. If i have my own webdav server somewhere on the internet there is no way to set it up in the assistant.
diff --git a/doc/todo/wishlist:_remove_files_and_symlinks_but_keep_in_remote.mdwn b/doc/todo/wishlist:_remove_files_and_symlinks_but_keep_in_remote.mdwn
deleted file mode 100644
--- a/doc/todo/wishlist:_remove_files_and_symlinks_but_keep_in_remote.mdwn
+++ /dev/null
@@ -1,7 +0,0 @@
-It would be nice to have a way to drop files without leaving broken symlinks around, at least while in direct mode.
-
-Here is my user case. I have a collection of music CDs ripped in FLAC formats. At the same time I convert all these files to MP3 files to that I can use them in various other devices and to save space.
-
-The problem is that if I `git annex drop` the FLAC files once they are converted and copied, they leave broken symlinks around; this result in various annoying error messages in almost all the music playback I tried. At the same time, if I `rm` or `git rm` these symlinks, the content of these files will be removed also from the remotes as they are signalled as no longer wanted.
-
-Couldn't git-annex keep a separate index of files that have been removed but are meant to be kept?
diff --git a/doc/todo/wishlist:_special_remote_mega.co.nz.mdwn b/doc/todo/wishlist:_special_remote_mega.co.nz.mdwn
--- a/doc/todo/wishlist:_special_remote_mega.co.nz.mdwn
+++ b/doc/todo/wishlist:_special_remote_mega.co.nz.mdwn
@@ -1,1 +1,3 @@
 mega.co.nz has 50gb for free accounts. They also have an API, so I guess it wouldn't be too hard to use it as a special remote.
+
+[[done]], see [[tips/megaannex]].
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: 4.20130516
+Version: 4.20130521
 Cabal-Version: >= 1.8
 License: GPL
 Maintainer: Joey Hess <joey@kitenet.net>
@@ -71,7 +71,7 @@
    containers, utf8-string, network (>= 2.0), mtl (>= 2),
    bytestring, old-locale, time, HTTP,
    extensible-exceptions, dataenc, SHA, process, json,
-   base (>= 4.5 && < 4.8), monad-control, transformers-base, lifted-base,
+   base (>= 4.5 && < 4.8), monad-control, MonadCatchIO-transformers,
    IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance, process,
    SafeSemaphore, uuid, random, dlist, unix-compat
   -- Need to list these because they're generated from .hsc files.
@@ -110,7 +110,6 @@
   if flag(Assistant) && ! os(windows) && ! os(solaris)
     Build-Depends: async, stm (>= 2.3)
     CPP-Options: -DWITH_ASSISTANT
-    GHC-Options: -threaded
 
   if flag(Android)
     Build-Depends: data-endian
@@ -138,6 +137,7 @@
      crypto-api, hamlet, clientsession, aeson, yesod-form,
      template-haskell, yesod-default, data-default
     CPP-Options: -DWITH_WEBAPP
+    GHC-Options: -threaded
 
   if flag(Pairing)
     Build-Depends: network-multicast, network-info
diff --git a/git-annex.hs b/git-annex.hs
--- a/git-annex.hs
+++ b/git-annex.hs
@@ -13,11 +13,8 @@
 import qualified GitAnnex
 import qualified GitAnnexShell
 #ifdef WITH_TESTSUITE
-#ifndef __WINDOWS__
 import qualified Test
-#define CHECK_TEST
 #endif
-#endif
 
 main :: IO ()
 main = run =<< getProgName
@@ -28,7 +25,7 @@
 	isshell n = takeFileName n == "git-annex-shell"
 	go a = do
 		ps <- getArgs
-#ifdef CHECK_TEST
+#ifdef WITH_TESTSUITE
 		if ps == ["test"]
 			then Test.main
 			else a ps
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/standalone/windows/build.sh b/standalone/windows/build.sh
new file mode 100644
--- /dev/null
+++ b/standalone/windows/build.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+# 
+# This script is run by the jenkins autobuilder, in a mingw environment,
+# to build git-annex for Windows.
+
+set -x
+set -e
+
+HP="/c/Program Files (x86)/Haskell Platform/2012.4.0.0"
+FLAGS="-Webapp -Assistant -XMPP"
+
+PATH="$HP/bin:$HP/lib/extralibs/bin:/c/Program Files (x86)/NSIS:$PATH"
+
+# Run a command with the cygwin environment available.
+# However, programs not from cygwin are preferred.
+withcyg () {
+	PATH="$PATH:/c/cygwin/bin" "$@"
+}
+
+# Don't allow build artifact from a past successfuly build to be extracted
+# if we fail.
+rm -f git-annex-installer.exe
+
+# Install haskell dependencies.
+# cabal install is not run in cygwin, because we don't want configure scripts
+# for haskell libraries to link them with the cygwin library.
+cabal update || true
+
+rm -rf MissingH-1.2.0.0
+cabal unpack MissingH
+cd MissingH-1.2.0.0
+withcyg patch -p1 <../standalone/windows/haskell-patches/MissingH_1.2.0.0-0001-hack-around-strange-build-problem-in-jenkins-autobui.patch
+cabal install || true
+cd ..
+
+cabal install --only-dependencies -f"$FLAGS"
+
+# Detect when the last build was an incremental build and failed, 
+# and try a full build. Done this way because this shell seems a bit
+# broken.
+if [ -e last-incremental-failed ]; then
+	cabal clean || true
+	# windows breakage..
+	rm -rf dist
+fi
+touch last-incremental-failed
+
+# Build git-annex
+withcyg cabal configure -f"$FLAGS"
+withcyg cabal build
+	
+# Build the installer
+cabal install nsis
+ghc --make Build/NullSoftInstaller.hs
+withcyg Build/NullSoftInstaller.exe
+
+rm -f last-incremental-failed
+
+# Test git-annex
+rm -rf .t
+withcyg dist/build/git-annex/git-annex.exe test || true
diff --git a/templates/configurators/pairing/xmpp/self/prompt.hamlet b/templates/configurators/pairing/xmpp/self/prompt.hamlet
--- a/templates/configurators/pairing/xmpp/self/prompt.hamlet
+++ b/templates/configurators/pairing/xmpp/self/prompt.hamlet
@@ -10,7 +10,9 @@
     laptop, and their repositories will automatically be kept in sync.
   <p>
     Make sure your other devices are online and configured to use #
-    your Jabber account before continuing.
+    your Jabber account before continuing. Note that <b>all</b> #
+    repositories configured to use the same Jabber account will be #
+    combined together when you do this.
   <p>
     <a .btn .btn-primary .btn-large href="@{RunningXMPPPairSelfR}">
       Start sharing with my other devices #
