Recent changes to this wiki:

Fix path separator bug on Windows that completely broke git-annex since version 7.20190122.
diff --git a/Annex/Link.hs b/Annex/Link.hs
index 40f5ac7ee..db691aea3 100644
--- a/Annex/Link.hs
+++ b/Annex/Link.hs
@@ -290,12 +290,12 @@ isPointerFile f = catchDefaultIO Nothing $ withFile f ReadMode $ \h ->
 isLinkToAnnex :: S.ByteString -> Bool
 isLinkToAnnex s = p `S.isInfixOf` s
 #ifdef mingw32_HOST_OS
-	-- '/' is still used inside pointer files on Windows, not the native
-	-- '\'
+	-- '/' is used inside pointer files on Windows, not the native '\'
 	|| p' `S.isInfixOf` s
 #endif
   where
-	p = toRawFilePath (pathSeparator:objectDir)
+	sp = (pathSeparator:objectDir)
+	p = toRawFilePath sp
 #ifdef mingw32_HOST_OS
-	p' = toRawFilePath ('/':objectDir)
+	p' = toRawFilePath (toInternalGitPath sp)
 #endif
diff --git a/CHANGELOG b/CHANGELOG
index 5738ed4cd..34cb40c7b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -29,6 +29,8 @@ git-annex (7.20190130) UNRELEASED; urgency=medium
     package.
   * init: Fix bug when direct mode needs to be enabled on a crippled
     filesystem, that left the repository in indirect mode.
+  * Fix path separator bug on Windows that completely broke git-annex
+    since version 7.20190122.
 
  -- Joey Hess <id@joeyh.name>  Wed, 30 Jan 2019 12:30:22 -0400
 
diff --git a/doc/bugs/windows_support_totally_bitrotted/comment_2_4a37c0d8abbc64abd383d0d306be31f6._comment b/doc/bugs/windows_support_totally_bitrotted/comment_2_4a37c0d8abbc64abd383d0d306be31f6._comment
new file mode 100644
index 000000000..1dd76847b
--- /dev/null
+++ b/doc/bugs/windows_support_totally_bitrotted/comment_2_4a37c0d8abbc64abd383d0d306be31f6._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2019-02-18T21:03:54Z"
+ content="""
+isLinkToAnnex failing on windows because of mixed path separator issues
+caused by [[!commit 5d98cba923e541be4dc9fd36f5f3c4a37b465bf1]].
+
+Verified it's working in both direct and v7 unlocked with that fixed.
+"""]]

followup
diff --git a/doc/bugs/windows_support_totally_bitrotted/comment_1_e6e629e4b6cbaf51b9b6ed88a6713afd._comment b/doc/bugs/windows_support_totally_bitrotted/comment_1_e6e629e4b6cbaf51b9b6ed88a6713afd._comment
new file mode 100644
index 000000000..21ff27672
--- /dev/null
+++ b/doc/bugs/windows_support_totally_bitrotted/comment_1_e6e629e4b6cbaf51b9b6ed88a6713afd._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-18T15:15:49Z"
+ content="""
+After a windows-appropriate amount of sturm und drang I have windows dev
+environment set up.
+
+Both `catKey` and `fileKey` are returning Nothing in a situation where at
+least `catKey` should find a key.
+"""]]

diff --git a/doc/forum/git_lfs_special_remote_implementation.mdwn b/doc/forum/git_lfs_special_remote_implementation.mdwn
new file mode 100644
index 000000000..b30d016b3
--- /dev/null
+++ b/doc/forum/git_lfs_special_remote_implementation.mdwn
@@ -0,0 +1,9 @@
+I'm thinking about how to implement a special remote that uses git-lfs (no promises to get it done).  I'm not sure if my thoughts are good since I've barely used remotes at all so far!
+
+I'm not sure when annex executes the various remote protocol commands (presumably it issues TRANSFER when pushing to the remote?), but I'm thinking something like this might be appropriate:
+
+The special remote is implemented as a local git-lfs working tree, which has not necessarily been pushed to an upstream remote supporting LFS (like gitlab or github).  Then, "TRANSFER" just arranges to copy the file to a file in the LFS repo -- named after the provided key -- and then `git add`, ensuring LFS is used.  Nothing is pushed from the git repo that implements the special remote, until the user explicitly does so.
+
+Does that sound sane and in keeping with how remotes work?
+
+Why do I want it to work this way?  Because if each TRANSFER is supposed to result in something stored, it seems odd to git push for every file stored to the remote.  As far as I can see, there's no FINISHED hook analogous to PREPARE at which time (after "all the files" are transferred, whatever that would mean) the special remote might choose to git push the LFS repo to gitlab/github?  If there were, I might choose instead to make the local working copy a hidden implementation detail of the special remote, which users of the LFS remote never directly see or use.

fix link to renamed bug
diff --git a/doc/bugs/rsync_fails_with_sync_error__58___syntax_or_usage_error/comment_1_ebf78424a6dc1a886bd9286daedd71ce._comment b/doc/bugs/rsync_fails_with_sync_error__58___syntax_or_usage_error/comment_1_ebf78424a6dc1a886bd9286daedd71ce._comment
index f7dcc1772..63041aba2 100644
--- a/doc/bugs/rsync_fails_with_sync_error__58___syntax_or_usage_error/comment_1_ebf78424a6dc1a886bd9286daedd71ce._comment
+++ b/doc/bugs/rsync_fails_with_sync_error__58___syntax_or_usage_error/comment_1_ebf78424a6dc1a886bd9286daedd71ce._comment
@@ -4,7 +4,7 @@
  date="2018-12-03T17:40:15Z"
  content="""
 This is the same problem as
-<http://git-annex.branchable.com/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./>
+<http://git-annex.branchable.com/bugs/loadlocale_error_with_standalone_build/>
 and I'll bet your git-annex on the remote machine was installed from the
 standalone tarball too?
 """]]

rename obnoxiously and non-portably long bug report filename
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed..mdwn b/doc/bugs/loadlocale_error_with_standalone_build.mdwn
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed..mdwn
rename to doc/bugs/loadlocale_error_with_standalone_build.mdwn
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_1_96a809060ed05100b6f8e5198ec699be._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_1_96a809060ed05100b6f8e5198ec699be._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_1_96a809060ed05100b6f8e5198ec699be._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_1_96a809060ed05100b6f8e5198ec699be._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_2_e1c443ba943a5883ca63fbf05d3e5b41._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_2_e1c443ba943a5883ca63fbf05d3e5b41._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_2_e1c443ba943a5883ca63fbf05d3e5b41._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_2_e1c443ba943a5883ca63fbf05d3e5b41._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_3_acb934b9e7d91aa59f49926e658c4bbe._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_3_acb934b9e7d91aa59f49926e658c4bbe._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_3_acb934b9e7d91aa59f49926e658c4bbe._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_3_acb934b9e7d91aa59f49926e658c4bbe._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_4_39366d601e4200a953fe375c7c96f106._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_4_39366d601e4200a953fe375c7c96f106._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_4_39366d601e4200a953fe375c7c96f106._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_4_39366d601e4200a953fe375c7c96f106._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_5_ce08bf5b13ae0db7fa06196f56a26e8a._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_5_ce08bf5b13ae0db7fa06196f56a26e8a._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_5_ce08bf5b13ae0db7fa06196f56a26e8a._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_5_ce08bf5b13ae0db7fa06196f56a26e8a._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_6_a852f4b40506edf9948c7953b01aa340._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_6_a852f4b40506edf9948c7953b01aa340._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_6_a852f4b40506edf9948c7953b01aa340._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_6_a852f4b40506edf9948c7953b01aa340._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_6_b387cdacf97c476284640195e9fd03c9._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_6_b387cdacf97c476284640195e9fd03c9._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_6_b387cdacf97c476284640195e9fd03c9._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_6_b387cdacf97c476284640195e9fd03c9._comment
diff --git a/doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_8_7820a310f6bf5f6f4c6c58fbfea8c85f._comment b/doc/bugs/loadlocale_error_with_standalone_build/comment_8_7820a310f6bf5f6f4c6c58fbfea8c85f._comment
similarity index 100%
rename from doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed./comment_8_7820a310f6bf5f6f4c6c58fbfea8c85f._comment
rename to doc/bugs/loadlocale_error_with_standalone_build/comment_8_7820a310f6bf5f6f4c6c58fbfea8c85f._comment

bug
diff --git a/doc/bugs/windows_support_totally_bitrotted.mdwn b/doc/bugs/windows_support_totally_bitrotted.mdwn
new file mode 100644
index 000000000..e5216bd53
--- /dev/null
+++ b/doc/bugs/windows_support_totally_bitrotted.mdwn
@@ -0,0 +1,29 @@
+Unfortunately, git-annex is completely broken in Windows right now:
+
+* In a direct mode repository, `git annex add` stages the git-annex link,
+  but then `git annex sync` stages the file content into git, so the file
+  content gets committed to git.
+* In a v7 repository (with or without adjusted branches), `git annex add`
+  stages the git-annex link, but `git status` then shows the file as
+  modified from what was staged, and `git diff --cached` shows a diff
+  from the git-annex link to the file content. And `git commit -a`
+  commits the file content to git, not to git-annex.
+
+Something has bitrotted. Note that git-annex in Windows Subsystem for Linux
+does not have these problems and seemed to work fairly well last time I
+tried it. --[[Joey]]
+
+These seem surprisingly related in some way, given that direct mode is only
+being maintained and should not have changed its behavior at all.
+Perhaps there has been some change that is causing both problems?
+
+In direct mode, `git annex sync --content` is running "git add -f" on each
+file in the work tree, including unmodified files and files that `git annex
+add` has staged annex links for already. That's `stageDirect`, apparently
+it falls through to the `addgit` action, that is supposed to only happen for
+not yet staged files that are dummy symlinks or something (this code is
+untouched since 2013). That suggests that `catKey` or `toInodeCache`
+returned Nothing which should not happen in this case.
+
+So, the next step will be to build git-annex on Windows and instrument
+`stageDirect` to work out which thing it depends on has broken..

diff --git a/doc/forum/No_known_copies_exist_of.mdwn b/doc/forum/No_known_copies_exist_of.mdwn
index f1fb920fc..c4b31a953 100644
--- a/doc/forum/No_known_copies_exist_of.mdwn
+++ b/doc/forum/No_known_copies_exist_of.mdwn
@@ -23,5 +23,9 @@ gives me:
     git-annex: get: 1 failed
 
 
+The objects directory is about the same size as the repo was before I annexed it.
+
 Any ideas on how to troubleshoot further?
+
+
 Thanks!

diff --git a/doc/forum/No_known_copies_exist_of.mdwn b/doc/forum/No_known_copies_exist_of.mdwn
new file mode 100644
index 000000000..f1fb920fc
--- /dev/null
+++ b/doc/forum/No_known_copies_exist_of.mdwn
@@ -0,0 +1,27 @@
+I just created a repo and added some content as described here:
+https://writequit.org/articles/getting-started-with-git-annex.html
+
+Some of the directories I added ended up fine, while others have lot's of broken symlinks.
+
+    git annex fsck file
+gives me :
+
+    ** No known copies exist of file
+    failed
+    git-annex: fsck: 1 failed
+
+
+and
+
+    git annex get file
+
+gives me:
+
+    get file (not available) 
+      No other repository is known to contain the file.
+    failed
+    git-annex: get: 1 failed
+
+
+Any ideas on how to troubleshoot further?
+Thanks!

Added a comment
diff --git a/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_2_3be98f57bcb2ba838046105276d1afd4._comment b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_2_3be98f57bcb2ba838046105276d1afd4._comment
new file mode 100644
index 000000000..d810b72d4
--- /dev/null
+++ b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_2_3be98f57bcb2ba838046105276d1afd4._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="chocolate.camera@ec2ecab153906be21ac5f36652c33786ad0e0b60"
+ nickname="chocolate.camera"
+ avatar="http://cdn.libravatar.org/avatar/4f00dfc3ad590ef7492788b854ceba78"
+ subject="comment 2"
+ date="2019-02-14T12:46:17Z"
+ content="""
+By device, I meant the computer-like device that needs to be able to read the files. Say, an MP3 player for example.
+
+>  used as a directory special remote […] you can't just plug the drive into random computer and read files
+
+Well, that rules it out since, the device being dumb, it does need to be able to find the real files at their standard path and be able to read them as is.
+
+Chunking is not a requirement, though, since the device's software, only supporting FAT32, would not support or expect files that cannot exist in FAT32.
+
+> Next option is to use direct mode.
+
+Direct mode is deprecated.
+"""]]

bug report for checkpresentkey
diff --git a/doc/bugs/checkpresentkey_wrongly_reports_key_absense.mdwn b/doc/bugs/checkpresentkey_wrongly_reports_key_absense.mdwn
new file mode 100644
index 000000000..3978b6ea3
--- /dev/null
+++ b/doc/bugs/checkpresentkey_wrongly_reports_key_absense.mdwn
@@ -0,0 +1,84 @@
+(master_env_v082_py36) 00:55  [ilya-work] $ git annex checkpresentkey MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam 19c05b2b-b155-4ac3-a9e7-fa7c8d67e430
+(master_env_v082_py36) 00:55  [ilya-work] $ echo $?
+0
+(master_env_v082_py36) 00:55  [ilya-work] $ git annex checkpresentkey MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+(master_env_v082_py36) 00:55  [ilya-work] $ echo $?
+1
+
+(master_env_v082_py36) 00:55  [ilya-work] $ git annex whereis --key MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+whereis MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam (1 copy)
+        19c05b2b-b155-4ac3-a9e7-fa7c8d67e430 -- [dnanexus]
+
+  dnanexus: dx://file-FBf5qFj06KBfVygf2pz3pXPQ/1375.cleaned.bam
+ok
+(master_env_v082_py36) 00:57  [ilya-work] $ git annex readpresentkey MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam 19c05b2b-b155-4ac3-a9e7-fa7c8d67e430
+(master_env_v082_py36) 00:57  [ilya-work] $ echo $?
+0
+
+(master_env_v082_py36) 00:57  [ilya-work] $ git annex --verbose --debug checkpresentkey MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam 19c05b2b-b155-4ac3-a9e7-fa7c8d67e430
+[2019-02-14 00:58:32.575463] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","show-ref","git-annex"]
+[2019-02-14 00:58:32.583744] process done ExitSuccess
+[2019-02-14 00:58:32.583809] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","show-ref","--hash","refs/heads/git-annex"]
+[2019-02-14 00:58:32.592023] process done ExitSuccess
+[2019-02-14 00:58:32.592656] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..5dde7d61a8183a50acf98e0847650df20547a0c5","--pretty=%H","-n1"]
+[2019-02-14 00:58:32.608022] process done ExitSuccess
+[2019-02-14 00:58:32.608086] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..aee29af11409cba48bb52ef5893249f5c964180c","--pretty=%H","-n1"]
+[2019-02-14 00:58:32.830193] process done ExitSuccess
+[2019-02-14 00:58:32.830306] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..818a062162bbac26adfd3d30f5ab076d3d0b77e0","--pretty=%H","-n1"]
+[2019-02-14 00:58:32.844831] process done ExitSuccess
+[2019-02-14 00:58:32.84522] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","cat-file","--batch"]
+[2019-02-14 00:58:32.845628] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","cat-file","--batch-check=%(objectname) %(objecttype) %(objectsize)"]
+[2019-02-14 00:58:32.860656] read: git ["config","--null","--list"]
+[2019-02-14 00:58:32.874021] process done ExitSuccess
+[2019-02-14 00:58:32.881706] chat: /data/ilya-work/viral-ngs/is-1808252024-add-benchmarks/tools/git-annex-remotes/git-annex-remote-dnanexus []
+[2019-02-14 00:58:32.924132] git-annex-remote-dnanexus[1] --> VERSION 1
+[2019-02-14 00:58:32.924215] git-annex-remote-dnanexus[1] <-- EXTENSIONS INFO
+[2019-02-14 00:58:32.924288] git-annex-remote-dnanexus[1] --> EXTENSIONS
+[2019-02-14 00:58:32.924322] git-annex-remote-dnanexus[1] <-- PREPARE
+[2019-02-14 00:58:32.924372] git-annex-remote-dnanexus[1] --> PREPARE-SUCCESS
+[2019-02-14 00:58:32.9244] git-annex-remote-dnanexus[1] <-- CHECKPRESENT MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+[2019-02-14 00:58:32.924461] git-annex-remote-dnanexus[1] --> DEBUG CHECKING PRESENCE OF KEY  MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+[2019-02-14 00:58:32.924491] CHECKING PRESENCE OF KEY  MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+[2019-02-14 00:58:32.924512] git-annex-remote-dnanexus[1] --> GETURLS MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam dx://
+[2019-02-14 00:58:32.925707] git-annex-remote-dnanexus[1] <-- VALUE dx://file-FBf5qFj06KBfVygf2pz3pXPQ/1375.cleaned.bam
+[2019-02-14 00:58:32.925755] git-annex-remote-dnanexus[1] <-- VALUE
+[2019-02-14 00:58:33.399433] git-annex-remote-dnanexus[1] --> CHECKPRESENT-SUCCESS MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995.bam
+(master_env_v082_py36) 00:58  [ilya-work] $
+-(master_env_v082_py36) 00:58  [ilya-work] $ git annex --verbose --debug checkpresentkey MD5E-s622177--a8c1aebb39d2093b08ce81a13661a995\
+.bam
+[2019-02-14 01:00:22.921333] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","show-ref","git-annex"]
+[2019-02-14 01:00:22.929444] process done ExitSuccess
+[2019-02-14 01:00:22.929505] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","show-ref","--hash","refs/heads/git-ann\
+ex"]
+[2019-02-14 01:00:22.937493] process done ExitSuccess
+[2019-02-14 01:00:22.938154] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..5dde7d61a8\
+183a50acf98e0847650df20547a0c5","--pretty=%H","-n1"]
+[2019-02-14 01:00:22.952747] process done ExitSuccess
+[2019-02-14 01:00:22.952809] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..aee29af114\
+09cba48bb52ef5893249f5c964180c","--pretty=%H","-n1"]
+[2019-02-14 01:00:23.174093] process done ExitSuccess
+[2019-02-14 01:00:23.174196] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","log","refs/heads/git-annex..818a062162\
+bbac26adfd3d30f5ab076d3d0b77e0","--pretty=%H","-n1"]
+[2019-02-14 01:00:23.189329] process done ExitSuccess
+[2019-02-14 01:00:23.189644] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","cat-file","--batch"]
+[2019-02-14 01:00:23.189965] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","cat-file","--batch-check=%(objectname)\
+ %(objecttype) %(objectsize)"]
+[2019-02-14 01:00:23.204081] read: git ["config","--null","--list"]
+[2019-02-14 01:00:23.21793] process done ExitSuccess
+
+(master_env_v082_py36) 01:00  [ilya-work] $ git  annex version
+git-annex version: 7.20181211-g1cdb7a2
+build flags: Assistant Webapp Pairing S3(multipartupload)(storageclasses) WebDAV Inotify TorrentParser MagicMime Feeds Testsuite
+dependency versions: aws-0.20 bloomfilter-2.0.1.0 cryptonite-0.25 DAV-1.3.2 feed-1.0.0.0 ghc-8.2.2 http-client-0.5.13.1 persistent-sql\
+ite-2.8.1.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.0
+key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_2\
+24 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE\
+2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256\
+ BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL
+remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar hook external
+operating system: linux x86_64
+supported repository versions: 5 7
+upgrade supported from repository versions: 0 1 2 3 4 5 6
+local repository version: 5
+
+Linux ip-172-31-85-193 4.14.97-74.72.amzn1.x86_64 #1 SMP Tue Feb 5 20:59:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

devblog
diff --git a/doc/devblog/day_572__thinking_please_wait.mdwn b/doc/devblog/day_572__thinking_please_wait.mdwn
new file mode 100644
index 000000000..667e3bc21
--- /dev/null
+++ b/doc/devblog/day_572__thinking_please_wait.mdwn
@@ -0,0 +1,17 @@
+Not a lot of coding the past few days, but a lot of skull sweat!
+
+I've been working through the design for the [[todo/import_tree]] feature,
+and I think I finally have a design that I'm happy with. There were some
+very challenging race conditions, and so import tree may only be safely
+able to be implemented for a few remotes; S3 (with versioning enabled),
+directory, maybe webdav and I hope adb. Work on this included finding
+equivilant race conditions in git's update of the worktree, which do
+turn out to exist if you go looking, but have much narrower time
+windows there.
+
+And I'll be running a tutorial for people who want to
+learn about git-annex internals at the code level, to start development or
+be better able to design their own features. That's in Montreal, March
+26th-27th (8 hours total), hosted at McGill university. There may be one or
+two seats left, so if you are interested in attending, please get in touch
+with me by email. Haskell is not a prerequisite.

