Recent changes to this wiki:

Added a comment: comment 2
diff --git a/doc/bugs/diff_driver_using_hash_without_subdir/comment_2_b89b1a8f84dff3730d80383e05d03619._comment b/doc/bugs/diff_driver_using_hash_without_subdir/comment_2_b89b1a8f84dff3730d80383e05d03619._comment
new file mode 100644
index 000000000..c541f7186
--- /dev/null
+++ b/doc/bugs/diff_driver_using_hash_without_subdir/comment_2_b89b1a8f84dff3730d80383e05d03619._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="bmx007@171b90624bc8f788a2a925a00b98aef5942e4787"
+ nickname="bmx007"
+ avatar="http://cdn.libravatar.org/avatar/d3a5bd12fe6d876527a3cf4ac0de5fc6"
+ subject="comment 2"
+ date="2021-09-24T20:08:22Z"
+ content="""
+I am using  `git annex diff-driver`. What do you mean by unlocked ?
+The file `$1` is unlocked indeed but it works, the problem is `$2` which is from `HEAD`.
+I'm not sure how a file from the git history could be unlocked ?
+"""]]

formatting
diff --git a/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment b/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment
index aaddce9b7..fbe5b2ac6 100644
--- a/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment
+++ b/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment
@@ -4,7 +4,7 @@
  date="2021-09-24T18:17:59Z"
  content="""
 I think the file in your repo is unlocked. An unlocked file is represented
-in git as a pointer file, the pointer contains "/annex/objects/<key>".
+in git as a pointer file, the pointer contains `"/annex/objects/<key>"`.
 
 Note that git-annex diffdriver exists to let a diff driver work with
 annexed files. I think you might want to use it. Amusingly

respond and close as not a bug
diff --git a/doc/bugs/diff_driver_using_hash_without_subdir.mdwn b/doc/bugs/diff_driver_using_hash_without_subdir.mdwn
index 948251dca..b87dcad51 100644
--- a/doc/bugs/diff_driver_using_hash_without_subdir.mdwn
+++ b/doc/bugs/diff_driver_using_hash_without_subdir.mdwn
@@ -75,3 +75,4 @@ echo diff -d --color=always $2 $target
 Yes and it works really well (and my diff driver works on other repos too).
 
 
+> [[notabug|done]]
diff --git a/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment b/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment
new file mode 100644
index 000000000..aaddce9b7
--- /dev/null
+++ b/doc/bugs/diff_driver_using_hash_without_subdir/comment_1_90246cb1d4dfc4577a1117ddd619b06a._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-24T18:17:59Z"
+ content="""
+I think the file in your repo is unlocked. An unlocked file is represented
+in git as a pointer file, the pointer contains "/annex/objects/<key>".
+
+Note that git-annex diffdriver exists to let a diff driver work with
+annexed files. I think you might want to use it. Amusingly
+I forgot about unlocked files in the implementation of it too;
+that got fixed in version 8.20210428.
+"""]]

sped up git-annex smudge --clean by 25%
Disabling git-annex branch update for this command is
ok, because it does not use any information from the branch,
but only logs the location when it adds a key.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/CHANGELOG b/CHANGELOG
index 7a1d4066e..32ba1e069 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,7 @@ git-annex (8.20210904) UNRELEASED; urgency=medium
     retrieving from a borg repository.
   * Resume where it left off when copying a file to/from a local git remote
     was interrupted.
+  * Sped up git-annex smudge --clean by 25%.
 
  -- Joey Hess <id@joeyh.name>  Fri, 03 Sep 2021 12:02:55 -0400
 
diff --git a/Command/Smudge.hs b/Command/Smudge.hs
index eb7b8b49c..bfb5916c0 100644
--- a/Command/Smudge.hs
+++ b/Command/Smudge.hs
@@ -30,6 +30,7 @@ import Annex.InodeSentinal
 import Utility.InodeCache
 import Config.GitConfig
 import qualified Types.Backend
+import qualified Annex.BranchState
 
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Lazy as L
@@ -87,6 +88,7 @@ smudge file = do
 -- injested content if so. Otherwise, the original content.
 clean :: RawFilePath -> CommandStart
 clean file = do
+	Annex.BranchState.disableUpdate -- optimisation
 	b <- liftIO $ L.hGetContents stdin
 	ifM fileoutsiderepo
 		( liftIO $ L.hPut stdout b
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment
index ce7bb4013..0eb1184d9 100644
--- a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment
@@ -13,8 +13,8 @@ The middle is slightly an outlier, and it would be better to have more data
 points, but what this says to me is it's probably around 38x more expensive
 on windows than on linux for git-annex smudge --clean to run.
 
-git-annex smudge --clean makes on the order of 3000 syscalls, including
-opening 200 files, execing git 30 times, and statting 400 files. That's
+git-annex smudge --clean makes on the order of 4000 syscalls, including
+opening 200 files, execing git 8 times, and statting 500 files. That's
 around 10x as many syscalls as git add makes. And it's run once per file. So
 relatively small differences in syscall performance between windows and
 linux can add up.
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_6_3947b44c88820da49d8a2d1afd3595ff._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_6_3947b44c88820da49d8a2d1afd3595ff._comment
new file mode 100644
index 000000000..7753eab2b
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_6_3947b44c88820da49d8a2d1afd3595ff._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2021-09-23T17:48:19Z"
+ content="""
+I noticed in the strace that smudge --clean ran git cat-file 2
+more times than necessary. Also was able to avoid updating the git-annex
+branch, which eliminates several calls to git (depending on the number of
+remotes). On Linux, this made it 25% faster. Might be more on Windows.
+
+Rest of the strace looks clean, nothing else stands out as unncessary.
+"""]]

Added a comment
diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored/comment_3_673ddf8dac9e6b9a3440c11af2103b92._comment b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_3_673ddf8dac9e6b9a3440c11af2103b92._comment
new file mode 100644
index 000000000..a760d6d06
--- /dev/null
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_3_673ddf8dac9e6b9a3440c11af2103b92._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="weinzwang"
+ avatar="http://cdn.libravatar.org/avatar/e73d7d9e358f3b974d283fb0834cc5d9"
+ subject="comment 3"
+ date="2021-09-23T21:40:50Z"
+ content="""
+Awesome, thank you!
+"""]]

comment
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_5_891b532bd662b1198351803b82ed634e._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_5_891b532bd662b1198351803b82ed634e._comment
new file mode 100644
index 000000000..cbed74725
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_5_891b532bd662b1198351803b82ed634e._comment
@@ -0,0 +1,21 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""
+ date="2021-09-23T17:22:19Z"
+ content="""
+Thank you for verifying this is not a recent speed reversion.
+
+You could use `git -c filter.annex.clean= add smallfile`
+to avoid the overhead of the smudge filter.
+Using `git annex add --force-small` or `git annex add` with an
+annex.largefiles config will also avoid it.
+
+ghc 8.12 has a new IO manager for windows, which
+I think is likely to be faster. (It avoids the win32 API.) I would
+want to look at that before other windows-specific optimisations.
+The windows build is still using lts-16.27 (ghc 8.8.3), there is not
+yet an lts release with ghc 8.12.
+
+I don't know if there are really any windows-specific optimisations to
+be had in the smudge code other than such low-level stuff.
+"""]]

diff --git a/doc/bugs/diff_driver_using_hash_without_subdir.mdwn b/doc/bugs/diff_driver_using_hash_without_subdir.mdwn
new file mode 100644
index 000000000..948251dca
--- /dev/null
+++ b/doc/bugs/diff_driver_using_hash_without_subdir.mdwn
@@ -0,0 +1,77 @@
+### Please describe the problem.
+I have a strange problem with diff driver.
+I wrote a diff driver which normally works, but one of my repo
+it comes up with an error that it can open `.git/annex/objects/SHA256E-s4545--9715...`.
+Indeed there is no such file, the file being not directly under `.git/annex/objects` but under `.git/annex/objects/mz/Px`.
+Also when I examine the file in git its value is `.git/annex/objects/SHA256E-s4545--9715...` but I thought it should have been `.git/annex/objects/mz/PX/SHA256E-s4545--9715...`.
+
+
+### What steps will reproduce the problem?
+
+Just use `git diff HEAD` on my corrupted repo.
+
+
+### What version of git-annex are you using? On what operating system?
+
+Version 7. I don't remember which version was used to create the repo.
+
+
+### 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
+
+     ❯ git diff HEAD .                                                                                                                                                                  [20:10:33]
+1:dumps/pages.sql
+2:/tmp/gwSIvM_pages.sql
+3:1bb7ca74a6701b1ea0d4f12ae64cfc3e83e22bad
+4:100644
+5:dumps/pages.sql
+6:0000000000000000000000000000000000000000
+7:100644
+-------          /tmp/gwSIvM_pages.sql-
+head: cannot open '.git/annex/objects/SHA256E-s4542--9715a9ca90cad176f69fb91ed650a02afb2885c96975064d49e85230513c2d9e.sql' for reading: No such file or directory
+--------         $ target
+--
+-- PostgreSQL database dump
+--
+ 
+-- Dumped from database version 13.3
+-- Dumped by pg_dump version 13.3
+ 
+ ❯ cat ~/.local/bin/mydiff.sh                                                                                                                                                       [20:16:07]
+echo 1:$1
+echo 2:$2
+echo 3:$3
+echo 4:$4
+echo 5:$5
+echo 6:$6
+echo 7:$7
+if [ "$5" = "/dev/null" ]
+then
+   echo "NULL"
+   target=$1
+else
+   target=$5
+fi
+#echo $@
+echo "-------          $2-"
+head .git$(cat $2)
+echo "--------         $target"
+head $target
+echo "-------- DIFF"
+#colordiff $1 $2
+
+#git --no-pager diff --color=auto --word-diff --no-ext-diff --no-index "$2" "$target"
+echo diff -d --color=always $2 $target
+#git --no-pager diff --color=auto --no-ext-diff --no-index -U0 "$2" "$5"
+
+
+# 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)
+Yes and it works really well (and my diff driver works on other repos too).
+
+

Added a comment: v7 v v8 comparison
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_4_0d2ab75208877136534040b726c7f97b._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_4_0d2ab75208877136534040b726c7f97b._comment
new file mode 100644
index 000000000..ab347d10a
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_4_0d2ab75208877136534040b726c7f97b._comment
@@ -0,0 +1,62 @@
+[[!comment format=mdwn
+ username="mih"
+ avatar="http://cdn.libravatar.org/avatar/f881df265a423e4f24eff27c623148fd"
+ subject="v7 v v8 comparison"
+ date="2021-09-23T06:29:30Z"
+ content="""
+> I'm still curious if there's an older version of git-annex that was faster (after it stopped using direct mode in v7). If I've understood correctly, you don't seem to be saying that there is.
+
+Here is some data that seems to support this view.
+
+I dug up another windows box on the higher performance end
+
+- windows 11 (not 10!) build 10.0.22000
+- intel 6-core xeon E-2136 @ 3.3ghz
+- NVMe drive
+
+All stats done with
+
+- git 2.32.0.windows.2
+
+I am reporting two values each, the first for git annex 8.20210715-g7334893d4 and the second for 7.20191107-g8ea269ef7 (the oldest windows build that I could find). Consequently, the first measurement is for a v8 repo, the second for a v7 repo (but no direct mode).
+
+init
+
+- git init: 0.1s / 0.1s
+- git annex init: 3.2s / 3.2s
+
+after creating a 3-byte text file:
+
+- git add file: 1.0s / 0.9s
+- git commit file -m msg: 1.8s / 1.8s
+
+after creating two new 3-byte test files:
+
+- git add .: 1.9s / 1.7s
+- git commit -m msg: 0.9s / 0.9s
+
+(NB: the `add` increase is caused by unique keys, adding a bunch of identical files is the same as adding one file)
+
+after creating eight more 3-byte text files:
+
+- git add .: 6.5s / 6.8s
+- git commit -m msg: 0.9s / 0.9s
+
+now adding a 280 MB binary file
+
+- git add binfiile: 9.8s / 9.9s
+- git commit -m msg: 1.2s / 1.2s
+
+no changes
+
+- git commit --amend -m msg: 1.2s / 1.2s
+
+
+
+
+So there is pretty much no change, and in particular no change attributable to the last ~2 years of git-annex evolution.
+
+However, what remains is that a substantially more capable windows workstation (internal NVMe, faster CPU) comes nowhere near the performance of my 6 year old Debian laptop with an external USB3 drive -- despite having to go through the same smudge filter complications.
+
+I would be grateful, if you could have a look at the windows implementation of `smudge --clean`. Please let me know what kind of contribution could help to push such an effort. Thanks!
+"""]]

avoid potentially very long bwlimit delay at start
I first saw this getting with -J2 over ssh, but later saw it also
without the -J2. It was resuming, and the calulated unboundDelay was
many minutes. The first update of the meter jumped to some large value,
because of the resuming, and so it thought the BW was super fast.
Avoid by waiting until the second meter update.
Might be a good idea to also guard for the delay being many seconds
and avoid waiting. But how many? If BW is legitimately super fast, and a
remote happens to read more than a 32kb or so chunk at a time, it could
in theory download megabytes or gigabytes of data before the first meter
update. It would actually be appropriate then to delay for a long time,
if the desired BW was low. Could make up some numbers that are sane now,
but tech may improve.
(BTW, pleased to see bwlimit does work with -J. I had worried that
it might not, if the meter update happened in a different thread than
the downloading, but it's done in the same thread.)
Sponsored-by: Brett Eisenberg on Patreon
diff --git a/Utility/Metered.hs b/Utility/Metered.hs
index 1e12444d6..21c9cd845 100644
--- a/Utility/Metered.hs
+++ b/Utility/Metered.hs
@@ -397,23 +397,27 @@ bwLimitMeterUpdate bwlimit duration meterupdate
 	| bwlimit <= 0 = return meterupdate
 	| otherwise = do
 		nowtime <- getPOSIXTime
-		mv <- newMVar (nowtime, 0)
+		mv <- newMVar (nowtime, Nothing)
 		return (mu mv)
   where
 	mu mv n@(BytesProcessed i) = do
 		endtime <- getPOSIXTime
-		(starttime, previ) <- takeMVar mv
-
-		let runtime = endtime - starttime
-		let currbw = fromIntegral (i - previ) / runtime
-		let pausescale = if currbw > bwlimit'
-			then (currbw / bwlimit') - 1
-			else 0
-		unboundDelay (floor (runtime * pausescale * msecs))
+		(starttime, mprevi) <- takeMVar mv
+
+		case mprevi of
+			Just previ -> do
+				let runtime = endtime - starttime
+				let currbw = fromIntegral (i - previ) / runtime
+				let pausescale = if currbw > bwlimit'
+					then (currbw / bwlimit') - 1
+					else 0
+				unboundDelay (floor (runtime * pausescale * msecs))
+			Nothing -> return ()
+
 		meterupdate n
 
 		nowtime <- getPOSIXTime
-		putMVar mv (nowtime, i)
+		putMVar mv (nowtime, Just i)
 
 	bwlimit' = fromIntegral (bwlimit * durationSeconds duration) 
 	msecs = fromIntegral oneSecond
diff --git a/doc/todo/bwlimit.mdwn b/doc/todo/bwlimit.mdwn
index a582f62b1..674aaa100 100644
--- a/doc/todo/bwlimit.mdwn
+++ b/doc/todo/bwlimit.mdwn
@@ -10,9 +10,9 @@ works, it will probably work to put the delay in there. --[[Joey]]
 
 [[confirmed]]
 
-> Implemented and works well.
-> 
-> A local git remote, when resuming an interrupted
+> Implemented and works well. [[done]] --[[Joey]]
+
+> Note: A local git remote, when resuming an interrupted
 > transfer, has to hash the file (with default annex.verify settings),
 > and that hashing updates the progress bar, and so the bwlimit can kick
 > in and slow down that initial hashing, before any data copying begins.
@@ -20,8 +20,7 @@ works, it will probably work to put the delay in there. --[[Joey]]
 > remote you're wanting to limit disk IO. Only reason it might not be ok
 > is if the intent is to limit IO to the disk containing the remote
 > but not the one containing the annex repo. (This also probably
-> holds for the directory special remote.)
-> 
+> holds for the directory special remote.) 
 > Other remotes, including git over ssh, when resuming don't have that
 > problem. Looks like chunked special remotes narrowly avoid it, just
 > because their implementation choose to not do incremental verification
@@ -37,5 +36,3 @@ works, it will probably work to put the delay in there. --[[Joey]]
 > tend to be 32kb or so, and the pauses a small fraction of a second. So
 > mentioning this only for completeness.) --[[Joey]]
 
-[[done]]
-

Added a comment
diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_4_023fb25740f8363f8e72c12f020aa0f4._comment b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_4_023fb25740f8363f8e72c12f020aa0f4._comment
new file mode 100644
index 000000000..35b861e00
--- /dev/null
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_4_023fb25740f8363f8e72c12f020aa0f4._comment
@@ -0,0 +1,30 @@
+[[!comment format=mdwn
+ username="mike@2d6d71f56ce2a992244350475251df87c26fe351"
+ nickname="mike"
+ avatar="http://cdn.libravatar.org/avatar/183fa439752e2f0c6f39ede658d81050"
+ subject="comment 4"
+ date="2021-09-22T21:18:10Z"
+ content="""
+> You did say it's not moving \"some files\" which suggests maybe it is moving other ones? Do you see it move any unused files at all?
+                                                                                                                        
+It's moving most of it, just not all of them!                                                                           
+                                                                                                                        
+> Do other commands that use the same --unused option work? Eg, does git annex whereis --unused list them?              
+                                                                                                                        
+`git annex whereis --unused` lists the files. `git annex copy --unused -t bims` OTOH does not do anything. I also tried `git annex drop --unused --in=bims` in an attempt to emulate `git annex move --unused -t bims`, but this also does not do anything.                                                                                                            
+                                                                                                                        
+~~~                                                                                                                     
+% git annex whereis --unused                                                                                            
+whereis SHA256E-s24643--167c39a6ca9aacb7d4a111ce58cb65405e9f43e0b0f686108fbbaf201e0561aa.jpg (2 copies)                 
+  ▹‧87f95204-e2d7-41bc-bf24-29c43c4a95d4 -- orange@leguin:~/Calibre Library [here]                                      
+   ▹994901a8-670e-403e-9a72-aff9ce07556a -- bims:/srv/annex/Calibre Library.git [bims]                                  
+[... more files ...]                                                                                                    
+~~~                                                                                                                     
+                                                                                                                        
+> The only way I can reproduce this behavior is if the remote has the same uuid                                         
+> as the current repository. Then any move is a no-op and it avoids operating on                                        
+> the files at all, the same as your output shows. So it seems possible that                                            
+> could be your real problem.                                                                                           
+                                                                                                                        
+I don't think that's the issue: If you look above, `here` and `bims` have different UUIDs. (I also notice the files are already on the `bims` remote.)              
+"""]]

comment
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment
new file mode 100644
index 000000000..ce7bb4013
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_3_85f07d906b836ac968ab877258180c0c._comment
@@ -0,0 +1,33 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2021-09-22T19:33:58Z"
+ content="""
+Look at the 3 git add results, dividing windows runtime by linux:
+
+	1.9/0.05 = 38.00
+	4.5/0.08 = 56.25
+	13.1/0.36 = 36.39
+
+The middle is slightly an outlier, and it would be better to have more data
+points, but what this says to me is it's probably around 38x more expensive
+on windows than on linux for git-annex smudge --clean to run.
+
+git-annex smudge --clean makes on the order of 3000 syscalls, including
+opening 200 files, execing git 30 times, and statting 400 files. That's
+around 10x as many syscalls as git add makes. And it's run once per file. So
+relatively small differences in syscall performance between windows and
+linux can add up.
+
+I've looked at just this kind of comparisons before, and it has always
+seemed explainable by differences in syscall overhead. I don't
+really see anything in your numbers that says otherwise.
+
+I'm still curious if there's an older version of git-annex that was faster
+(after it stopped using direct mode in v7).
+If I've understood correctly, you don't seem to be saying that there is.
+
+If it's always been this slow, then about all I can think to do to improve
+it is profile git-annex smudge --clean on windows and see if anything other
+than those syscalls is somehow being slow.
+"""]]

followup
diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored/comment_2_bc1c1a2cb0b45fcfc1423939b01e9319._comment b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_2_bc1c1a2cb0b45fcfc1423939b01e9319._comment
new file mode 100644
index 000000000..17bfabd23
--- /dev/null
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_2_bc1c1a2cb0b45fcfc1423939b01e9319._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2021-09-22T19:30:15Z"
+ content="""
+I've now implemented remote.name.annex-bwlimit, which works for most
+remotes, and can be used instead of the rsync options for git remotes
+that no longer use rsync.
+"""]]

mention tiny wart
diff --git a/doc/todo/bwlimit.mdwn b/doc/todo/bwlimit.mdwn
index 675896cfe..a582f62b1 100644
--- a/doc/todo/bwlimit.mdwn
+++ b/doc/todo/bwlimit.mdwn
@@ -31,5 +31,11 @@ works, it will probably work to put the delay in there. --[[Joey]]
 > I have not done so yet though, and am closing this..
 > --[[Joey]]
 
+> (One other small caveat is that it pauses after each chunk, which means
+> it pauses unncessarily after the last chunk of the file. It doesn't know
+> it's the last chunk, and it would be hard to teach it. And the chunks
+> tend to be 32kb or so, and the pauses a small fraction of a second. So
+> mentioning this only for completeness.) --[[Joey]]
+
 [[done]]
 

improved bwrate limiting implementation
New method is much better. Avoids unrestrained transfer at the beginning
(except for the first block. Keeps right at or a few kb/s below the
configured limit, with very little varation in the actual reported bandwidth.
Removed the /s part of the config as it's not needed.
Ready to merge.
Sponsored-by: Luke Shumaker on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 15e94fdc6..7a1d4066e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,8 @@
 git-annex (8.20210904) UNRELEASED; urgency=medium
 
-  * Added annex.bwlimit and remote.name.annex-bwlimit config that works
-    for git remotes and many but not all special remotes.
+  * Added annex.bwlimit and remote.name.annex-bwlimit config to limit
+    the bandwidth of transfers. It works for git remotes and many
+    but not all special remotes.
   * Bug fix: Git configs such as annex.verify were incorrectly overriding
     per-remote git configs such as remote.name.annex-verify.
     (Reversion in version 4.20130323)
diff --git a/Types/GitConfig.hs b/Types/GitConfig.hs
index affc7f014..46362de89 100644
--- a/Types/GitConfig.hs
+++ b/Types/GitConfig.hs
@@ -407,9 +407,9 @@ extractRemoteGitConfig r remotename = do
 		, remoteAnnexStallDetection =
 			either (const Nothing) Just . parseStallDetection
 				=<< getmaybe "stalldetection"
-		, remoteAnnexBwLimit =
-			either (const Nothing) Just . parseBwRate
-				=<< getmaybe "bwlimit"
+		, remoteAnnexBwLimit = do
+			sz <- readSize dataUnits =<< getmaybe "bwlimit"
+			return (BwRate sz (Duration 1))
 		, remoteAnnexAllowUnverifiedDownloads = (== Just "ACKTHPPT") $
 			getmaybe ("security-allow-unverified-downloads")
 		, remoteAnnexConfigUUID = toUUID <$> getmaybe "config-uuid"
diff --git a/Utility/Metered.hs b/Utility/Metered.hs
index ea391edb5..1e12444d6 100644
--- a/Utility/Metered.hs
+++ b/Utility/Metered.hs
@@ -389,39 +389,34 @@ rateLimitMeterUpdate delta (Meter totalsizev _ _ _) meterupdate = do
 -- same process and thread as the call to the MeterUpdate.
 --
 -- For example, if the desired bandwidth is 100kb/s, and over the past
--- second, 200kb was sent, then pausing for half a second, and then
--- running for half a second should result in the desired bandwidth.
--- But, if after that pause, only 75kb is sent over the next half a
--- second, then the next pause should be 2/3rds of a second.
+-- 1/10th of a second, 30kb was sent, then the current bandwidth is
+-- 300kb/s, 3x as fast as desired. So, after getting the next chunk,
+-- pause for twice as long as it took to get it.
 bwLimitMeterUpdate :: ByteSize -> Duration -> MeterUpdate -> IO MeterUpdate
-bwLimitMeterUpdate sz duration meterupdate = do
-	nowtime <- getPOSIXTime
-	lastpause <- newMVar (nowtime, toEnum 0 :: POSIXTime, 0)
-	return $ mu lastpause
-  where
-	mu lastpause n@(BytesProcessed i) = do
+bwLimitMeterUpdate bwlimit duration meterupdate
+	| bwlimit <= 0 = return meterupdate
+	| otherwise = do
 		nowtime <- getPOSIXTime
+		mv <- newMVar (nowtime, 0)
+		return (mu mv)
+  where
+	mu mv n@(BytesProcessed i) = do
+		endtime <- getPOSIXTime
+		(starttime, previ) <- takeMVar mv
+
+		let runtime = endtime - starttime
+		let currbw = fromIntegral (i - previ) / runtime
+		let pausescale = if currbw > bwlimit'
+			then (currbw / bwlimit') - 1
+			else 0
+		unboundDelay (floor (runtime * pausescale * msecs))
 		meterupdate n
-		lastv@(prevtime, prevpauselength, previ) <- takeMVar lastpause
-		let timedelta = nowtime - prevtime
-		if timedelta >= durationsecs
-			then do
-				let sz' = i - previ
-				let runtime = timedelta - prevpauselength
-				let pauselength = calcpauselength sz' runtime
-				if pauselength > 0
-					then do
-						unboundDelay (floor (pauselength * fromIntegral oneSecond))
-						putMVar lastpause (nowtime, pauselength, i)
-					else putMVar lastpause lastv
-			else putMVar lastpause lastv
-
-	calcpauselength sz' runtime
-		| sz' > sz && sz' > 0 && runtime > 0 =
-			durationsecs - (fromIntegral sz / fromIntegral sz') * runtime
-		| otherwise = 0
-	
-	durationsecs = fromIntegral (durationSeconds duration)
+
+		nowtime <- getPOSIXTime
+		putMVar mv (nowtime, i)
+
+	bwlimit' = fromIntegral (bwlimit * durationSeconds duration) 
+	msecs = fromIntegral oneSecond
 
 data Meter = Meter (MVar (Maybe TotalSize)) (MVar MeterState) (MVar String) DisplayMeter
 
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
index f781c09b0..def6ec1dc 100644
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -1389,21 +1389,12 @@ Remotes are configured using these settings in `.git/config`.
   This can be used to limit how much bandwidth is used for a transfer
   from or to a remote.
  
-  For example, to limit transfers to 1 gigabyte per second:
-  `git config annex.bwlimit "1GB/1s"`
-
+  For example, to limit transfers to 1 mebibyte per second:
+  `git config annex.bwlimit "1MiB"`
+  
   This will work with many remotes, including git remotes, but not
   for remotes where the transfer is run by a separate program than
-  git-annex.
-
-  The bandwidth limiting is implemented by pausing when
-  the transfer is running too fast, so it may use more bandwidth
-  than configured before being slowed down, either at the beginning
-  or if the available bandwidth changes while it is running.
-
-  It is different to use "1GB/1s" than "10GB/10s". git-annex will
-  track how much data was transferred over the time period, and then
-  pausing. So usually 1s is the best time period to use.
+  git-annex. 
 
 * `remote.<name>.annex-stalldetecton`, `annex.stalldetection`
 
diff --git a/doc/todo/bwlimit.mdwn b/doc/todo/bwlimit.mdwn
index 337ec8616..675896cfe 100644
--- a/doc/todo/bwlimit.mdwn
+++ b/doc/todo/bwlimit.mdwn
@@ -10,17 +10,17 @@ works, it will probably work to put the delay in there. --[[Joey]]
 
 [[confirmed]]
 
-> Implmentation in progress in the `bwlimit` branch. Seems to work, but see
-> commit message for what still needs to be done. --[[Joey]]
-
-> The directory special remote, when resuming an interrupted
+> Implemented and works well.
+> 
+> A local git remote, when resuming an interrupted
 > transfer, has to hash the file (with default annex.verify settings),
 > and that hashing updates the progress bar, and so the bwlimit can kick
 > in and slow down that initial hashing, before any data copying begins.
-> This seems perhaps ok; if you've bwlimited a directory special
+> This seems perhaps ok; if you've bwlimited a local git remote,
 > remote you're wanting to limit disk IO. Only reason it might not be ok
-> is if the intent is to limit IO to the disk containing the directory
-> special remote, but not the one containing the annex repo.
+> is if the intent is to limit IO to the disk containing the remote
+> but not the one containing the annex repo. (This also probably
+> holds for the directory special remote.)
 > 
 > Other remotes, including git over ssh, when resuming don't have that
 > problem. Looks like chunked special remotes narrowly avoid it, just
@@ -28,4 +28,8 @@ works, it will probably work to put the delay in there. --[[Joey]]
 > when resuming. It might be worthwhile to differentiate between progress
 > updates for incremental verification setup and for actual transfers, and
 > only rate limit the latter, just to avoid fragility in the code.
+> I have not done so yet though, and am closing this..
 > --[[Joey]]
+
+[[done]]
+

Added a comment: Performance stats on crippled Debian NTFS-mount
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_2_80c5ec6993e8a720fbd1841fdd21e311._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_2_80c5ec6993e8a720fbd1841fdd21e311._comment
new file mode 100644
index 000000000..7a339930a
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_2_80c5ec6993e8a720fbd1841fdd21e311._comment
@@ -0,0 +1,68 @@
+[[!comment format=mdwn
+ username="mih"
+ avatar="http://cdn.libravatar.org/avatar/f881df265a423e4f24eff27c623148fd"
+ subject="Performance stats on crippled Debian NTFS-mount"
+ date="2021-09-22T18:37:10Z"
+ content="""
+I am responding to your comments in an altered order:
+
+> So are the timings in those issues comparable, or is it an apples/oranges comparison with different hardware or OS version? And if the timings between those issues are not comparable, what exactly is this new issue comparing with when it says the performance has worsened?
+
+The timing are comparable insofar as they come from the same hardware and \"same\" OS, but that OS has seen various updates over time, hence cannot be considered constant.
+
+> Each git add has to run a new git annex smudge process. git commit will often run it as well. This is discussed in detail in git smudge clean interface suboptiomal.
+
+Yes, I understand that this is a limitation that is outside the control of git-annex. However, the difference between windows (with implied crippling) and a crippled FS mounted on a proper OS are substantial. Here are a bunch of stats, matching those above.
+
+From https://github.com/datalad/datalad/issues/5317#issuecomment-760767158 a description on how the real windows test system used above relates to my laptop used for the stats below:
+
+> The machine is a little older (1 year older than the machine I used for the stats posted before), but not a cripple (Core i5, two physical cores at 2.6GHz, 8GB RAM, mSATA SSD, uptodate bios from 2019, and an uptodate win10).
+
+Here I am using a Core i7-6500U CPU @ 2.50GHz, 8GB RAM, mSATA SSD (via USB3). git 2.33.0, git annex version 8.20210903:
+
+For convenience, I am putting the values from my original post with the stats computed on windows in *brackets*.
+
+- git init: 0.01s *[0.1s]*
+- git annex init: 0.1s *[5.7s]*
+
+First timing info is the command executed in an annex-repo (after git annex init), the second timing is the same command executed in a plain Git repo.
+
+after creating a 3-byte text file:
+
+- git add file: 0.05s (0.01s) *[1.9s (0.1s)]*
+- git commit file -m msg: 0.08s (0.02s) *[3.2s (0.1s)]*
+
+after creating two new 3-byte test files:
+
+- git add .: 0.08s (0.01s) *[4.5s (0.1s)]*
+- git commit -m msg: 0.04s (0.02s) *[2.4s (0.1s)]*
+
+after creating eight more 3-byte text files:
+
+- git add .: 0.36s (0.01s) *[13.1s (0.1s)]*
+- git commit -m msg: 0.04s (0.01s) *[1.6 (0.1s)]*
+
+now adding a 225 MB binary file
+
+- git add binfiile: 10.8s (10.9s) *[16s (13s)]*
+- git commit -m msg: 1.2s (0.03s) *[2s (0.2s)]*
+
+no changes
+
+- git commit --amend -m msg: 1.3s (0.01s) *[2s (0.1s)]*
+
+
+So taken together, we can clearly see the price that needs to be paid for the smudge filter approach. However, it is nowhere near the penalty paid on the real windows system, both in absolute terms, as well as relative to plain git operations. Critically (for me) the total difference for a `datalad create` amounts to:
+
+- 25s on a crippled NTFS drive on windows
+- 0.9s on a crippled NTFS drive on Debian
+
+(on otherwise fairly comparable hardware).
+
+I think this indicates that something is slower on windows that *cannot* be explained by
+
+- Git generally being slower on Windows
+- git-annex generally being slower when juggling smudge filters and adjusted branches
+- an overall performance difference between my two test machines
+
+"""]]

note
diff --git a/doc/todo/bwlimit.mdwn b/doc/todo/bwlimit.mdwn
index da1deffe6..337ec8616 100644
--- a/doc/todo/bwlimit.mdwn
+++ b/doc/todo/bwlimit.mdwn
@@ -12,3 +12,20 @@ works, it will probably work to put the delay in there. --[[Joey]]
 
 > Implmentation in progress in the `bwlimit` branch. Seems to work, but see
 > commit message for what still needs to be done. --[[Joey]]
+
+> The directory special remote, when resuming an interrupted
+> transfer, has to hash the file (with default annex.verify settings),
+> and that hashing updates the progress bar, and so the bwlimit can kick
+> in and slow down that initial hashing, before any data copying begins.
+> This seems perhaps ok; if you've bwlimited a directory special
+> remote you're wanting to limit disk IO. Only reason it might not be ok
+> is if the intent is to limit IO to the disk containing the directory
+> special remote, but not the one containing the annex repo.
+> 
+> Other remotes, including git over ssh, when resuming don't have that
+> problem. Looks like chunked special remotes narrowly avoid it, just
+> because their implementation choose to not do incremental verification
+> when resuming. It might be worthwhile to differentiate between progress
+> updates for incremental verification setup and for actual transfers, and
+> only rate limit the latter, just to avoid fragility in the code.
+> --[[Joey]]

Added a comment
diff --git a/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_1_92a7d08946a3ed95d0f0413e6c6208ae._comment b/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_1_92a7d08946a3ed95d0f0413e6c6208ae._comment
new file mode 100644
index 000000000..bad191d24
--- /dev/null
+++ b/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_1_92a7d08946a3ed95d0f0413e6c6208ae._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="comment 1"
+ date="2021-09-22T14:17:59Z"
+ content="""
+age v1.0.0 has been released: https://github.com/FiloSottile/age/releases/tag/v1.0.0
+"""]]

resume properly when copying a file to/from a local git remote is interrupted
Probably this fixes a reversion, but I don't know what version broke it.
This does use withOtherTmp for a temp file that could be quite large.
Though albeit a reflink copy that will not actually take up any space
as long as the file it was copied from still exists. So if the copy cow
succeeds but git-annex is interrupted just before that temp file gets
renamed into the usual .git/annex/tmp/ location, there is a risk that
the other temp directory ends up cluttered with a larger temp file than
later. It will eventually be cleaned up, and the changes of this being
a problem are small, so this seems like an acceptable thing to do.
Sponsored-by: Shae Erisson on Patreon
diff --git a/Annex/CopyFile.hs b/Annex/CopyFile.hs
index b49f2ef45..b4ed97dfd 100644
--- a/Annex/CopyFile.hs
+++ b/Annex/CopyFile.hs
@@ -15,6 +15,8 @@ import Utility.CopyFile
 import Utility.FileMode
 import Utility.Touch
 import Utility.Hash (IncrementalVerifier(..))
+import Annex.Tmp
+import Utility.Tmp
 
 import Control.Concurrent
 import qualified Data.ByteString as S
@@ -28,23 +30,34 @@ newCopyCoWTried :: IO CopyCoWTried
 newCopyCoWTried = CopyCoWTried <$> newEmptyMVar
 
 {- Copies a file is copy-on-write is supported. Otherwise, returns False. -}
-tryCopyCoW :: CopyCoWTried -> FilePath -> FilePath -> MeterUpdate -> IO Bool
+tryCopyCoW :: CopyCoWTried -> FilePath -> FilePath -> MeterUpdate -> Annex Bool
 tryCopyCoW (CopyCoWTried copycowtried) src dest meterupdate =
 	-- If multiple threads reach this at the same time, they
 	-- will both try CoW, which is acceptable.
-	ifM (isEmptyMVar copycowtried)
+	ifM (liftIO $ isEmptyMVar copycowtried)
 		( do
 			ok <- docopycow
-			void $ tryPutMVar copycowtried ok
+			void $ liftIO $ tryPutMVar copycowtried ok
 			return ok
-		, ifM (readMVar copycowtried)
+		, ifM (liftIO $ readMVar copycowtried)
 			( docopycow
 			, return False
 			)
 		)
   where
-	docopycow = watchFileSize dest meterupdate $
-		copyCoW CopyTimeStamps src dest
+	-- copyCow needs a destination file that does not exist,
+	-- but the dest file might already. So use it with another
+	-- temp file, and if it succeeds, rename it into place. If it fails,
+	-- the dest file is left as-is, to support resuming.
+	docopycow = withOtherTmp $ \othertmp -> liftIO $
+		withTmpFileIn (fromRawFilePath othertmp) (takeFileName dest) $ \tmpdest _h -> do
+			copied <- watchFileSize tmpdest meterupdate $
+				copyCoW CopyTimeStamps src tmpdest
+			if copied
+				then liftIO $ catchBoolIO $ do
+					rename tmpdest dest
+					return True
+				else return False
 
 data CopyMethod = CopiedCoW | Copied
 
@@ -70,7 +83,7 @@ fileCopier :: CopyCoWTried -> FilePath -> FilePath -> MeterUpdate -> Maybe Incre
 fileCopier _ src dest meterupdate iv = docopy
 #else
 fileCopier copycowtried src dest meterupdate iv =
-	ifM (liftIO $ tryCopyCoW copycowtried src dest meterupdate)
+	ifM (tryCopyCoW copycowtried src dest meterupdate)
 		( do
 			liftIO $ maybe noop unableIncremental iv
 			return CopiedCoW
diff --git a/CHANGELOG b/CHANGELOG
index 538ae62a4..c1e6446be 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,8 @@ git-annex (8.20210904) UNRELEASED; urgency=medium
 
   * borg: Avoid trying to extract xattrs, ACLS, and bsdflags when
     retrieving from a borg repository.
+  * Resume where it left off when copying a file to/from a local git remote
+    was interrupted.
 
  -- Joey Hess <id@joeyh.name>  Fri, 03 Sep 2021 12:02:55 -0400
 
diff --git a/Remote/Directory.hs b/Remote/Directory.hs
index 6a34df6bd..20d402732 100644
--- a/Remote/Directory.hs
+++ b/Remote/Directory.hs
@@ -412,7 +412,7 @@ retrieveExportWithContentIdentifierM dir cow loc cid dest mkkey p =
 	f = exportPath dir loc
 	f' = fromRawFilePath f
 
-	docopy = ifM (liftIO $ tryCopyCoW cow f' dest p)
+	docopy = ifM (tryCopyCoW cow f' dest p)
 		( do
 			k <- mkkey
 			postcheckcow (return k)
diff --git a/Utility/CopyFile.hs b/Utility/CopyFile.hs
index ed2da15e5..e91986593 100644
--- a/Utility/CopyFile.hs
+++ b/Utility/CopyFile.hs
@@ -56,11 +56,13 @@ copyFileExternal meta src dest = do
 		| otherwise = copyMetaDataParams meta
 
 {- When a filesystem supports CoW (and cp does), uses it to make
- - an efficient copy of a file. Otherwise, returns False. -}
+ - an efficient copy of a file. Otherwise, returns False.
+ -
+ - The dest file must not exist yet, or it will fail to make a CoW copy,
+ - and will return False. -}
 copyCoW :: CopyMetaData -> FilePath -> FilePath -> IO Bool
 copyCoW meta src dest
 	| BuildInfo.cp_reflink_supported = do
-		void $ tryIO $ removeFile dest
 		-- When CoW is not supported, cp will complain to stderr,
 		-- so have to discard its stderr.
 		ok <- catchBoolIO $ withNullHandle $ \nullh ->
diff --git a/doc/bugs/copy_--to_with_local_git_remote_does_not_resume.mdwn b/doc/bugs/copy_--to_with_local_git_remote_does_not_resume.mdwn
new file mode 100644
index 000000000..7361200da
--- /dev/null
+++ b/doc/bugs/copy_--to_with_local_git_remote_does_not_resume.mdwn
@@ -0,0 +1,21 @@
+A copy --to a local git remote that gets interrupted and is run again does
+not resume where it left off, but copies all the data again.
+
+This does not affect git remotes accessed over ssh.
+
+It's kind of hard to notice this, because normally a resume, has to read
+the src file and dest file, in order for incremental verification to
+get started. But it is somewhat slower to do that than it is to re-write
+the dest file from the start. And when annex.verify = false, it's a lot
+slower.
+
+Looks like it's due to copyCoW unlinking the dest file. Since the first
+file copy trues copyCoW to probe if that's supported, that happens.
+And when resuming an interrupted copy, that probe will generally happen
+with the file it was interrupted on.
+
+So, the solution seems like it would be to copyCoW to some other temp file,
+and if it succeeds, rename it to the dest.
+--[[Joey]]
+
+> [[fixed|done]] --[[Joey]]

bwlimit branch
diff --git a/doc/todo/bwlimit.mdwn b/doc/todo/bwlimit.mdwn
index 3821d93fa..da1deffe6 100644
--- a/doc/todo/bwlimit.mdwn
+++ b/doc/todo/bwlimit.mdwn
@@ -9,3 +9,6 @@ second when it's running too fast. The way the progress reporting interface
 works, it will probably work to put the delay in there. --[[Joey]]
 
 [[confirmed]]
+
+> Implmentation in progress in the `bwlimit` branch. Seems to work, but see
+> commit message for what still needs to be done. --[[Joey]]

bwlimit
Added annex.bwlimit and remote.name.annex-bwlimit config that works for git
remotes and many but not all special remotes.
This nearly works, at least for a git remote on the same disk. With it set
to 100kb/1s, the meter displays an actual bandwidth of 128 kb/s, with
occasional spikes to 160 kb/s. So it needs to delay just a bit longer...
I'm unsure why.
However, at the beginning a lot of data flows before it determines the
right bandwidth limit. A granularity of less than 1s would probably improve
that.
And, I don't know yet if it makes sense to have it be 100ks/1s rather than
100kb/s. Is there a situation where the user would want a larger
granularity? Does granulatity need to be configurable at all? I only used that
format for the config really in order to reuse an existing parser.
This can't support for external special remotes, or for ones that
themselves shell out to an external command. (Well, it could, but it
would involve pausing and resuming the child process tree, which seems
very hard to implement and very strange besides.) There could also be some
built-in special remotes that it still doesn't work for, due to them not
having a progress meter whose displays blocks the bandwidth using thread.
But I don't think there are actually any that run a separate thread for
downloads than the thread that displays the progress meter.
Sponsored-by: Graham Spencer on Patreon
diff --git a/Annex/Import.hs b/Annex/Import.hs
index 453965ba7..010808ef3 100644
--- a/Annex/Import.hs
+++ b/Annex/Import.hs
@@ -461,8 +461,9 @@ importKeys remote importtreeconfig importcontent thirdpartypopulated importablec
 					, providedMimeEncoding = Nothing
 					, providedLinkType = Nothing
 					}
+				bwlimit <- bwLimit (Remote.gitconfig remote)
 				islargefile <- checkMatcher' matcher mi mempty
-				metered Nothing sz $ const $ if islargefile
+				metered Nothing sz bwlimit $ const $ if islargefile
 					then doimportlarge importkey cidmap db loc cid sz f
 					else doimportsmall cidmap db loc cid sz
 	
@@ -557,11 +558,12 @@ importKeys remote importtreeconfig importcontent thirdpartypopulated importablec
 			Left e -> do
 				warning (show e)
 				return Nothing
+		bwlimit <- bwLimit (Remote.gitconfig remote)
 		checkDiskSpaceToGet tmpkey Nothing $
 			notifyTransfer Download af $
 				download' (Remote.uuid remote) tmpkey af Nothing stdRetry $ \p ->
 					withTmp tmpkey $ \tmpfile ->
-						metered (Just p) tmpkey $
+						metered (Just p) tmpkey bwlimit $
 							const (rundownload tmpfile)
 	  where
 		tmpkey = importKey cid sz
diff --git a/Annex/StallDetection.hs b/Annex/StallDetection.hs
index 02540a473..9c095b4c8 100644
--- a/Annex/StallDetection.hs
+++ b/Annex/StallDetection.hs
@@ -22,7 +22,7 @@ import Control.Monad.IO.Class (MonadIO)
 detectStalls :: (Monad m, MonadIO m) => Maybe StallDetection -> TVar (Maybe BytesProcessed) -> m () -> m ()
 detectStalls Nothing _ _ = noop
 detectStalls (Just StallDetectionDisabled) _ _ = noop
-detectStalls (Just (StallDetection minsz duration)) metervar onstall =
+detectStalls (Just (StallDetection (BwRate minsz duration))) metervar onstall =
 	detectStalls' minsz duration metervar onstall Nothing
 detectStalls (Just ProbeStallDetection) metervar onstall = do
 	-- Only do stall detection once the progress is confirmed to be
diff --git a/Annex/Transfer.hs b/Annex/Transfer.hs
index c57dbaf3e..c2c3b582e 100644
--- a/Annex/Transfer.hs
+++ b/Annex/Transfer.hs
@@ -20,6 +20,7 @@ module Annex.Transfer (
 	stdRetry,
 	pickRemote,
 	stallDetection,
+	bwLimit,
 ) where
 
 import Annex.Common
@@ -406,3 +407,9 @@ stallDetection r = maybe globalcfg (pure . Just) remotecfg
   where
 	globalcfg = annexStallDetection <$> Annex.getGitConfig
 	remotecfg = remoteAnnexStallDetection $ Remote.gitconfig r
+
+bwLimit :: RemoteGitConfig -> Annex (Maybe BwRate)
+bwLimit gc = maybe globalcfg (pure . Just) remotecfg
+  where
+	globalcfg = annexBwLimit <$> Annex.getGitConfig
+	remotecfg = remoteAnnexBwLimit gc
diff --git a/Annex/YoutubeDl.hs b/Annex/YoutubeDl.hs
index 5cbc9e7f3..52219827a 100644
--- a/Annex/YoutubeDl.hs
+++ b/Annex/YoutubeDl.hs
@@ -96,7 +96,7 @@ youtubeDl' url workdir p uo
 			-- with the size, which is why it's important the
 			-- meter is passed into commandMeter'
 			let unknownsize = Nothing :: Maybe FileSize
-			ok <- metered (Just p) unknownsize $ \meter meterupdate ->
+			ok <- metered (Just p) unknownsize Nothing $ \meter meterupdate ->
 				liftIO $ commandMeter' 
 					parseYoutubeDlProgress oh (Just meter) meterupdate cmd opts
 					(\pr -> pr { cwd = Just workdir })
diff --git a/CHANGELOG b/CHANGELOG
index 538ae62a4..fd9f1d06e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
 git-annex (8.20210904) UNRELEASED; urgency=medium
 
+  * Added annex.bwlimit and remote.name.annex-bwlimit config that works
+    for git remotes and many but not all special remotes.
   * borg: Avoid trying to extract xattrs, ACLS, and bsdflags when
     retrieving from a borg repository.
 
diff --git a/Command/Add.hs b/Command/Add.hs
index 0fe4351ab..4da6f7354 100644
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -192,7 +192,7 @@ perform o file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 		}
 	ld <- lockDown cfg (fromRawFilePath file)
 	let sizer = keySource <$> ld
-	v <- metered Nothing sizer $ \_meter meterupdate ->
+	v <- metered Nothing sizer Nothing $ \_meter meterupdate ->
 		ingestAdd (checkGitIgnoreOption o) meterupdate ld
 	finish v
   where
diff --git a/Messages/Progress.hs b/Messages/Progress.hs
index 397aebbb3..832dd9e0f 100644
--- a/Messages/Progress.hs
+++ b/Messages/Progress.hs
@@ -1,6 +1,6 @@
 {- git-annex progress output
  -
- - Copyright 2010-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2021 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -18,6 +18,7 @@ import Types
 import Types.Messages
 import Types.Key
 import Types.KeySource
+import Types.StallDetection (BwRate(..))
 import Utility.InodeCache
 import qualified Messages.JSON as JSON
 import Messages.Concurrent
@@ -72,11 +73,12 @@ metered
 	:: MeterSize sizer
 	=> Maybe MeterUpdate
 	-> sizer
+	-> Maybe BwRate
 	-> (Meter -> MeterUpdate -> Annex a)
 	-> Annex a
-metered othermeter sizer a = withMessageState $ \st -> do
+metered othermeterupdate sizer bwlimit a = withMessageState $ \st -> do
 	sz <- getMeterSize sizer
-	metered' st setclear othermeter sz showOutput a
+	metered' st setclear othermeterupdate sz bwlimit showOutput a
   where
 	setclear c = Annex.changeState $ \st -> st
 		{ Annex.output = (Annex.output st) { clearProgressMeter = c } }
@@ -90,11 +92,12 @@ metered'
 	-- NormalOutput.
 	-> Maybe MeterUpdate
 	-> Maybe TotalSize
+	-> Maybe BwRate
 	-> m ()
 	-- ^ this should run showOutput
 	-> (Meter -> MeterUpdate -> m a)
 	-> m a
-metered' st setclear othermeter msize showoutput a = go st
+metered' st setclear othermeterupdate msize bwlimit showoutput a = go st
   where
 	go (MessageState { outputType = QuietOutput }) = nometer
 	go (MessageState { outputType = NormalOutput, concurrentOutputEnabled = False }) = do
@@ -105,7 +108,7 @@ metered' st setclear othermeter msize showoutput a = go st
 		setclear clear
 		m <- liftIO $ rateLimitMeterUpdate consoleratelimit meter $
 			updateMeter meter
-		r <- a meter (combinemeter m)
+		r <- a meter =<< mkmeterupdate m
 		setclear noop
 		liftIO clear
 		return r
@@ -116,7 +119,7 @@ metered' st setclear othermeter msize showoutput a = go st
 				in Regions.setConsoleRegion r ('\n' : s)
 			m <- liftIO $ rateLimitMeterUpdate consoleratelimit meter $
 				updateMeter meter
-			a meter (combinemeter m)
+			a meter =<< mkmeterupdate m
 	go (MessageState { outputType = JSONOutput jsonoptions })
 		| jsonProgress jsonoptions = do
 			let buf = jsonBuffer st
@@ -124,7 +127,7 @@ metered' st setclear othermeter msize showoutput a = go st
 				JSON.progress buf msize' (meterBytesProcessed new)
 			m <- liftIO $ rateLimitMeterUpdate jsonratelimit meter $
 				updateMeter meter
-			a meter (combinemeter m)
+			a meter =<< mkmeterupdate m
 		| otherwise = nometer
 	go (MessageState { outputType = SerializedOutput h _ }) = do
 		liftIO $ outputSerialized h BeginProgressMeter
@@ -144,16 +147,21 @@ metered' st setclear othermeter msize showoutput a = go st
 				meterBytesProcessed new
 		m <- liftIO $ rateLimitMeterUpdate minratelimit meter $
 			updateMeter meter
-		a meter (combinemeter m)
+		(a meter =<< mkmeterupdate m)
 			`finally` (liftIO $ outputSerialized h EndProgressMeter)
 	nometer = do
 		dummymeter <- liftIO $ mkMeter Nothing $
 			\_ _ _ _ -> return ()
-		a dummymeter (combinemeter (const noop))
+		a dummymeter =<< mkmeterupdate (const noop)
 
-	combinemeter m = case othermeter of
-		Nothing -> m
-		Just om -> combineMeterUpdate m om
+	mkmeterupdate m = 
+		let mu = case othermeterupdate of
+			Nothing -> m
+			Just om -> combineMeterUpdate m om
+		in case bwlimit of
+			Nothing -> return mu

(Diff truncated)
comment
diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_3_40d839105047f4ac2996b53705601cc4._comment b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_3_40d839105047f4ac2996b53705601cc4._comment
new file mode 100644
index 000000000..c1fe12330
--- /dev/null
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_3_40d839105047f4ac2996b53705601cc4._comment
@@ -0,0 +1,62 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2021-09-21T17:34:08Z"
+ content="""
+I have not been able to reproduce this, it works for me.
+
+	joey@darkstar:~/tmp>git init a1
+	Initialized empty Git repository in /home/joey/tmp/a1/.git/
+	joey@darkstar:~/tmp>cd a1
+	joey@darkstar:~/tmp/a1>git annex init
+	init  ok
+	(recording state in git...)
+	joey@darkstar:~/tmp/a1>date > foo
+	joey@darkstar:~/tmp/a1>date > bar
+	joey@darkstar:~/tmp/a1>git annex add
+	add bar
+	ok
+	add foo
+	ok
+	(recording state in git...)
+	joey@darkstar:~/tmp/a1>git commit -m add
+	[master (root-commit) fdd215f] add
+	 2 files changed, 2 insertions(+)
+	 create mode 120000 bar
+	 create mode 120000 foo
+	joey@darkstar:~/tmp/a1>rm foo
+	joey@darkstar:~/tmp/a1>git commit -a -m rm
+	[master 1f84de1] rm
+	 1 file changed, 1 deletion(-)
+	 delete mode 120000 foo
+	joey@darkstar:~/tmp/a1>git annex unused
+	unused . (checking for unused data...) (checking master...) 
+	  Some annexed data is no longer used by any files:
+	    NUMBER  KEY
+	    1       SHA256E-s30--11de88d41da5962620f4a0590a577710e067d72e0f24220c4a4d2e81d594388a
+	  (To see where this data was previously used, run: git annex whereused --historical --unused
+	  
+	  To remove unwanted data: git-annex dropunused NUMBER
+	  
+	ok
+	joey@darkstar:~/tmp/a1>cd ..
+	joey@darkstar:~/tmp>git clone a1 a2
+	Cloning into 'a2'...
+	done.
+	joey@darkstar:~/tmp>cd a1
+	joey@darkstar:~/tmp/a1>git remote add a2 ../a2
+	joey@darkstar:~/tmp/a1>git annex move --unused --to a2
+	move SHA256E-s30--11de88d41da5962620f4a0590a577710e067d72e0f24220c4a4d2e81d594388a (to a2...) 
+	ok
+
+You did say it's not moving "some files" which suggests maybe it is moving
+other ones? Do you see it move any unused files at all?
+
+Do other commands that use the same --unused option work? Eg, does 
+`git annex whereis --unused` list them?
+
+The only way I can reproduce this behavior is if the remote has
+the same uuid as the current repository. Then any move is a no-op
+and it avoids operating on the files at all, the same as your output shows.
+So it seems possible that could be your real problem.
+"""]]

close as dup
diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
index df5736739..fbe5157ac 100644
--- a/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
@@ -150,3 +150,4 @@ annex.rsync-download-options "--bwlimit 1024" is ignored as well:
 ### 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've been using git-annex for many years with great success. It's a pleasure to use, I don't know how I ever managed to get by without it!
 
+> Closing as a duplicate of the bwlimit todo. [[done]] --[[Joey]]
diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored/comment_1_1e6ba80090eb07069027dfac6fbd0429._comment b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_1_1e6ba80090eb07069027dfac6fbd0429._comment
new file mode 100644
index 000000000..d4b11798f
--- /dev/null
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored/comment_1_1e6ba80090eb07069027dfac6fbd0429._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-21T17:24:59Z"
+ content="""
+Since that old version of git-annex, it has changed to using a different
+protocol than rsync for transfers over ssh. So the rsync options no longer
+apply to that. They are still used when git-annex does use rsync, either a
+rsync special remote or a server with too old a version of git-annex to use
+the new protocol.
+
+I think the main thing lost by this is bandwidth throttling.
+There is an open todo at [[todo/bwlimit]] to implement that in a way that
+will work more broadly than rsync's --bwlimit.
+
+Maybe also  --ipv4/--ipv6, but ssh configs can probably be used to
+accomplish the same thing as that.
+"""]]

comment
and reword possibly slightly confusing part of walkthrough
diff --git a/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_2_882d9e6b7b2297dfd29266bbd429b046._comment b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_2_882d9e6b7b2297dfd29266bbd429b046._comment
new file mode 100644
index 000000000..5fa717a92
--- /dev/null
+++ b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_2_882d9e6b7b2297dfd29266bbd429b046._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2021-09-21T17:22:22Z"
+ content="""
+It is perfectly safe. It can take some additional configuration
+to get the clients sending the objects that the other clients want
+to the server.
+"""]]
diff --git a/doc/walkthrough/adding_a_remote.mdwn b/doc/walkthrough/adding_a_remote.mdwn
index 0234e3ea1..8a1141db0 100644
--- a/doc/walkthrough/adding_a_remote.mdwn
+++ b/doc/walkthrough/adding_a_remote.mdwn
@@ -18,5 +18,5 @@ giving the repository a description helps when git-annex talks about it
 later.
 
 Notice that both repos are set up as remotes of one another. This lets
-either get annexed files from the other. You'll want to do that even
-if you are using git in a more centralized fashion.
+either get annexed files from the other. You'll often want to do that
+even when you are using git in a more centralized fashion.

improve docs for refspec format
It was especially unclear that "reflog" did not need a leading "+".
diff --git a/doc/git-annex-unused.mdwn b/doc/git-annex-unused.mdwn
index 2bb41d95f..fea0b98e4 100644
--- a/doc/git-annex-unused.mdwn
+++ b/doc/git-annex-unused.mdwn
@@ -40,6 +40,8 @@ For example, to move all unused data to origin:
   is not in the specified refs (and not used by the index) will then be
   considered unused.
 
+  See REFSPEC FORMAT below for details of the format of this setting.
+
   The git configuration annex.used-refspec can be used to configure
   this in a more permanent fashion.
 
@@ -47,13 +49,17 @@ For example, to move all unused data to origin:
 
 # REFSPEC FORMAT
 
-The refspec format for --used-refspec is a colon-separated list of
-additions and removals of refs. For example:
+The refspec format for --used-refspec and annex.used-refspec is
+a colon-separated list of additions and removals of refs.
+A somewhat contrived example:
 
-	+refs/heads/*:+HEAD^:+refs/tags/*:-refs/tags/old-tag
+	+refs/heads/*:+HEAD^:+refs/tags/*:-refs/tags/old-tag:reflog
 
 This adds all refs/heads/ refs, as well as the previous version
-of HEAD. It also adds all tags, except for old-tag.
+of HEAD. It also adds all tags, except for old-tag. And it adds
+all refs from the reflog.
+
+The default behavior is equivilant to `--used-refspec=+refs/*:+HEAD`
 
 The refspec is processed by starting with an empty set of refs,
 and walking the list in order from left to right.

response
diff --git a/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent/comment_1_2e16a4c08568ea37c16f4ea74203ae12._comment b/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent/comment_1_2e16a4c08568ea37c16f4ea74203ae12._comment
new file mode 100644
index 000000000..ecb6b82ca
--- /dev/null
+++ b/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent/comment_1_2e16a4c08568ea37c16f4ea74203ae12._comment
@@ -0,0 +1,29 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-21T15:35:23Z"
+ content="""
+Interesting, I had not known that ASIC design would involve
+the kind of large files git-annex would be useful for.
+
+I think you may want to use `git annex unused --used-refspec='+refs/heads/*:+HEAD:reflog'  
+That adds all versions that are in the reflog. Then you can
+can configure git to control how much reflog to keep around.
+(See `git-gc` man page`)
+
+The other possibility is a new git-annex feature, `git-annex whereused --unused --historical`  
+After you run `git annex unused`, you can run that to display
+each unused key, along with the git rev where that key was found to be
+used.
+
+The git rev looks like eg "master~4:filename" or "HEAD@{4}:filename".
+It will usually be the most recent use, although it prefers older uses
+that made it into a branch over any revs from the reflog. So you can
+filter for keys with numbers `> 8` or whatever, and get only the older
+versions of files. Then pipe the keys into `git-annex dropkey --batch`.
+
+Improving `git annex unused` to be able to do this kind of filtering itself
+is also a possibility. (See also
+[[forum/Drop__47__move_unused_files_older_than_x]] which was asking
+for a similar kind of thing with a similar response).
+"""]]

comment
diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_1_8623b98bf21427ae5734d74e2576af5b._comment b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_1_8623b98bf21427ae5734d74e2576af5b._comment
new file mode 100644
index 000000000..90bb281f8
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__/comment_1_8623b98bf21427ae5734d74e2576af5b._comment
@@ -0,0 +1,28 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-21T14:29:12Z"
+ content="""
+Each `git add` has to run a new `git annex smudge`
+process. `git commit` will often run it as well.
+This is discussed in detail in
+[[todo/git_smudge_clean_interface_suboptiomal]].
+
+According to
+<https://github.com/datalad/datalad/issues/5994#issuecomment-923744895>
+git-annex 8.20201127 had the same performance that you are seeing
+with the current version. So it cannot be any changes in the past year
+that impacted git-annex performance.
+
+But <https://github.com/datalad/datalad/issues/5317> seems like it would
+have been using around the same version of git-annex, and the timing there
+was much faster. Unfortunatly the issue does not say what version
+of git-annex was used, but it seems likely it would have been around
+8.20201127 since that was released 2 months prior.
+
+So are the timings in those issues comparable, or is it an apples/oranges
+comparison with different hardware or OS version?
+
+And if the timings between those issues are not comparable, what exactly
+is this new issue comparing with when it says the peformance has worsened?
+"""]]

diff --git a/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__.mdwn b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__.mdwn
new file mode 100644
index 000000000..bb3dc89f7
--- /dev/null
+++ b/doc/bugs/Windows__58___substantial_per-file_cost_for___96__add__96__.mdwn
@@ -0,0 +1,60 @@
+### Please describe the problem.
+
+Performance on Windows 10.0.19041 even on small text files is 10+ times slower than Git alone, and seems to have dropped over the past months of (windows, git, or git-annex developments).
+
+
+### What steps will reproduce the problem?
+
+Compare performance of `git add` and `git commit` in a Git repo with vs without `git annex init`.
+
+The observations below have been taken from [this DataLad issue](https://github.com/datalad/datalad/issues/5994). The hardware of the test system is described over there, but only the relative performance is relevant here.
+
+- `git init`: 0.1s
+- `git annex init`: 5.7s
+
+First timing info is the command executed in an annex-repo (after `git annex init`), the second timing is the same command executed in a plain Git repo.
+
+after creating a 3-byte text file:
+
+- `git add file`: 1.9s (0.1s)
+- `git commit file -m msg`: 3.2s (0.1s)
+
+after creating two new 3-byte test files:
+
+- `git add .`: 4.5s (0.1s)
+- `git commit -m msg`: 2.4s (0.1s)
+
+after creating eight more 3-byte text files:
+
+- `git add .`: 13.1s (0.1s)
+- `git commit -m msg`: 1.6 (0.1s)
+
+now adding a 225 MB binary file
+
+- `git add binfiile`: 16s (13s)
+- `git commit -m msg`: 2s (0.2s)
+
+no changes
+
+- `git commit --amend -m msg`: 2s (0.1s)
+
+So it looks as if there is a substantial per-file cost of `add` in an annex repo that is not explained by the underlying Git repo performance.
+
+
+
+### What version of git-annex are you using? On what operating system?
+
+- Windows 10.0.19041
+- Git 2.33.0.windows.2
+- git-annex 8.20210804-g1d3f59a9d
+
+### Please provide any additional information below.
+
+The linked datalad issue has more information on the configuration of the Git installation, but that only seems to affect performance broadly, and not git-annex specifically.
+
+I am reporting this behavior now, because it has worsened since [I last looked into performance on windows](https://github.com/datalad/datalad/issues/5317). It is unclear to me, which developments have led to this (also the windows version has progressed since then). However, even back then, it looked like there is a windows-specific performance issues that cannot be explained by the general handling of crippled filesystems or adjusted branches (comparing performance on an NTFS drive mounted on Debian vs a native windows box).
+
+
+Thanks for git-annex!
+
+[[!tag projects/datalad]]

Added a comment: Should be safe
diff --git a/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_1_7af462f69b9efc3bbe18d0dbc839cb39._comment b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_1_7af462f69b9efc3bbe18d0dbc839cb39._comment
new file mode 100644
index 000000000..f192a3775
--- /dev/null
+++ b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__/comment_1_7af462f69b9efc3bbe18d0dbc839cb39._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="weinzwang"
+ avatar="http://cdn.libravatar.org/avatar/e73d7d9e358f3b974d283fb0834cc5d9"
+ subject="Should be safe"
+ date="2021-09-21T07:55:57Z"
+ content="""
+I've been using git-annex with very similar setups for many years and never had any problems. I can't say anything about bare repos being more or less risky than non-bare ones, I've been using both on my server. Repos that can't see each other is a very common setup.
+"""]]

fixed a typo
diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
index 7f50c6247..df5736739 100644
--- a/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
@@ -1,5 +1,5 @@
 ### Please describe the problem.
-git-annex doesn't seem to honor the remote.\<name\>.annex-upload-options or annex.rsync-upload-options anymore. The problem seems to be on the server side. I noticed it when upgrading the ancient git-annex on my server from the version in stretch (6.20170101, --bwlimit worked fine with that) to the version in stretch-backports (7.20190129, --bwlimit broken). On the client I'm running bullseye and git-annex version 8.20210223. I then upgraded git-annex on the server to 8.20210903, the rsync-options don't work with that either.
+git-annex doesn't seem to honor the remote.\<name\>.annex-rsync-upload-options or annex.rsync-upload-options anymore. The problem seems to be on the server side. I noticed it when upgrading the ancient git-annex on my server from the version in stretch (6.20170101, --bwlimit worked fine with that) to the version in stretch-backports (7.20190129, --bwlimit broken). On the client I'm running bullseye and git-annex version 8.20210223. I then upgraded git-annex on the server to 8.20210903, the rsync-options don't work with that either.
 
 ### What steps will reproduce the problem?
 

Added a comment: Same here
diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_5_022f36242ef57158feab0918f41a7ff1._comment b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_5_022f36242ef57158feab0918f41a7ff1._comment
new file mode 100644
index 000000000..e3e055414
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_5_022f36242ef57158feab0918f41a7ff1._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joshmen"
+ avatar="http://cdn.libravatar.org/avatar/b182b7f2c9a8b168dd912bfe55bf2c2c"
+ subject="Same here"
+ date="2021-09-19T17:03:26Z"
+ content="""
+Confirming that assistant also adds files unlocked for me and I haven't found a way to change this (this is one of the main reasons I've moved away from using assistant to manual sync). If there is a way to change this behavior, I'd be glad to know about it!
+"""]]

diff --git a/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
new file mode 100644
index 000000000..7f50c6247
--- /dev/null
+++ b/doc/bugs/annex-rsync-upload-options_are_ignored.mdwn
@@ -0,0 +1,152 @@
+### Please describe the problem.
+git-annex doesn't seem to honor the remote.\<name\>.annex-upload-options or annex.rsync-upload-options anymore. The problem seems to be on the server side. I noticed it when upgrading the ancient git-annex on my server from the version in stretch (6.20170101, --bwlimit worked fine with that) to the version in stretch-backports (7.20190129, --bwlimit broken). On the client I'm running bullseye and git-annex version 8.20210223. I then upgraded git-annex on the server to 8.20210903, the rsync-options don't work with that either.
+
+### What steps will reproduce the problem?
+
+#### Client:
+
+    root@dition:/mnt/sneakerdisk9# mkdir testannex
+    root@dition:/mnt/sneakerdisk9# cd testannex
+    root@dition:/mnt/sneakerdisk9/testannex# git init
+    hint: Using 'master' as the name for the initial branch. This default branch name
+    hint: is subject to change. To configure the initial branch name to use in all
+    hint: of your new repositories, which will suppress this warning, call:
+    hint:
+    hint:   git config --global init.defaultBranch <name>
+    hint:
+    hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
+    hint: 'development'. The just-created branch can be renamed via this command:
+    hint:
+    hint:   git branch -m <name> 
+    Initialized empty Git repository in /mnt/sneakerdisk9/testannex/.git/
+    root@dition:/mnt/sneakerdisk9/testannex# git annex init dition
+    init dition (scanning for unlocked files...)
+    ok
+    (recording state in git...)
+    root@dition:/mnt/sneakerdisk9/testannex# dd if=/dev/random of=bigtestfile bs=1MiB count=500
+    500+0 records in
+    500+0 records out
+    524288000 bytes (524 MB, 500 MiB) copied, 6.00526 s, 87.3 MB/s
+    root@dition:/mnt/sneakerdisk9/testannex# git annex add bigtestfile
+    add bigtestfile
+    ok
+    (recording state in git...)
+
+#### Server:
+
+    dition@holm:/mnt/btrfs/testannex.git$ git init --bare
+    Initialized empty Git repository in /mnt/btrfs/testannex.git/
+
+#### Client:
+    root@dition:/mnt/sneakerdisk9/testannex# git remote add holm ssh://dition@holm:1322/mnt/btrfs/testannex.git
+    root@dition:/mnt/sneakerdisk9/testannex# git push --all holm
+    Enumerating objects: 10, done.
+    Counting objects: 100% (10/10), done.
+    Delta compression using up to 4 threads
+    Compressing objects: 100% (7/7), done. 
+    Writing objects: 100% (10/10), 871 bytes | 217.00 KiB/s, done.
+    Total 10 (delta 0), reused 0 (delta 0), pack-reused 0
+    To ssh://holm:1322/mnt/btrfs/testannex.git
+     * [new branch]      git-annex -> git-annex
+
+#### Server:
+
+    dition@holm:/mnt/btrfs/testannex.git$ git annex init holm
+    init holm ok
+    (recording state in git...)
+
+#### Client:
+    root@dition:/mnt/sneakerdisk9/testannex# git config --add annex.rsync-upload-options "--bwlimit 1024"
+    root@dition:/mnt/sneakerdisk9/testannex# git annex sync
+    commit
+    [master (root-commit) d0f28fe] git-annex in dition
+     Committer: root <root@dition.tulpe>
+    Your name and email address were configured automatically based
+    on your username and hostname. Please check that they are accurate.
+    You can suppress this message by setting them explicitly. Run the
+    following command and follow the instructions in your editor to edit
+    your configuration file:
+ 
+        git config --global --edit
+    
+    After doing this, you may fix the identity used for this commit with:
+ 
+        git commit --amend --reset-author
+    
+     1 file changed, 1 insertion(+)
+     create mode 120000 bigtestfile
+    ok
+    pull holm
+    remote: Counting objects: 3, done.
+    remote: Compressing objects: 100% (3/3), done.
+    remote: Total 3 (delta 0), reused 0 (delta 0) 
+    Unpacking objects: 100% (3/3), 363 bytes | 121.00 KiB/s, done.
+    From ssh://holm:1322/mnt/btrfs/testannex
+       5ace97f..70df03f  git-annex  -> holm/git-annex
+    ok
+    (merging holm/git-annex into git-annex...)
+    push holm
+    Enumerating objects: 3, done.
+    Counting objects: 100% (3/3), done.
+    Delta compression using up to 4 threads
+    Compressing objects: 100% (2/2), done.
+    Writing objects: 100% (3/3), 314 bytes | 314.00 KiB/s, done.
+    Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
+    To ssh://holm:1322/mnt/btrfs/testannex.git
+     * [new branch]      git-annex -> synced/git-annex
+     * [new branch]      master -> synced/master
+    ok
+    root@dition:/mnt/sneakerdisk9/testannex# git annex copy --to holm bigtestfile
+    copy bigtestfile (to holm...)
+    4%    19.27 MiB         3 MiB/s 2m31s
+
+### What version of git-annex are you using? On what operating system?
+#### Server:
+Debian stretch amd64 with git-annex from the neurodebian repo
+
+    dition@holm:/mnt/btrfs/testannex.git$ git annex version
+    git-annex version: 8.20210903-1~ndall+1
+    build flags: Assistant Webapp Pairing Inotify DBus DesktopNotify TorrentParser MagicMime Feeds Testsuite S3 WebDAV
+    dependency versions: aws-0.22 bloomfilter-2.0.1.0 cryptonite-0.26 DAV-1.3.4 feed-1.3.0.1 ghc-8.8.4 http-client-0.6.4.1
+    persistent-sqlite-2.10.6.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.1.0
+    key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 S
+    HA3_224E SHA3_224 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B51
+    2 BLAKE2B160E BLAKE2B160 BLAKE2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2BP512E BLAKE2BP512 BLAKE2S256E BLAKE2S256
+    BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WOR
+    M URL X*
+    remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar git-lfs httpalso borg
+    hook external
+    operating system: linux x86_64
+    supported repository versions: 8
+    upgrade supported from repository versions: 0 1 2 3 4 5 6 7
+
+#### Client:
+
+Debian bullseye arm64
+
+    root@dition:/mnt/sneakerdisk9# git annex version
+    git-annex version: 8.20210223
+    build flags: Assistant Webapp Pairing Inotify DBus DesktopNotify TorrentParser MagicMime Feeds Testsuite S3 WebDAV
+    dependency versions: aws-0.22 bloomfilter-2.0.1.0 cryptonite-0.26 DAV-1.3.4 feed-1.3.0.1 ghc-8.8.4 http-client-0.6.4.1 persistent-sqlite-2.10.6.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.1.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 BLAKE2BP512E BLAKE2BP512 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL X*
+    remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar git-lfs httpalso borg hook external
+    operating system: linux aarch64
+    supported repository versions: 8
+    upgrade supported from repository versions: 0 1 2 3 4 5 6 7
+
+I can also reproduce this with a debian buster client on amd64 with git-annex 8.20200330
+
+### Please provide any additional information below.
+
+As mentioned, the problem was introduced somewhere between 6.20170101 and 7.20190129.
+
+annex.rsync-download-options "--bwlimit 1024" is ignored as well:
+
+    root@dition:/mnt/sneakerdisk9/testannex# git config --add annex.rsync-download-options "--bwlimit 1024"
+    root@dition:/mnt/sneakerdisk9/testannex# git annex get --from holm bigtestfile 
+    get bigtestfile (from holm...) 
+    10%   51.82 MiB        19 MiB/s 23s 
+
+### 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've been using git-annex for many years with great success. It's a pleasure to use, I don't know how I ever managed to get by without it!
+

diff --git a/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__.mdwn b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__.mdwn
new file mode 100644
index 000000000..fc06eb437
--- /dev/null
+++ b/doc/forum/Must_all_nodes_be_accessible_to_one_another__63__.mdwn
@@ -0,0 +1,9 @@
+The [[walkthrough|https://git-annex.branchable.com/walkthrough/#index3h2]] says:
+
+> Notice that both repos are set up as remotes of one another. This lets either get annexed files from the other. You'll want to do that even if you are using git in a more centralized fashion.
+
+This is not always practical. I use a centralized setup where all of my devices sync (manually) with a bare repo on a server at home over the internet. Clients can access the server but the server cannot access the clients.
+
+Is it safe to use Git Annex in this way? Are there any pitfalls or gotchas I should be aware of? Is this setup any more (or less) risky when using a non-bare central repo?
+
+Thanks for this great piece of software!

Added a comment: Closing the loop
diff --git a/doc/forum/Pull_files_through_cloud/comment_2_f4dfc4b212e23f7443abfb28b5613f63._comment b/doc/forum/Pull_files_through_cloud/comment_2_f4dfc4b212e23f7443abfb28b5613f63._comment
new file mode 100644
index 000000000..5a2c5e715
--- /dev/null
+++ b/doc/forum/Pull_files_through_cloud/comment_2_f4dfc4b212e23f7443abfb28b5613f63._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="gabrielhidasy@c3d26e2c0b3e669d012f06736616088b42ad0dbe"
+ nickname="gabrielhidasy"
+ avatar="http://cdn.libravatar.org/avatar/8ae80704e96034c418e126326da2e7c8"
+ subject="Closing the loop"
+ date="2021-09-17T23:56:46Z"
+ content="""
+The metadata solution worked well, except that my cloud remote is unable to run the assistant to pull files from the desktop, and the assistant in the desktop was not pushing. 
+That is fixed by running ```git annex sync --content cloud``` every few minutes in the desktop.
+"""]]

Added a comment
diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_4_a8ebda7dd8d52e6c1137dbb646e21e5c._comment b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_4_a8ebda7dd8d52e6c1137dbb646e21e5c._comment
new file mode 100644
index 000000000..0090a3b66
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_4_a8ebda7dd8d52e6c1137dbb646e21e5c._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="weinzwang"
+ avatar="http://cdn.libravatar.org/avatar/e73d7d9e358f3b974d283fb0834cc5d9"
+ subject="comment 4"
+ date="2021-09-17T18:57:41Z"
+ content="""
+That's also not it:
+
+    ### git branch
+      git-annex
+    * master
+      synced/master
+"""]]

Added a comment
diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_3_c80d15f6844e29dee2bf7ba0a6d6d093._comment b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_3_c80d15f6844e29dee2bf7ba0a6d6d093._comment
new file mode 100644
index 000000000..6ee33fab0
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_3_c80d15f6844e29dee2bf7ba0a6d6d093._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Lukey"
+ avatar="http://cdn.libravatar.org/avatar/c7c08e2efd29c692cc017c4a4ca3406b"
+ subject="comment 3"
+ date="2021-09-17T15:24:47Z"
+ content="""
+It could also be due to being on a adjusted unlocked branch. Try `git checkout master`
+"""]]

diff --git a/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent.mdwn b/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent.mdwn
new file mode 100644
index 000000000..2edba50fe
--- /dev/null
+++ b/doc/forum/Drop_some_unused_objects_keeping___35__n_more_recent.mdwn
@@ -0,0 +1,18 @@
+Hello, thanks for all the work in Git-annex! We've been using since 2018 for Integrated Circuits (ASICs) design with a lot of success.
+
+One feature that would help us would be to be able to Drop Unused, but not all Unused.
+In our design flow, every 2 or 3 weeks, we update a collection of Objects, replacing, "overwriting" the objects with newer version.
+So the older becomes Unused.
+
+But we want to keep a certain history depth of Unused. And this cannot be based on dates.
+Because when we stop working on a certain block/module, the Unused Objects can become quite old (like 6 months), but we still want to keep like for example 3x Unused Objects.
+
+Example: we have overwritten 12x times the same Object "module_a.jpg". So we have 11x Unused versions of "module_a.jpg".
+We want to keep the latest and +3x latest Unused. So on total only 8x Unused copies would get Dropped.
+
+Is there a way to do that with Git-annex?
+Or we would need to create a custom script to parse the git-repository, list all the 12x keys of "module_a.jpg", and pick the 8x for Dropping?
+
+Thank you.
+Best regards.
+Davi Castro.

Added a comment
diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_2_c0c6e38fe99dd273764071574c8b3c1d._comment b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_2_c0c6e38fe99dd273764071574c8b3c1d._comment
new file mode 100644
index 000000000..210592cf1
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_2_c0c6e38fe99dd273764071574c8b3c1d._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="weinzwang"
+ avatar="http://cdn.libravatar.org/avatar/e73d7d9e358f3b974d283fb0834cc5d9"
+ subject="comment 2"
+ date="2021-09-16T20:56:29Z"
+ content="""
+Thanks for the hint - this doesn't seem to make any difference though. If I unterstand the manpage correctly, adding files in locked form is the default anyway. I tried it to be sure:
+
+    # git config --get annex.addunlocked
+    false
+
+and restarted the assistant. The behaviour is the same as before, files are added unlocked.
+"""]]

Added a comment
diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_1_70c8e23c87a6c48e728a6685e7189e98._comment b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_1_70c8e23c87a6c48e728a6685e7189e98._comment
new file mode 100644
index 000000000..ae1d509e0
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__/comment_1_70c8e23c87a6c48e728a6685e7189e98._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="Lukey"
+ avatar="http://cdn.libravatar.org/avatar/c7c08e2efd29c692cc017c4a4ca3406b"
+ subject="comment 1"
+ date="2021-09-16T15:00:49Z"
+ content="""
+Yes, by setting the `annex.addunlocked` config option to `false`
+"""]]

diff --git a/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__.mdwn b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__.mdwn
new file mode 100644
index 000000000..4b689e497
--- /dev/null
+++ b/doc/forum/Is_there_a_way_to_have_assistant_add_files_locked__63__.mdwn
@@ -0,0 +1,5 @@
+I'm using the version of git-annex in debian bullseye (8.20210223) and a newly
+created repository (v8). When I put a new file into the repository, the assistant
+notices and adds it, but the file is then unlocked. Is there a way to tell the
+assistant to add files in locked mode? This repository is in the "source" group,
+so contents get dropped soon and the pointer files regularly confuse me :)

comment
diff --git a/doc/todo/optimise_by_converting_Map_to_HashMap.mdwn b/doc/todo/optimise_by_converting_Map_to_HashMap.mdwn
index 49f8b3b32..448a26b2f 100644
--- a/doc/todo/optimise_by_converting_Map_to_HashMap.mdwn
+++ b/doc/todo/optimise_by_converting_Map_to_HashMap.mdwn
@@ -5,3 +5,8 @@ parts with HashMap. The uses in AnnexRead especially.
 (Set and HashSet too.)
 
 --[[Joey]]
+
+> Note that HashMap perfomance can degrade if an attacker provides keys
+> that collide. This has been used to DOS aeson parsing. (Which could
+> affect a few parts of git-annex in theory). So if converting to HashMap,
+> need to consider this. --[[Joey]]

Added a comment
diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_2_91cdee93a29eefca78c1e49878b71574._comment b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_2_91cdee93a29eefca78c1e49878b71574._comment
new file mode 100644
index 000000000..36833b19e
--- /dev/null
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_2_91cdee93a29eefca78c1e49878b71574._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="mike@2d6d71f56ce2a992244350475251df87c26fe351"
+ nickname="mike"
+ avatar="http://cdn.libravatar.org/avatar/183fa439752e2f0c6f39ede658d81050"
+ subject="comment 2"
+ date="2021-09-10T14:41:17Z"
+ content="""
+It's not fixed in 8.20210903, it just isn't `(checking refs/annex/last-index...)` anymore.
+"""]]

Added a comment
diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_1_3fe94ad69d28f16598fdb8f98d7ffa54._comment b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_1_3fe94ad69d28f16598fdb8f98d7ffa54._comment
new file mode 100644
index 000000000..8490c48cf
--- /dev/null
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files/comment_1_3fe94ad69d28f16598fdb8f98d7ffa54._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="mike@2d6d71f56ce2a992244350475251df87c26fe351"
+ nickname="mike"
+ avatar="http://cdn.libravatar.org/avatar/183fa439752e2f0c6f39ede658d81050"
+ subject="comment 1"
+ date="2021-09-10T14:28:23Z"
+ content="""
+May be fixed in 8.20210903 (\"unused: Skip the refs/annex/last-index ref that git-annex recently started creating.\") - I'm compiling it to confirm.
+"""]]

diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn b/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn
index c66f903c1..d25449630 100644
--- a/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn
@@ -20,7 +20,7 @@ Calibre Library % git annex move --unused -t bims
 Calibre Library %
 ```
 
-Is there anything hat explains this behavior?
+Is there anything that explains this behavior?
 
 ### What steps will reproduce the problem?
 I am not sure how to reproduce this in another repo.

diff --git a/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn b/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn
new file mode 100644
index 000000000..c66f903c1
--- /dev/null
+++ b/doc/bugs/git_annex_move_--unused_not_moving_some_files.mdwn
@@ -0,0 +1,33 @@
+### Please describe the problem.
+
+`git annex move --unused` is not moving some of the files `git annex unused` reports:
+
+```
+Calibre Library % git annex unused 
+unused . (checking for unused data...) (checking master...) (checking refs/annex/last-index...) 
+  Some annexed data is no longer used by any files:
+    NUMBER  KEY
+    1       SHA256E-s182401--1c42279d1ea477f2a1831a3f7543481e7d5c72cbbeffdd0442918253b4f3ecc9.jpg
+    2       SHA256E-s127374--17bfedf7453382df04a58d9c508d82285970148e49a84c488a728f7f770d8cc3.jpg
+[...]
+    56      SHA256E-s427315--3617fae344fb4fbc825963ee0ddfc5a31f1000c8d1c61ce3955bc16ae625e207.epub
+  (To see where this data was previously used, run: git annex whereused --historical --unused
+  
+  To remove unwanted data: git-annex dropunused NUMBER
+  
+ok
+Calibre Library % git annex move --unused -t bims 
+Calibre Library %
+```
+
+Is there anything hat explains this behavior?
+
+### What steps will reproduce the problem?
+I am not sure how to reproduce this in another repo.
+
+### What version of git-annex are you using? On what operating system?
+git-annex version: 8.20210803
+
+### 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 use it every day for various tasks! Great software!
+

response and close
diff --git a/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn b/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn
index f096a46c2..ce501c96c 100644
--- a/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn
+++ b/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn
@@ -204,3 +204,5 @@ push origin ok
 ### 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 am a total fan, thanks for your awesome work, as usual. ;) -- [[anarcat]]
+
+> [[notabug|done]] --[[Joey]]
diff --git a/doc/bugs/gets_confused_files_synchronized_out_of_band/comment_1_a8f081ee9a5ad8c45dcdf66cea368463._comment b/doc/bugs/gets_confused_files_synchronized_out_of_band/comment_1_a8f081ee9a5ad8c45dcdf66cea368463._comment
new file mode 100644
index 000000000..ca3c4c27b
--- /dev/null
+++ b/doc/bugs/gets_confused_files_synchronized_out_of_band/comment_1_a8f081ee9a5ad8c45dcdf66cea368463._comment
@@ -0,0 +1,36 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-08T15:56:35Z"
+ content="""
+The french output is by git, not git-annex. It looks like the standard
+message about there being a merge conflict. One part of the git
+output is not translated and it happens to be the part that explains
+what is going on.
+
+	warning: Cannot merge binary files: metadata.db (HEAD vs. refs/remotes/origin/master)
+
+So metadata.db is a binary file, so it is not stored in git-annex,
+but is checked directly into git. Or at least it is on one side of
+the merge conflict.
+
+I think that one repository added metadata.db to git-annex, and the other
+added it to directly into git. So then the merge conflict is between
+the binary content of the file and the git-annex symlink. Which is an
+actual merge conflict, even though the content of the file is the same.
+
+That kind of merge conflict is when `git-annex sync` resolves the merge
+conflict with a single `.variant` file for the annexed version along side the
+regular file as stored in git. Which is what your ls showed. 
+You can resolve the merge conflict by either deleting the annexed
+ `.variant` file or renaming it over the file stored in git.
+
+You can avoid this kind of merge conflict by configuring annex.largefiles
+the same in both repositories, so when a file like metadata.db gets added
+separately in each repository, the same annex link will be added.
+
+Or you can set annex.resolvemerge to false for sync to avoid resolving merge
+conflicts, and then just resolve them yourself.
+
+I don't see a bug here, this is all documented.
+"""]]

some weird corner case that might be handled a little better?
diff --git a/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn b/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn
new file mode 100644
index 000000000..f096a46c2
--- /dev/null
+++ b/doc/bugs/gets_confused_files_synchronized_out_of_band.mdwn
@@ -0,0 +1,206 @@
+### Please describe the problem.
+
+So this happened today: I did a `git-annex sync --content` over a git repository that is *also* (sorry!) synchronized by syncthing. Not the best idea, I know, but I *do* avoid synchronizing the `.git` directory to avoid going completely nuts. This has actually worked surprisingly well so far: I can add stuff in one repository and it trickles to the other either with syncthing (on my ebook reader, which doesn't have git-annex) or git-annex (on my other desktops).
+
+What happened here is that syncthing was faster than git-annex and copied the files over first. For new files, that wasn't much of a problem: git freaked out, but just skipped the files; I didn't get corruption on that front.
+
+For calibre's infamous `metadata.db`, however, it was another story. I ended up in this weird state where it found a conflict on the file, because it is binary:
+
+    warning: Cannot merge binary files: metadata.db (HEAD vs. refs/remotes/origin/master)
+
+So it left a `metadata.variant-1b96.db` file around. But the interesting thing here (and why I open a bug report instead of just moving on) is that the variant is actually *identical* to the other file:
+
+    anarcat@curie:books(master)$ ls -al metadata*db
+    -rw-r--r-- 1 anarcat anarcat 1 696 768 2021-09-07 09:40 metadata.db
+    -rw-r--r-- 1 anarcat anarcat 1 696 768 2021-09-05 21:39 metadata.variant-1b96.db
+    anarcat@curie:books(master)$ diff metadata*db
+    anarcat@curie:books(master)$
+
+Now I understand git-annex wants to be careful about merging binary data, but surely this is a low-hanging fruit that could be figured out?
+
+Similarly, the already present file could be merged as well, but I understand that this is also a hard problem in git itself: it doesn't like dirty work trees... 
+
+And I understand if this entire thing would be closed as "ugh, sorry, this is how git works, #wontfix", of course. :) I'm kind of doing crazy stuff with git-annex all the time and I understand if this won't be fixed, I can deal with the occasional trouble...
+
+### What steps will reproduce the problem?
+
+It's not exactly clear to me how to reproduce this. I think the trick is to:
+
+ 1. create a git-annex repository (A) with some content
+ 2. synchronize it to a new, second repository (B)
+ 3. create a binary file, on repo A
+ 4. synchronize it on repo B, once
+ 5. modify the binary file on repo A
+ 6. copy it by hand, behind git-annex's back, to repo B
+ 7. synchronize both repos again
+
+I suspect you might be able to reproduce the issue without binary files with:
+
+ 1. create a git-annex repository (A) with some content
+ 2. synchronize it to a new, second repository (B)
+ 3. create a new file, on repo A
+ 4. copy it by hand, behind git-annex's back, to repo B
+ 5. synchronize both repos again
+
+### What version of git-annex are you using? On what operating system?
+
+This is git-annex 8.20210223-2 on Debian bullseye, on both ends. Syncthing is running on both ends as well, and on my Kobo Clara HD e-reader.
+
+### Please provide any additional information below.
+
+This is the first crash, which occured under `myrepos` supervision:
+
+[[!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
+mr update: /home/anarcat/books
+(recording state in git...)
+[master 29a49c9c48] git-annex in anarcat@curie:~/books
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ rewrite metadata.db (100%)
+commit ok
+remote: Enumerating objects: 103, done.
+remote: Counting objects: 100% (103/103), done.
+remote: Compressing objects: 100% (80/80), done.
+remote: Total 83 (delta 48), reused 0 (delta 0)
+Dépaquetage des objets: 100% (83/83), 11.26 Kio | 23.00 Kio/s, fait.
+Depuis anarc.at:/srv/books
+   2a75cab2ba..b368a4b963  master           -> origin/master
+   3ddac40b17..f59c826035  git-annex        -> origin/git-annex
+   3ddac40b17..b46273adfc  synced/git-annex -> origin/synced/git-annex
+   2a75cab2ba..b368a4b963  synced/master    -> origin/synced/master
+error: Les fichiers suivants non suivis de la copie de travail seraient effacés par la fusion :
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/Medicine for Mountaineering & Other Wilder - James A. Wilkerson.epub
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/cover.jpg
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/metadata.opf
+Veuillez renommer ou effacer ces fichiers avant la fusion.
+Abandon
+error: Les fichiers suivants non suivis de la copie de travail seraient effacés par la fusion :
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/Medicine for Mountaineering & Other Wilder - James A. Wilkerson.epub
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/cover.jpg
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/metadata.opf
+Veuillez renommer ou effacer ces fichiers avant la fusion.
+Abandon
+pull origin failed
+(merging origin/git-annex origin/synced/git-annex into git-annex...)
+(recording state in git...)
+error: Les fichiers suivants non suivis de la copie de travail seraient effacés par la fusion :
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/Medicine for Mountaineering & Other Wilder - James A. Wilkerson.epub
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/cover.jpg
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/metadata.opf
+Veuillez renommer ou effacer ces fichiers avant la fusion.
+Abandon
+error: Les fichiers suivants non suivis de la copie de travail seraient effacés par la fusion :
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/Medicine for Mountaineering & Other Wilder - James A. Wilkerson.epub
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/cover.jpg
+	James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/metadata.opf
+Veuillez renommer ou effacer ces fichiers avant la fusion.
+Abandon
+copy metadata.db (to origin...) ok
+pull origin failed
+(recording state in git...)
+push origin 
+Énumération des objets: 23, fait.
+Décompte des objets: 100% (21/21), fait.
+Compression par delta en utilisant jusqu'à 4 fils d'exécution
+Compression des objets: 100% (15/15), fait.
+Écriture des objets: 100% (15/15), 1.94 Kio | 1.94 Mio/s, fait.
+Total 15 (delta 6), réutilisés 0 (delta 0), réutilisés du pack 0
+remote: PWD: /srv/books/.git
+remote: running git annex merge in /srv/books
+remote: Already up to date.
+remote: calling /srv/books/.git/hooks/calibre-hack
+remote: running /srv/books/.git/hooks/calibre-hack
+remote: fixing perms for calibre
+remote: + chown :media metadata.calibre metadata.db metadata_db_prefs_backup.json
+remote: + chmod g+w metadata.calibre metadata.db metadata_db_prefs_backup.json
+remote: + sudo service calibre-server restart
+To anarc.at:/srv/books
+   b46273adfc..de2a69481a  git-annex -> synced/git-annex
+ ! [rejected]              master -> synced/master (non-fast-forward)
+error: impossible de pousser des références vers 'anarc.at:/srv/books'
+astuce: Les mises à jour ont été rejetées car la pointe de la branche courante est derrière
+astuce: son homologue distant. Extrayez cette branche et intégrez les changements distants
+astuce: (par exemple 'git pull ...') avant de pousser à nouveau.
+To anarc.at:/srv/books ! [rejected]              master -> master (non-fast-forward)error: impossible de pousser des références vers 'anarc.at:/srv/books'astuce: Les mises à jour ont été rejetées car la pointe de la branche courante est derrièreastuce: son homologue distant. Intégrez les changements distants (par exemple 'git pull ...')astuce: avant de pousser à nouveau.astuce: Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus d'information.push origin 
+  Pushing to origin failed.
+failed
+git-annex: sync: 3 failed
+mr update: command failed
+
+
+# End of transcript or log.
+"""]]
+
+Notice the mojibake in the french. It seems like the `é` characters are getting double-encoded. `LANG=fr_CA.UTF-8`. But that's another story. ;)
+
+Also note that the `metadata.db` here is not yet corrupt. That happened on the second run:
+
+[[!format sh """
+anarcat@curie:books(master)$ git annex sync --content -J2
+Sur la branche master
+Votre branche et 'origin/master' ont divergé,
+et ont 1 et 2 commits différents chacune respectivement.
+  (utilisez "git pull" pour fusionner la branche distante dans la vôtre)
+
+Fichiers non suivis:
+  (utilisez "git add <fichier>..." pour inclure dans ce qui sera validé)
+	.stversions/
+
+aucune modification ajoutée à la validation mais des fichiers non suivis sont présents (utilisez "git add" pour les suivre)
+commit ok
+remote: Enumerating objects: 9, done.
+remote: Counting objects: 100% (9/9), done.
+remote: Compressing objects: 100% (5/5), done.
+remote: Total 5 (delta 3), reused 0 (delta 0)
+Dépaquetage des objets: 100% (5/5), 441 octets | 73.00 Kio/s, fait.
+Depuis anarc.at:/srv/books
+   de2a69481a..f5fc20c433  git-annex  -> origin/git-annex
+warning: Cannot merge binary files: metadata.db (HEAD vs. refs/remotes/origin/master)
+Fusion automatique de metadata.db
+CONFLIT (contenu) : Conflit de fusion dans metadata.db
+La fusion automatique a échoué ; réglez les conflits et validez le résultat.
+pull origin (recording state in git...)
+
+  Merge conflict was automatically resolved; you may want to examine the result.
+[master 01adc3214c] git-annex automatic merge conflict fix
+Déjà à jour.
+ok
+(merging origin/git-annex into git-annex...)
+get James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/cover.jpg (from origin...) ok
+get James A. Wilkerson/Medicine for Mountaineering & Other Wilderness Activities (1583)/Medicine for Mountaineering & Other Wilder - James A. Wilkerson.epub (from origin...) ok
+pull origin ok
+(recording state in git...)
+push origin 
+Énumération des objets: 22, fait.
+Décompte des objets: 100% (22/22), fait.
+Compression par delta en utilisant jusqu'à 4 fils d'exécution
+Compression des objets: 100% (13/13), fait.
+Écriture des objets: 100% (13/13), 2.08 Kio | 2.08 Mio/s, fait.
+Total 13 (delta 7), réutilisés 0 (delta 0), réutilisés du pack 0
+remote: PWD: /srv/books/.git
+remote: running git annex merge in /srv/books
+remote: Updating b368a4b96..01adc3214
+remote: Fast-forward
+remote:  metadata.variant-1b96.db | 1 +
+remote:  1 file changed, 1 insertion(+)
+remote:  create mode 100644 metadata.variant-1b96.db
+remote: running .git//hooks/calibre-hack
+remote: fixing perms for calibre
+remote: + chown :media metadata.calibre metadata.db metadata.variant-1b96.db metadata_db_prefs_backup.json
+remote: + chmod g+w metadata.calibre metadata.db metadata.variant-1b96.db metadata_db_prefs_backup.json
+remote: + sudo service calibre-server restart
+remote: calling /srv/books/.git/hooks/calibre-hack
+remote: running /srv/books/.git/hooks/calibre-hack

(Diff truncated)
comment
diff --git a/doc/forum/Unlocked_directory__63___Having_all_its_new_files_unlocked/comment_7_c6f1dd606d2aa9aa473cdd3a6d92bd33._comment b/doc/forum/Unlocked_directory__63___Having_all_its_new_files_unlocked/comment_7_c6f1dd606d2aa9aa473cdd3a6d92bd33._comment
new file mode 100644
index 000000000..430c3779b
--- /dev/null
+++ b/doc/forum/Unlocked_directory__63___Having_all_its_new_files_unlocked/comment_7_c6f1dd606d2aa9aa473cdd3a6d92bd33._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 7"""
+ date="2021-09-06T19:50:02Z"
+ content="""
+The annex.addunlocked config is able to do this, since version 7.20191230.
+It can be set to a [[git-annex-matching-expression]]
+eg "include=wiki/*" would make files in the top-level wiki directory always
+be added unlocked, and other files would be added locked.
+Matching on file extensions etc is also possible.
+"""]]

