diff --git a/Annex.hs b/Annex.hs
--- a/Annex.hs
+++ b/Annex.hs
@@ -325,7 +325,11 @@
 	}
 
 {- Adds an adjustment to the Repo data. Adjustments persist across reloads
- - of the repo's config. -}
+ - of the repo's config.
+ -
+ - Note that the action may run more than once, and should avoid eg,
+ - appending the same value to a repo's config when run repeatedly.
+ -}
 adjustGitRepo :: (Git.Repo -> IO Git.Repo) -> Annex ()
 adjustGitRepo a = do
 	changeState $ \s -> s { repoadjustment = \r -> repoadjustment s r >>= a }
diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs
--- a/Annex/AdjustedBranch.hs
+++ b/Annex/AdjustedBranch.hs
@@ -57,7 +57,6 @@
 import Annex.GitOverlay
 import Utility.Tmp.Dir
 import Utility.CopyFile
-import Utility.Directory
 import qualified Database.Keys
 import Config
 
diff --git a/Annex/Locations.hs b/Annex/Locations.hs
--- a/Annex/Locations.hs
+++ b/Annex/Locations.hs
@@ -66,7 +66,6 @@
 	gitAnnexJournalDir',
 	gitAnnexJournalLock,
 	gitAnnexGitQueueLock,
-	gitAnnexPreCommitLock,
 	gitAnnexMergeLock,
 	gitAnnexIndex,
 	gitAnnexIndexStatus,
@@ -468,10 +467,6 @@
  - other git state that should only have one writer at a time. -}
 gitAnnexGitQueueLock :: Git.Repo -> FilePath
 gitAnnexGitQueueLock r = fromRawFilePath $ gitAnnexDir r P.</> "gitqueue.lck"
-
-{- Lock file for the pre-commit hook. -}
-gitAnnexPreCommitLock :: Git.Repo -> FilePath
-gitAnnexPreCommitLock r = fromRawFilePath $ gitAnnexDir r P.</> "precommit.lck"
 
 {- Lock file for direct mode merge. -}
 gitAnnexMergeLock :: Git.Repo -> FilePath
diff --git a/Annex/Ssh.hs b/Annex/Ssh.hs
--- a/Annex/Ssh.hs
+++ b/Annex/Ssh.hs
@@ -37,8 +37,8 @@
 import Types.Concurrency
 import Git.Env
 import Git.Ssh
-#ifndef mingw32_HOST_OS
 import Annex.Perms
+#ifndef mingw32_HOST_OS
 import Annex.LockPool
 #endif
 
diff --git a/Assistant/WebApp/Configurators/WebDAV.hs b/Assistant/WebApp/Configurators/WebDAV.hs
--- a/Assistant/WebApp/Configurators/WebDAV.hs
+++ b/Assistant/WebApp/Configurators/WebDAV.hs
@@ -15,7 +15,7 @@
 import qualified Remote.WebDAV as WebDAV
 import Assistant.WebApp.MakeRemote
 import qualified Remote
-import Types.Remote (RemoteConfig)
+import Types.Remote (RemoteConfig, config)
 import Types.StandardGroups
 import Logs.Remote
 import Git.Types (RemoteName)
@@ -89,16 +89,16 @@
 
 #ifdef WITH_WEBDAV
 makeWebDavRemote :: SpecialRemoteMaker -> RemoteName -> CredPair -> RemoteConfig -> Handler ()
-makeWebDavRemote maker name creds config = 
+makeWebDavRemote maker name creds c = 
 	setupCloudRemote TransferGroup Nothing $
-		maker name WebDAV.remote (Just creds) config
+		maker name WebDAV.remote (Just creds) c
 
 {- Only returns creds previously used for the same hostname. -}
 previouslyUsedWebDAVCreds :: String -> Annex (Maybe CredPair)
 previouslyUsedWebDAVCreds hostname =
 	previouslyUsedCredPair WebDAV.davCreds WebDAV.remote samehost
   where
-	samehost url = case urlHost =<< WebDAV.configUrl url of
+	samehost r = case urlHost =<< WebDAV.configUrl (config r) of
 		Nothing -> False
 		Just h -> h == hostname
 #endif
diff --git a/Backend/Hash.hs b/Backend/Hash.hs
--- a/Backend/Hash.hs
+++ b/Backend/Hash.hs
@@ -157,7 +157,8 @@
 validInExtension :: Word8 -> Bool
 validInExtension c
 	| isAlphaNum (chr (fromIntegral c)) = True