finally an API happy with
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index b0ab8cedc..72e49f112 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -99,7 +99,9 @@ import tree, and an export then overwrites it with something else.
 One solution would be to only allow one of importtree or exporttree
 to a given remote. This reduces the use cases a lot though, and perhaps
 so far that the import tree feature is not worth building. The adb
-special remote needs both.
+special remote needs both. Also, such a limitation seems like one that
+users might try to work around by initializing two remotes using the same
+data and trying to use one for import and the other for export.
 
 Really fixing this race needs locking or an atomic operation. Locking seems
 unlikely to be a portable enough solution.
@@ -153,8 +155,12 @@ that git-annex did not know the file already had.
 with version-id-marker set to the previous version of the file,
 should list only the previous and current versions; if there's an
 intermediate version then the race occurred and it could roll the change
-back, or otherwise recover the overwritten version.
-(Note that there's a risk of a second race occuring during rollback.)
+back, or otherwise recover the overwritten version. This could be done at
+import time, to detect a previous race, and recover from it; importing
+a tree with the file(s) that were overwritten due to the race, leading to a
+tree import conflict that the user can resolve. This likely generalizes
+to importing a sequence of trees, so each version written to S3 gets
+imported.
 
 ----
 
@@ -194,7 +200,7 @@ importing from the remote.
 Pulling all of the above together, this is an extension to the
 ExportActions api.
 
-	listContents :: Annex [(ExportLocation, ContentIdentifier)]
+	listContents :: Annex (Tree [(ExportLocation, ContentIdentifier)])
 
 	getContentIdentifier :: ExportLocation -> Annex (Maybe ContentIdentifier)
 	
@@ -202,21 +208,43 @@ ExportActions api.
 
 	storeExportWithContentIdentifier :: FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex (Maybe ContentIdentifier)
 
+listContents finds the current set of files that are stored in the remote,
+some of which may have been written by other programs than git-annex,
+along with their content identifiers. It returns a list of those, often in
+a single node tree.
+
+listContents may also find past versions of files that are stored in the
+remote, when it supports storing multiple versions of files. Since it
+returns a tree of lists of files, it can represent anything from a linear
+history to a full branching version control history.
+
 retrieveExportWithContentIdentifier is used when downloading a new file from 
 the remote that listContents found. retrieveExport can't be used because
 it has a Key parameter and the key is not yet known in this case.
 (The callback generating a key will let eg S3 record the S3 version id for
 the key.)
 
+retrieveExportWithContentIdentifier should detect when the file it's
+downloaded may not match the requested content identifier (eg when
+something else wrote to it), and fail in that case.
+
 storeExportWithContentIdentifier is used to get the content identifier
 corresponding to what it stores. It can either get the content
 identifier in reply to the store (as S3 does with versioning), or it can
 store to a temp location, get the content identifier of that, and then
-rename the content into place. When there's a race with a concurrent
-writer, it needs to avoid getting the wrong ContentIdentifier for data
-written by the other writer.
-
-TODO what's needed to work around the other race condition discussed above?
+rename the content into place.
+
+storeExportWithContentIdentifier must avoid overwriting any file that may
+have been written to the remote by something else (unless that version of
+the file can later be recovered by listContents), so it will typically
+need to query for the content identifier before moving the new content
+into place.
+
+storeExportWithContentIdentifier needs to handle the case when there's a
+race with a concurrent writer. It needs to avoid getting the wrong
+ContentIdentifier for data written by the other writer. It may detect such
+races and fail, or it could succeed and overwrite the other file, so long
+as it can later be recovered by listContents.
 
 ----
 

clarified annex.maxextensionlength
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
index 529511026..35854eba0 100644
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -872,7 +872,8 @@ Here are all the supported configuration settings.
   Maximum length of what is considered a filename extension when adding a
   file to a backend that preserves filename extensions. The default length
   is 4, which allows extensions like "jpeg". The dot before the extension
-  is not counted part of its length.
+  is not counted part of its length.  At most two extensions at the end of
+  a filename will be preserved, e.g. .gz or .tar.gz .
 
 * `annex.diskreserve`
 

alternate way to support batch operations by remotes
diff --git a/doc/todo/batch_operations_for_remotes.mdwn b/doc/todo/batch_operations_for_remotes.mdwn
index 6058abb4b..226417cab 100644
--- a/doc/todo/batch_operations_for_remotes.mdwn
+++ b/doc/todo/batch_operations_for_remotes.mdwn
@@ -1 +1 @@
-Current special remote protocol works on one file at a time.  With some remotes, a batch operation can be more efficient, e.g. querying the status of many URLs in one API call.  It would be good if special remotes could optionally implement batch versions of their operations, and these versions were used by batch-mode git-annex commands.
+Current special remote protocol works on one file at a time.  With some remotes, a batch operation can be more efficient, e.g. querying the status of many URLs in one API call.  It would be good if special remotes could optionally implement batch versions of their operations, and these versions were used by batch-mode git-annex commands.  Or maybe, keep the current set of commands but let the remote read multiple requests and then send multiple replies?

added suggestion for batch-mode operations for remotes
diff --git a/doc/todo/batch_operations_for_remotes.mdwn b/doc/todo/batch_operations_for_remotes.mdwn
new file mode 100644
index 000000000..6058abb4b
--- /dev/null
+++ b/doc/todo/batch_operations_for_remotes.mdwn
@@ -0,0 +1 @@
+Current special remote protocol works on one file at a time.  With some remotes, a batch operation can be more efficient, e.g. querying the status of many URLs in one API call.  It would be good if special remotes could optionally implement batch versions of their operations, and these versions were used by batch-mode git-annex commands.

Added a comment
diff --git a/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__/comment_1_8620167702725aa8fb0e42dfe3820520._comment b/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__/comment_1_8620167702725aa8fb0e42dfe3820520._comment
new file mode 100644
index 000000000..eb37df8c0
--- /dev/null
+++ b/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__/comment_1_8620167702725aa8fb0e42dfe3820520._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Ilya_Shlyakhter"
+ avatar="http://cdn.libravatar.org/avatar/1647044369aa7747829c38b9dcc84df0"
+ subject="comment 1"
+ date="2019-02-13T16:48:08Z"
+ content="""
++1 for this.  Maybe, add a config option to pipe a file through a particular program before sending it to a remote?
+"""]]

Added a comment
diff --git a/doc/todo/add_sftp_special_remote/comment_2_1fcefc50041b363a86f596f8d4121611._comment b/doc/todo/add_sftp_special_remote/comment_2_1fcefc50041b363a86f596f8d4121611._comment
new file mode 100644
index 000000000..b60c1effd
--- /dev/null
+++ b/doc/todo/add_sftp_special_remote/comment_2_1fcefc50041b363a86f596f8d4121611._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Ilya_Shlyakhter"
+ avatar="http://cdn.libravatar.org/avatar/1647044369aa7747829c38b9dcc84df0"
+ subject="comment 2"
+ date="2019-02-13T16:46:25Z"
+ content="""
+This can be implemented as an external special remote in Python with a combination of https://github.com/althonos/fs.sshfs and  https://github.com/Lykos153/AnnexRemote .  Implementing using https://www.pyfilesystem.org/ would make a remote that works with a range of filesystems ( https://www.pyfilesystem.org/page/index-of-filesystems/ ).   I don't have time right now but maybe you can try.
+"""]]

Added a comment: Up-vote for sftp special remote
diff --git a/doc/todo/add_sftp_special_remote/comment_1_28c98e879c16f56da2a89ed9d50e4398._comment b/doc/todo/add_sftp_special_remote/comment_1_28c98e879c16f56da2a89ed9d50e4398._comment
new file mode 100644
index 000000000..e7fb49bf5
--- /dev/null
+++ b/doc/todo/add_sftp_special_remote/comment_1_28c98e879c16f56da2a89ed9d50e4398._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="Grothausmann.Roman@343b033fef650d362015d5593ec935121340e082"
+ nickname="Grothausmann.Roman"
+ avatar="http://cdn.libravatar.org/avatar/7883b2b02c31cc2b5e8672cfea4ec9da"
+ subject="Up-vote for sftp special remote"
+ date="2019-02-13T12:11:05Z"
+ content="""
+I'd really welcome an __sftp special remote__ because SFTP it is the only officially allowed way for our institution to exchange data with external collaborators and I had no luck using the [gvfs](https://github.com/grawity/code/blob/master/net/git-annex-remote-gvfs) approach. 
+
+Even without an sftp special remote, it would be great to have some short walkthrough how to use SFTP with git-annex (if SFTP is currently actually possible with e.g. gvfs).
+
+"""]]

diff --git a/doc/forum/enabling_git-annex-diffdriver_for_gitk.mdwn b/doc/forum/enabling_git-annex-diffdriver_for_gitk.mdwn
new file mode 100644
index 000000000..c17676438
--- /dev/null
+++ b/doc/forum/enabling_git-annex-diffdriver_for_gitk.mdwn
@@ -0,0 +1,5 @@
+If `git` with `git-annex-diffdriver` is properly configured and works, is it possible to also get `gitk` to show these diffs?
+
+It seems that `gitk` in general does not apply diff-filters to symlinks and I was not able to find a way to make `gitk` show the same as `git` with `git-annex-diffdriver` does.
+
+Are there some tricks to make that possible somehow?