retitle
diff --git a/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__.mdwn b/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__.mdwn
index 9108ab98c..c93e06664 100644
--- a/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__.mdwn
+++ b/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__.mdwn
@@ -722,3 +722,5 @@ from 159 to 296985).
 ### 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 is great. It works quite nicely with my multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend :)
+
+[[!meta title="windows: sync -C takes longer than get, apparently extra checksum"]] 

followup
diff --git a/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__/comment_1_fa2fb2493ea1244f334b54e6af2b553b._comment b/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__/comment_1_fa2fb2493ea1244f334b54e6af2b553b._comment
new file mode 100644
index 000000000..abcd6f171
--- /dev/null
+++ b/doc/bugs/__96__sync_-C__96___takes_longer_to_get_file_than___96__get__96__/comment_1_fa2fb2493ea1244f334b54e6af2b553b._comment
@@ -0,0 +1,73 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-06T18:52:21Z"
+ content="""
+I tried to reproduce this on Linux, and cannot:
+
+	joey@darkstar:~/tmp/r>/usr/bin/time git-annex get bigfile 
+	get bigfile (from origin...) 
+	ok                                   
+	(recording state in git...)
+	11.66user 2.08system 0:13.76elapsed 99%CPU (0avgtext+0avgdata 99012maxresident)k
+	206440inputs+2048856outputs (46major+21865minor)pagefaults 0swaps
+	joey@darkstar:~/tmp/r>git annex drop
+	drop bigfile ok
+	(recording state in git...)
+	joey@darkstar:~/tmp/r>/usr/bin/time git annex sync --no-commit --no-push --no-pull --content-of bigfile 
+	get bigfile (from origin...) 
+	ok                                  
+	(recording state in git...)
+	11.43user 1.91system 0:13.43elapsed 99%CPU (0avgtext+0avgdata 97132maxresident)k
+	60016inputs+2048312outputs (25major+11680minor)pagefaults 0swaps
+
+One reason it may do an extra checksum is if for some reason the inode
+cache is stale. Since [[!commit 3b5a3e168d8decd196509ad582ad4b8795d979a6]]
+it will check that and checksum before starting to send the content.
+
+In this case, it will display "(checksum...)" before the progress display
+has started.
+
+One way I can reproduce that, on Linux, is to touch the annex object file
+in the remote git repository before running git-annex. This is the result:
+
+	joey@darkstar:~/tmp/r>/usr/bin/time git-annex get bigfile 
+	get bigfile (from origin...) (checksum...) 
+	ok                                   
+	(recording state in git...)
+	22.06user 2.22system 0:24.85elapsed 97%CPU (0avgtext+0avgdata 97192maxresident)k
+	704inputs+2048432outputs (33major+10701minor)pagefaults 0swaps
+
+But that shows that git-annex get also behaves that way, which does not
+explain why only git-annex sync would have the problem for you. 
+(And the two run the same code; sync literally runs code from get.) Also
+I don't know why the inode cache would be getting stale on windows.
+
+If it does not display "(checksum...)" before the progress display,
+it's certianly not that. And if it does not display "(checksum...)"
+at any point at all, that would be good to know, because normally
+git-annex does display that when checksumming.
+Your transcript seems to show no such output.
+
+At the "2021-08-27 00:15:36.6868986" timestamp in your log, it's just
+completed updating the location log, and you identify the pause after that
+as where it's checksumming. So that seems to suggest it's already
+transferred the content there, and updated the location log, before
+checksumming. But it doesn't do anything normally
+after updating the location log, and it won't update the location log
+before it's satisfied it transferred the right content, so after any
+checksumming.
+
+Possibly `git annex sync` is trying to drop the content from some
+remote due to its preferred content setting no longer wanting the content
+to be there. Do you have preferred content expressions configured?
+(I'm doubtful about this theaory because I don't think it ever hashes the
+content during a drop, but one difference between `get` and `sync`
+is that sync drops..)
+
+Only other thing I can think that it could be is perhaps git is running
+the smudge/clean filter on the content, after the transfer, and git-annex
+hashes it again when run that way. Hard for me to tell from the filespray
+log if that might be the case. Setting `GIT_TRACE=1` would show if this
+happens.
+"""]]