-	| c <= 127 = False -- other ascii, spaces, punctuation, control chars
+	| fromIntegral c == ord '.' = True
+	| c <= 127 = False -- other ascii: spaces, punctuation, control chars
 	| otherwise = True -- utf8 is allowed, also other encodings
 
 {- Upgrade keys that have the \ prefix on their hash due to a bug, or
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,22 @@
+git-annex (8.20200330) upstream; urgency=medium
+
+  * fsck: Fix reversion in 8.20200226 that made it incorrectly warn
+    that hashed keys with an extension should be upgraded.
+  * add --force-small: Fix a bug that, when adding a symbolic link,
+    checked in the content of the file the symlink pointed to.
+    Thanks, Kyle Meyer for the patch.
+  * add --force-small: Fix failure when passed a modified submodule.
+    Thanks, Kyle Meyer for the patch.
+  * When syncing changes back from an adjusted branch to the basis branch,
+    include changes to submodules.
+    Thanks, Kyle Meyer for the patch.
+  * webdav: Made exporttree remotes faster by caching connection to the
+    server.
+  * Fix a minor bug that caused options provided with -c to be passed
+    multiple times to git.
+
+ -- Joey Hess <id@joeyh.name>  Mon, 30 Mar 2020 12:14:50 -0400
+
 git-annex (8.20200309) upstream; urgency=medium
 
   * Fix bug that caused unlocked annexed dotfiles to be added to git by the
diff --git a/CmdLine/GitAnnex/Options.hs b/CmdLine/GitAnnex/Options.hs
--- a/CmdLine/GitAnnex/Options.hs
+++ b/CmdLine/GitAnnex/Options.hs
@@ -93,8 +93,11 @@
   where
 	setnumcopies n = Annex.changeState $ \s -> s { Annex.forcenumcopies = Just $ NumCopies n }
 	setuseragent v = Annex.changeState $ \s -> s { Annex.useragent = Just v }
-	setgitconfig v = Annex.adjustGitRepo $ \r -> Git.Config.store (encodeBS' v) $ 
-		r { gitGlobalOpts = gitGlobalOpts r ++ [Param "-c", Param v] }
+	setgitconfig v = Annex.adjustGitRepo $ \r -> 
+		if Param v `elem` gitGlobalOpts r
+			then return r
+			else Git.Config.store (encodeBS' v) $ 
+				r { gitGlobalOpts = gitGlobalOpts r ++ [Param "-c", Param v] }
 	setdesktopnotify v = Annex.changeState $ \s -> s { Annex.desktopnotify = Annex.desktopnotify s <> v }
 
 {- Parser that accepts all non-option params. -}
diff --git a/Command/Add.hs b/Command/Add.hs
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -107,8 +107,8 @@
 addSmallOverridden file = do
 	showNote "adding content to git repository"
 	let file' = fromRawFilePath file
-	s <- liftIO $ getFileStatus file'
-	if isSymbolicLink s
+	s <- liftIO $ getSymbolicLinkStatus file'
+	if not (isRegularFile s)
 		then addFile file 
 		else do
 			-- Can't use addFile because the clean filter will
diff --git a/Command/PreCommit.hs b/Command/PreCommit.hs
--- a/Command/PreCommit.hs
+++ b/Command/PreCommit.hs
@@ -32,7 +32,7 @@
 	(withParams seek)
 
 seek :: CmdParams -> CommandSeek
-seek ps = lockPreCommitHook $ do
+seek ps = do
 	l <- workTreeItems ps
 	-- fix symlinks to files being committed
 	flip withFilesToBeCommitted l $ \f -> commandAction $
@@ -74,7 +74,3 @@
 	showset v
 		| isSet v = "+"
 		| otherwise = "-"
-
-{- Takes exclusive lock; blocks until available. -}
-lockPreCommitHook :: Annex a -> Annex a
-lockPreCommitHook = withExclusiveLock gitAnnexPreCommitLock
diff --git a/Git/Tree.hs b/Git/Tree.hs
--- a/Git/Tree.hs
+++ b/Git/Tree.hs
@@ -237,8 +237,12 @@
 				let !modified' = modified || slmodified || wasmodified
 				go h modified' (subtree : c) depth intree is'
 			Just CommitObject -> do
-				let ti = TreeCommit (LsTree.file i) (LsTree.mode i) (LsTree.sha i)
-				go h wasmodified (ti:c) depth intree is
+				let ti = TreeItem (LsTree.file i) (LsTree.mode i) (LsTree.sha i)
+				v <- adjusttreeitem ti
+				let commit = tc $ fromMaybe ti v
+				go h wasmodified (commit:c) depth intree is
+				where
+					tc (TreeItem f m s) = TreeCommit f m s
 			_ -> error ("unexpected object type \"" ++ decodeBS (LsTree.typeobj i) ++ "\"")
 		| otherwise = return (c, wasmodified, i:is)
 
diff --git a/Remote/Helper/Hooks.hs b/Remote/Helper/Hooks.hs
--- a/Remote/Helper/Hooks.hs
+++ b/Remote/Helper/Hooks.hs
@@ -17,9 +17,7 @@
 import qualified Annex
 import Annex.LockFile
 import Annex.LockPool
-#ifndef mingw32_HOST_OS
 import Annex.Perms
-#endif
 
 {- Modifies a remote's access functions to first run the
  - annex-start-command hook, and trigger annex-stop-command on shutdown.
diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs
--- a/Remote/WebDAV.hs
+++ b/Remote/WebDAV.hs
@@ -22,6 +22,7 @@
 import Control.Monad.Catch
 import Control.Monad.IO.Class (MonadIO)
 import System.Log.Logger (debugM)
+import Control.Concurrent.STM hiding (check)
 
 import Annex.Common
 import Types.Remote
@@ -64,15 +65,18 @@
 davcredsField = Accepted "davcreds"
 
 gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> RemoteStateHandle -> Annex (Maybe Remote)
-gen r u rc gc rs = new
-	<$> parsedRemoteConfig remote rc
-	<*> remoteCost gc expensiveRemoteCost
+gen r u rc gc rs = do
+	c <- parsedRemoteConfig remote rc
+	new
+		<$> pure c
+		<*> remoteCost gc expensiveRemoteCost
+		<*> mkDavHandleVar c gc u
   where
-	new c cst = Just $ specialRemote c
-		(prepareDAV this $ store chunkconfig)
-		(prepareDAV this $ retrieve chunkconfig)
-		(prepareDAV this $ remove)
-		(prepareDAV this $ checkKey this chunkconfig)
+	new c cst hdl = Just $ specialRemote c
+		(simplyPrepare $ store hdl chunkconfig)
+		(simplyPrepare $ retrieve hdl chunkconfig)
+		(simplyPrepare $ remove hdl)
+		(simplyPrepare $ checkKey hdl this chunkconfig)
 		this
 	  where
 		this = Remote
@@ -90,13 +94,13 @@
 			, checkPresent = checkPresentDummy
 			, checkPresentCheap = False
 			, exportActions = ExportActions
-				{ storeExport = storeExportDav this
-				, retrieveExport = retrieveExportDav this
-				, checkPresentExport = checkPresentExportDav this
-				, removeExport = removeExportDav this
+				{ storeExport = storeExportDav hdl
+				, retrieveExport = retrieveExportDav hdl
+				, checkPresentExport = checkPresentExportDav hdl this
+				, removeExport = removeExportDav hdl
 				, removeExportDirectory = Just $
-					removeExportDirectoryDav this
-				, renameExport = renameExportDav this
+					removeExportDirectoryDav hdl
+				, renameExport = renameExportDav hdl
 				}
 			, importActions = importUnsupported
 			, whereisKey = Nothing
@@ -133,18 +137,20 @@
 	c'' <- setRemoteCredPair encsetup c' gc (davCreds u) creds
 	return (c'', u)
 
-prepareDAV :: Remote -> (Maybe DavHandle -> helper) -> Preparer helper
-prepareDAV = resourcePrepare . const . withDAVHandle
-
-store :: ChunkConfig -> Maybe DavHandle -> Storer
-store _ Nothing = byteStorer $ \_k _b _p -> return False
-store (LegacyChunks chunksize) (Just dav) = fileStorer $ \k f p -> liftIO $
-	withMeteredFile f p $ storeLegacyChunked chunksize k dav
-store _  (Just dav) = httpStorer $ \k reqbody -> liftIO $ goDAV dav $ do
-	let tmp = keyTmpLocation k
-	let dest = keyLocation k
-	storeHelper dav tmp dest reqbody
-	return True
+store :: DavHandleVar -> ChunkConfig -> Storer
+store hv (LegacyChunks chunksize) = fileStorer $ \k f p -> 
+	withDavHandle hv $ \case
+		Nothing -> return False
+		Just dav -> liftIO $
+			withMeteredFile f p $ storeLegacyChunked chunksize k dav
+store hv _ = httpStorer $ \k reqbody -> 
+	withDavHandle hv $ \case
+		Nothing -> return False
+		Just dav -> liftIO $ goDAV dav $ do
+			let tmp = keyTmpLocation k
+			let dest = keyLocation k
+			storeHelper dav tmp dest reqbody
+			return True
 
 storeHelper :: DavHandle -> DavLocation -> DavLocation -> RequestBody -> DAVT IO ()
 storeHelper dav tmp dest reqbody = do
@@ -164,11 +170,14 @@
 retrieveCheap :: Key -> AssociatedFile -> FilePath -> Annex Bool
 retrieveCheap _ _ _ = return False
 
-retrieve :: ChunkConfig -> Maybe DavHandle -> Retriever
-retrieve _ Nothing = giveup "unable to connect"
-retrieve (LegacyChunks _) (Just dav) = retrieveLegacyChunked dav
-retrieve _ (Just dav) = fileRetriever $ \d k p -> liftIO $
-	goDAV dav $ retrieveHelper (keyLocation k) d p
+retrieve :: DavHandleVar -> ChunkConfig -> Retriever
+retrieve hv cc = fileRetriever $ \d k p ->
+	withDavHandle hv $ \case
+		Nothing -> giveup "unable to connect"
+		Just dav -> case cc of
+			LegacyChunks _ -> retrieveLegacyChunked d k p dav
+			_ -> liftIO $
+				goDAV dav $ retrieveHelper (keyLocation k) d p
 
 retrieveHelper :: DavLocation -> FilePath -> MeterUpdate -> DAVT IO ()
 retrieveHelper loc d p = do
@@ -176,12 +185,13 @@
 	inLocation loc $
 		withContentM $ httpBodyRetriever d p
 
-remove :: Maybe DavHandle -> Remover
-remove Nothing _ = return False
-remove (Just dav) k = liftIO $ goDAV dav $
-	-- Delete the key's whole directory, including any
-	-- legacy chunked files, etc, in a single action.
-	removeHelper (keyDir k)
+remove :: DavHandleVar -> Remover
+remove hv k = withDavHandle hv $ \case
+	Nothing -> return False
+	Just dav -> liftIO $ goDAV dav $
+		-- Delete the key's whole directory, including any
+		-- legacy chunked files, etc, in a single action.
+		removeHelper (keyDir k)
 
 removeHelper :: DavLocation -> DAVT IO Bool
 removeHelper d = do
@@ -195,20 +205,21 @@
 				Right False -> return True
 				_ -> return False
 
-checkKey :: Remote -> ChunkConfig -> Maybe DavHandle -> CheckPresent
-checkKey r _ Nothing _ = giveup $ name r ++ " not configured"
-checkKey r chunkconfig (Just dav) k = do
-	showChecking r
-	case chunkconfig of
-		LegacyChunks _ -> checkKeyLegacyChunked dav k
-		_ -> do
-			v <- liftIO $ goDAV dav $
-				existsDAV (keyLocation k)
-			either giveup return v
+checkKey :: DavHandleVar -> Remote -> ChunkConfig -> CheckPresent
+checkKey hv r chunkconfig k = withDavHandle hv $ \case
+	Nothing -> giveup $ name r ++ " not configured"
+	Just dav -> do
+		showChecking r
+		case chunkconfig of
+			LegacyChunks _ -> checkKeyLegacyChunked dav k
+			_ -> do
+				v <- liftIO $ goDAV dav $
+					existsDAV (keyLocation k)
+				either giveup return v
 
-storeExportDav :: Remote -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex Bool
-storeExportDav r f k loc p = case exportLocation loc of
-	Right dest -> withDAVHandle r $ \mh -> runExport mh $ \dav -> do
+storeExportDav :: DavHandleVar -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex Bool
+storeExportDav hdl f k loc p = case exportLocation loc of
+	Right dest -> withDavHandle hdl $ \mh -> runExport mh $ \dav -> do
 		reqbody <- liftIO $ httpBodyStorer f p
 		storeHelper dav (keyTmpLocation k) dest reqbody
 		return True
@@ -216,25 +227,25 @@
 		warning err
 		return False
 
-retrieveExportDav :: Remote -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Bool
-retrieveExportDav r  _k loc d p = case exportLocation loc of
-	Right src -> withDAVHandle r $ \mh -> runExport mh $ \_dav -> do
+retrieveExportDav :: DavHandleVar -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Bool
+retrieveExportDav hdl  _k loc d p = case exportLocation loc of
+	Right src -> withDavHandle hdl $ \mh -> runExport mh $ \_dav -> do
 		retrieveHelper src d p
 		return True
 	Left _err -> return False
 
-checkPresentExportDav :: Remote -> Key -> ExportLocation -> Annex Bool
-checkPresentExportDav r _k loc = case exportLocation loc of
-	Right p -> withDAVHandle r $ \case
+checkPresentExportDav :: DavHandleVar -> Remote -> Key -> ExportLocation -> Annex Bool
+checkPresentExportDav hdl r _k loc = case exportLocation loc of
+	Right p -> withDavHandle hdl $ \case
 		Nothing -> giveup $ name r ++ " not configured"
 		Just h -> liftIO $ do
 			v <- goDAV h $ existsDAV p
 			either giveup return v
 	Left err -> giveup err
 
-removeExportDav :: Remote -> Key -> ExportLocation -> Annex Bool
-removeExportDav r _k loc = case exportLocation loc of
-	Right p -> withDAVHandle r $ \mh -> runExport mh $ \_dav ->
+removeExportDav :: DavHandleVar-> Key -> ExportLocation -> Annex Bool
+removeExportDav hdl _k loc = case exportLocation loc of
+	Right p -> withDavHandle hdl $ \mh -> runExport mh $ \_dav ->
 		removeHelper p
 	-- When the exportLocation is not legal for webdav,
 	-- the content is certianly not stored there, so it's ok for
@@ -243,16 +254,16 @@
 	-- this will be called to make sure it's gone.
 	Left _err -> return True
 
-removeExportDirectoryDav :: Remote -> ExportDirectory -> Annex Bool
-removeExportDirectoryDav r dir = withDAVHandle r $ \mh -> runExport mh $ \_dav -> do
+removeExportDirectoryDav :: DavHandleVar -> ExportDirectory -> Annex Bool
+removeExportDirectoryDav hdl dir = withDavHandle hdl $ \mh -> runExport mh $ \_dav -> do
 	let d = fromRawFilePath $ fromExportDirectory dir
 	debugDav $ "delContent " ++ d
 	safely (inLocation d delContentM)
 		>>= maybe (return False) (const $ return True)
 
-renameExportDav :: Remote -> Key -> ExportLocation -> ExportLocation -> Annex (Maybe Bool)
-renameExportDav r _k src dest = case (exportLocation src, exportLocation dest) of
-	(Right srcl, Right destl) -> withDAVHandle r $ \case
+renameExportDav :: DavHandleVar -> Key -> ExportLocation -> ExportLocation -> Annex (Maybe Bool)
+renameExportDav hdl _k src dest = case (exportLocation src, exportLocation dest) of
+	(Right srcl, Right destl) -> withDavHandle hdl $ \case
 		Just h
 			-- box.com's DAV endpoint has buggy handling of renames,
 			-- so avoid renaming when using it.
@@ -270,8 +281,8 @@
 runExport Nothing _ = return False
 runExport (Just h) a = fromMaybe False <$> liftIO (goDAV h $ safely (a h))
 
-configUrl :: Remote -> Maybe URLString
-configUrl r = fixup <$> getRemoteConfigValue urlField (config r)
+configUrl :: ParsedRemoteConfig -> Maybe URLString
+configUrl c = fixup <$> getRemoteConfigValue urlField c
   where
 	-- box.com DAV url changed
 	fixup = replace "https://www.box.com/dav/" boxComUrl
@@ -407,15 +418,28 @@
 
 data DavHandle = DavHandle DAVContext DavUser DavPass URLString
 
-withDAVHandle :: Remote -> (Maybe DavHandle -> Annex a) -> Annex a
-withDAVHandle r a = do
-	mcreds <- getCreds (config r) (gitconfig r) (uuid r)
-	case (mcreds, configUrl r) of
-		(Just (user, pass), Just baseurl) ->
-			withDAVContext baseurl $ \ctx ->
-				a (Just (DavHandle ctx (toDavUser user) (toDavPass pass) baseurl))
-		_ -> a Nothing
+type DavHandleVar = TVar (Either (Annex (Maybe DavHandle)) (Maybe DavHandle))
 
+{- Prepares a DavHandle for later use. Does not connect to the server or do
+ - anything else expensive. -}
+mkDavHandleVar :: ParsedRemoteConfig -> RemoteGitConfig -> UUID -> Annex DavHandleVar
+mkDavHandleVar c gc u = liftIO $ newTVarIO $ Left $ do
+	mcreds <- getCreds c gc u
+	case (mcreds, configUrl c) of
+		(Just (user, pass), Just baseurl) -> do
+			ctx <- mkDAVContext baseurl
+			let h = DavHandle ctx (toDavUser user) (toDavPass pass) baseurl
+			return (Just h)
+		_ -> return Nothing
+
+withDavHandle :: DavHandleVar -> (Maybe DavHandle -> Annex a) -> Annex a
+withDavHandle hv a = liftIO (readTVarIO hv) >>= \case
+	Right hdl -> a hdl
+	Left mkhdl -> do
+		hdl <- mkhdl
+		liftIO $ atomically $ writeTVar hv (Right hdl)
+		a hdl
+
 goDAV :: DavHandle -> DAVT IO a -> IO a
 goDAV (DavHandle ctx user pass _) a = choke $ run $ prettifyExceptions $ do
 	prepDAV user pass
@@ -464,8 +488,8 @@
 	tmp = addTrailingPathSeparator $ keyTmpLocation k
 	dest = keyLocation k
 
-retrieveLegacyChunked :: DavHandle -> Retriever
-retrieveLegacyChunked dav = fileRetriever $ \d k p -> liftIO $
+retrieveLegacyChunked :: FilePath -> Key -> MeterUpdate -> DavHandle -> Annex ()
+retrieveLegacyChunked d k p dav = liftIO $
 	withStoredFilesLegacyChunked k dav onerr $ \locs ->
 		Legacy.meteredWriteFileChunks p d locs $ \l ->
 			goDAV dav $ do
diff --git a/Test.hs b/Test.hs
--- a/Test.hs
+++ b/Test.hs
@@ -1574,10 +1574,13 @@
   where
 	gpgcmd = Utility.Gpg.mkGpgCmd Nothing
 	testscheme scheme = do
-		gpgtmp <- (</> "gpgtest") <$> absPath tmpdir
+		abstmp <- absPath tmpdir
+		testscheme' scheme abstmp
+	testscheme' scheme abstmp = intmpclonerepo $ whenM (Utility.Path.inPath (Utility.Gpg.unGpgCmd gpgcmd)) $ do
+		-- Use a relative path to avoid too long path to gpg's
+		-- agent socket.
+		gpgtmp <- (</> "gpgtmp") <$> relPathCwdToFile abstmp
 		createDirectoryIfMissing False gpgtmp
-		testscheme' scheme gpgtmp
-	testscheme' scheme gpgtmp = intmpclonerepo $ whenM (Utility.Path.inPath (Utility.Gpg.unGpgCmd gpgcmd)) $ do
 		Utility.Gpg.testTestHarness gpgtmp gpgcmd 
 			@? "test harness self-test failed"
 		Utility.Gpg.testHarness gpgtmp gpgcmd $ do
diff --git a/Utility/Directory.hs b/Utility/Directory.hs
--- a/Utility/Directory.hs
+++ b/Utility/Directory.hs
@@ -14,12 +14,12 @@
 	module Utility.SystemDirectory
 ) where
 
-import System.IO.Error
 import Control.Monad
 import System.FilePath
 import System.PosixCompat.Files
 import Control.Applicative
 import Control.Monad.IO.Class
+import Control.Monad.IfElse
 import System.IO.Unsafe (unsafeInterleaveIO)
 import System.IO.Error
 import Data.Maybe
@@ -27,7 +27,6 @@
 
 #ifndef mingw32_HOST_OS
 import Utility.SafeCommand
-import Control.Monad.IfElse
 #endif
 
 import Utility.SystemDirectory
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
@@ -25,7 +25,6 @@
 modification of their content unless unlocked by [[git-annex-unlock]](1).
 (This is not the case however when a repository is in a filesystem not
 supporting symlinks.)
-To add a file to the annex in unlocked form, `git add` can be used instead.
 
 This command can also be used to add symbolic links, both symlinks to
 annexed content, and other symlinks.
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: 8.20200309
+Version: 8.20200330
 Cabal-Version: >= 1.8
 License: AGPL-3
 Maintainer: Joey Hess <id@joeyh.name>