diff --git a/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__.mdwn b/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__.mdwn
new file mode 100644
index 000000000..cf5f9d1ea
--- /dev/null
+++ b/doc/todo/option_for___40__fast__41___compression_on_special_remotes_like___34__directory__34__.mdwn
@@ -0,0 +1,6 @@
+Despite the possibility to [compress data on a special remote with encryption](http://git-annex.branchable.com/forum/huge_text_files___40__not_binary__41___-_compress/#comment-189a7960c1ed7fb6b8bb2345e3ea8c07)
+there are use-cases in which it would come in handy to have an option for a special remote like "directory" just for compression. 
+
+For example, I use git annex for very large scientific tomographic datasets and files originating from their processing like segmentations, distance maps, skeletons. While compressing the raw data makes little sense, compression e.g. segmentations and skeletons has a huge impact on the effective files size. Since compressing files of a few GBs to TBs is time consuming, I prefer to have an uncompressed version in the working tree (so I do not use file formats that are using compression by default e.g. .nii.gz) but it would be very helpful to have the option to push precious or older versions to a remote that then uses compression. Using encryption for this is a bit of an overkill and takes considerably longer than compressing with e.g. `pbzip`. A compressed file system for this purpose is no option, because the special remote is supposed to live on a restrictive archive server.
+
+Though, I guess, it would be possible to write a special remote wrapper for this, I wonder if this might qualify as an officially supported option to the already existing special remotes like "directory" or "rsync". E.g. in conjunction to `encryption` something like `compression` with possible values like `pbzip`, `bzip`, `pigz` and `gzip`.

diff --git a/doc/forum/short_exlusive_refspec_notation_for_git_annex_unused_.mdwn b/doc/forum/short_exlusive_refspec_notation_for_git_annex_unused_.mdwn
new file mode 100644
index 000000000..a7499cc2e
--- /dev/null
+++ b/doc/forum/short_exlusive_refspec_notation_for_git_annex_unused_.mdwn
@@ -0,0 +1,22 @@
+Hello!
+
+
+In relation to the description of `--used-refspec` in the docs https://git-annex.branchable.com/git-annex-unused/ 
+I was wondering why it is possible to add a positive refspec without the full path of the ref e.g.
+
+`git annex unused --used-refspec  +master`
+
+while for exclusions (negative refspec) it seems to be necessary to specify the full path, e.g.
+
+`git annex unused --used-refspec  '+*:-refs/heads/master'`
+
+or globbing:
+
+`git annex unused --used-refspec  '+*:-*master'`
+
+I guess the negative form of 
+
+> Each + without a glob adds the literal value to the set.  For example, "+HEAD^" adds "HEAD^".
+
+is actually (intentionally) missing. 
+If so, this might be worth noting in the docs.

update
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index e85c90917..b0ab8cedc 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -198,21 +198,23 @@ ExportActions api.
 
 	getContentIdentifier :: ExportLocation -> Annex (Maybe ContentIdentifier)
 	
-	retrieveExportWithContentIdentifier :: ExportLocation -> ContentIdentifier -> FilePath -> MeterUpdate -> Annex Bool
+	retrieveExportWithContentIdentifier :: ExportLocation -> ContentIdentifier -> (FilePath -> Annex Key) -> MeterUpdate -> Annex (Maybe Key)
 
 	storeExportWithContentIdentifier :: FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex (Maybe ContentIdentifier)
 
 retrieveExportWithContentIdentifier is used when downloading a new file from 
 the remote that listContents found. retrieveExport can't be used because
 it has a Key parameter and the key is not yet known in this case.
+(The callback generating a key will let eg S3 record the S3 version id for
+the key.)
 
 storeExportWithContentIdentifier is used to get the content identifier
-corresponding to what was just stored. It can either get the content
+corresponding to what it stores. It can either get the content
 identifier in reply to the store (as S3 does with versioning), or it can
 store to a temp location, get the content identifier of that, and then
 rename the content into place. When there's a race with a concurrent
-writer, it needs to avoid getting the ContentIdentifier for data written by
-the other writer.
+writer, it needs to avoid getting the wrong ContentIdentifier for data
+written by the other writer.
 
 TODO what's needed to work around the other race condition discussed above?
 

added suggestion for creating simpler-to-use trusted export remotes
diff --git a/doc/todo/simpler__44___trusted_export_remotes.mdwn b/doc/todo/simpler__44___trusted_export_remotes.mdwn
new file mode 100644
index 000000000..089721285
--- /dev/null
+++ b/doc/todo/simpler__44___trusted_export_remotes.mdwn
@@ -0,0 +1,3 @@
+Currently, some issues impede the use of export remotes: (1) they're untrusted, except for versioned ones -- and from  those keys cannot be dropped; (2) using them is different than using normal remotes: one can't just copy or move keys to them, one has to first make a tree-ish.   Maybe this could be fixed, as follows.   To copy a key to a external remote, if the key is not yet present in it, put it under .keys/aaa/bbb/keyname on the remote.  That is, take the tree-ish currently on the remote, merge .keys/aaa/bbb/keyname with it, and put that on the remote.   To drop a key from an external remote, take the tree-ish currently on the remote, drop all instances of the key from it, and push the changed tree-ish to the remote.  To git-annex-export add an option --add , which will add the tree-ish to the tree-ish currently on the remote, without losing any keys currently on the remote: take the tree-ish currently on the remote; overlay on it the treeish being exported; for any files that would be overwritten, if no copies of that key would be left, move it to .keys/aaa/bbb/keyname in the tree-ish that is then pushed to the remote.
+
+This way, can always just copy any tree to the remote, without worrying about losing data.

universal batch mode -- clarified difference from current batch modes
diff --git a/doc/todo/universal_batch_mode.mdwn b/doc/todo/universal_batch_mode.mdwn
index a9400ef67..0e85b960a 100644
--- a/doc/todo/universal_batch_mode.mdwn
+++ b/doc/todo/universal_batch_mode.mdwn
@@ -1,3 +1,3 @@
-It would help if there was a universal batch mode, where git-annex command lines are given as lines in an input file, and are executed as a batch.  git-annex could intelligently group, reorder and parallelize the execution, as long as the overall effect of the batch is unchanged.  (I.e. commands affecting different keys/paths could be run in parallel; commands repeatedly doing the same thing could be collapsed; git command batching could span different git-annex commands; etc.)  I find myself implementing something like that in python on top of git-annex, but it would be much more efficient and robust if supported natively.  Maybe, the DataLad project would also find this useful?
+It would help if there was a universal batch mode, where git-annex command lines are given as lines in an input file, and are executed as a batch.  A batch could contain different git-annex commands (as opposed to different parameters for one command). git-annex could intelligently group, reorder and parallelize the execution, as long as the overall effect of the batch is unchanged.  (I.e. commands affecting different keys/paths could be run in parallel; commands repeatedly doing the same thing could be collapsed; git command batching could span different git-annex commands; etc.)  I find myself implementing something like that in python on top of git-annex, but it would be much more efficient and robust if supported natively.  Maybe, the DataLad project would also find this useful?
 
 

universal batch mode
diff --git a/doc/todo/universal_batch_mode.mdwn b/doc/todo/universal_batch_mode.mdwn
new file mode 100644
index 000000000..a9400ef67
--- /dev/null
+++ b/doc/todo/universal_batch_mode.mdwn
@@ -0,0 +1,3 @@
+It would help if there was a universal batch mode, where git-annex command lines are given as lines in an input file, and are executed as a batch.  git-annex could intelligently group, reorder and parallelize the execution, as long as the overall effect of the batch is unchanged.  (I.e. commands affecting different keys/paths could be run in parallel; commands repeatedly doing the same thing could be collapsed; git command batching could span different git-annex commands; etc.)  I find myself implementing something like that in python on top of git-annex, but it would be much more efficient and robust if supported natively.  Maybe, the DataLad project would also find this useful?
+
+

diff --git a/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn b/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn
index 472677913..e68cd8e33 100644
--- a/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn
+++ b/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn
@@ -1,6 +1,6 @@
 ### Please describe the problem.
 
-"git status" returns a list of almost all files in the annex with these messages:
+"git status" returns a list of almost all files in the annex (located on a FAT32 device) with these messages:
 
     git-annex: git status will show music/a.flac to be modified, since content availability has changed and git-annex was unable to update the index. This is only a cosmetic problem affecting git status; git add, git commit, etc won't be affected. To fix the git status display, you can run: git update-index -q --refresh music/a.flac
     ... (a lot of similar messages for +30000 files)

diff --git a/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn b/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn
new file mode 100644
index 000000000..472677913
--- /dev/null
+++ b/doc/bugs/__34__git_status__34___thread_blocked_indefinitely_in_an_MVar_operation.mdwn
@@ -0,0 +1,42 @@
+### Please describe the problem.
+
+"git status" returns a list of almost all files in the annex with these messages:
+
+    git-annex: git status will show music/a.flac to be modified, since content availability has changed and git-annex was unable to update the index. This is only a cosmetic problem affecting git status; git add, git commit, etc won't be affected. To fix the git status display, you can run: git update-index -q --refresh music/a.flac
+    ... (a lot of similar messages for +30000 files)
+
+and then a new list of messages:
+
+    ...
+    git-annex: thread blocked indefinitely in an MVar operation
+    error: external filter 'git-annex smudge --clean %f' failed 1
+    error: external filter 'git-annex smudge --clean %f' failed
+    git-annex: thread blocked indefinitely in an MVar operation
+    error: external filter 'git-annex smudge --clean %f' failed 1
+    error: external filter 'git-annex smudge --clean %f' failed
+    ...
+    refresh index:  81% (30495/37394)
+
+
+
+### What steps will reproduce the problem?
+
+
+### What version of git-annex are you using? On what operating system?
+
+debian sid, git-annex version: 7.20190129
+
+
+### 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/daemon.log
+
+
+# End of transcript or log.
+"""]]
+
+### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
+
+

starting api design
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index 09122d146..e85c90917 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -11,7 +11,7 @@ that has the modifications in it.
 Updating the working copy is then done by merging the import treeish.
 This way, conflicts will be detected and handled as normal by git.
 
-----
+## content identifiers
 
 The remote is responsible for collecting a list of
 files currently in it, along with some content identifier. That data is
@@ -53,7 +53,28 @@ the same remote? In that case, perhaps different trees would be imported,
 and merged into master. So the two repositories then have differing
 masters, which can be reconciled in merge as usual.
 
-----
+Since exporttree remotes don't have content identifier information yet, it
+needs to be collected the first time import tree is used. (Or import
+everything, but that is probably too expensive). Any modifications made to
+exported files before the first import tree would not be noticed. Seems
+acceptible as long as this only affects exporttree remotes created before
+this feature was added.
+
+What if repo A is being used to import tree from R for a while, and the
+user gets used to editing files on R and importing them. Then they stop
+using A and switch to clone B. It would not have the content identifier
+information that A did. It seems that in this case, B needs to re-download
+everything, to build up the map of content identifiers.
+(Anything could have changed since the last time A imported).
+That seems too expensive!
+
+Would storing content identifiers in the git-annex branch be too
+expensive? Probably not.. For S3 with versioning a content identifier is
+already stored. When the content identifier is (mtime, size, inode),
+that's a small amount of data. The maximum size of a content identifier
+could be limited to the size of a typical hash, and if a remote for some
+reason gets something larger, it could simply hash it to generate
+the content identifier.
 
 ## race conditions TODO
 
@@ -152,25 +173,6 @@ Since this is acceptable in git, I suppose we can accept it here too..
 
 ----
 
-Since exporttree remotes don't have content identifier information yet, it
-needs to be collected the first time import tree is used. (Or import
-everything, but that is probably too expensive). Any modifications made to
-exported files before the first import tree would not be noticed. Seems
-acceptible as long as this only affects exporttree remotes created before
-this feature was added.
-
-What if repo A is being used to import tree from R for a while, and the
-user gets used to editing files on R and importing them. Then they stop
-using A and switch to clone B. It would not have the content identifier
-information that A did (unless it's stored in git-annex branch rather than
-locally). It seems that in this case, B needs to re-download everything,
-since anything could have changed since the last time A imported.
-That seems too expensive!
-
-Would storing content identifiers in the git-annex branch be too expensive?
-
-----
-
 If multiple repos can access the remote at the same time, then there's a
 potential problem when one is exporting a new tree, and the other one is
 importing from the remote.
@@ -187,6 +189,33 @@ importing from the remote.
 > to be on the remote. (May need to reword that prompt.)
 > --[[Joey]]
 
+## api design
+
+Pulling all of the above together, this is an extension to the
+ExportActions api.
+
+	listContents :: Annex [(ExportLocation, ContentIdentifier)]
+
+	getContentIdentifier :: ExportLocation -> Annex (Maybe ContentIdentifier)
+	
+	retrieveExportWithContentIdentifier :: ExportLocation -> ContentIdentifier -> FilePath -> MeterUpdate -> Annex Bool
+
+	storeExportWithContentIdentifier :: FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex (Maybe ContentIdentifier)
+
+retrieveExportWithContentIdentifier is used when downloading a new file from 
+the remote that listContents found. retrieveExport can't be used because
+it has a Key parameter and the key is not yet known in this case.
+
+storeExportWithContentIdentifier is used to get the content identifier
+corresponding to what was just stored. It can either get the content
+identifier in reply to the store (as S3 does with versioning), or it can
+store to a temp location, get the content identifier of that, and then
+rename the content into place. When there's a race with a concurrent
+writer, it needs to avoid getting the ContentIdentifier for data written by
+the other writer.
+
+TODO what's needed to work around the other race condition discussed above?
+
 ----
 
 See also, [[adb_special_remote]]

simplify
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index 01a39a3fd..09122d146 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -1,11 +1,10 @@
-When `git annex export treeish` is used to export to a remote, and the
-remote allows files to somehow be edited on it, then there ought to be a
-way to import the changes back from the remote into the git repository.
+When `git annex export treeish --to remote` is used to export to a remote,
+and the remote allows files to somehow be edited on it, then there ought
+to be a way to import the changes back from the remote into the git repository.
+The command could be `git annex import --from remote`
 
-The command could be `git annex import treeish` or something like that.
-
-It would ask the special remote to list changed/new files, and deleted
-files. Download the changed/new files and inject into the annex. 
+It would find changed/new/deleted files on the remote.
+Download the changed/new files and inject into the annex. 
 Generate a new treeish, with parent the treeish that was exported,
 that has the modifications in it.
 
@@ -14,67 +13,13 @@ This way, conflicts will be detected and handled as normal by git.
 
 ----
 
-The remote interface could have a new method, to list the changed/new and
-deleted files. It will be up to remotes to implement that if they can
-support importing.
-
-One way for a remote to do it, assuming it has mtimes, is to export
-files to the remote with their mtime set to the date of the treeish
-being exported (when the treeish is a commit, which has dates, and not
-a raw tree). Then the remote can simply enumerate all files,
-with their mtimes, and look for files that have mtimes
-newer than the last exported treeish's date.
-
-> But: If files on the remote are being changed at around the time
-> of the export, they could have older mtimes than the exported treeish's
-> date, and so be missed.
-> 
-> Also, a rename that swaps two files would be missed if mtimes
-> are only compared to the treeish's date.
-
-A perhaps better way is for the remote to keep track of the mtime,
-size, etc of all exported files, and use that state to find changes.
-Where to store that data?
-
-The data could be stored in a file/files on the remote, or perhaps
-the remote has a way to store some arbitrary metadata about a file
-that could be used.
-
-It could be stored in git-annex branch per-remote state. However,
-that state is per-key, not per-file. The export database could be
-used to convert a ExportLocation to a Key, which could be used
-to access the per-remote state. Querying the database for each file
-in the export could be a bottleneck without the right interface.
-
-If only one repository will ever access the remote, it could be stored
-in eg a local database. But access from only one repository is a 
-hard invariant to guarantee.
-
-Would local storage pose a problem when multiple repositories import from
-the same remote? In that case, perhaps different trees would be imported,
-and merged into master. So the two repositories then have differing
-masters, which can be reconciled as usual. It would mean extra downloads
-of content from the remote, since each import would download its own copy.
-Perhaps this is acceptable?
-
-This feels like it's reimplementing the git index, on a per-remote basis.
-So perhaps this is not the right interface.
-
-----
-
-Alternate interface: The remote is responsible for collecting a list of
+The remote is responsible for collecting a list of
 files currently in it, along with some content identifier. That data is
-sent to git-annex. git-annex keep track of which content identifier(s) map
+sent to git-annex. git-annex keeps track of which content identifier(s) map
 to which keys, and uses the information to determine when a file on the
 remote has changed or is new.
 
-This way, each special remote doesn't have to reimplement the equivilant of
-the git index, or comparing lists of files, it only needs a way to list
-files, and a good content identifier.
-
-This also simplifies implementation in git-annex, because it does not
-even need to look for changed/new/deleted files compared with the
-old tree. Instead, it can simply build git tree objects as the file list
+git-annex can simply build git tree objects as the file list
 comes in, looking up the key corresponding to each content identifier
 (or downloading the content from the remote and adding it to the annex
 when there's no corresponding key yet). It might be possible to avoid
@@ -87,22 +32,10 @@ A good content identifier needs to:
 * Be stable, so when a file has not changed, the content identifier
   remains the same.
 * Change when a file is modified.
-* Be reasonably unique, but not necessarily fully unique.  
-  For example, if the mtime of a file is used as the content identifier, then
-  a rename that swaps two files would be noticed, except for in the
-  unusual case where they have the same mtime. If a new file
-  is added with the same mtime as some other file in the tree though,
-  git-annex will see that the filename is new, and so can still import it,
-  even though it's seen that content identifier before. Of course, that might
-  result in unncessary downloads (eg of a renamed file), so a more unique
-  content identifer would be better.
-
-A (size, mtime, inode) tuple is as good a content identifier as git uses in
-its index. That or a hash of the content would be ideal. 
-
-Do remotes need to tell git-annex about the properties of content
-identifiers they use, or does git-annex assume a minimum bar, and pay the
-price with some unncessary transfers of renamed files etc?
+* Be as unique as possible, but not necessarily fully unique.  
+  A hash of the content would be ideal.
+  A (size, mtime, inode) tuple is as good a content identifier as git uses in
+  its index.
 
 git-annex will need a way to get the content identifiers of files
 that it stores on the remote when exporting a tree to it, so it can later
@@ -110,6 +43,18 @@ know if those files have changed.
 
 ----
 
+The content identifier needs to be stored somehow for later use.
+
+It would be good to store the content identifiers only locally, if
+possible.
+
+Would local storage pose a problem when multiple repositories import from
+the same remote? In that case, perhaps different trees would be imported,
+and merged into master. So the two repositories then have differing
+masters, which can be reconciled in merge as usual.
+
+----
+
 ## race conditions TODO
 
 A file could be modified on the remote while

thoughts
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index 76fabae1b..01a39a3fd 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -112,14 +112,20 @@ know if those files have changed.
 
 ## race conditions TODO
 
-There's a race here, since a file could be modified on the remote while
+A file could be modified on the remote while
 it's being exported, and if the remote then uses the mtime of the modified
 file in the content identifier, the modification would never be noticed by
 imports. 
 
 To fix this race, we need an atomic move operation on the remote. Upload
 the file to a temp file, then get its content identifier, and then move it
-from the temp file to its final location.
+from the temp file to its final location. Alternatively, upload a file and
+get the content identifier atomically, which eg S3 with versioning enabled
+provides. It would make sense to have the storeExport operation always return
+a content identifier and document that it needs to get it atomically by
+either using a temp file or something specific to the remote.
+
+----
 
 There's also a race where a file gets changed on the remote after an
 import tree, and an export then overwrites it with something else.
@@ -129,44 +135,76 @@ to a given remote. This reduces the use cases a lot though, and perhaps
 so far that the import tree feature is not worth building. The adb
 special remote needs both.
 
-Is this race really a significant problem? One way to look at it is
-analagous to a git pull overwriting a locally modified file, which should
-not happen. Git may in some condition not notice the modification before
-overwrite, but if so that would be considered a surprising bug; it's
-certianly *possible* for git pull to avoid such a race whether or not it
-currently does. But another way to look at it is the remote has two
-writers, one of which is git annex exporttree and the other something
-else, and it's up to the user to avoid them conflicting.
-
 Really fixing this race needs locking or an atomic operation. Locking seems
 unlikely to be a portable enough solution.
 
-An atomic move could at least narrow the race significantly, eg:
+An atomic rename operation could at least narrow the race significantly, eg:
 
-1. upload new version of $file to $tmp1
-2. atomic move current $file to $tmp2
-3. Get content identifier of $tmp2, check if it's what was expected to
+1. get content identifier of $file, check if it's what was expected else
+   abort (optional but would catch most problems)
+2. upload new version of $file to $tmp1
+3. rename current $file to $tmp2
+4. Get content identifier of $tmp2, check if it's what was expected to
    be. If not, $file was modified after the last import tree, and that
    conflict has to be resolved. Otherwise, delete $tmp2
-4. atomic move $tmp1 to $file
+5. rename $tmp1 to $file
+
+That leaves a race if the file gets overwritten after it's moved out
+of the way. If the rename refuses to overwrite existing files, that race
+would be detected by it failing. renameat(2) with `RENAME_NOREPLACE` can do that, 
+but probably many special remote interfaces don't provide a way to do that.
+
+S3 lacks a rename operation, can only copy and then delete. Which is not
+good enough; it risks the file being replaced with new content before
+the delete and the new content being deleted.
+
+Is this race really a significant problem? One way to look at it is
+analagous to a git merge overwriting a locally modified file.
+Git can certianly use similar techniques to entirely detect and recover
+from such races (but not the similar race described in the next section). 
+But, git does not actually do that! I modified git's
+merge.c to sleep for 10 seconds after `refresh_index()`, and verified
+that changes made to the work tree in that window were silently overwritten
+by git merge. In git's case, the race window is normally quite narrow
+and this is very unlikely to happen (the similar race described in the next
+section is more likely). 
+
+If git-annex could get the race window similarly small out would perhaps be
+ok. Eg:
+
+1. upload new version of $file to $tmp
+2. get content identifier of $file, check if it's what was expected else
+   abort
+3. rename (or copy and delete) $tmp to $file
+
+The race window between #2 and #3 could be quite narrow for some remotes.
+But S3, lacking a rename, does a copy that can be very slow for large files.
+
+S3, with versioning, could detect the race after the fact, by listing
+the versions of the file, and checking if any of the versions is one
+that git-annex did not know the file already had.
+[Using this api](https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETVersion.html),
+with version-id-marker set to the previous version of the file,
+should list only the previous and current versions; if there's an
+intermediate version then the race occurred and it could roll the change
+back, or otherwise recover the overwritten version.
+(Note that there's a risk of a second race occuring during rollback.)
+
+----
 
 A remaining race is that, if the file is open for write at the same
 time it's renamed, the write might happen after the content identifer
 is checked, and then whatever is written to it will be lost. 
 
 But: Git worktree update has the same race condition. Verified with
-this perl oneliner, run in a worktree, followed by a git pull. The lines
-that it appended to the file got lost:
+this perl oneliner, run in a worktree and a second later 
+followed by a git pull. The lines that it appended to the 
+file got lost:
 
-	perl -e 'open (OUT, ">>foo") || die "$!"; while (<>) { print OUT $_ }'
+	perl -e 'open (OUT, ">>foo") || die "$!"; sleep(10); while (<>) { print OUT $_ }'
 
 Since this is acceptable in git, I suppose we can accept it here too..
 
-Another remaining race is if the file gets recreated after it's moved out
-of the way. If the atomic move refuses to overwrite existing files, that race
-would be detected by it failing. renameat(2) with `RENAME_NOREPLACE` can do that, 
-but probably many special remote interfaces don't provide a way to do that.
-
 ----
 
 Since exporttree remotes don't have content identifier information yet, it

Added a comment: youtube feeds are limited
diff --git a/doc/bugs/does_not_handle_youtube_playlists/comment_1_3521c057eef592458704f3616ffc68a2._comment b/doc/bugs/does_not_handle_youtube_playlists/comment_1_3521c057eef592458704f3616ffc68a2._comment
new file mode 100644
index 000000000..36d32ad88
--- /dev/null
+++ b/doc/bugs/does_not_handle_youtube_playlists/comment_1_3521c057eef592458704f3616ffc68a2._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="np.gan@f5b13d2a374edae9a4c8b2f76022d8835781b9a5"
+ nickname="np.gan"
+ avatar="http://cdn.libravatar.org/avatar/a9b2109945678289ae279aea36904c84"
+ subject="youtube feeds are limited"
+ date="2019-02-10T13:09:54Z"
+ content="""
+RSS feeds corresponding to playlists are limited to 15 results.
+
+Since youtube-dl can parse and show the playlist contents as JSON it would be awesome for git-annex importfeed to support youtube-dl.
+
+Ref: https://superuser.com/a/1359132
+"""]]

thought
diff --git a/doc/todo/import_tree.mdwn b/doc/todo/import_tree.mdwn
index dfa816b9c..76fabae1b 100644
--- a/doc/todo/import_tree.mdwn
+++ b/doc/todo/import_tree.mdwn
@@ -129,6 +129,15 @@ to a given remote. This reduces the use cases a lot though, and perhaps
 so far that the import tree feature is not worth building. The adb
 special remote needs both.
 
+Is this race really a significant problem? One way to look at it is
+analagous to a git pull overwriting a locally modified file, which should
+not happen. Git may in some condition not notice the modification before
+overwrite, but if so that would be considered a surprising bug; it's
+certianly *possible* for git pull to avoid such a race whether or not it
+currently does. But another way to look at it is the remote has two
+writers, one of which is git annex exporttree and the other something
+else, and it's up to the user to avoid them conflicting.
+
 Really fixing this race needs locking or an atomic operation. Locking seems
 unlikely to be a portable enough solution.
 

update
diff --git a/doc/license.mdwn b/doc/license.mdwn
index 45e33714a..0de299db2 100644
--- a/doc/license.mdwn
+++ b/doc/license.mdwn
@@ -1,14 +1,6 @@
-git-annex is Free Software.
-
-The majority of git-annex is licensed under the [[GPL]], version 3 or
+git-annex is Free Software, licensed as a whole under the [[AGPL]], version 3 or
 higher.
 
-The git-annex webapp is licensed under the [[AGPL]], version 3 or higher.
-Note that builds of git-annex that include the webapp may be licensed
-under the AGPL as a whole. git-annex built without the webapp does
-not include this code, so remains GPLed.
-
-git-annex contains a variety of other code, artwork, etc copyright by
-others, under a variety of licences, including the BSD,
-MIT, and Apache 2.0 licenses. For details, see
-[this file](http://source.git-annex.branchable.com/?p=source.git;a=blob_plain;f=debian/copyright;hb=HEAD).
+Parts of git-annex are licensed under the GPL, BSD, and other licenses.
+For details, see
+[this file](http://source.git-annex.branchable.com/?p=source.git;a=blob_plain;f=COPYRIGHT;hb=HEAD).

add kyle
diff --git a/doc/users/datalad.mdwn b/doc/users/datalad.mdwn
index 4d33426a2..9ab203776 100644
--- a/doc/users/datalad.mdwn
+++ b/doc/users/datalad.mdwn
@@ -2,22 +2,22 @@ TODOs for DataLad
 =================
 
 [[!inline pages="todo/* and !todo/done and !link(todo/done) and
-(author(yoh) or author(mih) or author(ben) or author(yarikoptic))" sort=mtime feeds=no actions=yes archive=yes show=0]]
+(author(yoh) or author(mih) or author(ben) or author(yarikoptic) or author(kyle))" sort=mtime feeds=no actions=yes archive=yes show=0]]
 
 Done
 ----
 
 [[!inline pages="todo/* and !todo/done and link(todo/done) and
-(author(yoh) or author(mih) or author(ben) or author(yarikoptic))" feeds=no actions=yes archive=yes show=0]]
+(author(yoh) or author(mih) or author(ben) or author(yarikoptic) or author(kyle))" feeds=no actions=yes archive=yes show=0]]
 
 My bugs
 =======
 
 [[!inline pages="bugs/* and !bugs/done and !link(bugs/done) and
-(author(yoh) or author(mih) or author(ben) or author(yarikoptic))" sort=mtime feeds=no actions=yes archive=yes show=0  template=buglist]]
+(author(yoh) or author(mih) or author(ben) or author(yarikoptic) or author(kyle))" sort=mtime feeds=no actions=yes archive=yes show=0  template=buglist]]
 
 Fixed
 -----
 
 [[!inline pages="bugs/* and !bugs/done and link(bugs/done) and
-(author(yoh) or author(mih) or author(ben) or author(yarikoptic))" feeds=no actions=yes archive=yes show=0  template=buglist]]
+(author(yoh) or author(mih) or author(ben) or author(yarikoptic) or author(kyle))" feeds=no actions=yes archive=yes show=0  template=buglist]]

Added a comment
diff --git a/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_1_ee72caf2aeab4a8a2818f87653dc1cd2._comment b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_1_ee72caf2aeab4a8a2818f87653dc1cd2._comment
new file mode 100644
index 000000000..2052cf958
--- /dev/null
+++ b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__/comment_1_ee72caf2aeab4a8a2818f87653dc1cd2._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="guzik.sergey@9391b6c15e4938a539e36fbe5bab71df07111d2e"
+ nickname="guzik.sergey"
+ avatar="http://cdn.libravatar.org/avatar/69073e903eefa0bc2755123b26441a2f"
+ subject="comment 1"
+ date="2019-02-08T11:00:14Z"
+ content="""
+I would say that FAT32 devices are perfectly valid, but only if used as a [directory special remote](https://git-annex.branchable.com/special_remotes/directory/) with chunking enabled. This way you can transfer files of any size (given there is enough space of course) and keep all previous versions. The downside is that you can't just plug the drive into random computer and read files, git-annex is required, also it is important to remember that special remotes do not store metadata about files so you need your regular repository synced to a computer where you want to read special remote.
+
+Next option is to use [direct mode](https://git-annex.branchable.com/direct_mode/), this way files will be readable on any computer even without git-annex, but file size will be limited to 4Gb and only latest version of your files will be available. Note that you still can drop files from direct mode repository and these files will be replaced with placeholders, so you are not forced to copy big files to a repository with limited file size.
+
+Actually it is possible (and I think it is a valid use case) to use both \"directory special remote\" and \"direct mode repository\" on the same drive.
+
+If I was forced to use FAT32, I would use it this way: create directory special remote with chunking to store files, create bare repository just for metadata. This way the drive will store all versions of files, any size, even if other repositories disappear this will be a complete copy.
+"""]]

a common weird experience with git-annex
diff --git a/doc/forum/moving_annex_across_filesystems.mdwn b/doc/forum/moving_annex_across_filesystems.mdwn
new file mode 100644
index 000000000..28a6852f7
--- /dev/null
+++ b/doc/forum/moving_annex_across_filesystems.mdwn
@@ -0,0 +1,26 @@
+git-annex has this peculiar thing that blobs stored in `.git/annex/objects` are readonly, even for the current user. Naturally, git-annex itself bypasses those restrictions in some way to create/remove/move those files from time to time, but for other tools, this can actually be a problem. For example, if a user wants to completely remove a dead repository, they will naively try to just delete it (say) in their file manager, which will fail with permission errors.
+
+Similarly, moving a repository across filesystem boundaries will create problems, as this effectively means copying the files and removing the original copy. Normally, this should be a safe thing to do, but git-annex forbids it.
+
+The workaround I have found is to do exactly that: copy the files and then remove the original. I used the "tar + pv" hack to get a progress bar:
+
+    tar cf - Photos | pv -s 406G | ( cd /srv ; tar xf - )
+
+Just for good measure, I then also run fsck on the repository copy:
+
+    git -C /srv/Photos annex fsck --quiet --incremental
+
+If that succeeds, I then remove the original repository:
+
+    sudo rm -r Photos
+
+Notice how it's simpler for me to do `sudo rm` than the alternative:
+
+    chmod -R u+w Photos
+    \rm -r Photos
+
+I believe that's because rm won't fail to remove files if they are readonly when running as root.
+
+Anyways - what's the proper way of doing this? I know I could `git clone` the repository and `git get` everything, but that would create another repository with a new UUID. That's duplication I do not want.
+
+Thanks for the advice! -- [[anarcat]]

Added a comment
diff --git a/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_2_4f417206d5d83bd456c8e501ae272cba._comment b/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_2_4f417206d5d83bd456c8e501ae272cba._comment
new file mode 100644
index 000000000..279605be7
--- /dev/null
+++ b/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_2_4f417206d5d83bd456c8e501ae272cba._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="anarcat"
+ avatar="http://cdn.libravatar.org/avatar/4ad594c1e13211c1ad9edb81ce5110b7"
+ subject="comment 2"
+ date="2019-02-07T21:10:19Z"
+ content="""
+you'll notice that some of those processes have been hanging out there for days. i believe the TCP sockets are long gone, at least their parent SSH process are gone. this is why i related this to the \"kill git-annex assistant on logout\" - but since git-annex doesn't do setsid anymore (right?) I don't understand why those process could be left there without a parent... especially since that's on the server-side, which has a (relatively) up to date git-annex version...
+"""]]

Added a comment: not uploads
diff --git a/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_4_b9444184ab653718fca9df5c2068ad3c._comment b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_4_b9444184ab653718fca9df5c2068ad3c._comment
new file mode 100644
index 000000000..8552e9400
--- /dev/null
+++ b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_4_b9444184ab653718fca9df5c2068ad3c._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="anarcat"
+ avatar="http://cdn.libravatar.org/avatar/4ad594c1e13211c1ad9edb81ce5110b7"
+ subject="not uploads"
+ date="2019-02-07T20:59:13Z"
+ content="""
+ah. i somehow missed that... i was assuming a symmetry between the process of getting and sending files, after all it's similar: there's a list of files to move around, and we iterate of them the same way.
+
+cost doesn't apply to uploads? if so that would seem like a fair feature to add... :)
+"""]]

Added a comment
diff --git a/doc/forum/help_with_building_git-annex_on_conda-forge/comment_2_e72eaf736e2a534fa5970183a2f671af._comment b/doc/forum/help_with_building_git-annex_on_conda-forge/comment_2_e72eaf736e2a534fa5970183a2f671af._comment
new file mode 100644
index 000000000..826176b86
--- /dev/null
+++ b/doc/forum/help_with_building_git-annex_on_conda-forge/comment_2_e72eaf736e2a534fa5970183a2f671af._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Ilya_Shlyakhter"
+ avatar="http://cdn.libravatar.org/avatar/1647044369aa7747829c38b9dcc84df0"
+ subject="comment 2"
+ date="2019-02-07T20:45:11Z"
+ content="""
+Question is, why isn't ghc passing the -L/path/to/libgmp option to ld (or maybe stack isn't passing it to ghc), even though the path is listed in --extra-lib-dirs when calling stack?
+"""]]

response
diff --git a/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_3_47269e2c40c5ce27ca197ddfd5ac85c3._comment b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_3_47269e2c40c5ce27ca197ddfd5ac85c3._comment
new file mode 100644
index 000000000..1b87f402d
--- /dev/null
+++ b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_3_47269e2c40c5ce27ca197ddfd5ac85c3._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2019-02-07T20:29:19Z"
+ content="""
+That would make it do efficient parallelization of downloads, but not of
+the uploads that you showed it being bottlenecked on the slowest remote.
+"""]]

followup
diff --git a/doc/forum/help_with_building_git-annex_on_conda-forge/comment_1_369460f5de3219b80a7b2da5bda9aced._comment b/doc/forum/help_with_building_git-annex_on_conda-forge/comment_1_369460f5de3219b80a7b2da5bda9aced._comment
new file mode 100644
index 000000000..178817e8d
--- /dev/null
+++ b/doc/forum/help_with_building_git-annex_on_conda-forge/comment_1_369460f5de3219b80a7b2da5bda9aced._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T20:23:03Z"
+ content="""
+ghc links to libgmp, and uses the system library, and I can see in the log
+that is working; ghc outputs " [1 of 1] Compiling Main ..."
+
+The problem seems to be with
+`/home/conda/feedstock_root/build_artifacts/git-annex_1548210869175/_build_env/bin/../lib/gcc/x86_64-conda_cos6-linux-gnu/7.3.0/../../../../x86_64-conda_cos6-linux-gnu/bin/ld`
+which is not the system ld, nor is it coming from stack, so I guess it's
+conda's ld which is for whatever reason not finding the system libgmp.
+"""]]

Added a comment: use cost then?
diff --git a/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_2_dff46561e15ba5f386dc9cecb9c88e20._comment b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_2_dff46561e15ba5f386dc9cecb9c88e20._comment
new file mode 100644
index 000000000..66618e9ce
--- /dev/null
+++ b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_2_dff46561e15ba5f386dc9cecb9c88e20._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="anarcat"
+ avatar="http://cdn.libravatar.org/avatar/4ad594c1e13211c1ad9edb81ce5110b7"
+ subject="use cost then?"
+ date="2019-02-07T20:19:13Z"
+ content="""
+So I guess the answer here is to use \"cost\" to prioritize \"LAN-local\" repositories? Then we hit [[bugs/assistant_does_not_always_use_repo_cost_info_when_queueing_downloads]] but at least it will work in the general case...
+
+I think that, in my case, it means doing:
+
+    git config remote.origin.annex-cost 150
+
+... so that it's somewhere between local repositories (100) and remote (200). Would that solve my issue here? I don't have many files to transfer right now so I can't really test this until I import new photos, but I'll give it a shot! :)
+
+It would certainly be nice if git-annex was a little more clever with this - it could, for example, have a gray zone between \"remote\" and \"local\"... but I guess that's what the `annex-cost-command` is for...
+
+Thanks!
+"""]]

comment
diff --git a/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_2_502fedae83c3b6e0b32897ce4c1a8317._comment b/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_2_502fedae83c3b6e0b32897ce4c1a8317._comment
new file mode 100644
index 000000000..87912e32e
--- /dev/null
+++ b/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_2_502fedae83c3b6e0b32897ce4c1a8317._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2019-02-07T20:12:47Z"
+ content="""
+You don't need direct mode for this at all and there's no benefit to using
+it in most cases.
+
+You can just use regular git clones on the backup disks, and sync to them.
+Running "git annex merge" in the clone on the backup disk at any time will
+advance it to the most recently synced version with all the files in it,
+and you can do that when you plug it into the new laptop.
+"""]]

close
diff --git a/doc/todo/batch_command_result_status.mdwn b/doc/todo/batch_command_result_status.mdwn
index a660dfdbc..b405e7ef6 100644
--- a/doc/todo/batch_command_result_status.mdwn
+++ b/doc/todo/batch_command_result_status.mdwn
@@ -1 +1,3 @@
 For batch commands like fromkey and setpresentkey, when some operations in a batch succeed and others fail, it's hard to know which ones were done.  It would be good if the operations had an option to output the status to stdout or to a file, maybe in json format.  It would also be good if there was a --keep-going option to try each operation even if some early ones fail.  Currently, when some operations fail, the set of doable operations that gets done depends on the input order, and there is no reliable way to know which ones succeeded.  
+
+> Limiting this to fromkey, [[done]] --[[Joey]] 
diff --git a/doc/todo/batch_command_result_status/comment_3_4f0b9c3fe9405d9bbb4adee31e2aa36d._comment b/doc/todo/batch_command_result_status/comment_3_4f0b9c3fe9405d9bbb4adee31e2aa36d._comment
new file mode 100644
index 000000000..e03d4bf8d
--- /dev/null
+++ b/doc/todo/batch_command_result_status/comment_3_4f0b9c3fe9405d9bbb4adee31e2aa36d._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2019-02-07T20:09:22Z"
+ content="""
+Failure is always an option :) but if the failure is due to disk IO error
+or disk full or memory error or whatever, it doesn't much matter which
+particular item in the batch caused the error because you're going to want
+to redo from start anyway.
+
+I'll close this, if you find other commands whose batch interface can be
+improved open more todos.
+"""]]

followup
diff --git a/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_1_b66015587ccd3ca81de52271979bd026._comment b/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_1_b66015587ccd3ca81de52271979bd026._comment
new file mode 100644
index 000000000..17d8ecb47
--- /dev/null
+++ b/doc/bugs/leaves_many_stray_processes_on_remote_server/comment_1_b66015587ccd3ca81de52271979bd026._comment
@@ -0,0 +1,21 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T19:58:58Z"
+ content="""
+There is not much git-annex involved here; git receive-pack is being run in the
+usual way by a git push, and git-annex-shell only forks off a git-shell to
+handle it in the usual way.
+
+If you change the ssh command to git-shell then git-annex would be out of
+the picture, and so you could then tell if git-annex is somehow involved in
+the problem at all on the server.
+
+But I think that upgrading the client is the first step, or checking to
+see if it has corresponding git processes that are keeping those git
+receive-pack's going.
+
+It could be that the client is losing connection with the server in a way
+that doesn't cause the TCP connection to be closed on the server, and so it
+waits around for whatever TCP timeout might eventually close it.
+"""]]

response
diff --git a/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_1_764f43b51a075a8c94b433eda0cff946._comment b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_1_764f43b51a075a8c94b433eda0cff946._comment
new file mode 100644
index 000000000..ac6947761
--- /dev/null
+++ b/doc/forum/does_git-annex_parallelize_different_remotes__63__/comment_1_764f43b51a075a8c94b433eda0cff946._comment
@@ -0,0 +1,27 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T19:29:55Z"
+ content="""
+If there are several remotes that it can use, and they all have the same
+cost, then yes, `git-annex get` will spread the load amoung them and not
+use higher cost remotes.
+So will `git-annex sync` when getting files from remotes.
+
+There is not currently any similar smart thing done when sending files to
+multiple remotes (or dropping from multiple remotes). 
+And it's kind of hard to see an efficient way to improve it. 
+
+The simplest way would be to loop over remotes ordered by cost and
+then inner loop over files, rather than the current method of looping over
+files with an inner loop over remotes. But in a large tree with many remotes,
+that has to traverse the tree multiple times, which would slow down the
+overall sync.
+
+If instead there's one thread per remote, then the slowest remote will
+fall behind the others, and there will need to be a queue of the files
+that still need to be sent to it -- and that queue could grow to use a lot
+of memory when the tree is large. There would need to be some bound
+on how far behind a thread gets before it stops adding more files and waits
+for it to catch up.
+"""]]

response
diff --git a/doc/git-annex-addurl/comment_4_0951800d761d18614eb6c5f08cdbb885._comment b/doc/git-annex-addurl/comment_4_0951800d761d18614eb6c5f08cdbb885._comment
new file mode 100644
index 000000000..129046430
--- /dev/null
+++ b/doc/git-annex-addurl/comment_4_0951800d761d18614eb6c5f08cdbb885._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 4"""
+ date="2019-02-07T19:26:12Z"
+ content="""
+@john, the difference is that while addurl can make up a filename to use if
+you do not provide one, rmurl needs you to specifiy a filename.
+
+So, yes, "git annex rmurl --file=whatever url" would be more consistent,
+but it requires typing more my making something that is not actually
+optional into an option. And "git annex addurl file url" would make
+the command more consistent with rmurl, but harder to use.
+
+Consistency is not everything.
+
+(Also, the rmurl batch interface would then be less consistent to its
+command-line interface.)
+"""]]