close
diff --git a/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn b/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn
index 700ff29e4..9625e1ef2 100644
--- a/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn
+++ b/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn
@@ -56,3 +56,5 @@ git-annex 8.20210223-2 on Debian 11/Bullseye.
 ### 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)
 
 These sync worked fine before upgrade.
+
+> [[fixed|done]] in git-annex 8.20210803 --[[Joey]]
diff --git a/doc/bugs/sync_failing_after_debian_bullseye_upgrade/comment_1_c6d25991ae391f2ca2dba1b4ceda704d._comment b/doc/bugs/sync_failing_after_debian_bullseye_upgrade/comment_1_c6d25991ae391f2ca2dba1b4ceda704d._comment
new file mode 100644
index 000000000..820b77971
--- /dev/null
+++ b/doc/bugs/sync_failing_after_debian_bullseye_upgrade/comment_1_c6d25991ae391f2ca2dba1b4ceda704d._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-06T17:59:48Z"
+ content="""
+Upgrade -- these problems are fixed already in version 8.20210803.
+
+I have filed a debian bug report suggesting they backport the fixes.
+"""]]

update
diff --git a/doc/thanks/list b/doc/thanks/list
index da73304a5..a2f027fff 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -116,3 +116,5 @@ Nick White,
 dukeofcool199, 
 Matt, 
 Shae Erisson, 
