diff --git a/Annex.hs b/Annex.hs
--- a/Annex.hs
+++ b/Annex.hs
@@ -98,7 +98,7 @@
 	{ repo :: Git.Repo
 	, repoadjustment :: (Git.Repo -> IO Git.Repo)
 	, gitconfig :: GitConfig
-	, backends :: [BackendA Annex]
+	, backend :: Maybe (BackendA Annex)
 	, remotes :: [Types.Remote.RemoteA Annex]
 	, remoteannexstate :: M.Map UUID AnnexState
 	, output :: MessageState
@@ -149,7 +149,7 @@
 		{ repo = r
 		, repoadjustment = return
 		, gitconfig = c
-		, backends = []
+		, backend = Nothing
 		, remotes = []
 		, remoteannexstate = M.empty
 		, output = def
diff --git a/Annex/Multicast.hs b/Annex/Multicast.hs
new file mode 100644
--- /dev/null
+++ b/Annex/Multicast.hs
@@ -0,0 +1,54 @@
+{- git-annex multicast receive callback
+ -
+ - Copyright 2017 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module Annex.Multicast where
+
+import Config.Files
+import Utility.Env
+import Utility.PartialPrelude
+
+import System.Process
+import System.IO
+import GHC.IO.Handle.FD
+#if ! MIN_VERSION_process(1,4,2)
+import System.Posix.IO (handleToFd)
+#endif
+import Control.Applicative
+import Prelude
+
+multicastReceiveEnv :: String
+multicastReceiveEnv = "GIT_ANNEX_MULTICAST_RECEIVE"
+
+multicastCallbackEnv :: IO (FilePath, [(String, String)], Handle)
+multicastCallbackEnv = do
+	gitannex <- readProgramFile
+#if MIN_VERSION_process(1,4,2)
+	-- This will even work on Windows
+	(rfd, wfd) <- createPipeFd
+	rh <- fdToHandle rfd
+#else
+	(rh, wh) <- createPipe
+	wfd <- handleToFd wh
+#endif
+	environ <- addEntry multicastReceiveEnv (show wfd) <$> getEnvironment
+	return (gitannex, environ, rh)
+
+-- This is run when uftpd has received a file. Rather than move
+-- the file into the annex here, which would require starting up the
+-- Annex monad, parsing git config, and verifying the content, simply
+-- output to the specified FD the filename. This keeps the time
+-- that uftpd is not receiving the next file as short as possible.
+runMulticastReceive :: [String] -> String -> IO ()
+runMulticastReceive ("-I":_sessionid:fs) hs = case readish hs of
+	Just fd -> do
+		h <- fdToHandle fd
+		mapM_ (hPutStrLn h) fs
+		hClose h
+	Nothing -> return ()
+runMulticastReceive _ _ = return ()
diff --git a/Annex/Ssh.hs b/Annex/Ssh.hs
--- a/Annex/Ssh.hs
+++ b/Annex/Ssh.hs
@@ -52,10 +52,13 @@
 
 {- Generates a command to ssh to a given host (or user@host) on a given
  - port. This includes connection caching parameters, and any ssh-options.
- - If GIT_SSH or GIT_SSH_COMMAND is set, they are used instead. -}
+ - If GIT_SSH or GIT_SSH_COMMAND is enabled, they are used instead. -}
 sshCommand :: ConsumeStdin -> (SshHost, Maybe SshPort) -> RemoteGitConfig -> SshCommand -> Annex (FilePath, [CommandParam])
-sshCommand cs (host, port) gc remotecmd = maybe go return
-	=<< liftIO (gitSsh' host port remotecmd (consumeStdinParams cs))
+sshCommand cs (host, port) gc remotecmd = ifM (liftIO safe_GIT_SSH)
+	( maybe go return
+		=<< liftIO (gitSsh' host port remotecmd (consumeStdinParams cs))
+	, go
+	)
   where
 	go = do
 		ps <- sshOptions cs (host, port) gc []
@@ -81,6 +84,12 @@
 		, [Param "-T"]
 		]
 
+{- Due to passing -n to GIT_SSH and GIT_SSH_COMMAND, some settings
+ - of those that expect exactly git's parameters will break. So only
+ - use those if the user set GIT_ANNEX_USE_GIT_SSH to say it's ok. -}
+safe_GIT_SSH :: IO Bool
+safe_GIT_SSH = (== Just "1") <$> getEnv "GIT_ANNEX_USE_GIT_SSH"
+
 consumeStdinParams :: ConsumeStdin -> [CommandParam]
 consumeStdinParams ConsumeStdin = []
 consumeStdinParams NoConsumeStdin = [Param "-n"]
@@ -305,13 +314,13 @@
  - to set GIT_SSH=git-annex, and set sshOptionsEnv when running git
  - commands.
  -
- - If GIT_SSH or GIT_SSH_COMMAND are set, this has no effect. -}
+ - If GIT_SSH or GIT_SSH_COMMAND are enabled, this has no effect. -}
 sshOptionsTo :: Git.Repo -> RemoteGitConfig -> Git.Repo -> Annex Git.Repo
 sshOptionsTo remote gc localr
 	| not (Git.repoIsUrl remote) || Git.repoIsHttp remote = unchanged
 	| otherwise = case Git.Url.hostuser remote of
 		Nothing -> unchanged
-		Just host -> ifM (liftIO gitSshEnvSet)
+		Just host -> ifM (liftIO $ safe_GIT_SSH <&&> gitSshEnvSet)
 			( unchanged
 			, do
 				(msockfile, _) <- sshCachingInfo (host, Git.Url.port remote)
diff --git a/Assistant/Sync.hs b/Assistant/Sync.hs
--- a/Assistant/Sync.hs
+++ b/Assistant/Sync.hs
@@ -110,8 +110,14 @@
 pushToRemotes :: [Remote] -> Assistant [Remote]
 pushToRemotes remotes = do
 	now <- liftIO getCurrentTime
-	let remotes' = filter (not . remoteAnnexReadOnly . Remote.gitconfig) remotes
+	let remotes' = filter (wantpush . Remote.gitconfig) remotes
 	syncAction remotes' (pushToRemotes' now)
+  where
+	wantpush gc
+		| remoteAnnexReadOnly gc = False
+		| not (remoteAnnexPush gc) = False
+		| otherwise = True
+
 pushToRemotes' :: UTCTime -> [Remote] -> Assistant [Remote]
 pushToRemotes' now remotes = do
 	(g, branch, u) <- liftAnnex $ do
@@ -195,16 +201,20 @@
 manualPull currentbranch remotes = do
 	g <- liftAnnex gitRepo
 	let (_xmppremotes, normalremotes) = partition Remote.isXMPPRemote remotes
-	failed <- forM normalremotes $ \r -> do
-		g' <- liftAnnex $ sshOptionsTo (Remote.repo r) (Remote.gitconfig r) g
-		ifM (liftIO $ Git.Command.runBool [Param "fetch", Param $ Remote.name r] g')
-			( return Nothing
-			, return $ Just r
-			)
+	failed <- forM normalremotes $ \r -> if wantpull $ Remote.gitconfig r
+		then do
+			g' <- liftAnnex $ sshOptionsTo (Remote.repo r) (Remote.gitconfig r) g
+			ifM (liftIO $ Git.Command.runBool [Param "fetch", Param $ Remote.name r] g')
+				( return Nothing
+				, return $ Just r
+				)
+		else return Nothing
 	haddiverged <- liftAnnex Annex.Branch.forceUpdate
 	forM_ normalremotes $ \r ->
 		liftAnnex $ Command.Sync.mergeRemote r currentbranch Command.Sync.mergeConfig
 	return (catMaybes failed, haddiverged)
+  where
+	wantpull gc = remoteAnnexPull gc
 
 {- Start syncing a remote, using a background thread. -}
 syncRemote :: Remote -> Assistant ()
diff --git a/Assistant/WebApp/Documentation.hs b/Assistant/WebApp/Documentation.hs
--- a/Assistant/WebApp/Documentation.hs
+++ b/Assistant/WebApp/Documentation.hs
@@ -12,7 +12,7 @@
 import Assistant.WebApp.Common
 import Assistant.Install (standaloneAppBase)
 import Build.SysConfig (packageversion)
-import BuildFlags
+import BuildInfo
 
 {- The full license info may be included in a file on disk that can
  - be read in and displayed. -}
diff --git a/Backend.hs b/Backend.hs
--- a/Backend.hs
+++ b/Backend.hs
@@ -7,7 +7,7 @@
 
 module Backend (
 	list,
-	orderedList,
+	defaultBackend,
 	genKey,
 	getBackend,
 	chooseBackend,
@@ -33,40 +33,29 @@
 list :: [Backend]
 list = Backend.Hash.backends ++ Backend.WORM.backends ++ Backend.URL.backends
 
-{- List of backends in the order to try them when storing a new key. -}
-orderedList :: Annex [Backend]
-orderedList = do
-	l <- Annex.getState Annex.backends -- list is cached here
-	if not $ null l
-		then return l
-		else do
-			f <- Annex.getState Annex.forcebackend
-			case f of
-				Just name | not (null name) ->
-					return [lookupname name]
-				_ -> do
-					l' <- gen . annexBackends <$> Annex.getGitConfig
-					Annex.changeState $ \s -> s { Annex.backends = l' }
-					return l'
+{- Backend to use by default when generating a new key. -}
+defaultBackend :: Annex Backend
+defaultBackend = maybe cache return =<< Annex.getState Annex.backend
   where
-	gen [] = list
-	gen ns = map lookupname ns
+	cache = do
+		n <- maybe (annexBackend <$> Annex.getGitConfig) (return . Just)
+			=<< Annex.getState Annex.forcebackend
+		let b = case n of
+			Just name | valid name -> lookupname name
+			_ -> Prelude.head list
+		Annex.changeState $ \s -> s { Annex.backend = Just b }
+		return b
+	valid name = not (null name)
 	lookupname = lookupBackendVariety . parseKeyVariety
 
-{- Generates a key for a file, trying each backend in turn until one
- - accepts it. -}
+{- Generates a key for a file. -}
 genKey :: KeySource -> Maybe Backend -> Annex (Maybe (Key, Backend))
-genKey source trybackend = do
-	bs <- orderedList
-	let bs' = maybe bs (: bs) trybackend
-	genKey' bs' source
-genKey' :: [Backend] -> KeySource -> Annex (Maybe (Key, Backend))
-genKey' [] _ = return Nothing
-genKey' (b:bs) source = do
+genKey source preferredbackend = do
+	b <- maybe defaultBackend return preferredbackend
 	r <- B.getKey b source
-	case r of
-		Nothing -> genKey' bs source
-		Just k -> return $ Just (makesane k, b)
+	return $ case r of
+		Nothing -> Nothing
+		Just k -> Just (makesane k, b)
   where
 	-- keyNames should not contain newline characters.
 	makesane k = k { keyName = map fixbadchar (keyName k) }
@@ -82,13 +71,14 @@
 		return Nothing
 
 {- Looks up the backend that should be used for a file.
- - That can be configured on a per-file basis in the gitattributes file. -}
+ - That can be configured on a per-file basis in the gitattributes file,
+ - or forced with --backend. -}
 chooseBackend :: FilePath -> Annex (Maybe Backend)
 chooseBackend f = Annex.getState Annex.forcebackend >>= go
   where
-	go Nothing =  maybeLookupBackendVariety . parseKeyVariety
+	go Nothing = maybeLookupBackendVariety . parseKeyVariety
 		<$> checkAttr "annex.backend" f
-	go (Just _) = Just . Prelude.head <$> orderedList
+	go (Just _) = Just <$> defaultBackend
 
 {- Looks up a backend by variety. May fail if unsupported or disabled. -}
 lookupBackendVariety :: KeyVariety -> Backend
diff --git a/BuildFlags.hs b/BuildFlags.hs
deleted file mode 100644
--- a/BuildFlags.hs
+++ /dev/null
@@ -1,81 +0,0 @@
-{- git-annex build flags reporting
- -
- - Copyright 2013 Joey Hess <id@joeyh.name>
- -
- - Licensed under the GNU GPL version 3 or higher.
- -}
-
-{-# LANGUAGE CPP #-}
-
-module BuildFlags where
-
-buildFlags :: [String]
-buildFlags = filter (not . null)
-	[ ""
-#ifdef WITH_ASSISTANT
-	, "Assistant"
-#else
-#warning Building without the assistant.
-#endif
-#ifdef WITH_WEBAPP
-	, "Webapp"
-#else
-#warning Building without the webapp. You probably need to install Yesod..
-#endif
-#ifdef WITH_PAIRING
-	, "Pairing"
-#else
-#warning Building without local pairing.
-#endif
-#ifdef WITH_TESTSUITE
-	, "Testsuite"
-#else
-#warning Building without the testsuite.
-#endif
-#ifdef WITH_S3
-	, "S3"
-#if MIN_VERSION_aws(0,10,6)
-		++ "(multipartupload)"
-#endif
-#if MIN_VERSION_aws(0,13,0)
-		++ "(storageclasses)"
-#endif
-#else
-#warning Building without S3.
-#endif
-#ifdef WITH_WEBDAV
-	, "WebDAV"
-#else
-#warning Building without WebDAV.
-#endif
-#ifdef WITH_INOTIFY
-	, "Inotify"
-#endif
-#ifdef WITH_FSEVENTS
-	, "FsEvents"
-#endif
-#ifdef WITH_KQUEUE
-	, "Kqueue"
-#endif
-#ifdef WITH_DBUS
-	, "DBus"
-#endif
-#ifdef WITH_DESKTOP_NOTIFY
-	, "DesktopNotify"
-#endif
-#ifdef WITH_CONCURRENTOUTPUT
-	, "ConcurrentOutput"
-#else
-#warning Building without ConcurrentOutput
-#endif
-#ifdef WITH_TORRENTPARSER
-	, "TorrentParser"
-#endif
-#ifdef WITH_MAGICMIME
-	, "MagicMime"
-#endif
-	-- Always enabled now, but users may be used to seeing these flags
-	-- listed.
-	, "Feeds"
-	, "Quvi"
-	]
diff --git a/BuildInfo.hs b/BuildInfo.hs
new file mode 100644
--- /dev/null
+++ b/BuildInfo.hs
@@ -0,0 +1,114 @@
+{- git-annex build info reporting
+ -
+ - Copyright 2013-2017 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module BuildInfo where
+
+import Data.List
+import Data.Ord
+import qualified Data.CaseInsensitive as CI
+
+buildFlags :: [String]
+buildFlags = filter (not . null)
+	[ ""
+#ifdef WITH_ASSISTANT
+	, "Assistant"
+#else
+#warning Building without the assistant.
+#endif
+#ifdef WITH_WEBAPP
+	, "Webapp"
+#else
+#warning Building without the webapp. You probably need to install Yesod..
+#endif
+#ifdef WITH_PAIRING
+	, "Pairing"
+#else
+#warning Building without local pairing.
+#endif
+#ifdef WITH_TESTSUITE
+	, "Testsuite"
+#else
+#warning Building without the testsuite.
+#endif
+#ifdef WITH_S3
+	, "S3"
+#if MIN_VERSION_aws(0,10,6)
+		++ "(multipartupload)"
+#endif
+#if MIN_VERSION_aws(0,13,0)
+		++ "(storageclasses)"
+#endif
+#else
+#warning Building without S3.
+#endif
+#ifdef WITH_WEBDAV
+	, "WebDAV"
+#else
+#warning Building without WebDAV.
+#endif
+#ifdef WITH_INOTIFY
+	, "Inotify"
+#endif
+#ifdef WITH_FSEVENTS
+	, "FsEvents"
+#endif
+#ifdef WITH_KQUEUE
+	, "Kqueue"
+#endif
+#ifdef WITH_DBUS
+	, "DBus"
+#endif
+#ifdef WITH_DESKTOP_NOTIFY
+	, "DesktopNotify"
+#endif
+#ifdef WITH_CONCURRENTOUTPUT
+	, "ConcurrentOutput"
+#else
+#warning Building without ConcurrentOutput
+#endif
+#ifdef WITH_TORRENTPARSER
+	, "TorrentParser"
+#endif
+#ifdef WITH_MAGICMIME
+	, "MagicMime"
+#endif
+	-- Always enabled now, but users may be used to seeing these flags
+	-- listed.
+	, "Feeds"
+	, "Quvi"
+	]
+
+-- Not a complete list, let alone a listing transitive deps, but only
+-- the ones that are often interesting to know.
+dependencyVersions :: [String]
+dependencyVersions = map fmt $ sortBy (comparing (CI.mk . fst))
+	[ ("feed", VERSION_feed)
+	, ("uuid", VERSION_uuid)
+	, ("bloomfilter", VERSION_bloomfilter)
+	, ("http-client", VERSION_http_client)
+	, ("persistent-sqlite", VERSION_persistent_sqlite)
+	, ("cryptonite", VERSION_cryptonite)
+#ifdef WITH_S3
+	, ("aws", VERSION_aws)
+#endif
+#ifdef WITH_WEBDAV
+	, ("DAV", VERSION_DAV)
+#endif
+#ifdef WITH_TORRENTPARSER
+	, ("torrent", VERSION_torrent)
+#endif
+#ifdef WITH_WEBAPP
+	, ("yesod", VERSION_yesod)
+#endif 
+#ifdef TOOL_VERSION_ghc
+	, ("ghc", TOOL_VERSION_ghc)
+#endif
+	]
+  where
+	fmt (p, v) = p ++ "-" ++ v
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,33 @@
+git-annex (6.20170510) unstable; urgency=medium
+
+  * When a http remote does not expose an annex.uuid config, only warn
+    about it once, not every time git-annex is run.
+  * multicast: New command, uses uftp to multicast annexed files, for eg
+    a classroom setting.
+  * Added remote.<name>.annex-push and remote.<name>.annex-pull
+    which can be useful to make remotes that don't get fully synced with
+    local changes.
+  * Disable git-annex's support for GIT_SSH and GIT_SSH_COMMAND, unless
+    GIT_ANNEX_USE_GIT_SSH=1 is also set in the environment. This is
+    necessary because as feared, the extra -n parameter that git-annex
+    passes breaks uses of these environment variables that expect exactly
+    the parameters that git passes.
+  * enableremote: When enabling a non-special remote, param=value
+    parameters can't be used, so error out if any are provided.
+  * enableremote: Fix re-enabling of special remotes that have a git
+    url, so that eg, encryption key changes take effect. They were silently
+    ignored, a reversion introduced in 6.20160527.
+  * gcrypt: Support re-enabling to change eg, encryption parameters.
+    This was never supported before.
+  * git annex add -u now supported, analagous to git add -u
+  * version: Added "dependency versions" line.
+  * Keys marked as dead are now skipped by --all.
+  * annex.backend is the new name for what was annex.backends, and takes
+    a single key-value backend, rather than the unncessary and confusing
+    list. The old option still works if set.
+
+ -- Joey Hess <id@joeyh.name>  Wed, 10 May 2017 15:05:22 -0400
+
 git-annex (6.20170321) unstable; urgency=medium
 
   * Bugfix: Passing a command a filename that does not exist sometimes
diff --git a/CmdLine/GitAnnex.hs b/CmdLine/GitAnnex.hs
--- a/CmdLine/GitAnnex.hs
+++ b/CmdLine/GitAnnex.hs
@@ -14,6 +14,7 @@
 import Command
 import Utility.Env
 import Annex.Ssh
+import Annex.Multicast
 import Types.Test
 
 import qualified Command.Help
@@ -53,6 +54,7 @@
 import qualified Command.InitRemote
 import qualified Command.EnableRemote
 import qualified Command.EnableTor
+import qualified Command.Multicast
 import qualified Command.Expire
 import qualified Command.Repair
 import qualified Command.Unused
@@ -144,6 +146,7 @@
 	, Command.InitRemote.cmd
 	, Command.EnableRemote.cmd
 	, Command.EnableTor.cmd
+	, Command.Multicast.cmd
 	, Command.Reinject.cmd
 	, Command.Unannex.cmd
 	, Command.Uninit.cmd
@@ -242,4 +245,5 @@
 	envmodes =
 		[ (sshOptionsEnv, runSshOptions args)
 		, (sshAskPassEnv, runSshAskPass)
+		, (multicastReceiveEnv, runMulticastReceive args)
 		]
diff --git a/Command/Add.hs b/Command/Add.hs
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010, 2013 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2017 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -30,6 +30,7 @@
 	{ addThese :: CmdParams
 	, includeDotFiles :: Bool
 	, batchOption :: BatchMode
+	, updateOnly :: Bool
 	}
 
 optParser :: CmdParamsDesc -> Parser AddOptions
@@ -40,6 +41,11 @@
 		<> help "don't skip dotfiles"
 		)
 	<*> parseBatchOption
+	<*> switch
+		( long "update"
+		<> short 'u'
+		<> help "only update tracked files"
+		)
 
 seek :: AddOptions -> CommandSeek
 seek o = allowConcurrentOutput $ do
@@ -52,10 +58,14 @@
 			)
 		)
 	case batchOption o of
-		Batch -> batchFiles gofile
+		Batch
+			| updateOnly o ->
+				giveup "--update --batch is not supported"
+			| otherwise -> batchFiles gofile
 		NoBatch -> do
 			let go a = a gofile (addThese o)
-			go (withFilesNotInGit (not $ includeDotFiles o))
+			unless (updateOnly o) $
+				go (withFilesNotInGit (not $ includeDotFiles o))
 			go withFilesMaybeModified
 			unlessM (versionSupportsUnlockedPointers <||> isDirect) $
 				go withFilesOldUnlocked
diff --git a/Command/EnableRemote.hs b/Command/EnableRemote.hs
--- a/Command/EnableRemote.hs
+++ b/Command/EnableRemote.hs
@@ -39,16 +39,28 @@
 	matchingname r = Git.remoteName r == Just name
 	go [] = startSpecialRemote name (Logs.Remote.keyValToConfig rest)
 		=<< Annex.SpecialRemote.findExisting name
-	go (r:_) = startNormalRemote name r
+	go (r:_) = do
+		-- This could be either a normal git remote or a special
+		-- remote that has an url (eg gcrypt).
+		rs <- Remote.remoteList
+		case filter (\rmt -> Remote.name rmt == name) rs of
+			(rmt:_) | Remote.remotetype rmt == Remote.Git.remote ->
+				startNormalRemote name rest r
+			_  -> go []
 
-startNormalRemote :: Git.RemoteName -> Git.Repo -> CommandStart
-startNormalRemote name r = do
-	showStart "enableremote" name
-	next $ next $ do
-		setRemoteIgnore r False
-		r' <- Remote.Git.configRead False r
-		u <- getRepoUUID r'
-		return $ u /= NoUUID
+-- Normal git remotes are special-cased; enableremote retries probing
+-- the remote uuid.
+startNormalRemote :: Git.RemoteName -> [String] -> Git.Repo -> CommandStart
+startNormalRemote name restparams r
+	| null restparams = do
+		showStart "enableremote" name
+		next $ next $ do
+			setRemoteIgnore r False
+			r' <- Remote.Git.configRead False r
+			u <- getRepoUUID r'
+			return $ u /= NoUUID
+	| otherwise = giveup $
+		"That is a normal git remote; passing these parameters does not make sense: " ++ unwords restparams
 
 startSpecialRemote :: Git.RemoteName -> Remote.RemoteConfig -> Maybe (UUID, Remote.RemoteConfig) -> CommandStart
 startSpecialRemote name config Nothing = do
diff --git a/Command/Migrate.hs b/Command/Migrate.hs
--- a/Command/Migrate.hs
+++ b/Command/Migrate.hs
@@ -36,15 +36,13 @@
 		Nothing -> stop
 		Just oldbackend -> do
 			exists <- inAnnex key
-			newbackend <- choosebackend =<< chooseBackend file
+			newbackend <- maybe defaultBackend return 
+				=<< chooseBackend file
 			if (newbackend /= oldbackend || upgradableKey oldbackend key || forced) && exists
 				then do
 					showStart "migrate" file
 					next $ perform file key oldbackend newbackend
 				else stop
-  where
-	choosebackend Nothing = Prelude.head <$> orderedList
-	choosebackend (Just backend) = return backend
 
 {- Checks if a key is upgradable to a newer representation.
  - 
diff --git a/Command/Multicast.hs b/Command/Multicast.hs
new file mode 100644
--- /dev/null
+++ b/Command/Multicast.hs
@@ -0,0 +1,253 @@
+{- git-annex command
+ -
+ - Copyright 2017 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module Command.Multicast where
+
+import Command
+import Logs.Multicast
+import Annex.Multicast
+import Annex.WorkTree
+import Annex.Content
+import Annex.UUID
+import Annex.Perms
+import Utility.FileMode
+#ifndef mingw32_HOST_OS
+import Creds
+#endif
+import qualified Limit
+import Types.FileMatcher
+import qualified Git.LsFiles as LsFiles
+import Utility.Hash
+import Utility.Tmp
+import Config
+
+import Data.Char
+import qualified Data.ByteString.Lazy.UTF8 as B8
+import qualified Data.Map as M
+import Control.Concurrent.Async
+
+cmd :: Command
+cmd = command "multicast" SectionCommon "multicast file distribution"
+	paramNothing (seek <$$> optParser)
+
+data MultiCastAction
+	= GenAddress
+	| Send
+	| Receive
+	deriving (Show)
+
+data MultiCastOptions = MultiCastOptions MultiCastAction [CommandParam] [FilePath]
+	deriving (Show)
+
+optParser :: CmdParamsDesc -> Parser MultiCastOptions
+optParser _ = MultiCastOptions 
+	<$> (genaddressp <|> sendp <|> receivep)
+	<*> many uftpopt
+	<*> cmdParams paramPaths
+  where
+	genaddressp = flag' GenAddress
+		( long "gen-address"
+		<> help "generate multicast encryption key and store address in git-annex branch"
+		)
+	sendp = flag' Send
+		( long "send"
+		<> help "multicast files"
+		)
+	receivep = flag' Receive
+		( long "receive"
+		<> help "listen for multicast files and store in repository"
+		)
+	uftpopt = Param <$> strOption
+		( long "uftp-opt"
+		<> short 'U'
+		<> help "passed on to uftp/uftpd"
+		<> metavar "OPTION"
+		)
+
+seek :: MultiCastOptions -> CommandSeek
+seek (MultiCastOptions GenAddress _ _) = commandAction genAddress 
+seek (MultiCastOptions Send ups fs) = commandAction $ send ups fs
+seek (MultiCastOptions Receive ups []) = commandAction $ receive ups
+seek (MultiCastOptions Receive _ _) = giveup "Cannot specify list of files with --receive; this receives whatever files the sender chooses to send."
+
+genAddress :: CommandStart
+genAddress = do
+	showStart "gen-address" ""
+	k <- uftpKey
+	(s, ok) <- case k of
+		KeyContainer s -> liftIO $ genkey (Param s)
+		KeyFile f -> do
+			createAnnexDirectory (takeDirectory f)
+			liftIO $ nukeFile f
+			liftIO $ protectedOutput $ genkey (File f)
+	case (ok, parseFingerprint s) of
+		(False, _) -> giveup $ "uftp_keymgt failed: " ++ s
+		(_, Nothing) -> giveup $ "Failed to find fingerprint in uftp_keymgt output: " ++ s
+		(True, Just fp) -> next $ next $ do
+			recordFingerprint fp =<< getUUID
+			return True
+  where
+ 	-- Annoyingly, the fingerprint is output to stderr.
+	genkey p = processTranscript "uftp_keymgt" ps Nothing
+	  where
+		ps = toCommand $
+			[ Param "-g"
+			, keyparam
+			, p
+			]
+	-- uftp only supports rsa up to 2048 which is on the lower
+	-- limit of secure RSA key sizes. Instead, use an EC curve.
+	-- Except for on Windows XP, secp521r1 is supported on all
+	-- platforms by uftp. DJB thinks it's pretty good compared
+	-- with other NIST curves: "there's one standard NIST curve
+	-- using a nice prime, namely 2521−1  but the sheer size of this
+	-- prime makes it much slower than NIST P-256"
+	-- (http://blog.cr.yp.to/20140323-ecdsa.html)
+	-- Since this key is only used to set up the block encryption,
+	-- its slow speed is ok.
+	keyparam = Param "ec:secp521r1"
+
+parseFingerprint :: String -> Maybe Fingerprint
+parseFingerprint = Fingerprint <$$> lastMaybe . filter isfingerprint . words 
+  where
+	isfingerprint s = 
+		let os = filter (all isHexDigit) (splitc ':' s)
+		in length os == 20
+	
+send :: [CommandParam] -> [FilePath] -> CommandStart
+send ups fs = withTmpFile "send" $ \t h -> do
+	-- Need to be able to send files with the names of git-annex
+	-- keys, and uftp does not allow renaming the files that are sent.
+	-- In a direct mode repository, the annex objects do not have
+	-- the names of keys, and would have to be copied, which is too
+	-- expensive.
+	whenM isDirect $
+		giveup "Sorry, multicast send cannot be done from a direct mode repository."
+	
+	showStart "generating file list" ""
+	fs' <- seekHelper LsFiles.inRepo fs
+	matcher <- Limit.getMatcher
+	let addlist f o = whenM (matcher $ MatchingFile $ FileInfo f f) $
+		liftIO $ hPutStrLn h o
+	forM_ fs' $ \f -> do
+		mk <- lookupFile f
+		case mk of
+			Nothing -> noop
+			Just k -> withObjectLoc k (addlist f) (const noop)
+	liftIO $ hClose h
+	showEndOk
+
+	showStart "sending files" ""
+	showOutput
+	serverkey <- uftpKey
+	u <- getUUID
+	withAuthList $ \authlist -> do
+		let ps =
+			-- Force client authentication.
+			[ Param "-c"
+			, Param "-Y", Param "aes256-cbc"
+			, Param "-h", Param "sha512"
+			-- Picked ecdh_ecdsa for perfect forward secrecy,
+			-- and because a EC key exchange algorithm is
+			-- needed since all keys are EC.
+			, Param "-e", Param "ecdh_ecdsa"
+			, Param "-k", uftpKeyParam serverkey
+			, Param "-U", Param (uftpUID u)
+			-- only allow clients on the authlist
+			, Param "-H", Param ("@"++authlist)
+			-- pass in list of files to send
+			, Param "-i", File t
+			] ++ ups
+		liftIO (boolSystem "uftp" ps) >>= showEndResult
+	stop
+
+receive :: [CommandParam] -> CommandStart
+receive ups = do
+	showStart "receiving multicast files" ""
+	showNote "Will continue to run until stopped by ctrl-c"
+	
+	showOutput
+	clientkey <- uftpKey
+	u <- getUUID
+	(callback, environ, statush) <- liftIO multicastCallbackEnv
+	tmpobjdir <- fromRepo gitAnnexTmpObjectDir
+	createAnnexDirectory tmpobjdir
+	withTmpDirIn tmpobjdir "multicast" $ \tmpdir -> withAuthList $ \authlist -> do
+		abstmpdir <- liftIO $ absPath tmpdir
+		abscallback <- liftIO $ searchPath callback
+		let ps =
+			-- Avoid it running as a daemon.
+			[ Param "-d"
+			-- Require encryption.
+			, Param "-E"
+			, Param "-k", uftpKeyParam clientkey
+			, Param "-U", Param (uftpUID u)
+			-- Only allow servers on the authlist
+			, Param "-S", Param authlist
+			-- Receive files into tmpdir
+			-- (it needs an absolute path)
+			, Param "-D", File abstmpdir
+			-- Run callback after each file received
+			-- (it needs an absolute path)
+			, Param "-s", Param (fromMaybe callback abscallback)
+			] ++ ups
+		runner <- liftIO $ async $
+			hClose statush
+				`after` boolSystemEnv "uftpd" ps (Just environ)
+		mapM_ storeReceived . lines =<< liftIO (hGetContents statush)
+		showEndResult =<< liftIO (wait runner)
+	stop
+
+storeReceived :: FilePath -> Annex ()
+storeReceived f = do
+	case file2key (takeFileName f) of
+		Nothing -> do
+			warning $ "Received a file " ++ f ++ " that is not a git-annex key. Deleting this file."
+			liftIO $ nukeFile f
+		Just k -> void $
+			getViaTmp' AlwaysVerify k $ \dest -> unVerified $
+				liftIO $ catchBoolIO $ do
+					rename f dest
+					return True
+
+-- Under Windows, uftp uses key containers, which are not files on the
+-- filesystem.
+data UftpKey = KeyFile FilePath | KeyContainer String
+
+uftpKeyParam :: UftpKey -> CommandParam
+uftpKeyParam (KeyFile f) = File f
+uftpKeyParam (KeyContainer s) = Param s
+
+uftpKey :: Annex UftpKey
+#ifdef mingw32_HOST_OS
+uftpKey = do
+	u <- getUUID
+	return $ KeyContainer $ "annex-" ++ fromUUID u
+#else
+uftpKey = KeyFile <$> cacheCredsFile "multicast"
+#endif
+
+-- uftp needs a unique UID for each client and server, which 
+-- is a 8 digit hex number in the form "0xnnnnnnnn"
+-- Derive it from the UUID.
+uftpUID :: UUID -> String
+uftpUID u = "0x" ++ (take 8 $ show $ sha2_256 $ B8.fromString (fromUUID u))
+
+withAuthList :: (FilePath -> Annex a) -> Annex a
+withAuthList a = do
+	m <- knownFingerPrints
+	withTmpFile "authlist" $ \t h -> do
+		liftIO $ hPutStr h (genAuthList m)
+		liftIO $ hClose h
+		a t
+
+genAuthList :: M.Map UUID Fingerprint -> String
+genAuthList = unlines . map fmt . M.toList
+  where
+	fmt (u, Fingerprint f) = uftpUID u ++ "|" ++ f
diff --git a/Command/Sync.hs b/Command/Sync.hs
--- a/Command/Sync.hs
+++ b/Command/Sync.hs
@@ -360,7 +360,7 @@
 		] g
 
 pullRemote :: SyncOptions -> [Git.Merge.MergeConfig] -> Remote -> CurrBranch -> CommandStart
-pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o) $ do
+pullRemote o mergeconfig remote branch = stopUnless (pure $ pullOption o && wantpull) $ do
 	showStart "pull" (Remote.name remote)
 	next $ do
 		showOutput
@@ -370,6 +370,7 @@
 	fetch = inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $
 		Git.Command.runBool
 			[Param "fetch", Param $ Remote.name remote]
+	wantpull = remoteAnnexPull (Remote.gitconfig remote)
 
 {- The remote probably has both a master and a synced/master branch.
  - Which to merge from? Well, the master has whatever latest changes
@@ -400,7 +401,7 @@
 	showStart "push" (Remote.name remote)
 	next $ next $ do
 		showOutput
-		ok <- inRepoWithSshOptionsTo (Remote.repo remote) (Remote.gitconfig remote) $
+		ok <- inRepoWithSshOptionsTo (Remote.repo remote) gc $
 			pushBranch remote branch
 		if ok
 			then postpushupdate
@@ -410,7 +411,8 @@
 				return ok
   where
 	needpush
-		| remoteAnnexReadOnly (Remote.gitconfig remote) = return False
+		| remoteAnnexReadOnly gc = return False
+		| not (remoteAnnexPush gc) = return False
 		| otherwise = anyM (newer remote) [syncBranch branch, Annex.Branch.name]
 	-- Do updateInstead emulation for remotes on eg removable drives
 	-- formatted FAT, where the post-update hook won't run.
@@ -426,6 +428,7 @@
 					, return True
 					)
 		| otherwise = return True
+	gc = Remote.gitconfig remote
 
 {- Pushes a regular branch like master to a remote. Also pushes the git-annex
  - branch.
diff --git a/Command/Version.hs b/Command/Version.hs
--- a/Command/Version.hs
+++ b/Command/Version.hs
@@ -10,7 +10,7 @@
 import Command
 import qualified Build.SysConfig as SysConfig
 import Annex.Version
-import BuildFlags
+import BuildInfo
 import Types.Key
 import qualified Types.Backend as B
 import qualified Types.Remote as R
@@ -63,6 +63,7 @@
 showPackageVersion = do
 	vinfo "git-annex version" SysConfig.packageversion
 	vinfo "build flags" $ unwords buildFlags
+	vinfo "dependency versions" $ unwords dependencyVersions
 	vinfo "key/value backends" $ unwords $
 		map (formatKeyVariety . B.backendVariety) Backend.list
 	vinfo "remote types" $ unwords $ map R.typename Remote.remoteTypes
diff --git a/Logs.hs b/Logs.hs
--- a/Logs.hs
+++ b/Logs.hs
@@ -41,6 +41,7 @@
 	, scheduleLog
 	, activityLog
 	, differenceLog
+	, multicastLog
 	]
 
 {- All the ways to get a key from a presence log file -}
@@ -93,6 +94,8 @@
 differenceLog :: FilePath
 differenceLog = "difference.log"
 
+multicastLog :: FilePath
+multicastLog = "multicast.log"
 
 {- The pathname of the location log file for a given key. -}
 locationLogFile :: GitConfig -> Key -> String
diff --git a/Logs/Location.hs b/Logs/Location.hs
--- a/Logs/Location.hs
+++ b/Logs/Location.hs
@@ -86,7 +86,7 @@
 checkDead key = do
 	config <- Annex.getGitConfig
 	ls <- compactLog <$> readLog (locationLogFile config key)
-	return $ all (\l -> status l == InfoDead) ls
+	return $! all (\l -> status l == InfoDead) ls
 
 {- Updates the log to say that a key is dead. 
  - 
@@ -111,17 +111,24 @@
 	}
 
 {- Finds all keys that have location log information.
- - (There may be duplicate keys in the list.) -}
+ - (There may be duplicate keys in the list.)
+ -
+ - Keys that have been marked as dead are not included.
+ -}
 loggedKeys :: Annex [Key]
-loggedKeys = mapMaybe locationLogFileKey <$> Annex.Branch.files
+loggedKeys = loggedKeys' (not <$$> checkDead)
 
+{- Note that sel should be strict, to avoid the filterM building many
+ - thunks. -} 
+loggedKeys' :: (Key -> Annex Bool) -> Annex [Key]
+loggedKeys' sel = filterM sel =<<
+	(mapMaybe locationLogFileKey <$> Annex.Branch.files)
+
 {- Finds all keys that have location log information indicating
  - they are present for the specified repository. -}
 loggedKeysFor :: UUID -> Annex [Key]
-loggedKeysFor u = filterM isthere =<< loggedKeys
+loggedKeysFor u = loggedKeys' isthere
   where
-	{- This should run strictly to avoid the filterM
-	 - building many thunks containing keyLocations data. -}
 	isthere k = do
 		us <- loggedLocations k
 		let !there = u `elem` us
diff --git a/Logs/Multicast.hs b/Logs/Multicast.hs
new file mode 100644
--- /dev/null
+++ b/Logs/Multicast.hs
@@ -0,0 +1,33 @@
+{- git-annex multicast fingerprint log
+ -
+ - Copyright 2017 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Logs.Multicast (
+	Fingerprint(..),
+	recordFingerprint,
+	knownFingerPrints,
+) where
+
+import Data.Time.Clock.POSIX
+
+import Annex.Common
+import qualified Annex.Branch
+import Logs
+import Logs.UUIDBased
+
+import qualified Data.Map as M
+
+newtype Fingerprint = Fingerprint String
+	deriving (Eq, Read, Show)
+
+recordFingerprint :: Fingerprint -> UUID -> Annex ()
+recordFingerprint fp uuid = do
+	ts <- liftIO getPOSIXTime
+	Annex.Branch.change multicastLog $
+		showLog show . changeLog ts uuid fp . parseLog readish
+
+knownFingerPrints :: Annex (M.Map UUID Fingerprint)
+knownFingerPrints = simpleMap . parseLog readish <$> Annex.Branch.get activityLog
diff --git a/Remote/GCrypt.hs b/Remote/GCrypt.hs
--- a/Remote/GCrypt.hs
+++ b/Remote/GCrypt.hs
@@ -31,7 +31,6 @@
 import qualified Git.Config
 import qualified Git.GCrypt
 import qualified Git.Construct
-import qualified Git.Types as Git ()
 import qualified Annex.Branch
 import Config
 import Config.Cost
@@ -176,11 +175,18 @@
 	go Nothing = giveup "Specify gitrepo="
 	go (Just gitrepo) = do
 		(c', _encsetup) <- encryptionSetup c gc
-		inRepo $ Git.Command.run 
-			[ Param "remote", Param "add"
-			, Param remotename
-			, Param $ Git.GCrypt.urlPrefix ++ gitrepo
-			]
+
+		let url = Git.GCrypt.urlPrefix ++ gitrepo
+		rs <- fromRepo Git.remotes
+		case filter (\r -> Git.remoteName r == Just remotename) rs of
+			[] -> inRepo $ Git.Command.run 
+				[ Param "remote", Param "add"
+				, Param remotename
+				, Param url
+				]
+			(r:_)
+				| Git.repoLocation r == url -> noop
+				| otherwise -> error "Another remote with the same name already exists."		
 
 		setGcryptEncryption c' remotename
 
diff --git a/Remote/Git.hs b/Remote/Git.hs
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -248,18 +248,15 @@
 				, return Nothing
 				)
 		case v of
-			Nothing -> do
-				warning $ "Failed to get annex.uuid configuration of repository " ++ Git.repoDescribe r
-				return r
-			Just (Left _) -> do
-				set_ignore "not usable by git-annex" False
-				return r
 			Just (Right r') -> do
 				-- Cache when http remote is not bare for
 				-- optimisation.
 				unless (Git.Config.isBare r') $
 					setremote setRemoteBare False
 				return r'
+			_ -> do
+				set_ignore "not usable by git-annex" False
+				return r
 
 	store = observe $ \r' -> do
 		g <- gitRepo
diff --git a/RemoteDaemon/Common.hs b/RemoteDaemon/Common.hs
--- a/RemoteDaemon/Common.hs
+++ b/RemoteDaemon/Common.hs
@@ -8,7 +8,7 @@
 module RemoteDaemon.Common
 	( liftAnnex
 	, inLocalRepo
-	, checkNewShas
+	, checkShouldFetch
 	, ConnectionStatus(..)
 	, robustConnection
 	) where
@@ -34,6 +34,13 @@
 
 inLocalRepo :: TransportHandle -> (Git.Repo -> IO a) -> IO a
 inLocalRepo (TransportHandle (LocalRepo g) _) a = a g
+
+-- Check if some shas should be fetched from the remote,
+-- and presumably later merged.
+checkShouldFetch :: RemoteGitConfig -> TransportHandle -> [Git.Sha] -> IO Bool
+checkShouldFetch gc transporthandle shas
+	| remoteAnnexPull gc = checkNewShas transporthandle shas
+	| otherwise = return False
 
 -- Check if any of the shas are actally new in the local git repo,
 -- to avoid unnecessary fetching.
diff --git a/RemoteDaemon/Transport/Ssh.hs b/RemoteDaemon/Transport/Ssh.hs
--- a/RemoteDaemon/Transport/Ssh.hs
+++ b/RemoteDaemon/Transport/Ssh.hs
@@ -36,7 +36,7 @@
 	transportUsingCmd' cmd params rr url transporthandle ichan ochan
 
 transportUsingCmd' :: FilePath -> [CommandParam] -> Transport
-transportUsingCmd' cmd params (RemoteRepo r _) url transporthandle ichan ochan =
+transportUsingCmd' cmd params (RemoteRepo r gc) url transporthandle ichan ochan =
 	robustConnection 1 $ do
 		(Just toh, Just fromh, Just errh, pid) <-
 			createProcess (proc cmd (toCommand params))
@@ -74,7 +74,7 @@
 				send (CONNECTED url)
 				handlestdout fromh
 			Just (SshRemote.CHANGED (ChangedRefs shas)) -> do
-				whenM (checkNewShas transporthandle shas) $
+				whenM (checkShouldFetch gc transporthandle shas) $
 					fetch
 				handlestdout fromh
 			-- avoid reconnect on protocol error
diff --git a/RemoteDaemon/Transport/Tor.hs b/RemoteDaemon/Transport/Tor.hs
--- a/RemoteDaemon/Transport/Tor.hs
+++ b/RemoteDaemon/Transport/Tor.hs
@@ -129,7 +129,7 @@
 
 -- Connect to peer's tor hidden service.
 transport :: Transport
-transport (RemoteRepo r _) url@(RemoteURI uri) th ichan ochan =
+transport (RemoteRepo r gc) url@(RemoteURI uri) th ichan ochan =
 	case unformatP2PAddress (show uri) of
 		Nothing -> return ()
 		Just addr -> robustConnection 1 $ do
@@ -168,7 +168,7 @@
 		v <- runNetProto conn P2P.notifyChange
 		case v of
 			Right (Just (ChangedRefs shas)) -> do
-				whenM (checkNewShas th shas) $
+				whenM (checkShouldFetch gc th shas) $
 					fetch
 				handlepeer conn
 			_ -> return ConnectionClosed
diff --git a/Test.hs b/Test.hs
--- a/Test.hs
+++ b/Test.hs
@@ -2110,6 +2110,7 @@
 		-- Make git and git-annex access ssh remotes on the local
 		-- filesystem, without using ssh at all.
 		, ("GIT_SSH_COMMAND", "git-annex test --fakessh --")
+		, ("GIT_ANNEX_USE_GIT_SSH", "1")
 		, ("TESTMODE", show testmode)
 		]
 
diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs
--- a/Types/GitConfig.hs
+++ b/Types/GitConfig.hs
@@ -48,7 +48,7 @@
 	, annexNumCopies :: Maybe NumCopies
 	, annexDiskReserve :: Integer
 	, annexDirect :: Bool
-	, annexBackends :: [String]
+	, annexBackend :: Maybe String
 	, annexQueueSize :: Maybe Int
 	, annexBloomCapacity :: Maybe Int
 	, annexBloomAccuracy :: Maybe Int
@@ -98,7 +98,12 @@
 	, annexDiskReserve = fromMaybe onemegabyte $
 		readSize dataUnits =<< getmaybe (annex "diskreserve")
 	, annexDirect = getbool (annex "direct") False
-	, annexBackends = getwords (annex "backends")
+	, annexBackend = maybe
+		-- annex.backends is the old name of the option, still used
+		-- when annex.backend is not set.
+		(headMaybe $ getwords (annex "backends"))
+		Just
+		(getmaybe (annex "backend"))
 	, annexQueueSize = getmayberead (annex "queuesize")
 	, annexBloomCapacity = getmayberead (annex "bloomcapacity")
 	, annexBloomAccuracy = getmayberead (annex "bloomaccuracy")
@@ -181,6 +186,8 @@
 	, remoteAnnexCostCommand :: Maybe String
 	, remoteAnnexIgnore :: Bool
 	, remoteAnnexSync :: Bool
+	, remoteAnnexPull :: Bool
+	, remoteAnnexPush :: Bool
 	, remoteAnnexReadOnly :: Bool
 	, remoteAnnexVerify :: Bool
 	, remoteAnnexTrustLevel :: Maybe String
@@ -218,6 +225,8 @@
 	, remoteAnnexCostCommand = notempty $ getmaybe "cost-command"
 	, remoteAnnexIgnore = getbool "ignore" False
 	, remoteAnnexSync = getbool "sync" True
+	, remoteAnnexPull = getbool "pull" True
+	, remoteAnnexPush = getbool "push" True
 	, remoteAnnexReadOnly = getbool "readonly" False
 	, remoteAnnexVerify = getbool "verify" True
 	, remoteAnnexTrustLevel = notempty $ getmaybe "trustlevel"
diff --git a/Utility/FileMode.hs b/Utility/FileMode.hs
--- a/Utility/FileMode.hs
+++ b/Utility/FileMode.hs
@@ -177,7 +177,10 @@
 	(\h -> hPutStr h content)
 
 writeFileProtected' :: FilePath -> (Handle -> IO ()) -> IO ()
-writeFileProtected' file writer = withUmask 0o0077 $
+writeFileProtected' file writer = protectedOutput $
 	withFile file WriteMode $ \h -> do
 		void $ tryIO $ modifyFileMode file $ removeModes otherGroupModes
 		writer h
+
+protectedOutput :: IO a -> IO a
+protectedOutput = withUmask 0o0077
diff --git a/doc/git-annex-add.mdwn b/doc/git-annex-add.mdwn
--- a/doc/git-annex-add.mdwn
+++ b/doc/git-annex-add.mdwn
@@ -56,6 +56,11 @@
   Adds multiple files in parallel. This may be faster.
   For example: `-J4`  
 
+* `--update` `-u`
+
+  Like `git add --update`, this does not add new files, but any updates
+  to tracked files will be added to the index.
+
 * `--json`
 
   Enable JSON output. This is intended to be parsed by programs that use
diff --git a/doc/git-annex-calckey.mdwn b/doc/git-annex-calckey.mdwn
--- a/doc/git-annex-calckey.mdwn
+++ b/doc/git-annex-calckey.mdwn
@@ -12,7 +12,7 @@
 to refer to a file. The file is not added to the annex by this command.
 The key is output to stdout.
 
-The backend used is the first listed in the annex.backends configuration
+The backend used is the one from the annex.backend configuration
 setting, which can be overridden by the --backend option.
 For example, to force use of the SHA1 backend:
 
diff --git a/doc/git-annex-dead.mdwn b/doc/git-annex-dead.mdwn
--- a/doc/git-annex-dead.mdwn
+++ b/doc/git-annex-dead.mdwn
@@ -17,8 +17,9 @@
 description, or their UUID. (To undo, use `git-annex semitrust`.)
 
 When a key is specified, indicates that the content of that key has been
-irretrievably lost. This prevents `git annex fsck --all` from complaining
-about it. (To undo, add the key's content back to the repository,
+irretrievably lost. This prevents commands like `git annex fsck --all`
+from complaining about it; `--all` will not operate on the key anymore.
+(To undo, add the key's content back to the repository, 
 by using eg, `git-annex reinject`.)
 
 # SEE ALSO
diff --git a/doc/git-annex-multicast.mdwn b/doc/git-annex-multicast.mdwn
new file mode 100644
--- /dev/null
+++ b/doc/git-annex-multicast.mdwn
@@ -0,0 +1,95 @@
+# NAME
+
+git-annex multicast - multicast file distribution
+
+# SYNOPSIS
+
+git annex multicast [options]
+
+# DESCRIPTION
+
+Multicast allows files to be broadcast to multiple receivers,
+typically on a single local network.
+
+The uftp program is used for multicast.
+<http://uftp-multicast.sourceforge.net/>
+
+# OPTIONS
+
+* `--gen-address`
+
+  Generates a multicast encryption key and stores a corresponding multicast
+  address to the git-annex branch.
+
+* `--send [file]`
+
+  Sends the specified files to any receivers whose multicast addresses
+  are stored in the git-annex branch.
+
+  When no files are specified, all annexed files in the current directory
+  and subdirectories are sent.
+
+  The [[git-annex-matching-options]] can be used to control which files to
+  send. For example:
+
+	git annex multicast send . --not --copies 2
+
+* `--receive`
+
+  Receives files from senders whose multicast addresses
+  are stored in the git-annex brach.
+
+  As each file is received, its filename is displayed. This is the filename
+  that the sender used; the local working tree may use a different name
+  for the file, or not contain a link to the file.
+
+  This command continues running, until it is interrupted by you pressing
+  ctrl-c.
+
+  Note that the configured annex.diskreserve is not honored by this
+  command, because `uftpd` receives the actual files, and can receive
+  any size file.
+
+* `--uftp-opt=option` `-Uoption`
+
+  Pass an option on to the uftp/uftpd command. May be specified multiple
+  times.
+
+  For example, to broadcast at 50 Mbps:
+
+	git annex multicast send -U-R -U50000
+
+# EXAMPLE
+
+Suppose a teacher wants to multicast files to students in a classroom.
+
+This assumes that the teacher and students have cloned a git-annex
+repository, and both can push changes to its git-annex branch,
+or otherwise push changes to each-other.
+
+First, the teacher runs `git annex multicast --gen-address; git annex sync`
+
+Next, students each run `git annex multicast --gen-address; git annex sync`
+
+Once all the students have generated addresses, the teacher runs
+`git annex sync` once more. (Now the students all have received the
+teacher's address, and the teacher has received all the student's addresses.)
+
+Next students each run `git annex multicast --receive`
+
+Finally, once the students are all listening (ahem), teacher runs
+`git annex multicast --send`
+
+# SEE ALSO
+
+[[git-annex]](1)
+
+uftp(1)
+
+uftpd(1)
+
+# AUTHOR
+
+Joey Hess <id@joeyh.name>
+
+Warning: Automatically converted into a man page by mdwn2man. Edit with care.
diff --git a/doc/git-annex-remotedaemon.mdwn b/doc/git-annex-remotedaemon.mdwn
--- a/doc/git-annex-remotedaemon.mdwn
+++ b/doc/git-annex-remotedaemon.mdwn
@@ -29,6 +29,9 @@
 repository. This is only done if you first run `git annex enable-tor`.
 Use `git annex p2p` to configure access to tor-annex remotes.
 
+Note that when `remote.<name>.annex-pull` is set to false, the remotedaemon
+will avoid fetching changes from that remote.
+
 # OPTIONS
 
 * `--foreground`
diff --git a/doc/git-annex-sync.mdwn b/doc/git-annex-sync.mdwn
--- a/doc/git-annex-sync.mdwn
+++ b/doc/git-annex-sync.mdwn
@@ -8,10 +8,7 @@
 
 # DESCRIPTION
 
-Use this command when you want to synchronize the local repository with
-one or more of its remotes. You can specify the remotes (or remote
-groups) to sync with by name; the default if none are specified is to
-sync with all remotes.
+This command synchronizes the local repository with its remotes.
 
 The sync process involves first committing any local changes to files
 that have previously been added to the repository,
@@ -36,6 +33,12 @@
 
 # OPTIONS
 
+* `[remote]`
+
+  By default, all remotes are synced, except for remotes that have
+  `remote.<name>.annex-sync` set to false. By specifying the names
+  of remotes (or remote groups), you can control which ones to sync with.
+
 * `--fast`
 
   Only sync with the remotes with the lowest annex-cost value configured.
@@ -52,11 +55,21 @@
 
 * `--pull`, `--no-pull`
 
-  By default, git pulls from remotes. Use --no-pull to disable.
+  By default, git pulls from remotes. Use --no-pull to disable all pulling.
 
+  When `remote.<name>.annex-pull` or `remote.<name>.annex-sync`
+  are set to false, pulling is disabled for those remotes, and using
+  `--pull` will not enable it.
+
 * `--push`, `--no-push` 
 
-  By default, git pushes to remotes. Use --no-push to disable.
+  By default, git pushes changes to remotes.
+  Use --no-push to disable all pushing.
+  
+  When `remote.<name>.annex-push` or `remote.<name>.annex-sync` are
+  set to false, or `remote.<name>.annex-readonly` is set to true,
+  pushing is disabled for those remotes, and using `--push` will not enable
+  it.
 
 * `--content`, `--no-content`
 
@@ -64,7 +77,7 @@
   The --content option causes the content of files in the work tree
   to also be uploaded and downloaded as necessary.
 
-  The annex.synccontent configuration can be set to true to make content
+  The `annex.synccontent` configuration can be set to true to make content
   be synced by default.
 
   Normally this tries to get each annexed file in the work tree
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -164,6 +164,12 @@
   
   See [[git-annex-undo]](1) for details.
 
+* `multicast`
+
+  Multicast file distribution.
+
+  See [[git-annex-multicast]](1) for details.
+
 * `watch`
 
   Watch for changes and autocommit.
@@ -821,14 +827,17 @@
 
   A unique UUID for this repository (automatically set).
 
-* `annex.backends`
+* `annex.backend`
 
-  Space-separated list of names of the key-value backends to use
-  when adding new files to the repository.
+  Name of the default key-value backend to use when adding new files
+  to the repository.
 
   This is overridden by annex annex.backend configuration in the
-  .gitattributes files.
+  .gitattributes files, and by the --backend option.
 
+  (This used to be named `annex.backends`, and that will still be used
+  if set.)
+
 * `annex.securehashesonly`
 
   Set to true to indicate that the repository should only use
@@ -1150,8 +1159,19 @@
 * `remote.<name>.annex-sync`
 
   If set to `false`, prevents git-annex sync (and the git-annex assistant)
-  from syncing with this remote.
+  from syncing with this remote by default. However, `git annex sync <name>`
+  can still be used to sync with the remote.
 
+* `remote.<name>.annex-pull`
+
+  If set to `false`, prevents git-annex sync (and the git-annex assistant
+  etc) from ever pulling (or fetching) from the remote.
+
+* `remote.<name>.annex-push`
+
+  If set to `false`, prevents git-annex sync (and the git-annex assistant
+  etc) from ever pushing to the remote.
+
 * `remote.<name>.annex-readonly`
 
   If set to `true`, prevents git-annex from making changes to a remote.
@@ -1200,6 +1220,10 @@
   to or from this remote. For example, to force IPv6, and limit
   the bandwidth to 100Kbyte/s, set it to `-6 --bwlimit 100`
 
+  Note that git-annex-shell has a whitelist of allowed rsync options,
+  and others will not be be passed to the remote rsync. So using some
+  options may break the communication between the local and remote rsyncs.
+
 * `remote.<name>.annex-rsync-upload-options`
 
   Options to use when using rsync to upload a file to a remote.
@@ -1417,7 +1441,10 @@
   Handled similarly to the same as described in git(1).
   The one difference is that git-annex will sometimes pass an additional
   "-n" parameter to these, as the first parameter, to prevent ssh from
-  reading from stdin.
+  reading from stdin. Since that can break existing uses of these
+  environment variables that don't expect the extra parameter, you will
+  need to set `GIT_ANNEX_USE_GIT_SSH=1` to make git-annex support
+  these.
 
   Note that setting either of these environment variables prevents
   git-annex from automatically enabling ssh connection caching
diff --git a/git-annex.cabal b/git-annex.cabal
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -1,11 +1,11 @@
 Name: git-annex
-Version: 6.20170321
+Version: 6.20170510
 Cabal-Version: >= 1.8
 License: GPL-3
 Maintainer: Joey Hess <id@joeyh.name>
 Author: Joey Hess
 Stability: Stable
-Copyright: 2010-2016 Joey Hess
+Copyright: 2010-2017 Joey Hess
 License-File: COPYRIGHT
 Homepage: http://git-annex.branchable.com/
 Build-type: Custom
@@ -90,6 +90,7 @@
   doc/git-annex-migrate.mdwn
   doc/git-annex-mirror.mdwn
   doc/git-annex-move.mdwn
+  doc/git-annex-multicast.mdwn
   doc/git-annex-numcopies.mdwn
   doc/git-annex-p2p.mdwn
   doc/git-annex-pre-commit.mdwn
@@ -383,7 +384,7 @@
 
   if (os(windows))
     Build-Depends: Win32, Win32-extras, unix-compat (>= 0.4.1.3), setenv,
-      process (>= 1.3.0.0)
+      process (>= 1.4.2.0)
   else
     Build-Depends: unix
     if impl(ghc <= 7.6.3)
@@ -518,6 +519,7 @@
     Annex.MakeRepo
     Annex.MetaData
     Annex.MetaData.StandardFields
+    Annex.Multicast
     Annex.Notification
     Annex.NumCopies
     Annex.Path
@@ -662,7 +664,7 @@
     Build.Standalone
     Build.TestConfig
     Build.Version
-    BuildFlags
+    BuildInfo
     CmdLine
     CmdLine.Action
     CmdLine.Batch
@@ -732,6 +734,7 @@
     Command.Migrate
     Command.Mirror
     Command.Move
+    Command.Multicast
     Command.NotifyChanges
     Command.NumCopies
     Command.P2P
@@ -857,6 +860,7 @@
     Logs.Location
     Logs.MapLog
     Logs.MetaData
+    Logs.Multicast
     Logs.NumCopies
     Logs.PreferredContent
     Logs.PreferredContent.Raw
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -17,11 +17,10 @@
 packages:
 - '.'
 extra-deps:
-- esqueleto-2.5.1
-- yesod-default-1.2.0
+- aws-0.16
 - bloomfilter-2.0.1.0
-- network-multicast-0.2.0
-- torrent-10000.0.1
+- torrent-10000.1.1
+- yesod-default-1.2.0
 explicit-setup-deps:
   git-annex: true
-resolver: lts-7.18
+resolver: lts-8.6
diff --git a/templates/documentation/about.hamlet b/templates/documentation/about.hamlet
--- a/templates/documentation/about.hamlet
+++ b/templates/documentation/about.hamlet
@@ -32,3 +32,4 @@
       Version: #{packageversion}
       <br>
       Build flags: #{unwords buildFlags}
+      Dependency versions: #{unwords dependencyVersions}