response
diff --git a/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_3_efae569e57322f263f626e330c64aac5._comment b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_3_efae569e57322f263f626e330c64aac5._comment
new file mode 100644
index 000000000..a84a4ea69
--- /dev/null
+++ b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_3_efae569e57322f263f626e330c64aac5._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2019-02-07T19:22:23Z"
+ content="""
+Right, but if you're not sure if it's encrypted to a given subkey, you can
+simply add the subkey with `keyid+='xxx!'` and then you'll know for sure
+it's encrypting to it.
+
+Seems to me that users who want to do things with multiple subkeys can just
+do that, once, and we avoid needing to add a whole lot of complication to
+git-annex.
+"""]]

comment
diff --git a/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_2_bca95a097c90e6bbea616b06fe12aa2c._comment b/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_2_bca95a097c90e6bbea616b06fe12aa2c._comment
new file mode 100644
index 000000000..1b344e28c
--- /dev/null
+++ b/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_2_bca95a097c90e6bbea616b06fe12aa2c._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2019-02-07T19:21:31Z"
+ content="""
+Also, "error: unable to create file" is an error message from git, not
+git-annex. And it seems like a perfectly fine error message for the
+situation.
+"""]]

response
diff --git a/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_1_3a1ff3b4a5fcebedd9bff38ffbf620c1._comment b/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_1_3a1ff3b4a5fcebedd9bff38ffbf620c1._comment
new file mode 100644
index 000000000..1acb072b4
--- /dev/null
+++ b/doc/bugs/special_characters___34____63____58___on_FAT_partition/comment_1_3a1ff3b4a5fcebedd9bff38ffbf620c1._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T19:16:11Z"
+ content="""
+A FAT filesystem cannot contain those characters.
+
+This affects cloning or pulling from any git repository that happens
+to contain such files, it really doesn't have much to do with git-annex.
+"""]]

close
diff --git a/doc/bugs/webdav_remote_fails_on_pound_signs.mdwn b/doc/bugs/webdav_remote_fails_on_pound_signs.mdwn
index 88593a8ed..35455023d 100644
--- a/doc/bugs/webdav_remote_fails_on_pound_signs.mdwn
+++ b/doc/bugs/webdav_remote_fails_on_pound_signs.mdwn
@@ -57,3 +57,5 @@ It wouldn't be so bad if git-annex would fail to upload to a webdav repo and jus
 ### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
 
 I'm running out of ideas here, to be honest. I've certainly had a lot of "luck" running git-annex before, but as others have said, "there's no such thing as luck" and the reason this whole thing works at all is because you work so hard on making it. So: thanks again! --[[anarcat]]
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment
index c2dbf1a29..9413e7b5f 100644
--- a/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment
+++ b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment
@@ -4,4 +4,9 @@
  date="2019-02-07T17:47:06Z"
  content="""
 Fixed for webdav, still need to check S3.
+
+Update: S3 does not have the problem, because the path to a S3 object is
+also part of the url, and yet S3 doesn't special case '#' and '?'.
+
+Kind of makes me wonder if all webdav servers have the problem either.
 """]]

Added a comment
diff --git a/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_2_81f2725bf6cfbef43cd9253a61eb7cfa._comment b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_2_81f2725bf6cfbef43cd9253a61eb7cfa._comment
new file mode 100644
index 000000000..17472f523
--- /dev/null
+++ b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_2_81f2725bf6cfbef43cd9253a61eb7cfa._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="kirelagin@6d93475882c55a329fedae6be1971868a775ec7e"
+ nickname="kirelagin"
+ avatar="http://cdn.libravatar.org/avatar/325af9a946cb4337c6640f0e95044be1"
+ subject="comment 2"
+ date="2019-02-07T19:03:35Z"
+ content="""
+> If you use \"keyid!\" to specify a subkey, it is shown by git-annex info
+
+But if I do not, then it is not shown, even though there can still be multiple valid encryption subkeys and god knows which one it chose.
+"""]]

followup
diff --git a/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_1_2cbdc33f22d18b8ea25473d361e591eb._comment b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_1_2cbdc33f22d18b8ea25473d361e591eb._comment
new file mode 100644
index 000000000..ce80fe609
--- /dev/null
+++ b/doc/bugs/Get_gpg_subkey_id_used_for_encryption/comment_1_2cbdc33f22d18b8ea25473d361e591eb._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T17:57:01Z"
+ content="""
+If you use "keyid!" to specify a subkey, it is shown by git-annex info:
+
+	joey@darkstar:/tmp/bob>git annex initremote d type=directory directory=../d encryption=pubkey keyid='B38389A117065459!'
+	initremote d (encryption setup) (to gpg keys: B38389A117065459!) ok
+	(recording state in git...)
+	joey@darkstar:/tmp/bob>git annex info d | grep encryption
+	encryption: pubkey (to gpg keys: B38389A117065459!)
+
+So I don't see a bug here?
+"""]]

deal with attempt to export filename with # or ? to webdav
xporting files with '#' or '?' in their name won't work because urls get
truncated on those. Fail in a better way in this case, and avoid failing
when removing such files from the export, so after the user has renamed the
problem files the export will succeed.
diff --git a/CHANGELOG b/CHANGELOG
index cf0670a4e..2b54dbb9d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,6 +21,10 @@ git-annex (7.20190130) UNRELEASED; urgency=medium
     used in the repository.
   * init: Don't let --force be used to override a .noannex file,
     instead the user can just delete the file.
+  * webdav: Exporting files with '#' or '?' in their name won't work because
+    urls get truncated on those. Fail in a better way in this case,
+    and avoid failing when removing such files from the export, so
+    after the user has renamed the problem files the export will succeed.
 
  -- Joey Hess <id@joeyh.name>  Wed, 30 Jan 2019 12:30:22 -0400
 
diff --git a/Remote/WebDAV.hs b/Remote/WebDAV.hs
index dee047385..12672a51d 100644
--- a/Remote/WebDAV.hs
+++ b/Remote/WebDAV.hs
@@ -194,26 +194,41 @@ checkKey r chunkconfig (Just dav) k = do
 			either giveup return v
 
 storeExportDav :: Remote -> FilePath -> Key -> ExportLocation -> MeterUpdate -> Annex Bool
-storeExportDav r f k loc p = withDAVHandle r $ \mh -> runExport mh $ \dav -> do
-	reqbody <- liftIO $ httpBodyStorer f p
-	storeHelper dav (keyTmpLocation k) (exportLocation loc) reqbody
-	return True
+storeExportDav r f k loc p = case exportLocation loc of
+	Right dest -> withDAVHandle r $ \mh -> runExport mh $ \dav -> do
+		reqbody <- liftIO $ httpBodyStorer f p
+		storeHelper dav (keyTmpLocation k) dest reqbody
+		return True
+	Left err -> do
+		warning err
+		return False
 
 retrieveExportDav :: Remote -> Key -> ExportLocation -> FilePath -> MeterUpdate -> Annex Bool
-retrieveExportDav r  _k loc d p = withDAVHandle r $ \mh -> runExport mh $ \_dav -> do
-	retrieveHelper (exportLocation loc) d p
-	return True
+retrieveExportDav r  _k loc d p = case exportLocation loc of
+	Right src -> withDAVHandle r $ \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 = withDAVHandle r $ \case
-	Nothing -> giveup $ name r ++ " not configured"
-	Just h -> liftIO $ do
-		v <- goDAV h $ existsDAV (exportLocation loc)
-		either giveup return v
+checkPresentExportDav r _k loc = case exportLocation loc of
+	Right p -> withDAVHandle r $ \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 = withDAVHandle r $ \mh -> runExport mh $ \_dav ->
-	removeHelper (exportLocation loc)
+removeExportDav r _k loc = case exportLocation loc of
+	Right p -> withDAVHandle r $ \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
+	-- removal to succeed. This allows recovery after failure to store
+	-- content there, as the user can rename the problem file and
+	-- 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
@@ -223,16 +238,18 @@ removeExportDirectoryDav r dir = withDAVHandle r $ \mh -> runExport mh $ \_dav -
 		>>= maybe (return False) (const $ return True)
 
 renameExportDav :: Remote -> Key -> ExportLocation -> ExportLocation -> Annex Bool
-renameExportDav r _k src dest = withDAVHandle r $ \case
-	Just h
-		-- box.com's DAV endpoint has buggy handling of renames,
-		-- so avoid renaming when using it.
-		| boxComUrl `isPrefixOf` baseURL h -> return False
-		| otherwise -> runExport (Just h) $ \dav -> do
-			maybe noop (void . mkColRecursive) (locationParent (exportLocation dest))
-			moveDAV (baseURL dav) (exportLocation src) (exportLocation dest)
-			return True
-	Nothing -> return False
+renameExportDav r _k src dest = case (exportLocation src, exportLocation dest) of
+	(Right srcl, Right destl) -> withDAVHandle r $ \case
+		Just h
+			-- box.com's DAV endpoint has buggy handling of renames,
+			-- so avoid renaming when using it.
+			| boxComUrl `isPrefixOf` baseURL h -> return False
+			| otherwise -> runExport (Just h) $ \dav -> do
+				maybe noop (void . mkColRecursive) (locationParent destl)
+				moveDAV (baseURL dav) srcl destl
+				return True
+		Nothing -> return False
+	_ -> return False
 
 runExport :: Maybe DavHandle -> (DavHandle -> DAVT IO Bool) -> Annex Bool
 runExport Nothing _ = return False
diff --git a/Remote/WebDAV/DavLocation.hs b/Remote/WebDAV/DavLocation.hs
index 42c9d35d4..fec9a6d5f 100644
--- a/Remote/WebDAV/DavLocation.hs
+++ b/Remote/WebDAV/DavLocation.hs
@@ -46,8 +46,14 @@ keyDir k = addTrailingPathSeparator $ hashdir </> keyFile k
 keyLocation :: Key -> DavLocation
 keyLocation k = keyDir k ++ keyFile k
 