+mortoise, 
+Markus, 

diff --git a/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn b/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn
new file mode 100644
index 000000000..700ff29e4
--- /dev/null
+++ b/doc/bugs/sync_failing_after_debian_bullseye_upgrade.mdwn
@@ -0,0 +1,58 @@
+### Please describe the problem.
+
+After upgrading Debian Buster to Bullseye (and thus git-annex to 8.20210223-2), two sync `git annex sync --content` jobs have started failing.
+
+One is to an encrypted remote:
+
+    sending incremental file list
+    54c/e47/
+    54c/e47/GPGHMACSHA1--REDACTED/
+    54c/e47/GPGHMACSHA1--REDACTED/GPGHMACSHA1--REDACTED
+    
+             32,768   8%    0.00kB/s    0:00:00
+            375,589 100%  326.94MB/s    0:00:00 (xfr#1, to-chk=0/5)
+    
+    failed
+      content changed while it was being sent
+      content changed while it was being sent
+    git-annex: sync: 1 failed
+
+The content hasn't changed as far as I know.
+
+Another is to another local filesystem (from ext4 to ext4):
+
+```
+  failed to send content to remote                                                                                                                                                            
+  failed to send content to remote                                                                                                                                                            
+  failed to send content to remote                                                                                                                                                            
+To /path/to/remote                                                                                                                                                                  
+   XXXXXXXXX..XXXXXXXXX  git-annex -> synced/git-annex                                                                                                                                        
+   XXXXXXXXX..XXXXXXXXX  master -> synced/master                                                                                                                                              
+git-annex: sync: 884 failed        
+```
+
+In the latter case, `--debug` prints two ExitFailures:
+
+```
+[2021-09-04 13:21:26.943294346] process [2934668] read: cp ["--reflink=always","--preserve=timestamps",".git/annex/objects/gM/mZ/SHA256E-REDACTED.pdf/SHA256E-REDACTED.pdf","/path/to/remote/.git/annex/tmp/SHA256E-REDACTED.pdf"]                                                                                                                                           
+[2021-09-04 13:21:26.944187444] process [2934668] done ExitFailure 1
+<clip>
+2021-09-04 13:21:52.79239764] process [2936730] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","push","remote","master"]                                                  
+[2021-09-04 13:21:52.808201959] process [2936730] done ExitFailure 1
+```
+
+Manually running that cp command fails with "Permission denied", because the tmp file now exists and has 444 permissions.
+
+### What steps will reproduce the problem?
+
+I haven't tried to reproduce this from a clean repository, but it happens 100 % of the time.
+
+### What version of git-annex are you using? On what operating system?
+
+git-annex 8.20210223-2 on Debian 11/Bullseye.
+
+### Please provide any additional information below.
+
+### 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)
+
+These sync worked fine before upgrade.

Added a comment
diff --git a/doc/forum/Borg_and_xattrs/comment_2_a1f4e0758c3e02f4f8558d167a26c0c4._comment b/doc/forum/Borg_and_xattrs/comment_2_a1f4e0758c3e02f4f8558d167a26c0c4._comment
new file mode 100644
index 000000000..1b26f69d7
--- /dev/null
+++ b/doc/forum/Borg_and_xattrs/comment_2_a1f4e0758c3e02f4f8558d167a26c0c4._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="strmd"
+ avatar="http://cdn.libravatar.org/avatar/035707b9756129bbdea6b36a7f7b38d3"
+ subject="comment 2"
+ date="2021-09-03T18:22:30Z"
+ content="""
+Sounds good to me. Thanks, Joey!
+"""]]

borg: Avoid trying to extract xattrs, ACLS, and bsdflags
when retrieving from a borg repository
That broke restoring on linux from a borg backup made on OSX.
Sponsored-by: Boyd Stephen Smith Jr. on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 73d0cde3a..538ae62a4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+git-annex (8.20210904) UNRELEASED; urgency=medium
+
+  * borg: Avoid trying to extract xattrs, ACLS, and bsdflags when
+    retrieving from a borg repository.
+
+ -- Joey Hess <id@joeyh.name>  Fri, 03 Sep 2021 12:02:55 -0400
+
 git-annex (8.20210903) upstream; urgency=medium
 
   * Deal with clock skew, both forwards and backwards, when logging
diff --git a/Remote/Borg.hs b/Remote/Borg.hs
index 3cf488844..86db4bf21 100644
--- a/Remote/Borg.hs
+++ b/Remote/Borg.hs
@@ -360,6 +360,15 @@ retrieveExportWithContentIdentifierM borgrepo loc _ dest mkk _ = do
 		absborgrepo <- absBorgRepo borgrepo
 		let p = proc "borg" $ toCommand
 			[ Param "extract"
+			-- git-annex object files do not need any
+			-- xattrs or ACLs, and trying to extract
+			-- any that are set from the backup can lead
+			-- to problems when doing a retrieve from a
+			-- different OS than the one where the backup was
+			-- made.
+			, Param "--noxattrs"
+			, Param "--noacls"
+			, Param "--nobsdflags"
 			, Param (borgArchive absborgrepo archivename)
 			, File (fromRawFilePath archivefile)
 			]
diff --git a/doc/forum/Borg_and_xattrs/comment_1_703c9a9be1e0b0ca9ce4dd0309546a04._comment b/doc/forum/Borg_and_xattrs/comment_1_703c9a9be1e0b0ca9ce4dd0309546a04._comment
new file mode 100644
index 000000000..305659540
--- /dev/null
+++ b/doc/forum/Borg_and_xattrs/comment_1_703c9a9be1e0b0ca9ce4dd0309546a04._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-03T16:07:53Z"
+ content="""
+I see no reason for it to need to preserve xattrs or ACLs.
+I've changed it to disable extracting those.
+"""]]

add news item for git-annex 8.20210903
diff --git a/doc/news/version_8.20210428.mdwn b/doc/news/version_8.20210428.mdwn
deleted file mode 100644
index 0e86ea40d..000000000
--- a/doc/news/version_8.20210428.mdwn
+++ /dev/null
@@ -1,29 +0,0 @@
-git-annex 8.20210428 released with [[!toggle text="these changes"]]
-[[!toggleable text="""  * New annex.private and remote.name.annex-private configs that can
-    prevent storing information about a repository and remotes in the
-    git-annex branch.
-  * initremote: Added --private option to set up a private special remote.
-  * importfeed: Made "checking known urls" phase run 12 times faster.
-  * Added --debugfilter (and annex.debugfilter)
-  * diffdriver: Support unlocked files.
-  * forget: Preserve currently exported trees, avoiding problems with
-    exporttree remotes in some unusual circumstances.
-  * fsck: When downloading content from a remote, if the content is able
-    to be verified during the transfer, skip checksumming it a second time.
-  * directory: When cp supports reflinks, use it.
-  * Avoid excess commits to the git-annex branch when stall detection is
-    enabled.
-  * git-annex-config: Allow setting annex.securehashesonly, which has
-    otherwise been supported since 2019, but was missing from the list of
-    allowed repo-global configs.
-  * init: Fix a crash when the repo's was cloned from a repo that had an
-    adjusted branch checked out, and the origin remote is not named "origin".
-  * Fix some bugs that made git-annex not see recently recorded status
-    information when configured with annex.alwayscommit=false.
-  * When mincopies is set to a larger value than numcopies, make sure that
-    mincopies is satisfied. Before, it assumed a sane configuration would
-    have numcopies larger or equal to mincopies. It's still a good idea
-    not to configure git-annex this way.
-  * Avoid more than 1 gpg password prompt at the same time, which
-    could happen occasionally before when concurrency is enabled.
-  * Fix build with persistent-2.12.0.1"""]]
\ No newline at end of file
diff --git a/doc/news/version_8.20210903.mdwn b/doc/news/version_8.20210903.mdwn
new file mode 100644
index 000000000..8faa2ef22
--- /dev/null
+++ b/doc/news/version_8.20210903.mdwn
@@ -0,0 +1,32 @@
+git-annex 8.20210903 released with [[!toggle text="these changes"]]
+[[!toggleable text="""  * Deal with clock skew, both forwards and backwards, when logging
+    information to the git-annex branch.
+  * GIT\_ANNEX\_VECTOR\_CLOCK can now be set to a fixed value (eg 1)
+    rather than needing to be advanced each time a new change is made.
+  * Misuse of GIT\_ANNEX\_VECTOR\_CLOCK will no longer confuse git-annex.
+  * add: When adding a dotfile, avoid treating its name as an extension.
+  * rsync special remote: Stop displaying rsync progress, and use
+    git-annex's own progress display.
+  * Many special remotes now checksum content while it is being retrieved,
+    instead of in a separate pass at the end. This is supported for all
+    special remotes on Linux (except for bittorrent), and for many
+    on other OS's (except for adb, external, gcrypt, hook, and rsync).
+  * unused: Skip the refs/annex/last-index ref that git-annex recently
+    started creating.
+  * Fix test suite failure on Windows.
+  * New --batch-keys option added to these commands:
+    get, drop, move, copy, whereis
+  * Added annex.youtube-dl-command config. This can be used to run some
+    forks of youtube-dl.
+  * Run cp -a with --no-preserve=xattr, to avoid problems with copied
+    xattrs, including them breaking permissions setting on some NFS
+    servers.
+  * add, import: Detect when xattrs or perhaps ACLs prevent removing
+    write permissions from an annexed file, and fail with an informative
+    message.
+  * Fix support for readonly git remotes.
+    (Reversion in version 8.20210621)
+  * When downloading urls fail, explain which urls failed for which
+    reasons.
+  * web: Avoid displaying a warning when downloading one url failed
+    but another url later succeeded."""]]
\ No newline at end of file