-exportLocation :: ExportLocation -> DavLocation
-exportLocation = fromExportLocation
+{- Paths containing # or ? cannot be represented in an url, so fails on
+ - those. -}
+exportLocation :: ExportLocation -> Either String DavLocation
+exportLocation l =
+	let p = fromExportLocation l
+	in if any (`elem` p) ['#', '?']
+		then Left ("Cannot store file containing '#' or '?' on webdav: " ++ p)
+		else Right p
 
 {- Where we store temporary data for a key as it's being uploaded. -}
 keyTmpLocation :: Key -> DavLocation
diff --git a/doc/bugs/webdav_remote_fails_on_pound_signs/comment_1_b6b1cff5972a655d14b592652ae8d1a8._comment b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_1_b6b1cff5972a655d14b592652ae8d1a8._comment
new file mode 100644
index 000000000..a4695178a
--- /dev/null
+++ b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_1_b6b1cff5972a655d14b592652ae8d1a8._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T17:11:22Z"
+ content="""
+I guess that "#" and "?" in a webdav path is just not possible, since
+the path becomes part of the url that is used. It might even be legal for a
+webdav server to cut those off the url and store data to the basename,
+which could overwrite another file that shared that basename.
+
+I don't like escaping those somehow, because the user should expect to see
+the same filenames in the export that they have in their tree. So exporting
+should fail. And removal should can succeed without doing anything, to
+let it recover, which is ok since the file content is certianly not located
+at an url containing either of those characters.
+
+I think that S3 might have the same problem, have not tried it yet.
+"""]]
diff --git a/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment
new file mode 100644
index 000000000..c2dbf1a29
--- /dev/null
+++ b/doc/bugs/webdav_remote_fails_on_pound_signs/comment_2_a88aa0458413481787b4a59990a210df._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2019-02-07T17:47:06Z"
+ content="""
+Fixed for webdav, still need to check S3.
+"""]]

add
diff --git a/doc/todo/some_way_to_get_a_list_of_options_for_a_special_remote_of_a_given_type/comment_1_8ea993d1acc92f3aa9594ed8e2d3ec7d._comment b/doc/todo/some_way_to_get_a_list_of_options_for_a_special_remote_of_a_given_type/comment_1_8ea993d1acc92f3aa9594ed8e2d3ec7d._comment
new file mode 100644
index 000000000..56431892f
--- /dev/null
+++ b/doc/todo/some_way_to_get_a_list_of_options_for_a_special_remote_of_a_given_type/comment_1_8ea993d1acc92f3aa9594ed8e2d3ec7d._comment
@@ -0,0 +1,30 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-07T15:57:26Z"
+ content="""
+The remote options could be part of option parsing, and then --help would
+list them.
+
+That was not originally done because the option parser was too crude
+to support options specific not only to a given command but to a given type
+of special remote, but with optparse-applicative, it could certianly be done.
+
+I don't know about supporting it in the external special remote protocol
+though. Communicating the full power of applicative option parsing over
+that pipe would add a great deal of complexity. 
+And it would need to retain backwards compatibility.
+
+Also, since git-annex doesn't know the name of the external special remote
+to use until it's parsed the command line options, it wouldn't really
+be possible to use any information from externals to configure the option
+parsing.
+
+Kind of feels like the simplest thing with externals would be best, and
+that's probably something like a "CONFIGSYNOPSIS" that lets the external
+answer with a preformatted string describing its options for display to the
+user.
+
+(Encryption needing to be explicitly disabled is a good thing, I think; it
+avoids any confusion about it.)
+"""]]

Added a comment: Windows build with stack
diff --git a/doc/install/fromsource/comment_73_bc163a82180b00a8b757edf269cebc06._comment b/doc/install/fromsource/comment_73_bc163a82180b00a8b757edf269cebc06._comment
new file mode 100644
index 000000000..aa0c94fd0
--- /dev/null
+++ b/doc/install/fromsource/comment_73_bc163a82180b00a8b757edf269cebc06._comment
@@ -0,0 +1,24 @@
+[[!comment format=mdwn
+ username="prabindh"
+ avatar="http://cdn.libravatar.org/avatar/d5c12329a2a524ebf6105721dafcc0c2"
+ subject="Windows build with stack"
+ date="2019-02-07T14:59:51Z"
+ content="""
+1. git checkout failed,
+
+fatal: cannot create directory at 
+
+'doc/bugs/Assertion___96__cnt___60_____40__sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__41_____47___sizeof___40____95__nl__95__value__95__type__95__LC__95__TIME__91__0__93____41____41____39___failed.': Filename too long
+
+fixed by updating longpaths in gitconfig, 
+
+2. stack setup ok
+
+3. stack install failed, 
+
+regex-tdfa-1.2.3.1: copy/register
+Progress 58/168
+removeDirectoryRecursive:removeContentsRecursive:removePathRecursive:removeContentsRecursive:removePathRecursive:removeContentsRecursive:removePathRecursive:removeContentsRecursive:removePathRecursive:removeContentsRecursive:RemoveDirectory 
+
+Will have to try the Make approach on Windows ?
+"""]]

diff --git a/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
index 072d9473f..5f9d51a1a 100644
--- a/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
+++ b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
@@ -3,6 +3,9 @@
 `git annex sync --content` in an annex on a FAT partition fails if it contains filenames with special characters like `"?:`
 
 
+    error: unable to create file music/File "Test".mp3: Invalid argument
+
+
 ### What version of git-annex are you using? On what operating system?
 
 debian sid

diff --git a/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
index 5cdac28f5..072d9473f 100644
--- a/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
+++ b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
@@ -10,7 +10,7 @@ debian sid
     git-annex version: 7.20190129
     build flags: Assistant Webapp Pairing S3(multipartupload)(storageclasses) WebDAV Inotify DBus DesktopNotify TorrentParser MagicMime Feeds Testsuite
     dependency versions: aws-0.20 bloomfilter-2.0.1.0 cryptonite-0.25 DAV-1.3.3 feed-1.0.0.0 ghc-8.4.4 http-client-0.5.13.1 persistent-sqlite-2.8.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.0
-key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_224 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL
+    key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_224 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL
     remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar hook external
     operating system: linux x86_64
     supported repository versions: 5 7

diff --git a/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
new file mode 100644
index 000000000..5cdac28f5
--- /dev/null
+++ b/doc/bugs/special_characters___34____63____58___on_FAT_partition.mdwn
@@ -0,0 +1,19 @@
+### Please describe the problem.
+
+`git annex sync --content` in an annex on a FAT partition fails if it contains filenames with special characters like `"?:`
+
+
+### What version of git-annex are you using? On what operating system?
+
+debian sid
+
+    git-annex version: 7.20190129
+    build flags: Assistant Webapp Pairing S3(multipartupload)(storageclasses) WebDAV Inotify DBus DesktopNotify TorrentParser MagicMime Feeds Testsuite
+    dependency versions: aws-0.20 bloomfilter-2.0.1.0 cryptonite-0.25 DAV-1.3.3 feed-1.0.0.0 ghc-8.4.4 http-client-0.5.13.1 persistent-sqlite-2.8.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.0
+key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_224 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL
+    remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar hook external
+    operating system: linux x86_64
+    supported repository versions: 5 7
+    upgrade supported from repository versions: 0 1 2 3 4 5 6
+    local repository version: 7
+

Added a comment
diff --git a/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__/comment_1_644835e95676da0aa38737b3419022e2._comment b/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__/comment_1_644835e95676da0aa38737b3419022e2._comment
new file mode 100644
index 000000000..556711287
--- /dev/null
+++ b/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__/comment_1_644835e95676da0aa38737b3419022e2._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="gueux"
+ avatar="http://cdn.libravatar.org/avatar/47e44a21505727b2d6bb5d88f0468f34"
+ subject="comment 1"
+ date="2019-02-07T14:02:41Z"
+ content="""
+That looks like https://git-annex.branchable.com/bugs/VFAT_crazy_limit_on_max_filenames_in_directory/
+"""]]