Revert spam
This reverts commit e2038ef412bf6ddfe8ccb73bf2bc1af02480a7c5.
diff --git a/doc/lazer-epilasyon.mdwn b/doc/lazer-epilasyon.mdwn
deleted file mode 100644
index a57d0c28a..000000000
--- a/doc/lazer-epilasyon.mdwn
+++ /dev/null
@@ -1,3 +0,0 @@
-<strong><a href="https://www.kastipmerkezi.com.tr/lazer-epilasyon/">Lazer epilasyon</a></strong> istenmeyen tüylerden kurtulmada en etkili ve en güvenli yöntemdir. Cilde uygulanan lazer enerjisi kıl köklerindeki melanin pigmenti tarafından emilir ve bu sayede  kıl kökleri azalarak yok olurlar.
-<a href="https://www.kastipmerkezi.com.tr/bizden-haberler/medikal-estetik/lazer-epilasyon-fiyatlari-neye-gore-belirlenmektedir/"><strong>Lazer epilasyon  fiyatları</strong></a> bölgelerin ayrı ayrı hesaplanması ile de yukarı yönlü etkilenir.  Bu nedenle tüm vücut lazer epilasyon fiyatları sizin için daha uygun olabilir.
-<a href="https://www.kastipmerkezi.com.tr/bizden-haberler/medikal-estetik/alexandrite-lazer-epilasyon-nedir-avantajlari-nelerdir/"><strong>Alexandrite lazer epilasyon</strong></a>, yüz ve kol gibi daha ince kılların olduğu noktalarda da etkili sonuçlar vermektedir.

close
diff --git a/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn b/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn
index 7c364f488..61b2d459d 100644
--- a/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn
+++ b/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn
@@ -28,3 +28,5 @@ cron-20210831/build-windows.yaml-369-69466103-failed/1_test-annex.txt:2021-08-31
 
 [[!meta author=yoh]]
 [[!tag projects/datalad]]
+
+> [[fixed|done]] in [[!commit afdca0aff43747ce985a4caf655e8b3363dea370]] --[[Joey]]

comment
diff --git a/doc/bugs/windows_autostart/comment_5_43077649e3ca6a132958c2a2b4914c3b._comment b/doc/bugs/windows_autostart/comment_5_43077649e3ca6a132958c2a2b4914c3b._comment
new file mode 100644
index 000000000..4493f16f8
--- /dev/null
+++ b/doc/bugs/windows_autostart/comment_5_43077649e3ca6a132958c2a2b4914c3b._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""
+ date="2021-09-03T15:42:41Z"
+ content="""
+The autostart file is generated by the nullsoft installer, and
+uses `$INSTDIR/cmd`, it does not hard code any path.
+
+It's installed into `Program Files/Git/cmd` because that is the only
+way to make sure that git finds the git-annex command when you run
+"git annex".
+"""]]

comment
diff --git a/doc/tips/automatically_adding_metadata/comment_13_065b018dc290549b5ef00b50c3b09fcc._comment b/doc/tips/automatically_adding_metadata/comment_13_065b018dc290549b5ef00b50c3b09fcc._comment
new file mode 100644
index 000000000..4135d0ae0
--- /dev/null
+++ b/doc/tips/automatically_adding_metadata/comment_13_065b018dc290549b5ef00b50c3b09fcc._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Re: Automatically adding metadata can be very slow"""
+ date="2021-09-03T15:36:08Z"
+ content="""
+You could add a config to the script that skips over files larger than a
+certian size.
+
+Or for that matter, the script could be adapted to filter the files
+to only include images/videos, using eg:
+
+	git annex find --mimetype='image/*' --or --mimetype='video/*'
+
+Should be a fairly easy change, patches accepted.
+"""]]

Added a comment: Automatically adding metadata can be very slow
diff --git a/doc/tips/automatically_adding_metadata/comment_12_92c28dee004562d7085191f3b9e29fec._comment b/doc/tips/automatically_adding_metadata/comment_12_92c28dee004562d7085191f3b9e29fec._comment
new file mode 100644
index 000000000..5d9ba60c9
--- /dev/null
+++ b/doc/tips/automatically_adding_metadata/comment_12_92c28dee004562d7085191f3b9e29fec._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="aurelf"
+ avatar="http://cdn.libravatar.org/avatar/99aa02fcf7f5a1453b1e2528f2f543ac"
+ subject="Automatically adding metadata can be very slow "
+ date="2021-09-03T12:46:06Z"
+ content="""
+Running extract on very large files (system backups) can be too long (killed it after running several hours). 
+In general `extract` seem slow on tar.gz archives. 
+
+I added `timeout 100s` before the tool is called in the pre commit script:
+
+`LC_ALL=C timeout 100s $tool_exec \"./$f\" | ...`
+
+This allows to have the commit to complete in reasonable time, probably loosing some metadata.
+"""]]

close dup
diff --git a/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn b/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn
index 093db99b6..a933b0a27 100644
--- a/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn
+++ b/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn
@@ -2,3 +2,5 @@ As of commit ddd7d1d11, trying to build git-annex on Windows fails due to a func
 
 [[!meta author=jwodder]]
 [[!tag projects/datalad]]
+
+> [[fixed|done]] --[[Joey]]

defer write permissions checking in import until after copy to repo
This should complete the fix started in
6329997ac44691937f1d7fe6a71da3184237b13b, fixing the actual cause of the
test suite failure this time.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/Annex/Ingest.hs b/Annex/Ingest.hs
index 8c09243ab..4b4290fde 100644
--- a/Annex/Ingest.hs
+++ b/Annex/Ingest.hs
@@ -9,6 +9,7 @@ module Annex.Ingest (
 	LockedDown(..),
 	LockDownConfig(..),
 	lockDown,
+	checkLockedDownWritePerms,
 	ingestAdd,
 	ingestAdd',
 	ingest,
@@ -61,7 +62,9 @@ data LockDownConfig = LockDownConfig
 	{ lockingFile :: Bool
 	-- ^ write bit removed during lock down
 	, hardlinkFileTmpDir :: Maybe RawFilePath
-	-- ^ hard link to temp directory
+	-- ^ hard link to temp directorya
+	, checkWritePerms :: Bool
+	-- ^ check that write perms are successfully removed
 	}
 	deriving (Show)
 
@@ -79,7 +82,7 @@ data LockDownConfig = LockDownConfig
  - Lockdown can fail if a file gets deleted, or if it's unable to remove
  - write permissions, and Nothing will be returned.
  -}
-lockDown :: LockDownConfig -> FilePath -> Annex (Maybe LockedDown)
+lockDown :: LockDownConfig-> FilePath -> Annex (Maybe LockedDown)
 lockDown cfg file = either 
 		(\e -> warning (show e) >> return Nothing)
 		(return . Just)
@@ -128,13 +131,17 @@ lockDown' cfg file = tryNonAsync $ ifM crippledFileSystem
 		
 	setperms = when (lockingFile cfg) $ do
 		freezeContent file'
-		checkContentWritePerm file' >>= \case
-			Just False -> giveup $ unwords
-				[ "Unable to remove all write permissions from"
-				, file
-				, "-- perhaps it has an xattr or ACL set."
-				]
-			_ -> return ()
+		when (checkWritePerms cfg) $
+			maybe noop giveup =<< checkLockedDownWritePerms file' file'
+
+checkLockedDownWritePerms :: RawFilePath -> RawFilePath -> Annex (Maybe String)
+checkLockedDownWritePerms file displayfile = checkContentWritePerm file >>= return . \case
+	Just False -> Just $ unwords
+		[ "Unable to remove all write permissions from"
+		, fromRawFilePath displayfile
+		, "-- perhaps it has an xattr or ACL set."
+		]
+	_ -> Nothing
 
 {- Ingests a locked down file into the annex. Updates the work tree and
  - index. -}
diff --git a/Assistant/Threads/Committer.hs b/Assistant/Threads/Committer.hs
index b7651917b..7b06e7d76 100644
--- a/Assistant/Threads/Committer.hs
+++ b/Assistant/Threads/Committer.hs
@@ -265,6 +265,7 @@ handleAdds lockdowndir havelsof largefilematcher delayadd cs = returnWhen (null
 	let lockdownconfig = LockDownConfig
 		{ lockingFile = False
 		, hardlinkFileTmpDir = Just (toRawFilePath lockdowndir)
+		, checkWritePerms = True
 		}
 	(postponed, toadd) <- partitionEithers
 		<$> safeToAdd lockdowndir lockdownconfig havelsof delayadd pending inprocess
@@ -310,6 +311,7 @@ handleAdds lockdowndir havelsof largefilematcher delayadd cs = returnWhen (null
 		let cfg = LockDownConfig
 			{ lockingFile = False
 			, hardlinkFileTmpDir = Just (toRawFilePath lockdowndir)
+			, checkWritePerms = True
 			}
 		if M.null m
 			then forM toadd (addannexed' cfg)
diff --git a/CHANGELOG b/CHANGELOG
index 7cb757be5..2f232da31 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -22,8 +22,9 @@ git-annex (8.20210804) UNRELEASED; urgency=medium
   * Run cp -a with --no-preserve=xattr, to avoid problems with copied
     xattrs, including them breaking permissions setting on some NFS
     servers.
-  * add: Detect when xattrs or perhaps ACLs prevent locking down
-    a file's content, and fail with an informative message.
+  * add, import: Detect when xattrs or perhaps ACLs prevent removing
+    write permissions from an annexed file, and fail with an informative
+    message.
   * Fix support for readonly git remotes.
     (Reversion in version 8.20210621)
   * When downloading urls fail, explain which urls failed for which
diff --git a/Command/Add.hs b/Command/Add.hs
index 56495825d..0fe4351ab 100644
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -188,6 +188,7 @@ perform o file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 	let cfg = LockDownConfig
 		{ lockingFile = lockingfile
 		, hardlinkFileTmpDir = Just tmpdir
+		, checkWritePerms = True
 		}
 	ld <- lockDown cfg (fromRawFilePath file)
 	let sizer = keySource <$> ld
diff --git a/Command/Import.hs b/Command/Import.hs
index b0a1b0898..f31cdb18f 100644
--- a/Command/Import.hs
+++ b/Command/Import.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2012-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2012-2021 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -200,13 +200,31 @@ startLocal o addunlockedmatcher largematcher mode (srcfile, destfile) =
 		-- Move or copy the src file to the dest file.
 		-- The dest file is what will be ingested.
 		createWorkTreeDirectory (parentDir destfile)
-		liftIO $ if mode == Duplicate || mode == SkipDuplicates
-			then void $ copyFileExternal CopyAllMetaData 
-				(fromRawFilePath srcfile)
-				(fromRawFilePath destfile)
-			else moveFile 
-				(fromRawFilePath srcfile)
-				(fromRawFilePath destfile)
+		unwind <- liftIO $ if mode == Duplicate || mode == SkipDuplicates
+			then do
+				void $ copyFileExternal CopyAllMetaData 
+					(fromRawFilePath srcfile)
+					(fromRawFilePath destfile)
+				return $ removeWhenExistsWith R.removeLink destfile
+			else do
+				moveFile 
+					(fromRawFilePath srcfile)
+					(fromRawFilePath destfile)
+				return $ moveFile
+					(fromRawFilePath destfile)
+					(fromRawFilePath srcfile)
+		-- Make sure that the dest file has its write permissions
+		-- removed; the src file normally already did, but may
+		-- have imported it from a filesystem that does not allow
+		-- removing write permissions, to a repo on a filesystem
+		-- that does.
+		when (lockingFile (lockDownConfig ld)) $ do
+			freezeContent destfile
+			checkLockedDownWritePerms destfile srcfile >>= \case
+				Just err -> do
+					liftIO unwind
+					giveup err
+				Nothing -> noop
 		-- Get the inode cache of the dest file. It should be
 		-- weakly the same as the originally locked down file's
 		-- inode cache. (Since the file may have been copied,
@@ -249,6 +267,12 @@ startLocal o addunlockedmatcher largematcher mode (srcfile, destfile) =
 		let cfg = LockDownConfig
 			{ lockingFile = lockingfile
 			, hardlinkFileTmpDir = Nothing
+			-- The write perms of the file may not be able to be
+			-- removed, if it's being imported from a crippled
+			-- filesystem. So lockDown is asked to not check
+			-- the write perms. They will be checked later, after
+			-- the file gets copied into the repository.
+			, checkWritePerms = False
 			}
 		v <- lockDown cfg (fromRawFilePath srcfile)
 		case v of
diff --git a/Command/Smudge.hs b/Command/Smudge.hs
index c242b7e1b..eb7b8b49c 100644
--- a/Command/Smudge.hs
+++ b/Command/Smudge.hs
@@ -160,6 +160,7 @@ clean file = do
 	cfg = LockDownConfig
 		{ lockingFile = False
 		, hardlinkFileTmpDir = Nothing
+		, checkWritePerms = True
 		}
 
 	-- git diff can run the clean filter on files outside the
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
index 749e980ed..b4f264559 100644
--- a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
@@ -38,3 +38,5 @@ Both look like
 
 [[!meta author=yoh]]
 [[!tag projects/datalad]]
+
+> [[fixed|done]] (provisionally, waiting on test run) --[[Joey]]
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_5_46acb87752c0f0574d8f3b91fdfb1697._comment b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_5_46acb87752c0f0574d8f3b91fdfb1697._comment
new file mode 100644
index 000000000..09332c3e0
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_5_46acb87752c0f0574d8f3b91fdfb1697._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 5"""