diff --git a/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__.mdwn b/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__.mdwn
new file mode 100644
index 000000000..8b3b7c2c3
--- /dev/null
+++ b/doc/bugs/bla__47__.git__47__annex__47__transfer__47__failed__47__download__47__...tmp__58___rename__58___resource_exhausted___40__No_space_left_on_device__41__.mdwn
@@ -0,0 +1,62 @@
+### Please describe the problem.
+
+I'm syncing the content of an annex to another annex on a fat32 partition connected with usb (on an ipod video, with rockbox). I created this annex  with "git init && git annex init", so it is using unlocked adusted branch.
+
+Here is the .git/config:
+
+    [core]
+            repositoryformatversion = 0
+            filemode = false
+            bare = false
+            logallrefupdates = true
+            symlinks = false
+            ignorecase = true
+    [annex]
+            uuid = 25e5f762-db7f-49ad-a504-93e02bba642f
+            sshcaching = false
+            crippledfilesystem = true
+            version = 7
+    [filter "annex"]
+            smudge = git-annex smudge %f
+            clean = git-annex smudge --clean %f
+
+
+
+The sync seems to go well, all files are being copied, but I see an error for each copy during the checksum:
+
+
+    ...
+    copy music/file.flac (to ipod...) 
+    SHA256E-s26126286--32b36b6ff0e9a667c588229f159ff7a553fcd08012b8a74a65bf9f09874044e2.flac
+     26,126,286 100%   25.21MB/s    0:00:00 (xfr#1, to-chk=0/1)
+    (checksum...) 
+      ../../../media/gueux/IPOD/audio/.git/annex/othertmp/927_692_SHA256E-s26126286--32b36b6ff0e9a667c588229f159ff7a553fcd08012b8a74a65bf9f09874044e2.flac.log: openFile: resource exhausted (No space left on device)
+    
+      ../../../media/gueux/IPOD/audio/.git/annex/transfer/failed/download/fa8db49d-df5f-4c65-9af4-712d38729ef0/SHA256E-s26126286--32b36b6ff0e9a667c588229f159ff7a553fcd08012b8a74a65bf9f09874044e2.flac26375-82266.tmp: rename: resource exhausted (No space left on device)
+    ok
+    ...
+
+Note that there is plenty space left on device (300 out of 500Go), and this happened since the beginning of the sync, when the disk was almost empty.
+
+
+### What steps will reproduce the problem?
+
+
+### What version of git-annex are you using? On what operating system?
+
+Debian sid,
+
+    git-annex version: 7.20190129
+    build flags: Assistant Webapp Pairing S3(multipartupload)(storageclasses) WebDAV Inotify DBus DesktopNotify TorrentParser MagicMime Feeds Testsuite
+    dependency versions: aws-0.20 bloomfilter-2.0.1.0 cryptonite-0.25 DAV-1.3.3 feed-1.0.0.0 ghc-8.4.4 http-client-0.5.13.1 persistent-sqlite-2.8.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.0
+    key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_224 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL
+    remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar hook external
+    operating system: linux x86_64
+    supported repository versions: 5 7
+    upgrade supported from repository versions: 0 1 2 3 4 5 6
+    local repository version: 7
+
+
+### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
+
+Sure, I love it, and unlocked branch is really a great addition for crippled filesystems!

diff --git a/doc/forum/android_-_assistant_-_local_pairing_.mdwn b/doc/forum/android_-_assistant_-_local_pairing_.mdwn
new file mode 100644
index 000000000..23339bfa4
--- /dev/null
+++ b/doc/forum/android_-_assistant_-_local_pairing_.mdwn
@@ -0,0 +1,15 @@
+Hello!
+
+I'm trying to *local pair* a recent build of assistant (7.20190129-ge3dce20cf) on android with debian (6.20170101.1).
+
+Either way around one assistant doesn't show the pair request of the other.
+
+When the debian box initiates the pairing android can see [the packets](http://git-annex.branchable.com/assistant/local_pairing_walkthrough/#comment-3eb46bd30deac03bda8eaba18a85751c) (in tcpdump) but there's nothing in the webapp.
+When I try initiating the pairing from android no pairing packets seem to show up (this is using tcpdump on android. I didn't check on the Debian box - I'm assuming they have to exist on Android first).
+
+
+Termux needs the ssh server to be started manually (I'm using the openssh package). Termux is (by default) configured to run it as the termux user, so it's on port 8022. I can ssh to that from debian.
+
+The Termux shell shows the hostname as "localhost" because that's what gethostname() returns (<https://github.com/termux/termux-packages/issues/352#issuecomment-458278586>). Could that be causing problems?
+
+What can I try next?

Added a comment: Inconsistent idiom
diff --git a/doc/git-annex-addurl/comment_3_46e1b62d37619521a60eb2339b8d094f._comment b/doc/git-annex-addurl/comment_3_46e1b62d37619521a60eb2339b8d094f._comment
new file mode 100644
index 000000000..a5e3827b2
--- /dev/null
+++ b/doc/git-annex-addurl/comment_3_46e1b62d37619521a60eb2339b8d094f._comment
@@ -0,0 +1,21 @@
+[[!comment format=mdwn
+ username="pellman.john@e59e5c6d98d49cd9632d6fbe0c5026ddc71d52fd"
+ nickname="pellman.john"
+ avatar="http://cdn.libravatar.org/avatar/eaca2e5f6d5888d139129293c7293b1f"
+ subject="Inconsistent idiom"
+ date="2019-02-06T20:53:00Z"
+ content="""
+In using git-annex in the past, I've always found it counterintuitive that [rmurl](https://git-annex.branchable.com/git-annex-rmurl/) uses the following form to remove a URL from a file:
+
+```
+git annex rmurl [file] [url]
+```
+
+While, in contrast, addurl uses a flag to designate the file that a URL should be added to the list of URLs a file points to:
+
+```
+git annex addurl [url] --file=[file]
+```
+
+It would make sense (at least to me) to make the syntax for these more congruous so that both commands use either two positional arguments or one positional argument and one keyword argument / flag.
+"""]]

question about bandwidth optimization
diff --git a/doc/forum/does_git-annex_parallelize_different_remotes__63__.mdwn b/doc/forum/does_git-annex_parallelize_different_remotes__63__.mdwn
new file mode 100644
index 000000000..a28bf37ae
--- /dev/null
+++ b/doc/forum/does_git-annex_parallelize_different_remotes__63__.mdwn
@@ -0,0 +1,46 @@
+Hi!
+
+I'm wondering how clever git-annex is at parallelizing different
+remotes with different bandwidth. For example here I have a `origin`
+remote that is on the local LAN (so it's fast) and an `offsite-annex`
+remote that's, well, offsite, so it's much slower. If I run `git annex
+sync --content -J2`, it *looks* like it indiscriminately starts
+copying over files to either host without too much though:
+
+    $ git annex sync --content -J2
+    [...]
+    copy 2019/01/29/montreal/DSCF8148.JPG (to origin...) ok
+    copy 2019/01/29/montreal/DSCF8148.JPG (checking offsite-annex...) (to offsite-annex...) ok
+    copy 2019/01/29/montreal/DSCF8147.RAF (checking offsite-annex...) (to offsite-annex...) 
+    copy 2019/01/29/montreal/DSCF8148.RAF (to origin...) ok
+    copy 2019/01/29/montreal/DSCF8148.RAF (checking offsite-annex...) (to offsite-annex...) 
+
+The interactivity of this doesn't show well here, but what happens is
+this, in order:
+
+ 1. a first JPG is copied to origin and offsite-annex in parallel
+    (good)
+
+ 2. the origin (local) JPG transfer completes, a RAF file transfer
+    gets started to offsite-annex (not so great - a best strategy
+    would be to continue copying files to the local remote, as it's
+    fast and its bandwidth is now unused)
+
+ 3. the offsite-annex JPG transfer completes, a RAF transfer starts to
+    origin (good)
+
+ 4. that transfer completes, the same file is now copied to offsite
+    (again, not so great - local remote is now unused)
+
+What I think git-annex should be doing is try, as much as possible to
+saturate the different network links represented by the different
+remotes. In my case, files should be transfered on the local LAN as
+soon as possible: a single thread should be busy with that `origin`
+remote as long as files are missing there, while the other thread can
+slowly trickle files to `offsite`. Only when `origin` is full should
+both threads work on the `offsite` one. A simple heuristic would be
+"is there a thread already busy with that remote? if yes, see if
+another remote needs a file transfered that is not busy".
+
+Am I analysing this correctly? Is this a bug? or feature request?
+:) -- [[anarcat]]

sign
diff --git a/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn b/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn
index 256c5a4b3..4bad284ba 100644
--- a/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn
+++ b/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn
@@ -236,3 +236,5 @@ In general, the user is quite happy about git-annex now that they
 understood how to fetch files through their file manager and see
 progress through the GUI. I'm similarly happy to have less
 hand-holding to do myself. :)
+
+Thanks again for your great work! -- [[anarcat]]

some weird issue i keep postponing to report
diff --git a/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn b/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn
new file mode 100644
index 000000000..256c5a4b3
--- /dev/null
+++ b/doc/bugs/leaves_many_stray_processes_on_remote_server.mdwn
@@ -0,0 +1,238 @@
+### Please describe the problem.
+
+git-annex leaves many stray processes around on a remote SSH server.
+
+### What steps will reproduce the problem?
+
+I'm not exactly sure, to be honest. I have setup a user for a friend on my home server with the following SSH key:
+
+    command="git-annex-shell -c \"$SSH_ORIGINAL_COMMAND\"",restrict ssh-rsa AAAAB[...]
+
+They use that access to synchronize a repository in `/srv/media` which is read-only for them, thanks to simple file permissions (files are owned by another user and another group).
+
+The friend uses the assistant to see progress of their transfers and regularly keep their local copy up to date. I suspect what is happening is the assistant is trying to *push* local changes to the remote server, and fails to do so.
+
+### What version of git-annex are you using? On what operating system?
+
+The remote git-annex client is, I believe, the `6.20170101-1+deb9u2` version shipped in Debian stable. The server is running 7.20190122-1~bpo9+1 on Debian stretch.
+
+### Please provide any additional information below.
+
+Here's a dump of the stray processes:
+
+[[!format sh """
+# ps axfu | grep friend
+root      8493  0.0  0.0 114176  7272 ?        Ss   12:24   0:00  \_ sshd: friend [priv]
+friend    8502  0.0  0.0 114360  4536 ?        S    12:24   0:00  |   \_ sshd: friend@notty
+friend    8511  0.0  0.0 1074101432 7760 ?     Ssl  12:24   0:00  |       \_ git-annex-shell -c git-receive-pack '/srv/media'
+friend    8520  0.0  0.0  39356  3248 ?        Sl   12:24   0:00  |           \_ git receive-pack /srv/media
+root     16282  0.0  0.0 114176  7240 ?        Ss   12:51   0:00  \_ sshd: friend [priv]
+friend   16291  0.0  0.0 115036  5292 ?        S    12:51   0:00  |   \_ sshd: friend@notty
+friend   16298  0.0  0.0 1074101432 7792 ?     Ssl  12:51   0:00  |       \_ git-annex-shell -c git-receive-pack '/srv/media'
+friend   16302  0.0  0.0  39356  3280 ?        Sl   12:51   0:00  |       |   \_ git receive-pack /srv/media
+friend   16312  0.0  0.0 1074101432 7720 ?     Ssl  12:52   0:00  |       \_ git-annex-shell -c git-receive-pack '/srv/media'
+friend   16317  0.0  0.0  39356  3276 ?        Sl   12:52   0:00  |       |   \_ git receive-pack /srv/media
+friend   16324  0.0  0.0 1074101432 7828 ?     Ssl  12:52   0:00  |       \_ git-annex-shell -c git-receive-pack '/srv/media'
+friend   16328  0.0  0.0  39356  3336 ?        Sl   12:52   0:00  |       |   \_ git receive-pack /srv/media
+friend   20296  0.0  0.0 1074101432 7680 ?     Ssl  13:06   0:00  |       \_ git-annex-shell -c git-receive-pack '/srv/media'
+friend   20300  0.0  0.0  39356  3260 ?        Sl   13:06   0:00  |           \_ git receive-pack /srv/media
+root     32041  0.0  0.0  14004   972 pts/5    S+   13:49   0:00      \_ grep friend
+friend   25870  0.0  0.0  65116  4560 ?        Ss   fév02   0:00 /lib/systemd/systemd --user
+friend   25874  0.0  0.0 244548   720 ?        S    fév02   0:00  \_ (sd-pam)
+friend   25952  0.0  0.0 1074101432 2088 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25956  0.0  0.0  39172   564 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   26062  0.0  0.0 1074101432 2196 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   26066  0.0  0.0  39172   548 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   29069  0.0  0.0 1074101432 2188 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   29073  0.0  0.0  39172   608 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30475  0.0  0.0 1074101432 2140 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30488  0.0  0.0  39172   568 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30476  0.0  0.0 1074101432 2196 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30492  0.0  0.0  39172   780 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30477  0.0  0.0 1074101432 2244 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30485  0.0  0.0  39172   544 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30478  0.0  0.0 1074101432 2140 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30495  0.0  0.0  39172   740 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30499  0.0  0.0 1074101432 2220 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30503  0.0  0.0  39172   628 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30505  0.0  0.0 1074101432 2096 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30510  0.0  0.0  39172   588 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30506  0.0  0.0 1074101432 2140 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30514  0.0  0.0  39172   728 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   30546  0.0  0.0 1074101432 2076 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   30550  0.0  0.0  39172   588 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend   16664  0.0  0.0 1074101432 2144 ?     Ssl  fév02   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   16668  0.0  0.0  39172   756 ?        Sl   fév02   0:00  \_ git receive-pack /srv/media
+friend    5225  0.0  0.0 1074101432 2520 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    5229  0.0  0.0  39324   904 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25302  0.0  0.0 1074101432 2256 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25337  0.0  0.0  39356   328 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25304  0.0  0.0 1074101432 2408 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25342  0.0  0.0  39356   376 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25305  0.0  0.0 1074101432 2248 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25336  0.0  0.0  39356   340 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25307  0.0  0.0 1074101432 2432 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25341  0.0  0.0  39356   364 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25362  0.0  0.0 1074101432 2392 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25366  0.0  0.0  39356   400 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25386  0.0  0.0 1074101432 2136 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25399  0.0  0.0  39356   564 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25387  0.0  0.0 1074101432 2440 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25400  0.0  0.0  39356   500 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend   25388  0.0  0.0 1074101432 2136 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25394  0.0  0.0  39356   420 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend    1181  0.0  0.0 1074101432 2308 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    1186  0.0  0.0  39356   380 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend    6483  0.0  0.0 1074101432 2336 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    6487  0.0  0.0  39356   436 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend    9714  0.0  0.0 1074101432 2340 ?     Ssl  fév03   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    9718  0.0  0.0  39356   360 ?        Sl   fév03   0:00  \_ git receive-pack /srv/media
+friend    9117  0.0  0.0 1074101432 2392 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    9121  0.0  0.0  39356   536 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   13542  0.0  0.0 1074101432 2264 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   13558  0.0  0.0  39356   436 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   13543  0.0  0.0 1074101432 2328 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   13553  0.0  0.0  39356   532 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   13544  0.0  0.0 1074101432 2272 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   13561  0.0  0.0  39356   540 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   13545  0.0  0.0 1074101432 2380 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   13560  0.0  0.0  39356   592 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    1940  0.0  0.0 1074101432 2264 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    1944  0.0  0.0  39356   468 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    8515  0.0  0.0 1074101432 2348 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    8519  0.0  0.0  39356   520 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   11968  0.0  0.0 1074101432 2424 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   11972  0.0  0.0  39356   640 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   24944  0.0  0.0 1074101432 2264 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   24948  0.0  0.0  39356   504 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   25327  0.0  0.0 1074101432 2416 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   25335  0.0  0.0  39356   668 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   10241  0.0  0.0 1074101432 2272 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   10245  0.0  0.0  39356   552 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   15442  0.0  0.0 1074101432 2272 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   15446  0.0  0.0  39356   524 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   16915  0.0  0.0 1074101432 2248 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   16919  0.0  0.0  39356   592 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   29211  0.0  0.0 1074101432 2420 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   29215  0.0  0.0  39356   552 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    1623  0.0  0.0 1074101432 2220 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    1630  0.0  0.0  39356   312 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    1624  0.0  0.0 1074101432 2360 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    1632  0.0  0.0  39356   268 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   11255  0.0  0.0 1074101432 2312 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   11259  0.0  0.0  39356   340 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   29421  0.0  0.0 1074101432 2424 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   29425  0.0  0.0  39356   260 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    8089  0.0  0.0 1074101432 2220 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    8100  0.0  0.0  39356   372 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    8090  0.0  0.0 1074101432 2024 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    8101  0.0  0.0  39356   432 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend    8091  0.0  0.0 1074101432 2048 ?     Ssl  fév04   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    8103  0.0  0.0  39356   500 ?        Sl   fév04   0:00  \_ git receive-pack /srv/media
+friend   32567  0.0  0.0 1074101432 2680 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   32571  0.0  0.0  39356   588 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    3556  0.0  0.0 1074101432 2672 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    3561  0.0  0.0  39356   632 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    3557  0.0  0.0 1074101432 2688 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    3565  0.0  0.0  39356   656 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    3566  0.0  0.0 1074101432 2816 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    3578  0.0  0.0  39356   680 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    3567  0.0  0.0 1074101432 2656 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    3577  0.0  0.0  39356   916 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    3587  0.0  0.0 1074101432 2880 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    3591  0.0  0.0  39356   956 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   14117  0.0  0.0 1074101432 3444 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   14121  0.0  0.0  39356   984 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   20449  0.0  0.0 1074101432 3464 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20453  0.0  0.0  39356   888 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   20469  0.0  0.0 1074101432 3528 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20480  0.0  0.0  39356   904 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   20483  0.0  0.0 1074101432 3140 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20492  0.0  0.0  39356  1056 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   20484  0.0  0.0 1074101432 3168 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20491  0.0  0.0  39356  1040 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   20498  0.0  0.0 1074101432 3348 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20502  0.0  0.0  39356   848 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   32112  0.0  0.0 1074101432 3360 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   32116  0.0  0.0  39356   768 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   28244  0.0  0.0 1074101432 3452 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   28248  0.0  0.0  39356   936 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   28266  0.0  0.0 1074101432 3352 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   28276  0.0  0.0  39356   976 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   28267  0.0  0.0 1074101432 3332 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   28273  0.0  0.0  39356  1016 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   28284  0.0  0.0 1074101432 3388 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   28289  0.0  0.0  39356   900 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   28285  0.0  0.0 1074101432 3328 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   28293  0.0  0.0  39356   748 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   31816  0.0  0.0 1074101432 3384 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   31827  0.0  0.0  39356   852 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   31822  0.0  0.0 1074101432 2612 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   31835  0.0  0.0  39356   780 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   31823  0.0  0.0 1074101432 2496 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   31832  0.0  0.0  39356   748 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   31842  0.0  0.0 1074101432 3272 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   31846  0.0  0.0  39356   964 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    9226  0.0  0.0 1074101432 3416 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    9230  0.0  0.0  39356   508 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   13564  0.0  0.0 1074101432 3456 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   13568  0.0  0.0  39356   708 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    4424  0.0  0.0 1074101432 3348 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    4433  0.0  0.0  39356   788 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend    4425  0.0  0.0 1074101432 3500 ?     Ssl  fév05   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend    4432  0.0  0.0  39356   664 ?        Sl   fév05   0:00  \_ git receive-pack /srv/media
+friend   12235  0.0  0.0 1074101432 7784 ?     Ssl  08:37   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   12239  0.0  0.0  39356  3172 ?        Sl   08:37   0:00  \_ git receive-pack /srv/media
+friend   15636  0.0  0.0 1074101432 7600 ?     Ssl  08:50   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   15640  0.0  0.0  39356  3248 ?        Sl   08:50   0:00  \_ git receive-pack /srv/media
+friend   17626  0.0  0.0 1074101432 7560 ?     Ssl  09:00   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   17638  0.0  0.0  39356  3232 ?        Sl   09:00   0:00  \_ git receive-pack /srv/media
+friend   17628  0.0  0.0 1074101432 7660 ?     Ssl  09:00   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   17633  0.0  0.0  39356  3240 ?        Sl   09:00   0:00  \_ git receive-pack /srv/media
+friend   20252  0.0  0.0 1074101432 7820 ?     Ssl  11:10   0:00 git-annex-shell -c git-receive-pack '/srv/media'
+friend   20258  0.0  0.0  39356  3228 ?        Sl   11:10   0:00  \_ git receive-pack /srv/media
+friend   20253  0.0  0.0 1074101432 7884 ?     Ssl  11:10   0:00 git-annex-shell -c git-receive-pack '/srv/media'

(Diff truncated)
update
diff --git a/doc/thanks/list b/doc/thanks/list
index 0871f5ace..614da60da 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -46,3 +46,10 @@ Michael Tolly,
 AlexS, 
 paul walmsley, 
 tdittr, 
+Peter, 
+Fernando Jimenez, 
+Sergey Karpukhin, 
+Jeff Moe, 
+Alexander Thompson, 
+Nicolas Pouillard, 
+Tyler Cipriani, 

Added a comment
diff --git a/doc/todo/batch_command_result_status/comment_2_c89014d3088aa448f4c980d26ab43104._comment b/doc/todo/batch_command_result_status/comment_2_c89014d3088aa448f4c980d26ab43104._comment
new file mode 100644
index 000000000..fa586cd09
--- /dev/null
+++ b/doc/todo/batch_command_result_status/comment_2_c89014d3088aa448f4c980d26ab43104._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Ilya_Shlyakhter"
+ avatar="http://cdn.libravatar.org/avatar/1647044369aa7747829c38b9dcc84df0"
+ subject="comment 2"
+ date="2019-02-05T20:55:59Z"
+ content="""
+Thanks a lot!  I mostly use fromkey/setpresentkey/registerurl for now.  registerurl, I guess, also cannot fail?  Though, I think I've seen failures related to git locks being held?
+"""]]

followup
diff --git a/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync/comment_1_172936aeca215d4a28fd0f1dbc942616._comment b/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync/comment_1_172936aeca215d4a28fd0f1dbc942616._comment
new file mode 100644
index 000000000..3dba3458b
--- /dev/null
+++ b/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync/comment_1_172936aeca215d4a28fd0f1dbc942616._comment
@@ -0,0 +1,51 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-05T18:45:10Z"
+ content="""
+I'm not sure there's a bug here. It sounds like your repository's git-annex
+branch was out of date, and so it did not know about a change to the
+location of the files.
+
+For example, I can replicate the error message like this:
+
+	joey@darkstar:/tmp> git init A
+	joey@darkstar:/tmp> cd A
+	joey@darkstar:/tmp/A> git annex init
+	joey@darkstar:/tmp/A> touch file
+	joey@darkstar:/tmp/A> git annex add file
+	joey@darkstar:/tmp/A> git commit -m added
+	joey@darkstar:/tmp/A> cd ..
+	joey@darkstar:/tmp> git clone A B
+	joey@darkstar:/tmp> cd B
+	joey@darkstar:/tmp/B> git annex move --from origin
+	joey@darkstar:/tmp/B> git annex get # this succeeds
+	joey@darkstar:/tmp/B> cd ..
+	joey@darkstar:/tmp> cd A
+	joey@darkstar:/tmp/A> git annex get
+	get file (not available)
+	  No other repository is known to contain the file.
+	failed
+	git-annex: get: 1 failed
+
+Here repo A has out-of-date information in its git-annex branch;
+it doesn't know that the file content was moved to repo B.
+Running `git annex sync` in A does not improve matters, because A has
+no connection to repo B. But once the updated git-annex branch does
+reach repo A, it will again know:
+
+	joey@darkstar:/tmp/A> cd ../B
+	joey@darkstar:/tmp/B> git annex sync
+	joey@darkstar:/tmp/B> cd ../A
+	joey@darkstar:/tmp/B>cd ../A
+	joey@darkstar:/tmp/A>git annex get
+	get file (not available) 
+	  Try making some of these repositories available:
+	  	b7bc6700-0009-457c-b490-0531bb830d80 -- joey@darkstar:/tmp/B
+
+
+Here running git-annex sync in B was enough to get the git-annex branch
+synced up, because B has a connection to A, but in your situation,
+depending on the topology of your repositories, you may need to do
+something else.
+"""]]

make .noannex file prevent repo fixups
Avoid performing repository fixups for submodules and git-worktrees
when there's a .noannex file that will prevent git-annex from being
used in the repository.
This change is ok as long as the .noannex file is really going to prevent
git-annex from being used. But, init --force could override the file.
Which would result in the repo being initialized without the fixups
having run.
To avoid that situation decided to change init, to not let --force be used
to override a .noannex file. Instead the user can just delete the file.
diff --git a/Annex/Fixup.hs b/Annex/Fixup.hs
index 049aad3ad..6f74a1bbf 100644
--- a/Annex/Fixup.hs
+++ b/Annex/Fixup.hs
@@ -1,6 +1,6 @@
 {- git-annex repository fixups
  -
- - Copyright 2013-2018 Joey Hess <id@joeyh.name>
+ - Copyright 2013-2019 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -10,6 +10,7 @@ module Annex.Fixup where
 import Git.Types
 import Git.Config
 import Types.GitConfig
+import Config.Files
 import qualified Git.BuildVersion
 import Utility.Path
 import Utility.SafeCommand
@@ -22,6 +23,7 @@ import System.IO
 import System.FilePath
 import System.PosixCompat.Files
 import Data.List
+import Data.Maybe
 import Control.Monad
 import Control.Monad.IfElse
 import qualified Data.Map as M
@@ -80,18 +82,25 @@ fixupDirect r = r
  - When the filesystem doesn't support symlinks, we cannot make .git
  - into a symlink. But we don't need too, since the repo will use direct
  - mode.
+ -
+ - Before making any changes, check if there's a .noannex file
+ - in the repo. If that file will prevent git-annex from being used,
+ - there's no need to fix up the repository.
  -}
 fixupUnusualRepos :: Repo -> GitConfig -> IO Repo
 fixupUnusualRepos r@(Repo { location = l@(Local { worktree = Just w, gitdir = d }) }) c
-	| needsSubmoduleFixup r = do
-		when (coreSymlinks c) $
-			(replacedotgit >> unsetcoreworktree)
-				`catchNonAsync` \_e -> hPutStrLn stderr
-					"warning: unable to convert submodule to form that will work with git-annex"
-		return $ r'
-			{ config = M.delete "core.worktree" (config r)
-			}
-	| otherwise = ifM (needsGitLinkFixup r)
+	| needsSubmoduleFixup r = ifM notnoannex
+		( do
+			when (coreSymlinks c) $
+				(replacedotgit >> unsetcoreworktree)
+					`catchNonAsync` \_e -> hPutStrLn stderr
+						"warning: unable to convert submodule to form that will work with git-annex"
+			return $ r'
+				{ config = M.delete "core.worktree" (config r)
+				}
+		, return r
+		)
+	| otherwise = ifM (needsGitLinkFixup r <&&> notnoannex)
 		( do
 			when (coreSymlinks c) $
 				(replacedotgit >> worktreefixup)
@@ -131,6 +140,8 @@ fixupUnusualRepos r@(Repo { location = l@(Local { worktree = Just w, gitdir = d
 	r'
 		| coreSymlinks c = r { location = l { gitdir = dotgit } }
 		| otherwise = r
+
+	notnoannex = isNothing <$> noAnnexFileContent r
 fixupUnusualRepos r _ = return r
 
 needsSubmoduleFixup :: Repo -> Bool
diff --git a/Annex/Init.hs b/Annex/Init.hs
index 59c4ffdae..803f89adb 100644
--- a/Annex/Init.hs
+++ b/Annex/Init.hs
@@ -8,7 +8,6 @@
 {-# LANGUAGE CPP #-}
 
 module Annex.Init (
-	AutoInit(..),
 	ensureInitialized,
 	isInitialized,
 	initialize,
@@ -36,6 +35,7 @@ import Annex.UUID
 import Annex.Link
 import Annex.WorkTree
 import Config
+import Config.Files
 import Config.Smudge
 import Annex.Direct
 import qualified Annex.AdjustedBranch as AdjustedBranch
@@ -52,22 +52,14 @@ import System.Posix.User
 import qualified Utility.LockFile.Posix as Posix
 #endif
 
-newtype AutoInit = AutoInit Bool
-
-checkCanInitialize :: AutoInit -> Annex a -> Annex a
-checkCanInitialize (AutoInit True) a = a
-checkCanInitialize (AutoInit False) a = fromRepo Git.repoWorkTree >>= \case
+checkCanInitialize :: Annex a -> Annex a
+checkCanInitialize a = inRepo noAnnexFileContent >>= \case
 	Nothing -> a
-	Just wt -> liftIO (catchMaybeIO (readFile (wt </> ".noannex"))) >>= \case
-		Nothing -> a
-		Just noannexmsg -> ifM (Annex.getState Annex.force)
-			( a
-			, do
-				warning "Initialization prevented by .noannex file (use --force to override)"
-				unless (null noannexmsg) $
-					warning noannexmsg
-				giveup "Not initialized."
-			)
+	Just noannexmsg -> do
+		warning "Initialization prevented by .noannex file (remove the file to override)"
+		unless (null noannexmsg) $
+			warning noannexmsg
+		giveup "Not initialized."
 
 genDescription :: Maybe String -> Annex UUIDDesc
 genDescription (Just d) = return $ UUIDDesc $ encodeBS d
@@ -80,8 +72,8 @@ genDescription Nothing = do
 		Right username -> [username, at, hostname, ":", reldir]
 		Left _ -> [hostname, ":", reldir]
 
-initialize :: AutoInit -> Maybe String -> Maybe RepoVersion -> Annex ()
-initialize ai mdescription mversion = checkCanInitialize ai $ do
+initialize :: Maybe String -> Maybe RepoVersion -> Annex ()
+initialize mdescription mversion = checkCanInitialize $ do
 	{- Has to come before any commits are made as the shared
 	 - clone heuristic expects no local objects. -}
 	sharedclone <- checkSharedClone
@@ -91,7 +83,7 @@ initialize ai mdescription mversion = checkCanInitialize ai $ do
 	ensureCommit $ Annex.Branch.create
 
 	prepUUID
-	initialize' (AutoInit True) mversion
+	initialize' mversion
 	
 	initSharedClone sharedclone
 
@@ -100,8 +92,8 @@ initialize ai mdescription mversion = checkCanInitialize ai $ do
 
 -- Everything except for uuid setup, shared clone setup, and initial
 -- description.
-initialize' :: AutoInit -> Maybe RepoVersion -> Annex ()
-initialize' ai mversion = checkCanInitialize ai $ do
+initialize' :: Maybe RepoVersion -> Annex ()
+initialize' mversion = checkCanInitialize  $ do
 	checkLockSupport
 	checkFifoSupport
 	checkCrippledFileSystem
@@ -153,7 +145,7 @@ ensureInitialized :: Annex ()
 ensureInitialized = getVersion >>= maybe needsinit checkUpgrade
   where
 	needsinit = ifM Annex.Branch.hasSibling
-			( initialize (AutoInit True) Nothing Nothing
+			( initialize Nothing Nothing
 			, giveup "First run: git-annex init"
 			)
 
diff --git a/Annex/MakeRepo.hs b/Annex/MakeRepo.hs
index f80e30359..25565629e 100644
--- a/Annex/MakeRepo.hs
+++ b/Annex/MakeRepo.hs
@@ -77,7 +77,7 @@ initRepo False _ dir desc mgroup = inDir dir $ do
 
 initRepo' :: Maybe String -> Maybe StandardGroup -> Annex ()
 initRepo' desc mgroup = unlessM isInitialized $ do
-	initialize (AutoInit False) desc Nothing
+	initialize desc Nothing
 	u <- getUUID
 	maybe noop (defaultStandardGroup u) mgroup
 	{- Ensure branch gets committed right away so it is
diff --git a/CHANGELOG b/CHANGELOG
index f9fbff605..cf0670a4e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,11 @@ git-annex (7.20190130) UNRELEASED; urgency=medium
   * fromkey --batch output changed to support using it with --json.
     The old output was not parseable for any useful information, so
     this is not expected to break anything.
+  * Avoid performing repository fixups for submodules and git-worktrees
+    when there's a .noannex file that will prevent git-annex from being
+    used in the repository.
+  * init: Don't let --force be used to override a .noannex file,
+    instead the user can just delete the file.
 
  -- Joey Hess <id@joeyh.name>  Wed, 30 Jan 2019 12:30:22 -0400
 
diff --git a/Command/ConfigList.hs b/Command/ConfigList.hs
index df674a9ad..e8af8f53e 100644
--- a/Command/ConfigList.hs
+++ b/Command/ConfigList.hs
@@ -45,7 +45,7 @@ findOrGenUUID = do
 		else ifM (Annex.Branch.hasSibling <||> (isJust <$> Fields.getField Fields.autoInit))
 			( do
 				liftIO checkNotReadOnly
-				initialize (AutoInit True) Nothing Nothing

(Diff truncated)
same
diff --git a/doc/bugs/__34__git_update-index_--refresh_bigfile__34___fails_with___34__fatal__58___Out_of_memory__44___realloc_failed__34__/comment_1_990498b543812acb1adf206d8d7d24eb/comment_1_9deb76907a5b1182e201b0c6bc708caf._comment b/doc/bugs/__34__git_update-index_--refresh_bigfile__34___fails_with___34__fatal__58___Out_of_memory__44___realloc_failed__34__/comment_1_990498b543812acb1adf206d8d7d24eb/comment_1_9deb76907a5b1182e201b0c6bc708caf._comment
new file mode 100644
index 000000000..007cea355
--- /dev/null
+++ b/doc/bugs/__34__git_update-index_--refresh_bigfile__34___fails_with___34__fatal__58___Out_of_memory__44___realloc_failed__34__/comment_1_990498b543812acb1adf206d8d7d24eb/comment_1_9deb76907a5b1182e201b0c6bc708caf._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-05T18:04:51Z"
+ content="""
+This appears to be the same bug in git as 
+<http://git-annex.branchable.com/bugs/v7_unlock_big_file__58___out_of_memory/>
+and I suppose that the simple patch to git to make it avoid an excessive
+malloc of memory it never actually needs to use would also fix this case.
+"""]]

fromkey --json
* fromkey: Added --json.
* fromkey --batch output changed to support using it with --json.
The old output was not parseable for any useful information, so
this is not expected to break anything.
diff --git a/CHANGELOG b/CHANGELOG
index fa68ca2f1..f9fbff605 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,6 +12,10 @@ git-annex (7.20190130) UNRELEASED; urgency=medium
   * Fix race in cleanup of othertmp directory that could result in a failure
     attempting to access it.
   * fromkey: Made idempotent.
+  * fromkey: Added --json.
+  * fromkey --batch output changed to support using it with --json.
+    The old output was not parseable for any useful information, so
+    this is not expected to break anything.
 
  -- Joey Hess <id@joeyh.name>  Wed, 30 Jan 2019 12:30:22 -0400
 
diff --git a/CmdLine/Batch.hs b/CmdLine/Batch.hs
index 324552dd5..bcff74420 100644
--- a/CmdLine/Batch.hs
+++ b/CmdLine/Batch.hs
@@ -93,15 +93,15 @@ batchCommandAction a = maybe (batchBadInput (Batch BatchLine)) (const noop)
 -- to handle them.
 --
 -- File matching options are not checked.
-allBatchFiles :: BatchFormat -> (FilePath -> CommandStart) -> Annex ()
-allBatchFiles fmt a = batchInput fmt Right $ batchCommandAction . a
+batchStart :: BatchFormat -> (String -> CommandStart) -> Annex ()
+batchStart fmt a = batchInput fmt Right $ batchCommandAction . a
 
--- Like allBatchFiles, but checks the file matching options
+-- Like batchStart, but checks the file matching options
 -- and skips non-matching files.
 batchFilesMatching :: BatchFormat -> (FilePath -> CommandStart) -> Annex ()
 batchFilesMatching fmt a = do
 	matcher <- getMatcher
-	allBatchFiles fmt $ \f ->
+	batchStart fmt $ \f ->
 		ifM (matcher $ MatchingFile $ FileInfo f f)
 			( a f
 			, return Nothing
diff --git a/Command/FromKey.hs b/Command/FromKey.hs
index 401ae9225..1b066a963 100644
--- a/Command/FromKey.hs
+++ b/Command/FromKey.hs
@@ -19,7 +19,7 @@ import qualified Backend.URL
 import Network.URI
 
 cmd :: Command
-cmd = notDirect $ notBareRepo $
+cmd = notDirect $ notBareRepo $ withGlobalOptions [jsonOptions] $
 	command "fromkey" SectionPlumbing "adds a file using a specific key"
 		(paramRepeating (paramPair paramKey paramPath))
 		(seek <$$> optParser)
@@ -36,13 +36,25 @@ optParser desc = FromKeyOptions
 
 seek :: FromKeyOptions -> CommandSeek
 seek o = case (batchOption o, keyFilePairs o) of
-	(Batch fmt, _) -> commandAction $ startMass fmt
+	(Batch fmt, _) -> seekBatch fmt
 	-- older way of enabling batch input, does not support BatchNull
-	(NoBatch, []) -> commandAction $ startMass BatchLine
+	(NoBatch, []) -> seekBatch BatchLine
 	(NoBatch, ps) -> do
 		force <- Annex.getState Annex.force
 		withPairs (commandAction . start force) ps
 
+seekBatch :: BatchFormat -> CommandSeek
+seekBatch fmt = batchInput fmt parse commandAction
+  where
+	parse s = 
+		let (keyname, file) = separate (== ' ') s
+		in if not (null keyname) && not (null file)
+			then Right $ go file (mkKey keyname)
+			else Left "Expected pairs of key and filename"
+	go file key = do
+		showStart "fromkey" file
+		next $ perform key file
+
 start :: Bool -> (String, FilePath) -> CommandStart
 start force (keyname, file) = do
 	let key = mkKey keyname
@@ -53,22 +65,6 @@ start force (keyname, file) = do
 	showStart "fromkey" file
 	next $ perform key file
 
-startMass :: BatchFormat -> CommandStart
-startMass fmt = do
-	showStart' "fromkey" (Just "stdin")
-	next (massAdd fmt)
-
-massAdd :: BatchFormat -> CommandPerform
-massAdd fmt = go True =<< map (separate (== ' ')) <$> batchLines fmt
-  where
-	go status [] = next $ return status
-	go status ((keyname,f):rest) | not (null keyname) && not (null f) = do
-		let key = mkKey keyname
-		ok <- perform' key f
-		let !status' = status && ok
-		go status' rest
-	go _ _ = giveup "Expected pairs of key and file on stdin, but got something else."
-
 -- From user input to a Key.
 -- User can input either a serialized key, or an url.
 --
@@ -85,18 +81,20 @@ mkKey s = case parseURI s of
 		Nothing -> giveup $ "bad key/url " ++ s
 
 perform :: Key -> FilePath -> CommandPerform
-perform key file = do
-	ok <- perform' key file
-	next $ return ok
-
-perform' :: Key -> FilePath -> Annex Bool
-perform' key file = lookupFileNotHidden file >>= \case
-	Nothing -> do
-		link <- calcRepo $ gitAnnexLink file key
-		liftIO $ createDirectoryIfMissing True (parentDir file)
-		liftIO $ createSymbolicLink link file
-		Annex.Queue.addCommand "add" [Param "--"] [file]
-		return True
+perform key file = lookupFileNotHidden file >>= \case
+	Nothing -> ifM (liftIO $ doesFileExist file)
+		( hasothercontent
+		, do
+			link <- calcRepo $ gitAnnexLink file key
+			liftIO $ createDirectoryIfMissing True (parentDir file)
+			liftIO $ createSymbolicLink link file
+			Annex.Queue.addCommand "add" [Param "--"] [file]
+			next $ return True
+		)
 	Just k
-		| k == key -> return True
-		| otherwise -> giveup $ file ++ " already exists with different content"
+		| k == key -> next $ return True
+		| otherwise -> hasothercontent
+  where
+	hasothercontent = do
+		warning $ file ++ " already exists with different content"
+		next $ return False
diff --git a/doc/git-annex-fromkey.mdwn b/doc/git-annex-fromkey.mdwn
index ba6181dc3..bdae927e4 100644
--- a/doc/git-annex-fromkey.mdwn
+++ b/doc/git-annex-fromkey.mdwn
@@ -42,6 +42,11 @@ to do that.
   (Note that for this to be used, you have to explicitly enable batch mode
   with `--batch`)
 
+* `--json`
+
+  Enable JSON output. This is intended to be parsed by programs that use
+  git-annex. Each line of output is a JSON object.
+
 # SEE ALSO
 
 [[git-annex]](1)
diff --git a/doc/todo/batch_command_result_status/comment_1_78290ab147983ab8efdeb21b4ae53832._comment b/doc/todo/batch_command_result_status/comment_1_78290ab147983ab8efdeb21b4ae53832._comment
new file mode 100644
index 000000000..551bedab0
--- /dev/null
+++ b/doc/todo/batch_command_result_status/comment_1_78290ab147983ab8efdeb21b4ae53832._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-05T17:20:40Z"
+ content="""
+Many commands do have an interface that works like that. For example
+`git annex get --batch --json` has json output and does not stop when a get
+fails.
+
+So this comes down to specific behaviors of specific commands.
+I've added --json to fromkey.
+
+I don't think that setpresentkey can fail, unless there's a disk IO error
+or something?
+
+Any others?
+"""]]

fromkey: Made idempotent
If the worktree file already exists, and is annexed and uses the same
key, avoid failing, nothing needs to be done.
Had to add lookupFileNotHidden to handle the case where an adjust --hide-missing
is in use, and the worktree file was hidden due to the object content
being missing. lookupFile would return the key of the hidden file,
but it makes sense that after fromkey succeeds, the worktree must
contain the file it was supposed to set up.
diff --git a/Annex/WorkTree.hs b/Annex/WorkTree.hs
index fc01daef1..6dcd75f66 100644
--- a/Annex/WorkTree.hs
+++ b/Annex/WorkTree.hs
@@ -1,6 +1,6 @@
 {- git-annex worktree files
  -
- - Copyright 2013-2018 Joey Hess <id@joeyh.name>
+ - Copyright 2013-2019 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -36,13 +36,28 @@ import qualified Database.Keys.SQL
  - pointer to a key in the original branch.
  -}
 lookupFile :: FilePath -> Annex (Maybe Key)
-lookupFile file = isAnnexLink file >>= \case
-	Just key -> return (Just key)
-	Nothing -> ifM (versionSupportsUnlockedPointers <||> isDirect)
-		( ifM (liftIO $ doesFileExist file)
+lookupFile = lookupFile' catkeyfile
+  where
+	catkeyfile file =
+		ifM (liftIO $ doesFileExist file)
 			( catKeyFile file
 			, catKeyFileHidden file =<< getCurrentBranch
 			)
+
+lookupFileNotHidden :: FilePath -> Annex (Maybe Key)
+lookupFileNotHidden = lookupFile' catkeyfile
+  where
+	catkeyfile file =
+		ifM (liftIO $ doesFileExist file)
+			( catKeyFile file
+			, return Nothing
+			)
+
+lookupFile' :: (FilePath -> Annex (Maybe Key)) -> FilePath -> Annex (Maybe Key)
+lookupFile' catkeyfile file = isAnnexLink file >>= \case
+	Just key -> return (Just key)
+	Nothing -> ifM (versionSupportsUnlockedPointers <||> isDirect)
+		( catkeyfile file
 		, return Nothing 
 		)
 
diff --git a/CHANGELOG b/CHANGELOG
index 3e691dfd3..fa68ca2f1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,6 +11,7 @@ git-annex (7.20190130) UNRELEASED; urgency=medium
   * Display progress bar when getting files from export remotes.
   * Fix race in cleanup of othertmp directory that could result in a failure
     attempting to access it.
+  * fromkey: Made idempotent.
 
  -- Joey Hess <id@joeyh.name>  Wed, 30 Jan 2019 12:30:22 -0400
 
diff --git a/Command/FromKey.hs b/Command/FromKey.hs
index 4e1bc4fca..401ae9225 100644
--- a/Command/FromKey.hs
+++ b/Command/FromKey.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010-2018 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2019 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU GPL version 3 or higher.
  -}
@@ -12,6 +12,7 @@ module Command.FromKey where
 import Command
 import qualified Annex.Queue
 import Annex.Content
+import Annex.WorkTree
 import qualified Annex
 import qualified Backend.URL
 
@@ -89,9 +90,13 @@ perform key file = do
 	next $ return ok
 
 perform' :: Key -> FilePath -> Annex Bool
-perform' key file = do
-	link <- calcRepo $ gitAnnexLink file key
-	liftIO $ createDirectoryIfMissing True (parentDir file)
-	liftIO $ createSymbolicLink link file
-	Annex.Queue.addCommand "add" [Param "--"] [file]
-	return True
+perform' key file = lookupFileNotHidden file >>= \case
+	Nothing -> do
+		link <- calcRepo $ gitAnnexLink file key
+		liftIO $ createDirectoryIfMissing True (parentDir file)
+		liftIO $ createSymbolicLink link file
+		Annex.Queue.addCommand "add" [Param "--"] [file]
+		return True
+	Just k
+		| k == key -> return True
+		| otherwise -> giveup $ file ++ " already exists with different content"
diff --git a/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn b/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn
index 743c027cf..17414993b 100644
--- a/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn
+++ b/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn
@@ -1 +1,3 @@
 Currently, git-annex-fromkey errors if the target file exists.  It would help if, when the target file is the same symlink that would be created by the command (i.e. to the same key), this wasn't considered an error.  Together with [[todo/batch_command_result_status]] this would make the command a more robust building block for higher-level operations.
+
+> idempotency is good, [[done]] --[[Joey]]

response
diff --git a/doc/forum/standalone_tarballs_for_specific_versions/comment_1_945d08e40897ee964be5e6945ad9914a._comment b/doc/forum/standalone_tarballs_for_specific_versions/comment_1_945d08e40897ee964be5e6945ad9914a._comment
new file mode 100644
index 000000000..38e9c4a5d
--- /dev/null
+++ b/doc/forum/standalone_tarballs_for_specific_versions/comment_1_945d08e40897ee964be5e6945ad9914a._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2019-02-05T16:43:09Z"
+ content="""
+The git-annex repository https://downloads.kitenet.net/.git/ keeps track of
+all the old versions, which get moved to archive.org eventually to free up
+space on my server. So it would be possible to build such a page from the
+information in that repository. Or just look up the archive.org url for
+the version of the file that you want using git annex whereis.
+"""]]

stand-alone tarballs for specific versions
diff --git a/doc/forum/standalone_tarballs_for_specific_versions.mdwn b/doc/forum/standalone_tarballs_for_specific_versions.mdwn
new file mode 100644
index 000000000..b1d0b5acb
--- /dev/null
+++ b/doc/forum/standalone_tarballs_for_specific_versions.mdwn
@@ -0,0 +1,2 @@
+The page at https://git-annex.branchable.com/install/Linux_standalone/ has a link to the tarball for the standalone git-annex for the most recent version.
+Is it possible to make a page with link to stand-alone builds for specific git-annex versions?  This is for creating a conda-forge recipe, which requires a pointer to a stable URL for a given version.

diff --git a/doc/tips/using_box.com_as_a_special_remote.mdwn b/doc/tips/using_box.com_as_a_special_remote.mdwn
index a20561d56..e1cf8b8fe 100644
--- a/doc/tips/using_box.com_as_a_special_remote.mdwn
+++ b/doc/tips/using_box.com_as_a_special_remote.mdwn
@@ -1,6 +1,6 @@
 [Box.com](http://box.com/) is a file storage service.
 
-** WebDAV access to box.com will be deprecated on January 31, 2019. At that point, the method described on this page will no longer work. See [this announcement](https://community.box.com/t5/Box-Product-News/Deprecation-WebDAV-Support/ba-p/55684) for further details. **
+** WebDAV access to box.com will be deprecated at some point in the near future (originally was scheduled to be January 31, 2019 - but it has been pushed back to a yet to be defined date). At that point, the method described on this page will no longer work. See [this announcement](https://community.box.com/t5/Box-Product-News/Deprecation-WebDAV-Support/ba-p/55684) for further details. **
 
 git-annex can use Box as a [[special remote|special_remotes]].
 Recent versions of git-annex make this very easy to set up

diff --git a/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__.mdwn b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__.mdwn
new file mode 100644
index 000000000..fed743e3f
--- /dev/null
+++ b/doc/forum/Git-annex_with_dumb_FAT32_devices_not_a_valid_use_case__63__.mdwn
@@ -0,0 +1,5 @@
+Am I right in concluding that using an array of limited storage devices that require FAT32 is not a valid use case for git-annex?
+
+Dumb devices that require FAT32, that need all present files to be unlocked in order to not choke on them (i.e. unlike with git-annex’ text-files-as-symlinks on crippled filesystems), that also therefore need missing files to be hidden, and that are storage space-constrained, I would say are extremely common. Yet, if I understood correctly, v7 repos are required for being able to hiding missing files, but at the same time, on v7 and FAT32 filesystems, files that are present take up double their file size.
+
+Am I right, or I am missing a way to use those?

git-annex-fromkey behavior when target file exists
diff --git a/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn b/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn
new file mode 100644
index 000000000..743c027cf
--- /dev/null
+++ b/doc/todo/git-annex-fromkey_behavior_when_target_file_exists.mdwn
@@ -0,0 +1 @@
+Currently, git-annex-fromkey errors if the target file exists.  It would help if, when the target file is the same symlink that would be created by the command (i.e. to the same key), this wasn't considered an error.  Together with [[todo/batch_command_result_status]] this would make the command a more robust building block for higher-level operations.

fix broken link
diff --git a/doc/devblog/day_571__survey_results.mdwn b/doc/devblog/day_571__survey_results.mdwn
index 1e75f4ebc..3587f9ce7 100644
--- a/doc/devblog/day_571__survey_results.mdwn
+++ b/doc/devblog/day_571__survey_results.mdwn
@@ -7,7 +7,7 @@ trends as well.
 
 Very similar numbers of people responded in 2018 as in 2015.
 The 2013 survey remains a high water mark in participation.
-[My thoughts on the 2015 survey](day_360__results_of_2015_user_survey)
+[[My thoughts on the 2015 survey|day_360__results_of_2015_user_survey]]
 participation level mostly still stand, although there has been a
 consistent downwards trend in [Debian popcon](https://qa.debian.org/popcon-graph.php?packages=git-annex&show_installed=on&want_legend=on&want_ticks=on&date_fmt=%25Y-%25m&beenhere=1)
 since 2015.

fix image link
diff --git a/doc/devblog/day_571__survey_results.mdwn b/doc/devblog/day_571__survey_results.mdwn
index 494cd168e..1e75f4ebc 100644
--- a/doc/devblog/day_571__survey_results.mdwn
+++ b/doc/devblog/day_571__survey_results.mdwn
@@ -27,7 +27,7 @@ decent progress toward eliminating the need for direct mode.
 
 ## command line vs assistant
 
-<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/num_respondants.png">
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/command_line_vs_assistant.png">
 
 Well that's plain enough isn't it? Although note that I myself have
 the assistant running in some repos all the time, but would of course

devblog
diff --git a/doc/devblog/day_571__survey_results.mdwn b/doc/devblog/day_571__survey_results.mdwn
new file mode 100644
index 000000000..494cd168e
--- /dev/null
+++ b/doc/devblog/day_571__survey_results.mdwn
@@ -0,0 +1,173 @@
+The [2018 user's survey](http://git-annex-survey.branchable.com/polls/2018/) 
+is closed, time for a look at the results. Several of the questions were
+also on the two past surveys, so we can start to look at historical
+trends as well.
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/num_respondants.png">
+
+Very similar numbers of people responded in 2018 as in 2015.
+The 2013 survey remains a high water mark in participation.
+[My thoughts on the 2015 survey](day_360__results_of_2015_user_survey)
+participation level mostly still stand, although there has been a
+consistent downwards trend in [Debian popcon](https://qa.debian.org/popcon-graph.php?packages=git-annex&show_installed=on&want_legend=on&want_ticks=on&date_fmt=%25Y-%25m&beenhere=1)
+since 2015.
+
+Also interesting that several people skipped the first question on the
+survey, perhaps because it was a fairly challenging question? And later
+questions saw much higher response rates this time than in either of the
+previous surveys, thanks to improvements in the survey interface.
+
+## v7
+
+v7 unlocked files are being used by 7% of users, pretty impressive uptake
+for a feature that has only been really finished for a couple of months.
+Direct mode is still used by 7% of users, while its v7 replacement of
+adjusted unlocked branches is only used by 1% so far. That's still some
+decent progress toward eliminating the need for direct mode.
+
+## command line vs assistant
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/num_respondants.png">
+
+Well that's plain enough isn't it? Although note that I myself have
+the assistant running in some repos all the time, but would of course
+vote "command line" since I interact with that much more.
+
+Also notice that people who apparently don't use git-annex
+but wanted to fill out the survey anyway was the same for 2013-2015,
+but has now declined.
+
+## operating system
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/operating_system.png">
+
+Android users have more or less gone away since I deprecated the app.
+I hope the termux integration brings some back.
+
+## how git-annex is installed
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/how_installed.png">
+
+Good to see the increase in using git-annex packages from the OS
+or a third-party package manager.
+
+## missing/incomplete ports
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/missing_ports.png">
+
+Good improvement here since 2015 with 60% now satisfied with available
+ports.
+
+Worth noting that in 2013, 6% wanted a way to use git-annex on
+Synology NAS. That is possible now via the standalone linux tarball.
+This year, 2% wanted "Synology NAS (app store package)".
+
+Also honorable mention to the anonymous person who rewrote
+git-annex in another language. You should release the code!
+
+## number of repositories
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/number_of_repositories.png">
+
+Increasingly users seem to have just a couple repositories or a large
+number, with the middle ground shrinking. A few percent have 200+ 
+repositories now. The sense is of a split between causual users who
+perhaps clone one repository to a few places, and power users who
+are adding new repositories over time.
+
+## data stored in git-annex
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/size_of_annex.png">
+
+Increasing growth in the high end with many users storing dozens of terabytes
+of data in git-annex and a couple storing more than 64 terabytes. 
+And a bit of growth in the low end storing under 100 gb.
+
+The total data stored in git-annex looks to be around 650-1300 terabytes
+now. It was around 150-300 terabytes in 2013. That doesn't count redundant
+data. And it could be off slightly if shared repositories were reported by
+multiple users.
+
+(Compare with the Internet Archive, which was 15000 terabytes in 2016
+but I think they keep two copies of everything, so call it 7000
+terabytes of unique data.)
+
+## git level
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/git_level.png">
+
+The same question was asked in the [git surveys](https://git.wiki.kernel.org/index.php/GitSurvey2016)
+so I have included those in the graph for comparison.
+
+git-annex users trend more experienced than git users,
+which is not surprising. You have to know some stuff about 
+git to understand why you'd want to use git-annex.
+
+Notice that git knowledge level is generally going up over time in both
+surveys.
+
+## happyness with the software
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/happyness.png">
+
+A similar question on the git survey included for comparison.
+
+There's a bimodal distribution to git-annex user's happyness,
+with more unhappy with it than with git, but also more so happy
+they gravitate toward extreme praise.
+
+There seem to be more unhappy users in 2018 than in 2015 though.
+The 2018 results are very close to the 2013 results.
+
+## blocking problems
+
+Notably 15% of users now find git-annex too hard to use, up from 5% in
+2015. Which seems to correlate with some users being more unhappy with it.
+I don't think git-annex has gotten any harder to use, so this must
+reflect a change in expectations and/or demographics. (2013 had similar
+numbers to 2018.)
+
+Very few complain about the documentation now, down to 3% from 13% in 2015,
+but 12% want to see more tutorials showing how to tie the features
+together.
+
+And a staggering 21% picked a write-in, "no issues personally, but people
+don't see (or realize they need) the immense benefits it provides".
+Need to find better ways to market git-annex, essentially.
+
+## size of group using git-annex together
+
+A similar distribution to 2015. One person said they're using git-annex in
+a group of 50+, and 5 reported groups larger than 10 people.
+
+## scientific data
+
+A new high of 11% of respondants are using git-annex to store scientific data.
+(Other kinds of data it's used for seem more or less the same.)
+
+Part of that growth is because of the companion 
+[2018 git-annex scientific data survey](http://git-annex-survey.branchable.com/polls/2018_science/)
+which was promoted in some scientific communities, and
+so brought more scientists to the main survey.
+
+<img src="https://git-annex-survey.branchable.com/polls/2018/graphs/field_of_science.png">
+
+The use for neuroscience is no surprise, but so much use for astronomy and
+physics is. And "other" in that pie chart includes statistics,
+social sciences, mathematics, education, linguistics, biomedical
+engineering, EE, and physiology -- wow!
+
+## survey reach
+
+All participants in the science survey did go on to answer at least part of
+the main survey. So 37% of respondants to the main survey are scientists.
+
+A full 27% of survey respondants have their name on the thanks
+page, many for financial support. Which is really great, but also speaks to
+the fraction of the git-annex user base who saw the survey,
+because I really doubt that a quarter of the users of *any* free software
+are financially supporting it. 
+
+As with any online survey, the results are skewed by who bothers to answer it.
+Still, a lot of useful information to mull over.
+

diff --git a/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync.mdwn b/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync.mdwn
new file mode 100644
index 000000000..b92c995d3
--- /dev/null
+++ b/doc/bugs/fsck_is_required_to_resolve___34__no_other_repository_is_known_to_contain_the_file__34___after_sync.mdwn
@@ -0,0 +1,19 @@
+### Please describe the problem.
+We use git annex and found an issue where one machine that had run 'git annex sync' and 'git annex sync --content' was not reporting any issues, but any other machines were reporting 99 issues when running 'git annex get'.
+
+The message for each file was that it was not known to exist on any repo. Doing some searching I found 'git annex fsck' so ran that in the following fashion on the 'broken' machines: 'git annex fsck --from=LocalMirrorName --fast'. During this we saw git was updating the location log for nearly all the missing files. Running 'git annex get' afterwards resolved the issue and got the files. We found we had to repeat this on all affected machines.
+
+### What steps will reproduce the problem?
+We've not been able to reproduce a brand new stance of the problem (and don't want to, rightly so). Some other machines still have this issue where we have not run the fsck.
+
+### What version of git-annex are you using? On what operating system?
+The machine that did the original sync was running git 2.7.4 on Ubuntu 16 and other
+machines were either running the same, or 2.17.1 on Ubuntu 18.
+
+### Please provide any additional information below.
+A test I also tried: I copied one annex file from inside the .git folder of the original machine that ran the sync, and added it to the same location on a broken machine, then ran sync and get, and it reported 98 issues instead.
+
+Does this seem like a bug with git annex? Is there a possible gap in the way that git annex reports that all is fine - perhaps to do with the index? Or does this seem to be more likely an issue we created ourselves? Would there have been any other commands we could have run to resolve this issue or should have tried out first? (Apart from just deleting each of our clones and starting again)
+
+### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
+Git annex works for us the majority of the time and solves a lot of space issues for us in a unique way. We do get confused by it sometimes, but that's probably our implementation of it and not knowing enough about it across our team (seems to be a set and forget approach).

Added a comment
diff --git a/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_1_5d45ee72f91af9ec8384b178af77bc2d._comment b/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_1_5d45ee72f91af9ec8384b178af77bc2d._comment
new file mode 100644
index 000000000..63cf5695f
--- /dev/null
+++ b/doc/forum/Use_case__58___backup_on_multiple_USB_Drives/comment_1_5d45ee72f91af9ec8384b178af77bc2d._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="andrew"
+ avatar="http://cdn.libravatar.org/avatar/acc0ece1eedf07dd9631e7d7d343c435"
+ subject="comment 1"
+ date="2019-02-01T01:51:16Z"
+ content="""
+Are you using the assistant or command-line?
+"""]]

close
diff --git a/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn b/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn
index 0b5c3c8be..aaa65a87b 100644
--- a/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn
+++ b/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn
@@ -4,3 +4,5 @@ retrieval for export remotes.
 Probably Remote.Helper.Special ought to do this, since it already deals
 with setting up progress displays for normal storage and retrieval when
 requested by the remote. --[[Joey]]
+
+> [[fixed|done]] --[[Joey]]

reporting the status of each operation in a batch; completing doable ops even if others fail.
diff --git a/doc/todo/batch_command_result_status.mdwn b/doc/todo/batch_command_result_status.mdwn
new file mode 100644
index 000000000..a660dfdbc
--- /dev/null
+++ b/doc/todo/batch_command_result_status.mdwn
@@ -0,0 +1 @@
+For batch commands like fromkey and setpresentkey, when some operations in a batch succeed and others fail, it's hard to know which ones were done.  It would be good if the operations had an option to output the status to stdout or to a file, maybe in json format.  It would also be good if there was a --keep-going option to try each operation even if some early ones fail.  Currently, when some operations fail, the set of doable operations that gets done depends on the input order, and there is no reliable way to know which ones succeeded.  

devblog
diff --git a/doc/devblog/day_570__brrr.mdwn b/doc/devblog/day_570__brrr.mdwn
new file mode 100644
index 000000000..2fa21132a
--- /dev/null
+++ b/doc/devblog/day_570__brrr.mdwn
@@ -0,0 +1,20 @@
+Started off the day with some more improvements and bug fixes for export
+remotes.
+
+Then I noticed that there is no progress displayed for transfers to export
+remotes; it seems I forgot to wire that up. That really ought to be handled
+by the special remote setup code, the same way it is for non-export
+remotes. But it was not possible to do it there the way that export actions
+are structured.
+
+I got sidetracked with how S3 prepares a handle to the server. That didn't
+work as well as it might have; most of the time each request to the remote
+actually prepared a new handle, rather than reusing a single handle. Though
+the http connection to the server did get reused, that still caused a lot
+of unncessary work. I fixed that, and the fix also allowed me to
+restructure export actions in the way I need for progress bars.
+
+I've ran out of time to finish adding the missing progress bars today, so
+I'll do it tomorrow.
+
+Today's work was sponsored by Jake Vosloo [on Patreon](https://patreon.com/joeyh).

bug report
diff --git a/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn b/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn
new file mode 100644
index 000000000..0b5c3c8be
--- /dev/null
+++ b/doc/bugs/no_progress_display_for_transfer_to_or_from_export_remote.mdwn
@@ -0,0 +1,6 @@
+Seems that progress displays are not set up for storage and 
+retrieval for export remotes.
+
+Probably Remote.Helper.Special ought to do this, since it already deals
+with setting up progress displays for normal storage and retrieval when
+requested by the remote. --[[Joey]]

Added a comment: Ah, ok.
diff --git a/doc/forum/How_to_view_progress_of_in-progress_upload__63__/comment_2_6347e08b599239b6416957e9a7114157._comment b/doc/forum/How_to_view_progress_of_in-progress_upload__63__/comment_2_6347e08b599239b6416957e9a7114157._comment
new file mode 100644
index 000000000..e3491dc91
--- /dev/null
+++ b/doc/forum/How_to_view_progress_of_in-progress_upload__63__/comment_2_6347e08b599239b6416957e9a7114157._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="aaron@784fd85903b38afc8a44d117ba95e3dcce0bd230"
+ nickname="aaron"
+ avatar="http://cdn.libravatar.org/avatar/5675f02765c91919c6a70f2a71681925"
+ subject="Ah, ok."
+ date="2019-01-30T16:36:12Z"
+ content="""
+Thank you for the clarification.
+"""]]

devblog
diff --git a/doc/devblog/day_569__another_week_another_release.mdwn b/doc/devblog/day_569__another_week_another_release.mdwn
new file mode 100644
index 000000000..232671a34
--- /dev/null
+++ b/doc/devblog/day_569__another_week_another_release.mdwn
@@ -0,0 +1,11 @@
+Today's release is to fix a
+[data loss bug](http://git-annex.branchable.com/bugs/potential_data_loss_after_late_enabling_of_S3_versioning/),
+that affects S3 remotes configured with exporttree=yes that got versioning=yes
+turned on after some unversioned data is stored in them. If you use the new
+versioning=yes feature with S3, please upgrade.
+
+Also, there are only two days left to fill out the
+[git-annex user survey](http://git-annex-survey.branchable.com/polls/2018/)
+if you have not already.
+
+Today's work was sponsored by Jake Vosloo [on Patreon](https://patreon.com/joeyh/).

add news item for git-annex 7.20190129
diff --git a/doc/news/version_7.20181105.mdwn b/doc/news/version_7.20181105.mdwn
deleted file mode 100644
index 0e26fc585..000000000
--- a/doc/news/version_7.20181105.mdwn
+++ /dev/null
@@ -1,8 +0,0 @@
-git-annex 7.20181105 released with [[!toggle text="these changes"]]
-[[!toggleable text="""
-   * Fix test suite failure when git-annex test is not run inside a git
-     repository.
-   * Fix a P2P protocol hang.
-   * importfeed: Avoid erroring out when a feed has been repeatedly broken,
-     as that can leave other imported files not checked into git.
-   * Increase minimum QuickCheck version."""]]
\ No newline at end of file
diff --git a/doc/news/version_7.20190129.mdwn b/doc/news/version_7.20190129.mdwn
new file mode 100644
index 000000000..7c4087e72
--- /dev/null
+++ b/doc/news/version_7.20190129.mdwn
@@ -0,0 +1,12 @@
+git-annex 7.20190129 released with [[!toggle text="these changes"]]
+[[!toggleable text="""
+   * initremote S3: When configured with versioning=yes, either ask the user
+     to enable bucket versioning, or auto-enable it when built with aws-0.22.
+   * enableremote S3: Do not let versioning=yes be set on existing remote,
+     because when git-annex lacks S3 version IDs for files stored in
+     the bucket, deleting them would cause data loss.
+   * S3: Detect when version=yes but an exported file lacks a S3 version ID,
+     and refuse to delete it, to avoid data loss.
+   * S3: Send a Content-Type header when storing objects in S3,
+     so exports to public buckets can be linked to from web pages.
+     (When git-annex is built with MagicMime support.)"""]]
\ No newline at end of file

S3: Detect when version=yes but an exported file lacks versioning, and refuse to delete it, to avoid data loss.
This commit was sponsored by Denis Dzyubenko on Patreon.
diff --git a/CHANGELOG b/CHANGELOG
index 74f187667..70d9caadb 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,8 @@ git-annex (7.20190123) UNRELEASED; urgency=medium
   * enableremote S3: Do not let versioning=yes be set on existing remote,
     because when git-annex lacks S3 version IDs for files stored in
     the bucket, deleting them would cause data loss.
+  * S3: Detect when version=yes but an exported file lacks versioning,
+    and refuse to delete it, to avoid data loss.
 
  -- Joey Hess <id@joeyh.name>  Wed, 23 Jan 2019 12:54:56 -0400
 
diff --git a/Remote/S3.hs b/Remote/S3.hs
index d97d37876..19ac6dcd3 100644
--- a/Remote/S3.hs
+++ b/Remote/S3.hs
@@ -383,7 +383,7 @@ retrieveExportS3 u info mh _k loc f p =
 	exporturl = bucketExportLocation info loc
 
 removeExportS3 :: UUID -> S3Info -> Maybe S3Handle -> Key -> ExportLocation -> Annex Bool
-removeExportS3 _u info (Just h) _k loc = 
+removeExportS3 u info (Just h) k loc = checkVersioning info u $
 	catchNonAsync go (\e -> warning (show e) >> return False)
   where
 	go = do
@@ -406,7 +406,8 @@ checkPresentExportS3 u info Nothing k loc = case getPublicUrlMaker info of
 
 -- S3 has no move primitive; copy and delete.
 renameExportS3 :: UUID -> S3Info -> Maybe S3Handle -> Key -> ExportLocation -> ExportLocation -> Annex Bool
-renameExportS3 _u info (Just h) _k src dest = catchNonAsync go (\_ -> return False)
+renameExportS3 u info (Just h) k src dest = checkVersioning info u k src $ 
+	catchNonAsync go (\_ -> return False)
   where
 	go = do
 		let co = S3.copyObject (bucket info) dstobject
@@ -887,3 +888,22 @@ enableBucketVersioning ss c gc u = do
 			, "It's important you enable versioning before storing anything in the bucket!"
 			]
 #endif
+
+-- If the remote has versioning enabled, but the version ID is for some
+-- reason not being recorded, it's not safe to perform an action that
+-- will remove the unversioned file. The file may be the only copy of an
+-- annex object.
+--
+-- This code could be removed eventually, since enableBucketVersioning
+-- will avoid this situation. Before that was added, some remotes
+-- were created without versioning, some unversioned files exported to
+-- them, and then versioning enabled, and this is to avoid data loss in
+-- those cases.
+checkVersioning :: S3Info -> UUID -> Key -> Annex Bool -> Annex Bool
+checkVersioning info u k a
+	| versioning info = getS3VersionID u k >>= \case
+		[] -> do
+			warning $ "Remote is configured to use versioning, but no S3 version ID is recorded for this key, so it cannot safely be modified."
+			return False
+		_ -> a
+	| otherwise = a
diff --git a/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn b/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
index 44d3347b8..be83852b9 100644
--- a/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
+++ b/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
@@ -3,10 +3,6 @@ it, and then it's later changed to also have versioning=yes, an exporttree
 that removes some of the original files can lose the only remaining copy of
 them.
 
-exporttree does not currently check numcopies before removing from an
-export. Normally all export remotes are untrusted, so they can't count as a
-copy, and so removing something from them cannot violate numcopies.
-
 An appendonly remote, such as S3 with exporttree=yes, is supposed to not
 let git-annex remove content from it. So such a remote can be not
 untrusted, and exporttree can remove content from its exported tree without
@@ -19,25 +15,31 @@ appendonly remote. When exporttree removes a file from that S3 remote,
 it could have contained the only copy of the file, and it may not have
 versioning info for that file, so the only copy is lost.
 
-S3 remotes could refuse to allow versioning=yes to be set during
-enableremote, and only allow it at initremote time. And check that the
-bucket does indeed have versioning enabled or refuse to allow that
-configuration. That would avoid the problem.
+## Migration advice for users affected by this bug
+
+If you think your S3 remote may be affected by this problem, you should
+immediately set it to untrusted to avoid data loss: 
+`git annex untrust $mys3remotename`
+
+If you see a warning message "Remote is configured to use versioning, but no S3 version ID is recorded for this key", 
+your S3 remote is affected.
 
-(Unless the user changed the bucket configuration later to not allow
-versioning. But if they did so, and an old version of the bucket was the
-only place a file was stored, they would lose data without git-annex being
-run at all, so it's equivilant to them deleting the bucket, so this seems
-not something it needs to worry about).
+Also, the fixed git-annex (version 7.20190129) will detect the problem,
+and refuse to delete unversioned files from your versioned S3 bucket.
 
-Plan:
+This will leave you with a S3 remote containing some versioned and some
+unversioned files. Kind of a mess. Best thing to do is to make a new
+S3 remote, with versioning=yes exporttree=yes set from the beginning,
+and copy all the content that was in the old S3 remote over to it.
+Then you can delete the old S3 bucket, and use `git annex dead` to
+make git-annex stop using it.
+
+## Fix
 
-* Wait for the PutBucketVersioning pull request to be merged.
-  (Done, not in a release yet, but will probably be aws-0.22)
 * Auto-enable versioning during initremote (and not enableremote)
   when versioning=yes. (Or prompt user to do it when aws is too old.)
-  (Done)
 * Do not allow changing versioning= during enableremote.
-* Any repos that previously enabled versioning after storing some
-  unversioned files still are at risk of data loss. Detect this
-  case and treat them as versioning=no. How?
+* Make removeExport and renameExport check that
+  there is a S3 version ID known, and fail if not.
+
+[[done]] --[[Joey]]

update
diff --git a/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn b/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
index b924ec393..44d3347b8 100644
--- a/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
+++ b/doc/bugs/potential_data_loss_after_late_enabling_of_S3_versioning.mdwn
@@ -19,11 +19,6 @@ appendonly remote. When exporttree removes a file from that S3 remote,
 it could have contained the only copy of the file, and it may not have
 versioning info for that file, so the only copy is lost.
 
-So are those requirements wrong, or is the S3 remote wrong? In either case,
-something needs to be done to prevent this situation from losing data.
-
-# change S3
-
 S3 remotes could refuse to allow versioning=yes to be set during
 enableremote, and only allow it at initremote time. And check that the
 bucket does indeed have versioning enabled or refuse to allow that
@@ -46,39 +41,3 @@ Plan:
 * Any repos that previously enabled versioning after storing some
   unversioned files still are at risk of data loss. Detect this
   case and treat them as versioning=no. How?
-
-# change exporttree
-
-Exporttree could do some kind of check, but the regular numcopies drop check
-doesn't seem right for this situation. 
-
-The regular pre-drop check with an untrusted export remote would fail when
-a file's content was removed from the local repo (or was overwritten with
-annex.thin eg), even though the export remote, being untrusted, can't be
-guaranteed to continue to have a copy of the file. It would add some
-measure of safety, but not a strong guarantee of not losing the last copy,
-in that situation. To continue with the export, the user would need to
-first somehow download the object from from the export remote. The UI for
-that is lacking.
-
-The regular pre-drop check with a versioned S3 export remote is just wrong,
-because the content is not normally going to be removed from that remote,
-due to the versioning.
-
-Hmm, the versioned S3 remote could check, in removeExport 
-that the keys being removed have S3 versioning information available. If
-not, refuse to remove, or treat this removal as a drop, verifying numcopies
-first.
-(Luckily S3 does not implement removeExportDirectory so don't need to handle
-that much harder case.)
-
-Need to consider export split brain situations. If repo A has updated a
-file in the S3 export, and has not yet synced to repo B, and repo B is
-removing the file, it would check the wrong key.
-
-Also, repo A and repo B may both run storeExport using different keys, 
-and one overwrite the other, which cannot be detected by either until they
-merge. It's always possible to lose an object this way, if no S3 version ID was
-recorded.
-
-This approach is not looking promising..

remove manual versioning mention from here
git-annex will tell the user if they need to manually enable versioning
diff --git a/doc/special_remotes/S3.mdwn b/doc/special_remotes/S3.mdwn
index c3b650b1e..73cfa4af0 100644
--- a/doc/special_remotes/S3.mdwn
+++ b/doc/special_remotes/S3.mdwn
@@ -76,7 +76,6 @@ the S3 remote.
   It will not be usable as a general-purpose special remote.
 
 * `versioning` - Setting this to "yes" along with "exporttree=yes",
-  and [manually enabling versioning for the S3 bucket in the AWS console](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/enable-versioning.html)
   allows git-annex to access old versions of files that were 
   exported to the special remote by [[git-annex export|git-annex-export]].
 

doc/git-annex-export: fix a typo
diff --git a/doc/git-annex-export.mdwn b/doc/git-annex-export.mdwn
index 6c963990b..0a55ea9d5 100644
--- a/doc/git-annex-export.mdwn
+++ b/doc/git-annex-export.mdwn
@@ -41,7 +41,7 @@ And, git-annex will never trust an export to retain the content of a key.
 However, some special remotes, notably S3, support keeping track of old
 versions of files stored in them. If a special remote is set up to do 
 that, it can be used as a key/value store and the limitations in the above
-paragraph do not appy. Note that dropping content from such a remote is
+paragraph do not apply. Note that dropping content from such a remote is
 not supported. See individual special remotes' documentation for
 details of how to enable such versioning.