(Diff truncated)
comment
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_4_3bdf724f5e9203424b7cb5630fb49a46._comment b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_4_3bdf724f5e9203424b7cb5630fb49a46._comment
new file mode 100644
index 000000000..0b400facc
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_4_3bdf724f5e9203424b7cb5630fb49a46._comment
@@ -0,0 +1,27 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 4"""
+ date="2021-09-02T16:26:14Z"
+ content="""
+My prior analysis seems right, as far as it running as root would go, but it is
+not running as root. So I missed something.
+
+The test failures are both of `git-annex import`.
+Otherwise locking down files does succeed. The difference with import
+must be that the file located in a directory outside the repository.
+
+Aha... The test suite is being run with TEMPDIR set to the crippled FS,
+but `.t` is in another, non-crippled FS. A very smart idea to test that,
+although I think this import test is the only one that actually uses
+TEMPDIR. (Reading the workflow file, I think it was maybe expected that
+all the tests would run in TEMPDIR, but they don't; `git-annex test`
+writes to `./.t`, other than this one test.
+
+When the import directory is on a crippled FS, and the repo
+is not, it will think the FS is not crippled. Then it fails
+to remove write perms from the file while it is in the import
+directory, and the perm check then fails.
+
+So, I think it should skip the perm check when doing the initial lockdown
+of the file it's going to import.
+"""]]

reopen
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
index 4297da9a3..749e980ed 100644
--- a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
@@ -38,6 +38,3 @@ Both look like
 
 [[!meta author=yoh]]
 [[!tag projects/datalad]]
-
-> [[fixed|done]] (provisionally; can't test running git-annex as root on
-> OSX) --[[Joey]]
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_3_ac3fe75560c020015ecca615cf0e7abe._comment b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_3_ac3fe75560c020015ecca615cf0e7abe._comment
new file mode 100644
index 000000000..65cf9084d
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_3_ac3fe75560c020015ecca615cf0e7abe._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2021-09-02T16:22:56Z"
+ content="""
+OSX test is still failing after that fix, reopened.
+"""]]

fix windows build
diff --git a/Annex/Init.hs b/Annex/Init.hs
index 30c4f9753..1193e0b24 100644
--- a/Annex/Init.hs
+++ b/Annex/Init.hs
@@ -258,7 +258,7 @@ probeCrippledFileSystem'
 	-> Maybe (RawFilePath -> m ())
 	-> m (Bool, [String])
 #ifdef mingw32_HOST_OS
-probeCrippledFileSystem' _ _ _ _ = return (True, [])
+probeCrippledFileSystem' _ _ _ = return (True, [])
 #else
 probeCrippledFileSystem' tmp freezecontent thawcontent = do
 	let f = tmp P.</> "gaprobe"
diff --git a/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
index 1d59f4451..1a87aa5eb 100644
--- a/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
+++ b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
@@ -85,3 +85,5 @@ This then compiled cleanly and completed the test suite ok.
 Git Annex is great. It works quite nicely with my multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend :)
 
 [[!meta author=jkniiv]]
+
+> Thank you! [[done]] --[[Joey]]

diff --git a/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn b/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn
new file mode 100644
index 000000000..093db99b6
--- /dev/null
+++ b/doc/bugs/Build_fails_on_Windows_as_of_commit_ddd7d1d11.mdwn
@@ -0,0 +1,4 @@
+As of commit ddd7d1d11, trying to build git-annex on Windows fails due to a function being defined with too many arguments.  [This patch](https://raw.githubusercontent.com/datalad/git-annex/master/patches/20210902-ddd7d1d11-fix-windows.patch) fixes it.
+
+[[!meta author=jwodder]]
+[[!tag projects/datalad]]

Added a comment
diff --git a/doc/bugs/windows_autostart/comment_4_b8d0b38aa7363e97c295b049bd939ad1._comment b/doc/bugs/windows_autostart/comment_4_b8d0b38aa7363e97c295b049bd939ad1._comment
new file mode 100644
index 000000000..a6f775d8a
--- /dev/null
+++ b/doc/bugs/windows_autostart/comment_4_b8d0b38aa7363e97c295b049bd939ad1._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="scy"
+ avatar="http://cdn.libravatar.org/avatar/f5a8df59fb198cf762f77aa239193e20"
+ subject="comment 4"
+ date="2021-09-02T08:17:02Z"
+ content="""
+I’ve also encountered this problem. Found it weird that git-annex on Windows (according to the installation manual) requires Git for Windows to be installed and then defaults to installing itself into the same directory (`C:\Program Files\Git`). Is this required? Won’t this cause problems when Git for Windows updates? That’s why I installed it to `C:\Program Files\git-annex`, but yeah, the link that gets put into `Startup` apparently doesn’t take the real installation directory into account and just uses a hard-coded path…?
+"""]]

Added a comment
diff --git a/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_2_9773981747ca29120aef1b2a09c88f1b._comment b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_2_9773981747ca29120aef1b2a09c88f1b._comment
new file mode 100644
index 000000000..f697d3eaf
--- /dev/null
+++ b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_2_9773981747ca29120aef1b2a09c88f1b._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="jkniiv"
+ avatar="http://cdn.libravatar.org/avatar/05fd8b33af7183342153e8013aa3713d"
+ subject="comment 2"
+ date="2021-09-02T05:24:14Z"
+ content="""
+I can confirm that commit 9595a247a seems to fix the test case with quickcheck random seed value 564957
+(`--quickcheck-replay=564957`) on my Windows laptop.
+"""]]

add author meta
diff --git a/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
index 50878ed03..1d59f4451 100644
--- a/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
+++ b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
@@ -83,3 +83,5 @@ This then compiled cleanly and completed the test suite ok.
 ### 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 is great. It works quite nicely with my multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend :)
+
+[[!meta author=jkniiv]]

commit 9595a247a doesn't compile on Windows without reverting a small previous change
diff --git a/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
new file mode 100644
index 000000000..50878ed03
--- /dev/null
+++ b/doc/bugs/commit_9595a247a_needs_a_fix_to_build_on_Windows.mdwn
@@ -0,0 +1,85 @@
+### Please describe the problem.
+
+Git-annex commit 9595a247a doesn't compile on Windows without a small patch.
+Commit [[!commit 6329997ac44691937f1d7fe6a71da3184237b13b]] introduces a change for `mingw32_HOST_OS`
+that is not needed (function pattern gains an extra _ parameter). I guess the change was a leftover
+from earlier testing.
+
+### What steps will reproduce the problem?
+
+`stack setup && stack build`
+
+### What version of git-annex are you using? On what operating system?
+
+[[!format sh """
+git-annex version: 8.20210804-g9595a247a
+build flags: Assistant Webapp Pairing TorrentParser Feeds Testsuite S3 WebDAV
+dependency versions: aws-0.22 bloomfilter-2.0.1.0 cryptonite-0.26 DAV-1.3.4 feed-1.3.0.1 ghc-8.8.4 http-client-0.6.4.1 persistent-sqlite-2.10.6.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.1.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 BLAKE2BP512E BLAKE2BP512 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL X*
+remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar git-lfs httpalso borg hook external
+operating system: mingw32 x86_64
+supported repository versions: 8
+upgrade supported from repository versions: 2 3 4 5 6 7
+"""]]
+
+Windows 10 version 21H1 (build 19043.1165), 64 bit.
+
+### Please provide any additional information below.
+
+Relevant parts of the build log:
+
+[[!format sh """
+jkniiv@AINESIS MINGW64 /c/annx
+$ tail -n 25 /c/Projektit/git-annex.branchable.com/git-annex--BUILD-210902-9595a247a/stack.build.LOG~102
+[428 of 665] Compiling Remote.GitLFS
+[429 of 665] Compiling Remote.Bup
+[430 of 665] Compiling Assistant.Gpg
+[431 of 665] Compiling Annex.Environment
+[432 of 665] Compiling Annex.Init
+
+Annex\Init.hs:261:1: error:
+    * Couldn't match type `(Bool, [a0])' with `[String]'
+      Expected type: m (Bool, [String])
+        Actual type: p0 -> (Bool, (Bool, [a0]))
+    * The equation(s) for probeCrippledFileSystem' have four arguments,
+      but its type `RawFilePath
+                    -> Maybe (RawFilePath -> m ())
+                    -> Maybe (RawFilePath -> m ())
+                    -> m (Bool, [String])'
+      has only three
+    |
+261 | probeCrippledFileSystem' _ _ _ _ = return (True, [])
+
+    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+--  While building package git-annex-8.20210803 (scroll up to its section to see the error) using:
+      C:\Users\jkniiv\Projektit\git-annex.branchable.com\git-annex--BUILD-210902-9595a247a\.stack-work\dist\29cc6475\setup\setup --builddir=.stack-work\dist\29cc6475 build exe:git-annex --ghc-options " -fdiagnostics-color=always"
+    Process exited with code: ExitFailure 1
+
+# End of transcript.
+"""]]
+
+The change I made was a simple revertion, as follows:
+
+[[!format diff """
+diff --git a/Annex/Init.hs b/Annex/Init.hs
+index 30c4f9753..1193e0b24 100644
+--- a/Annex/Init.hs
++++ b/Annex/Init.hs
+@@ -258,7 +258,7 @@ probeCrippledFileSystem'
+        -> Maybe (RawFilePath -> m ())
+        -> m (Bool, [String])
+ #ifdef mingw32_HOST_OS
+-probeCrippledFileSystem' _ _ _ _ = return (True, [])
++probeCrippledFileSystem' _ _ _ = return (True, [])
+ #else
+ probeCrippledFileSystem' tmp freezecontent thawcontent = do
+        let f = tmp P.</> "gaprobe"
+"""]]
+
+This then compiled cleanly and completed the test suite ok.
+
+### 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 is great. It works quite nicely with my multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend :)

improve url download failure display
* When downloading urls fail, explain which urls failed for which
reasons.
* web: Avoid displaying a warning when downloading one url failed
but another url later succeeded.
Some other uses of downloadUrl use urls that are effectively internal use,
and should not all be displayed to the user on failure. Eg, Remote.Git
tries different urls where content could be located depending on how the
remote repo is set up. Exposing those urls to the user would lead to wild
goose chases. So had to parameterize it to control whether it displays urls
or not.
A side effect of this change is that when there are some youtube urls
and some regular urls, it will try regular urls first, even if the
youtube urls are listed first. This seems like an improvement if
anything, but in any case there's no defined order of urls that it's
supposed to use.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/Annex/Content.hs b/Annex/Content.hs
index 8dd1dec0f..da65143ab 100644
--- a/Annex/Content.hs
+++ b/Annex/Content.hs
@@ -632,18 +632,20 @@ saveState nocommit = doSideAction $ do
 			Annex.Branch.commit =<< Annex.Branch.commitMessage
 
 {- Downloads content from any of a list of urls, displaying a progress
- - meter. -}
-downloadUrl :: Key -> MeterUpdate -> Maybe IncrementalVerifier -> [Url.URLString] -> FilePath -> Url.UrlOptions -> Annex Bool
-downloadUrl k p iv urls file uo = 
+ - meter.
+ -
+ - Only displays error message if all the urls fail to download.
+ - When listfailedurls is set, lists each url and why it failed.
+ - Otherwise, only displays one error message, from one of the urls
+ - that failed.
+ -}
+downloadUrl :: Bool -> Key -> MeterUpdate -> Maybe IncrementalVerifier -> [Url.URLString] -> FilePath -> Url.UrlOptions -> Annex Bool
+downloadUrl listfailedurls k p iv urls file uo = 
 	-- Poll the file to handle configurations where an external
 	-- download command is used.
-	meteredFile file (Just p) k (go urls Nothing)
+	meteredFile file (Just p) k (go urls [])
   where
-  	-- Display only one error message, if all the urls fail to
-	-- download.
-	go [] (Just err) = warning err >> return False
-	go [] Nothing = return False
-	go (u:us) _ = Url.download' p iv u file uo >>= \case
+	go (u:us) errs = Url.download' p iv u file uo >>= \case
 		Right () -> return True
 		Left err -> do
 			-- If the incremental verifier was fed anything
@@ -655,7 +657,14 @@ downloadUrl k p iv urls file uo =
 					Just n | n > 0 -> unableIncremental iv'
 					_ -> noop
 				Nothing -> noop
-			go us (Just err)
+			go us ((u, err) : errs)
+	go [] [] = return False
+	go [] errs@((_, err):_) = do
+		if listfailedurls
+			then warning $ unlines $ flip map errs $ \(u, err') ->
+				u ++ " " ++ err'
+			else warning err
+		return False
 
 {- Copies a key's content, when present, to a temp file.
  - This is used to speed up some rsyncs. -}
diff --git a/CHANGELOG b/CHANGELOG
index 897b01007..7cb757be5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -26,6 +26,10 @@ git-annex (8.20210804) UNRELEASED; urgency=medium
     a file's content, and fail with an informative message.
   * Fix support for readonly git remotes.
     (Reversion in version 8.20210621)
+  * When downloading urls fail, explain which urls failed for which
+    reasons.
+  * web: Avoid displaying a warning when downloading one url failed
+    but another url later succeeded.
 
  -- Joey Hess <id@joeyh.name>  Tue, 03 Aug 2021 12:22:45 -0400
 
diff --git a/Command/AddUrl.hs b/Command/AddUrl.hs
index eb99119ac..f256aed2d 100644
--- a/Command/AddUrl.hs
+++ b/Command/AddUrl.hs
@@ -314,7 +314,7 @@ downloadWeb addunlockedmatcher o url urlinfo file =
 	go =<< downloadWith' downloader urlkey webUUID url (AssociatedFile (Just file))
   where
 	urlkey = addSizeUrlKey urlinfo $ Backend.URL.fromUrl url Nothing
-	downloader f p = Url.withUrlOptions $ downloadUrl urlkey p Nothing [url] f
+	downloader f p = Url.withUrlOptions $ downloadUrl False urlkey p Nothing [url] f
 	go Nothing = return Nothing
 	go (Just tmp) = ifM (pure (not (rawOption o)) <&&> liftIO (isHtmlFile (fromRawFilePath tmp)))
 		( tryyoutubedl tmp
diff --git a/Remote/External.hs b/Remote/External.hs
index 102b6caef..1137cd74f 100644
--- a/Remote/External.hs
+++ b/Remote/External.hs
@@ -810,7 +810,7 @@ checkUrlM external url =
 retrieveUrl :: Retriever
 retrieveUrl = fileRetriever' $ \f k p iv -> do
 	us <- getWebUrls k
-	unlessM (withUrlOptions $ downloadUrl k p iv us (fromRawFilePath f)) $
+	unlessM (withUrlOptions $ downloadUrl True k p iv us (fromRawFilePath f)) $
 		giveup "failed to download content"
 
 checkKeyUrl :: CheckPresent
diff --git a/Remote/Git.hs b/Remote/Git.hs
index 8d97c59a2..81a6bc5fd 100644
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -543,7 +543,7 @@ copyFromRemote'' repo forcersync r st@(State connpool _ _ _ _) key file dest met
 		iv <- startVerifyKeyContentIncrementally vc key
 		gc <- Annex.getGitConfig
 		ok <- Url.withUrlOptionsPromptingCreds $
-			Annex.Content.downloadUrl key meterupdate iv (keyUrls gc repo r key) dest
+			Annex.Content.downloadUrl False key meterupdate iv (keyUrls gc repo r key) dest
 		unless ok $
 			giveup "failed to download content"
 		snd <$> finishVerifyKeyContentIncrementally iv
diff --git a/Remote/S3.hs b/Remote/S3.hs
index 9e0fd01f4..7a4a8541b 100644
--- a/Remote/S3.hs
+++ b/Remote/S3.hs
@@ -414,7 +414,7 @@ retrieve hv r rs c info = fileRetriever' $ \f k p iv -> withS3Handle hv $ \case
 			Left failreason -> do
 				warning failreason
 				giveup "cannot download content"
-			Right us -> unlessM (withUrlOptions $ downloadUrl k p iv us (fromRawFilePath f)) $
+			Right us -> unlessM (withUrlOptions $ downloadUrl False k p iv us (fromRawFilePath f)) $
 				giveup "failed to download content"
 
 retrieveHelper :: S3Info -> S3Handle -> (Either S3.Object S3VersionID) -> FilePath -> MeterUpdate -> Maybe IncrementalVerifier -> Annex ()
diff --git a/Remote/Web.hs b/Remote/Web.hs
index 938881e37..94e9391e3 100644
--- a/Remote/Web.hs
+++ b/Remote/Web.hs
@@ -86,26 +86,30 @@ downloadKey :: Key -> AssociatedFile -> FilePath -> MeterUpdate -> VerifyConfig
 downloadKey key _af dest p vc = go =<< getWebUrls key
   where
 	go [] = giveup "no known url"
-	go urls = getM dl urls >>= \case
+	go urls = dl (partition (not . isyoutube) (map getDownloader urls)) >>= \case
 		Just v -> return v
-		Nothing -> giveup "download failed"
+		Nothing -> giveup $ unwords
+			[ "downloading from all"
+			, show (length urls)
+			, "known url(s) failed"
+			]
 
-	dl u = do
-		let (u', downloader) = getDownloader u
-		case downloader of
-			YoutubeDownloader ->
-				ifM (youtubeDlTo key u' dest p)
-					( return (Just UnVerified)
-					, return Nothing
-					)
-			_ -> do
-				iv <- startVerifyKeyContentIncrementally vc key
-				ifM (Url.withUrlOptions $ downloadUrl key p iv [u'] dest)
-					( finishVerifyKeyContentIncrementally iv >>= \case
-						(True, v) -> return (Just v)
-						(False, _) -> return Nothing
-					, return Nothing
-					)
+	dl ([], ytus) = flip getM (map fst ytus) $ \u ->
+		ifM (youtubeDlTo key u dest p)
+			( return (Just UnVerified)
+			, return Nothing
+			)
+	dl (us, ytus) = do
+		iv <- startVerifyKeyContentIncrementally vc key
+		ifM (Url.withUrlOptions $ downloadUrl True key p iv (map fst us) dest)
+			( finishVerifyKeyContentIncrementally iv >>= \case
+				(True, v) -> return (Just v)
+				(False, _) -> dl ([], ytus)
+			, dl ([], ytus)
+			)
+
+	isyoutube (_, YoutubeDownloader) = True
+	isyoutube _ = False
 
 uploadKey :: Key -> AssociatedFile -> MeterUpdate -> Annex ()
 uploadKey _ _ _ = giveup "upload to web not supported"
diff --git a/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn b/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn
index 0a79c556f..3b8916478 100644
--- a/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn
+++ b/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn
@@ -41,3 +41,15 @@ refs in DataLad issues:
 
 - from web remote: ["download failed: Not Found"](https://github.com/datalad/datalad/pull/5936)
 - from ["failed to retrieve content from remote"](https://github.com/datalad/datalad/issues/5750)
+
+> I think this is specific to downloading urls, although it can happen
+> for a few remotes (web, external). There's really no reason to display
+> a download failed message if it successfully downloads a later url.
+> (After all, if it had tried the working url first, it would never display
+> anything about the broken url.)
+> 
+> When all urls fail, it makes sense to display each url and why it failed
+> when using the web (or external) remote, so the user can decide what to
+> do about each of the problems.
+> 
+> [[done]] --[[Joey]] 

Added a comment
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_10_2562d33a1cafb01b2cddb9a42a635616._comment b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_10_2562d33a1cafb01b2cddb9a42a635616._comment
new file mode 100644
index 000000000..c17064a8f
--- /dev/null
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_10_2562d33a1cafb01b2cddb9a42a635616._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 10"
+ date="2021-09-01T18:05:26Z"
+ content="""
+That issue I encountered using [reproducer](https://git.kitenet.net/index.cgi/git-annex.git/tree/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_5_9e4b82d27ddbb42d74f10a85635590e7._comment) (without ssh; sorry for broken markdown formatting, but can't edit a comment) still reproduces locally (no NFS) using originally reported 8.20210803+git133-ga7cf5abf3 if I reinstall that version, but it does not reproduce using recent 8.20210803+git167-g608593fec , so I guess it was addressed too.
+"""]]

Added a comment
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_2_71e985981c14780cc172a07beb0e9854._comment b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_2_71e985981c14780cc172a07beb0e9854._comment
new file mode 100644
index 000000000..b43efea8b
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_2_71e985981c14780cc172a07beb0e9854._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 2"
+ date="2021-09-01T16:22:22Z"
+ content="""
+> Is the test suite running as root? Looks like probably yes. 
+
+FWIW, it is a `runner` user  [ref](https://github.com/datalad/git-annex/pull/76/checks?check_run_id=3486443350#step:8:1) (did in a temp [PR](https://github.com/datalad/git-annex/pull/76)) who is not `root` but is part of the `admin` group thus with super privileges indeed (that is why I guess we can also use `hdiutil` directly to mount that crippled FS).
+"""]]

comment
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_9_b70343f8cb81807a7da0da5637d8c750._comment b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_9_b70343f8cb81807a7da0da5637d8c750._comment
new file mode 100644
index 000000000..c9f6e0503
--- /dev/null
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_9_b70343f8cb81807a7da0da5637d8c750._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 9"""
+ date="2021-09-01T15:42:35Z"
+ content="""
+The user does not need to do anything, I have reproduced and fixed their
+problem.
+
+You reported a *different* error message when you tried to reproduce it
+yourself. That is the only thing i do not understand, and why I have,
+repeatedly, been trying to ask you to reproduce that error message again.
+"""]]

Added a comment: clarification
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_8_95ff27c621afe43abfd7403f343b612c._comment b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_8_95ff27c621afe43abfd7403f343b612c._comment
new file mode 100644
index 000000000..e94fe4d5e
--- /dev/null
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_8_95ff27c621afe43abfd7403f343b612c._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="clarification"
+ date="2021-09-01T15:39:06Z"
+ content="""
+FWIW, as I have mentioned, the original issue is reported by a user and I have no access to reproduce \"exactly\" myself.  I am still waiting on user to report success/failure while trying with the [8.20210803-165-g249d424b8](https://git.kitenet.net/index.cgi/git-annex.git/commit/?id=8.20210803+git165-g249d424b8) \"up-to-date-back-then\" standalone build on the remote ssh server side.  If still fails, I will build even newer standalone and ask to try again.  Great to hear there is a test case now -- thank you!
+"""]]

fix test suite failure on windows
This was maybe a real bug too, although I don't know what circumstances
it would be a problem. See comment for analysis of this windows drive
letter wackyness issue.
Sponsored-by: Brock Spratlen on Patreon
diff --git a/Utility/Path.hs b/Utility/Path.hs
index cfda748b9..8c6aa7f70 100644
--- a/Utility/Path.hs
+++ b/Utility/Path.hs
@@ -187,7 +187,13 @@ relPathDirToFileAbs from to
 	dotdots = replicate (length pfrom - numcommon) ".."
 	numcommon = length common
 #ifdef mingw32_HOST_OS
-	normdrive = map toLower . takeWhile (/= ':') . fromRawFilePath . takeDrive
+	normdrive = map toLower
+		-- Get just the drive letter, removing any leading
+		-- path separator, which takeDrive leaves on the drive
+		-- letter.
+		. dropWhileEnd (isPathSeparator . fromIntegral . ord)
+		. fromRawFilePath 
+		. takeDrive
 #endif
 
 {- Checks if a command is available in PATH.
diff --git a/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn
index 63151051c..f65e66893 100644
--- a/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn
+++ b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn
@@ -49,3 +49,5 @@ Windows version 21H1 (build 19043.1165), 64 bit.
 Git Annex is great. It works with multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend just dandy :)
 
 [[!meta author=jkniiv]]
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_1_7becd972ce41d14ace89a9bc1302abba._comment b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_1_7becd972ce41d14ace89a9bc1302abba._comment
new file mode 100644
index 000000000..f5e468d6e
--- /dev/null
+++ b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn/comment_1_7becd972ce41d14ace89a9bc1302abba._comment
@@ -0,0 +1,28 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-01T14:54:56Z"
+ content="""
+Reproduced on Linux using System.FilePath.Windows.
+
+Minimal case is:
+
+	ghci> let p = "\\\\\DLEJ\STXm{u5;4*\EOTKo1"
+	ghci> relPathDirToFileAbs (p </> "bar") p
+	"\\\\\DLEJ\STXm{u5;4*\EOTKo1"
+
+Which should be "bar", but the "normdrive" case in
+relPathDirToFileAbs causes it to not return that.
+
+Ah, that whole value is treated as a "drive letter" due to starting
+with "\\\\" and not containing any path separator.
+
+	ghci> takeDrive p
+	"\\\\\DLEJ\STXm{u5;4*\EOTKo1"
+
+And takeDrive includes the first path separator, which is present in one
+string and not in another. So, it thinks these paths are on
+two different drives, when they are not. And that's the root
+of the problem. normdrive was working around that by taking up until 
+the ':', but there *is* no ':' in this drive letter!
+"""]]

close
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms.mdwn b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms.mdwn
index 917879012..fe09223da 100644
--- a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms.mdwn
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms.mdwn
@@ -72,3 +72,5 @@ create a git-annex repo owned by another user, provide permissions for others to
 
 [[!meta author=yoh]]
 [[!tag projects/datalad]]
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_7_ba7e53135285dbf198f5aa12d7edc1cd._comment b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_7_ba7e53135285dbf198f5aa12d7edc1cd._comment
new file mode 100644
index 000000000..d59723763
--- /dev/null
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_7_ba7e53135285dbf198f5aa12d7edc1cd._comment
@@ -0,0 +1,22 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 7"""
+ date="2021-09-01T14:32:11Z"
+ content="""
+I'm going to close this, since I was able to replicate and fix one of the
+errors, and was not able to replicate the other one. If the other one
+still happens, follow up with the output of the current version of
+git-annex, as I've requested 3 times now.
+
+There is a test case now that tests the same thing, too. It has passed
+on all the builders. (Except windows. I've learned git-annex cannot support
+readonly repos on Windows, because it has to be able to create lock files
+there.)
+
+(There are still situations where git-annex needs to write to a repo in order to
+use it. The main one is if the it needs to upgrade the repo. I think
+failing in that specific situation is ok, and it fails with an informative
+error message IIRC. It's also possible there's some other situation of some
+kind where the readonly repo is in a state where git-annex wants to write
+to it, but if there is, I can fix it when the situation comes to light.)
+"""]]

init: check for filesystem where write bit cannot be removed
This fixes a reversion caused by a99a84f3420a1d93e4ecd4b8cf30638b48e2758e,
when git-annex init is run as root on a FAT filesystem mounted with
hdiutil on OSX. Such a mount point has file mode 777 for everything and
it cannot be changed. The existing crippled filesystem test tried to
write to a file after removing write bit, but that test does not run as
root (since root can write to unwritable files). So added a check of the
write permissions of the file, after attempting to remove them.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/Annex/Init.hs b/Annex/Init.hs
index 7bfd13b11..30c4f9753 100644
--- a/Annex/Init.hs
+++ b/Annex/Init.hs
@@ -258,7 +258,7 @@ probeCrippledFileSystem'
 	-> Maybe (RawFilePath -> m ())
 	-> m (Bool, [String])
 #ifdef mingw32_HOST_OS
-probeCrippledFileSystem' _ _ _ = return (True, [])
+probeCrippledFileSystem' _ _ _ _ = return (True, [])
 #else
 probeCrippledFileSystem' tmp freezecontent thawcontent = do
 	let f = tmp P.</> "gaprobe"
@@ -275,18 +275,22 @@ probeCrippledFileSystem' tmp freezecontent thawcontent = do
 		liftIO $ createSymbolicLink f f2
 		liftIO $ removeWhenExistsWith R.removeLink (toRawFilePath f2)
 		(fromMaybe (liftIO . preventWrite) freezecontent) (toRawFilePath f)
-		-- Should be unable to write to the file, unless
-		-- running as root, but some crippled
-		-- filesystems ignore write bit removals.
-		liftIO $ ifM ((== 0) <$> getRealUserID)
-			( return (False, [])
-			, do
-				r <- catchBoolIO $ do
-					writeFile f "2"
-					return True
-				if r
-					then return (True, ["Filesystem allows writing to files whose write bit is not set."])
-					else return (False, [])
+		-- Should be unable to write to the file (unless
+		-- running as root). But some crippled
+		-- filesystems ignore write bit removals or ignore
+		-- permissions entirely.
+		ifM ((== Just False) <$> liftIO (checkContentWritePerm' UnShared (toRawFilePath f)))
+			( return (True, ["Filesystem does not allow removing write bit from files."])
+			, liftIO $ ifM ((== 0) <$> getRealUserID)
+				( return (False, [])
+				, do
+					r <- catchBoolIO $ do
+						writeFile f "2"
+						return True
+					if r
+						then return (True, ["Filesystem allows writing to files whose write bit is not set."])
+						else return (False, [])
+				)
 			)
 #endif
 
diff --git a/Annex/Perms.hs b/Annex/Perms.hs
index 214fa3114..aa3fd694d 100644
--- a/Annex/Perms.hs
+++ b/Annex/Perms.hs
@@ -17,6 +17,7 @@ module Annex.Perms (
 	freezeContent,
 	freezeContent',
 	checkContentWritePerm,
+	checkContentWritePerm',
 	thawContent,
 	thawContent',
 	createContentDir,
@@ -165,16 +166,18 @@ freezeContent' sr file = do
 checkContentWritePerm :: RawFilePath -> Annex (Maybe Bool)
 checkContentWritePerm file = ifM crippledFileSystem
 	( return (Just True)
-	, withShared go
+	, withShared (\sr -> liftIO (checkContentWritePerm' sr file))
 	)
-  where
-	go GroupShared = want sharedret 
-		(includemodes [ownerWriteMode, groupWriteMode])
-	go AllShared = want sharedret (includemodes writeModes)
-	go _ = want Just (excludemodes writeModes)
 
-	want mk f =
-		liftIO (catchMaybeIO $ fileMode <$> R.getFileStatus file) >>= return . \case
+checkContentWritePerm' :: SharedRepository -> RawFilePath -> IO (Maybe Bool)
+checkContentWritePerm' sr file = case sr of
+	GroupShared -> want sharedret
+		(includemodes [ownerWriteMode, groupWriteMode])
+	AllShared -> want sharedret (includemodes writeModes)
+	_ -> want Just (excludemodes writeModes)
+  where
+	want mk f = catchMaybeIO (fileMode <$> R.getFileStatus file)
+		>>= return . \case
 			Just havemode -> mk (f havemode)
 			Nothing -> mk True
 	
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
index 749e980ed..4297da9a3 100644
--- a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
@@ -38,3 +38,6 @@ Both look like
 
 [[!meta author=yoh]]
 [[!tag projects/datalad]]
+
+> [[fixed|done]] (provisionally; can't test running git-annex as root on
+> OSX) --[[Joey]]
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_1_22a1f4b025a6046e4d11f90590a6e4bd._comment b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_1_22a1f4b025a6046e4d11f90590a6e4bd._comment
new file mode 100644
index 000000000..038f8ed29
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write/comment_1_22a1f4b025a6046e4d11f90590a6e4bd._comment
@@ -0,0 +1,34 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-09-01T13:48:57Z"
+ content="""
+Seems that mounting that way on OSX results in a FS where files are always mode
+777 and the permissions cannot be changed.
+
+When I tried using git-annex on such a FS, I saw:
+
+	datalads-imac:x joey$ git annex init
+	init
+	  Detected a filesystem without fifo support.
+	
+	  Disabling ssh connection caching.
+	
+	  Filesystem allows writing to files whose write bit is not set.
+	
+	  Detected a crippled filesystem.
+
+And it skips the new permissions check when on a crippled filesystem.
+
+But in that that test run, it seems it is failing to detect a crippled
+filesystem. Both because of the failure and also the test suite does
+not even run the "v8 unlocked" tests when it detects a crippled filesystem.
+
+Is the test suite running as root? Looks like probably yes. Running as
+root prevents detecting the issue that made it use a crippled FS above. And it
+seems that, when a FAT fs is mounted on OSX that way, symlinks actually work
+(!!!) so the other crippled FS tests also don't notice a problem.
+
+So, the fix should be for init to also test if it can remove the write
+bits from a file, and it should try that test even when root.
+"""]]

prop_relPathDirToFileAbs_basics fails on occasion
diff --git a/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn
new file mode 100644
index 000000000..63151051c
--- /dev/null
+++ b/doc/bugs/test_prop__95__relPathDirToFileAbs__95__basics_fails_now__38__thn.mdwn
@@ -0,0 +1,51 @@
+### Please describe the problem.
+
+Here's a new one :)
+
+Running the test QuickCheck.prop_relPathDirToFileAbs_basics fails in a particular case (but only occasionally).
+
+Running the following fails:
+
+[[!format sh """
+jkniiv@AINESIS MINGW64 /c/annx
+$ ./git-annex test -p QuickCheck.prop_relPathDirToFileAbs_basics --quickcheck-replay=564957 2>&1 | tee git-annex.test--p-QuickCheck_prop_relPathDirToFileAbs_basics.LOG~201
+Tests
+  QuickCheck
+    prop_relPathDirToFileAbs_basics: FAIL (0.05s)
+      *** Failed! Falsified (after 795 tests):
+      TestableFilePath {fromTestableFilePath = ":\\\DLEJ\STXm{u5;4*\EOTKo1"}
+      Use --quickcheck-replay=564957 to reproduce.
+
+1 out of 1 tests failed (0.05s)
+  (Failures above could be due to a bug in git-annex, or an incompatibility
+   with utilities, such as git, installed on this system.)
+
+# End of transcript.
+"""]]
+
+If you remove the option `--quickcheck-replay=564957`, the test usually passes.
+
+### What version of git-annex are you using? On what operating system?
+
+[[!format sh """
+git-annex version: 8.20210804-gccf7e5b94
+build flags: Assistant Webapp Pairing TorrentParser Feeds Testsuite S3 WebDAV
+dependency versions: aws-0.22 bloomfilter-2.0.1.0 cryptonite-0.26 DAV-1.3.4 feed-1.3.0.1 ghc-8.8.4 http-client-0.6.4.1 persistent-sqlite-2.10.6.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.6.1.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 BLAKE2BP512E BLAKE2BP512 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256 BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL X*
+remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar git-lfs httpalso borg hook external
+operating system: mingw32 x86_64
+supported repository versions: 8
+upgrade supported from repository versions: 2 3 4 5 6 7
+"""]]
+
+Windows version 21H1 (build 19043.1165), 64 bit.
+
+### Please provide any additional information below.
+
+.
+
+### 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 is great. It works with multi-gigabyte backup files (largest around 180GB) via the BLAKE2B160E backend just dandy :)
+
+[[!meta author=jkniiv]]

Added a comment
diff --git a/doc/bugs/test_fail_on_windows__58___permission_denied_/comment_1_9f46a99e5f570de2313a5521b58640b3._comment b/doc/bugs/test_fail_on_windows__58___permission_denied_/comment_1_9f46a99e5f570de2313a5521b58640b3._comment
new file mode 100644
index 000000000..7c9f57481
--- /dev/null
+++ b/doc/bugs/test_fail_on_windows__58___permission_denied_/comment_1_9f46a99e5f570de2313a5521b58640b3._comment
@@ -0,0 +1,78 @@
+[[!comment format=mdwn
+ username="jkniiv@b330fc3a602d36a37a67b2a2d99d4bed3bb653cb"
+ nickname="jkniiv"
+ avatar="http://cdn.libravatar.org/avatar/419f2eee8b0c37256488fabcc2737ff2"
+ subject="comment 1"
+ date="2021-08-31T20:47:24Z"
+ content="""
+At my end I ran the `readonly` test with the `--keep-failures` flags and found out the following:
+
+[[!format sh \"\"\"
+jkniiv@AINESIS MINGW64 /c/annx
+$ ./git-annex test -p \"Unit Tests v8 adjusted unlocked branch.readonly\" --keep-failures
+Tests
+  Unit Tests v8 adjusted unlocked branch
+    readonly: Init Tests
+  init: OK (4.00s)
+  add:  OK (5.78s)
+
+All 2 tests passed (9.77s)
+** Preserving repo for failure analysis in .t\tmprepo1
+** Preserving repo for failure analysis in .t\tmprepo0
+FAIL (16.98s)
+      .\\Test\\Framework.hs:57:
+      drop vs readonly repo failed (transcript follows)
+      drop foo
+      failed
+      git-annex: .git\annex\objects\6cd\e82\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77: DeleteFile \"\\\\?\\C:\\annx\\.t\\tmprepo1\\.git\\annex\\objects\\6cd\\e82\\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77\\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77\": permission denied (Käyttö estetty.)
+      drop: 1 failed
+
+1 out of 1 tests failed (26.78s)
+  (Failures above could be due to a bug in git-annex, or an incompatibility
+   with utilities, such as git, installed on this system.)
+
+jkniiv@AINESIS MINGW64 /c/annx
+$ cd .t/tmprepo1
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ ls -ld .git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+drwxr-xr-x 1 jkniiv 197121 0 Aug 31 23:08 .git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ ls -ld .git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+-r--r--r-- 1 jkniiv 197121 20 Aug 31 23:08 .git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ chmod -Rv +w .git/annex/objects
+mode of '.git/annex/objects' retained as 0755 (rwxr-xr-x)
+mode of '.git/annex/objects/6cd' retained as 0755 (rwxr-xr-x)
+mode of '.git/annex/objects/6cd/e82' retained as 0755 (rwxr-xr-x)
+mode of '.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77' retained as 0755 (rwxr-xr-x)
+mode of '.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77' changed from 0444 (r--r--r--) to 0644 (rw-r--r--)
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ ../../git-annex drop foo
+drop foo ok
+(recording state in git...)
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ ls -ld ../tmprepo0/.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+drwxr-xr-x 1 jkniiv 197121 0 Aug 31 23:15 ../tmprepo0/.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/
+
+jkniiv@AINESIS MINGW64 /c/annx/.t/tmprepo1 (adjusted/master(unlocked))
+$ ls -ld ../tmprepo0/.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+-r--r--r-- 1 jkniiv 197121 20 Aug 31 23:07 ../tmprepo0/.git/annex/objects/6cd/e82/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77/SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77
+
+\"\"\"]]
+
+So it looks like the readonly status of the annex object got copied between r1 (`tmprepo0`) and r2
+(`temprepo1`) at the point of `get` and that prevented the removal of the file during `drop`.
+
+(Btw. I found out that in Msys2/MinGW what is represented as the POSIX readonly mode bits seems
+to be really represented by the readonly file attribute and not as readonly permissions/ACLs
+on the file. At least that seems to be the default setting. Cygwin on the other hand defaults
+to mapping mode bits to ACLs in a rather complex fashion. All of this are just some details
+that lie behind what calling out to an external `chmod` binary entails on Windows both above
+in my Git Bash session and in git-annex' unit test `test_readonly`.)
+
+"""]]

initial TODO on improving 'get' errors reporting
diff --git a/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn b/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn
new file mode 100644
index 000000000..0a79c556f
--- /dev/null
+++ b/doc/todo/get__58___improve_error_reporting_for_failed_attempts.mdwn
@@ -0,0 +1,43 @@
+ATM `annex get` (in particular '--json --json-error-messages --json-progress') would channel to the user the error from an attempt to get a key from a remote with a message which lacks information about remote and/or specifics of that particular attempt (e.g. which URL was attempted from web remote), e.g.
+
+```
+$> git clone https://github.com/dandisets/000029 && cd 000029
+Cloning into '000029'...
+remote: Enumerating objects: 326, done.
+remote: Counting objects: 100% (326/326), done.
+remote: Compressing objects: 100% (160/160), done.
+remote: Total 326 (delta 137), reused 295 (delta 106), pack-reused 0
+Receiving objects: 100% (326/326), 45.53 KiB | 1.30 MiB/s, done.
+Resolving deltas: 100% (137/137), done.
+dandiset.yaml  sub-RAT123/  sub-anm369962/  sub-anm369963/  sub-anm369964/
+
+$> git update-ref refs/remotes/origin/git-annex b822a8d40ff348a60602f13d0add989bd24e727a  # URLs fixed since then
+
+$> git annex get sub-RAT123                                                                                      
+get sub-RAT123/sub-RAT123.nwb (from web...) 
+
+  download failed: Not Found
+
+ok
+(recording state in git...)
+
+$> git annex version | head -n 1
+git-annex version: 8.20210803+git165-g249d424b8-1~ndall+1
+```
+
+NB. That "download failed: Not Found" is also channeled in that form (without any extra information) among "errors" of `--json-error-messages` (and each progress message within `--json-progress`) 
+
+As such the message is not informative really, and might even be a bit confusing to the user since `get` does `ok` eventually here.
+I think it is useful to channel such information but it should be extended, e.g. in this case could be
+
+```
+  failed to retrieve content from 'web' remote: https://api.dandiarchive.org/api/dandisets/000029/versions/draft/assets/b3675aad-db07-4fd4-9cce-c95f1184e7a3/download/ - Not Found
+```
+
+or alike. Even though considerably longer, it immediately provides feedback from which remote it failed to retrieve, and what was that particular URL.
+
+
+refs in DataLad issues:
+
+- from web remote: ["download failed: Not Found"](https://github.com/datalad/datalad/pull/5936)
+- from ["failed to retrieve content from remote"](https://github.com/datalad/datalad/issues/5750)

report on 2 tests fail on crippled FS on mac
diff --git a/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
new file mode 100644
index 000000000..749e980ed
--- /dev/null
+++ b/doc/bugs/2_mac_crippled_FS__58___Unable_to_remove_all_write.mdwn
@@ -0,0 +1,40 @@
+### Please describe the problem.
+
+Since cron build of 20210828
+
+```
+(git)smaug:/mnt/datasets/datalad/ci/git-annex/builds/2021/08[master]git
+$> git grep -l 'Unable to remove all write permissions'
+cron-20210828/build-macos.yaml-403-69466103-failed/2_test-annex (crippled-tmp).txt
+cron-20210828/build-macos.yaml-403-69466103-failed/test-annex (crippled-tmp)/8_Run tests.txt
+cron-20210829/build-macos.yaml-404-69466103-failed/2_test-annex (crippled-tmp).txt
+cron-20210829/build-macos.yaml-404-69466103-failed/test-annex (crippled-tmp)/8_Run tests.txt
+cron-20210830/build-macos.yaml-405-69466103-failed/2_test-annex (crippled-tmp).txt
+cron-20210830/build-macos.yaml-405-69466103-failed/test-annex (crippled-tmp)/8_Run tests.txt
+cron-20210831/build-macos.yaml-406-69466103-failed/2_test-annex (crippled-tmp).txt
+cron-20210831/build-macos.yaml-406-69466103-failed/test-annex (crippled-tmp)/8_Run tests.txt
+```
+
+we got two test fails on a crippled FS on Mac (does not happen on linux afaik)
+
+[example CI log](https://github.com/datalad/git-annex/runs/3468283573?check_suite_focus=true)
+
+Both look like
+
+```
+2021-08-31T02:15:42.0758760Z     magic:                                                OK (2.41s)
+2021-08-31T02:15:42.6972710Z     import:                                               FAIL (0.62s)
+2021-08-31T02:15:42.6973680Z       ./Test/Framework.hs:57:
+2021-08-31T02:15:42.6974230Z       import failed (transcript follows)
+2021-08-31T02:15:42.6974760Z       import import1/f
+2021-08-31T02:15:42.6976570Z         Unable to remove all write permissions from /Volumes/crippledfs/importtestvjfjz3/import1/f -- perhaps it has an xattr or ACL set.
+2021-08-31T02:15:42.6977430Z       failed
+2021-08-31T02:15:42.6977830Z       import: 1 failed
+2021-08-31T02:15:44.1985050Z     reinject:                                             OK (1.50s)
+```
+
+[here is the script](https://github.com/datalad/git-annex/blob/master/.github/workflows/tools/setup_crippledfs#L24) to setup such a crippled (FAT32) FS on OSX.
+
+
+[[!meta author=yoh]]
+[[!tag projects/datalad]]

initial report
diff --git a/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn b/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn
new file mode 100644
index 000000000..7c364f488
--- /dev/null
+++ b/doc/bugs/test_fail_on_windows__58___permission_denied_.mdwn
@@ -0,0 +1,30 @@
+### Please describe the problem.
+
+In the recent build of datalad/git-annex (8.20210803+git167-g608593fec) a test failed on Windows
+
+[CI log](https://github.com/datalad/git-annex/runs/3468868101?check_suite_focus=true)
+
+```
+    readonly:                                             FAIL (7.55s)
+      .\\Test\\Framework.hs:57:
+      drop vs readonly repo failed (transcript follows)
+      drop foo 
+      failed
+      git-annex: .git\annex\objects\6cd\e82\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77: DeleteFile "\\\\?\\C:\\Users\\runneradmin\\.t\\tmprepo3\\.git\\annex\\objects\\6cd\\e82\\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77\\SHA256E-s20--e394a389d787383843decc5d3d99b6d184ffa5fddeec23b911f9ee7fc8b9ea77": permission denied (Access is denied.)
+      drop: 1 failed
+    ignore deleted files:                                 OK (2.51s)
+```
+
+
+this is the first and only occurrence so far according to
+
+
+```
+(git)smaug:/mnt/datasets/datalad/ci/git-annex/builds/2021/08[master]
+$> git grep 'permission denied (Access is denied.)'
+cron-20210831/build-windows.yaml-369-69466103-failed/1_test-annex.txt:2021-08-31T03:47:09.0093593Z ...
+``` 
+
+
+[[!meta author=yoh]]
+[[!tag projects/datalad]]

Added a comment: just an update on my testing
diff --git a/doc/bugs/__34__357_out_of_984_tests_failed__34___on_NFS_lustre_mount/comment_15_6354d1e144830bea5a0d7872f698ccc0._comment b/doc/bugs/__34__357_out_of_984_tests_failed__34___on_NFS_lustre_mount/comment_15_6354d1e144830bea5a0d7872f698ccc0._comment
new file mode 100644
index 000000000..e7e235c58
--- /dev/null
+++ b/doc/bugs/__34__357_out_of_984_tests_failed__34___on_NFS_lustre_mount/comment_15_6354d1e144830bea5a0d7872f698ccc0._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="just an update on my testing"
+ date="2021-08-30T22:17:53Z"
+ content="""
+FWIW, I've tested a debianstadalone build of `8.20210803+git153-g63c2cfd47-1~ndall+1` on that system, and besides 3 fails due to no `--kill` in elderly system wide gpgconf (apparently debian bundle does not come with one) -- it is all clean! (although took nearly a full work day to complete on that filesystem: `3 out of 984 tests failed (18157.38s)`)
+"""]]

comment
diff --git a/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_6_11cb8251521014ef81b53090f5c78376._comment b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_6_11cb8251521014ef81b53090f5c78376._comment
new file mode 100644
index 000000000..35f9d8d72
--- /dev/null
+++ b/doc/bugs/unable_to_get_content_via_ssh_without_write_perms/comment_6_11cb8251521014ef81b53090f5c78376._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2021-08-30T18:39:33Z"
+ content="""
+Well I've already fixed their problem AFAICS. Just perhaps not the problem
+you are seeing.
+
+Your script does not reproduce any problem here. Are you running it on NFS
+or something?
+
+Anyway, if you can build current master the improved error message will
+probably help me find the problem.
+"""]]

update
diff --git a/doc/todo/importtree_only_remotes.mdwn b/doc/todo/importtree_only_remotes.mdwn
index d7a4289ff..b0e376a96 100644
--- a/doc/todo/importtree_only_remotes.mdwn
+++ b/doc/todo/importtree_only_remotes.mdwn
@@ -7,6 +7,9 @@ for exporttree=yes.
 Few special remotes support that interface, and probably a lot of them 
 just can't; they don't have something that can be used as a ContentIdentifier,
 or lack the necessary atomicity properties to implement it safely.
+(For example, `storeExportWithContentIdentifier` has a list of old
+ContentIdentifiers that are allowed to be overwritten, and requires
+that other content does not get overwritten.)
 
 The external special remote protocol does not support that interface
 yet, due to its complexity and also because noone has requested it.
@@ -60,9 +63,10 @@ What is needed in such an interface?
 listImportableContents is unchanged, and checkPresentImport above
 is identical to checkPresentExport. retrieveImport is very similar
 to retrieveExport, except that the content retrieved is not guaranteed
-to be the same as the content of any key. Actually, it may be identical;
-the only thing that uses retrieveExport forces verification of the content
-retrived since it could have been changed by another writer.
+to be the same as the content of any key. Actually, it may be an identical
+interface; the only thing I can find that uses retrieveExport forces
+verification of the content retrived since it could have been changed by
+another writer.
 
 The similarity with interface that we already have suggests that
 perhaps this does not need changes to Types.Remote to implement.

idea for making more special remotes support importtree
Sponsored-by: Jack Hill on Patreon
diff --git a/doc/todo/add_import_tree_to_external_special_remote_protocol.mdwn b/doc/todo/add_import_tree_to_external_special_remote_protocol.mdwn
index 878e98b9d..eda3fe530 100644
--- a/doc/todo/add_import_tree_to_external_special_remote_protocol.mdwn
+++ b/doc/todo/add_import_tree_to_external_special_remote_protocol.mdwn
@@ -5,3 +5,7 @@ My main concern about this is, will external special remotes pick good
 ContentIdentifiers and will they manage the race conditions documented in
 [[import_tree]]? Mistakes in these things can result in data loss, and it's
 rather subtle stuff. --[[Joey]]
+
+> It may be better to implement [[importtree_only_remotes]] and make
+> a simpler protocol extension that supports that, rather than supporting
+> both export and import tree together. --[[Joey]]
diff --git a/doc/todo/import_tree_from_rsync_special_remote/comment_1_b1f97f8d62c4e2f9bbe02955c7a4dec4._comment b/doc/todo/import_tree_from_rsync_special_remote/comment_1_b1f97f8d62c4e2f9bbe02955c7a4dec4._comment
new file mode 100644
index 000000000..db9ea7915
--- /dev/null
+++ b/doc/todo/import_tree_from_rsync_special_remote/comment_1_b1f97f8d62c4e2f9bbe02955c7a4dec4._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2021-08-30T18:04:21Z"
+ content="""
+This seems more tractable if a rsync remote supports only importtree=yes
+but not also exporttree=yes.
+
+That would prevent needing to worry about git-annex making changes
+to the remote at the same time it's getting content from it. Any changes
+would be made by something else, and git-annex would only import them.
+
+store/remove would not do anything. checkpresent would perhaps always
+fail.
+"""]]
diff --git a/doc/todo/importtree_only_remotes.mdwn b/doc/todo/importtree_only_remotes.mdwn
new file mode 100644
index 000000000..d7a4289ff
--- /dev/null
+++ b/doc/todo/importtree_only_remotes.mdwn
@@ -0,0 +1,72 @@
+Currently for a special remote to support being configured
+with exporttree=no importtree=yes, it needs to implement the
+ImportActions interface, which uses ContentIdentifiers
+for safety and includes some methods that are only needed
+for exporttree=yes.
+
+Few special remotes support that interface, and probably a lot of them 
+just can't; they don't have something that can be used as a ContentIdentifier,
+or lack the necessary atomicity properties to implement it safely.
+
+The external special remote protocol does not support that interface
+yet, due to its complexity and also because noone has requested it.
+(There is a draft protocol extension for export and import, see 
+<https://git-annex.branchable.com/design/external_special_remote_protocol/export_and_import_appendix/#index2h2>)
+(See also [[todo/add_import_tree_to_external_special_remote_protocol]])
+
+A simpler interface that supoorts only importtree=yes without needing to
+worry about exporttree=yes, could let a lot more special remotes support
+tree import. (For example [[todo/import_tree_from_rsync_special_remote]].)
+
+Such a special remote could be populated in any way by something outside
+git-annex, and `git annex import --from remote` would download the content
+and generate a remote tracking branch. Once imported, other clones could
+use `git annex get` to download files from the special remote.
+
+Bearing in mind that since something is writing to the special remote, any
+file on it could be overwritten at any point, so such a get may download
+the wrong content. (So the remote should have retrievalSecurityPolicy =
+RetrievalVerifiableKeysSecure to make downloads be verified well enough.)
+
+I said this would not use a ContentIdentifier, but it seems it needs some
+simple form of ContentIdentifier, which could be just an mtime.
+Without any ContentIdentifier, it seems that each time 
+`git annex import --from remote` is run, it would need to re-download
+all files from the remote, because it would have no way of knowing
+if it had seen a version of a file before. This ContentIdentifier would
+be used only to avoid re-downloading when importing. It would not be used
+by any other methods. It could even be a dummy value if re-downloading
+every file on import is acceptable.
+
+What is needed in such an interface?
+
+	listImportableContents :: Annex (Maybe (ContentIdentifier, ImportableContents ByteSize))
+	-- Retrieves content from an import location to a file.
+	-- The content retrieved could be anything; it needs to be
+	-- strongly verified if this is used to download a particular Key
+	-- that was at one point stored on the remote, since the content
+	-- of the remote could change at any time.
+	-- (The MeterUpdate does not need to be used if 
+        -- sequentially to the file.)
+	-- Throws exception on failure.
+	retrieveImport :: ImportLocation -> FilePath -> MeterUpdate -> Annex ()
+	-- Checks if anything is present on the remote at the specified
+	-- ImportLocation. It may check the size or other characteristics
+        -- of the Key, but does not need to guarantee that the content on
+        -- the remote is the same as the Key's content.
+        -- Throws an exception if the remote cannot be accessed.
+	checkPresentImport :: Key -> ImportLocation -> Annex Bool
+
+listImportableContents is unchanged, and checkPresentImport above
+is identical to checkPresentExport. retrieveImport is very similar
+to retrieveExport, except that the content retrieved is not guaranteed
+to be the same as the content of any key. Actually, it may be identical;
+the only thing that uses retrieveExport forces verification of the content
+retrived since it could have been changed by another writer.
+
+The similarity with interface that we already have suggests that
+perhaps this does not need changes to Types.Remote to implement.
+It could be done as a Remote.Helper.SimpleImport that takes those
+3 methods and translates them to the current interface.
+Or by complicating Remote.Helper.ExportImport further..
+--[[Joey]]