Recent changes to this wiki:

marking as done
diff --git a/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
index 7dbede3634..538fd45399 100644
--- a/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
+++ b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
@@ -46,3 +46,4 @@ Test image used in this bug report: https://wallpapercave.com/wp/wp8215486.jpg
 I'm really loving git-annex. I've been reading up and I'm more than halfway through the devlog and I'm hoping to use git-annex for a lot and a long while. Mostly, I'm expecting to use it on Linux systems, but I'd love to be able to rely on it on my (Windows) work laptop as well. Thanks for looking at my report!
 
 [[!meta title="assistant on windows misses copy+rename of file"]]
+[[done]]

Added a comment
diff --git a/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_2_7e2604bad32225682513b51c1be6f571._comment b/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_2_7e2604bad32225682513b51c1be6f571._comment
new file mode 100644
index 0000000000..6b7f71ed00
--- /dev/null
+++ b/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_2_7e2604bad32225682513b51c1be6f571._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="contr-error"
+ avatar="http://cdn.libravatar.org/avatar/d79c2491dfc6947b8084796b06174cdb"
+ subject="comment 2"
+ date="2022-06-29T19:13:56Z"
+ content="""
+Hi Joey, thanks for looking into this and your work on git-annex! You're correct on both counts. After posting the report I tried manually adding the file and, indeed, restarting the assistant also makes it notice the file. I'm still exploring, but what seemed to be inscrutable behaviour at first, now seems reasonable enough.
+
+I've also encountered some access violations and I'm running into sqlite errors on WSL (1 & 2), both of which probably aren't worth reporting at this time, and the latter of which is probably out of scope of this bug tracker, anyway.
+"""]]

close bug that was recently fixed
diff --git a/doc/bugs/_git-annex__58___bad_url_ssh__58____47____47__git__64__gitlab.com__58____126____47__gitlabname__47__reponame.git.mdwn b/doc/bugs/_git-annex__58___bad_url_ssh__58____47____47__git__64__gitlab.com__58____126____47__gitlabname__47__reponame.git.mdwn
index 74e9235dd9..2045310559 100644
--- a/doc/bugs/_git-annex__58___bad_url_ssh__58____47____47__git__64__gitlab.com__58____126____47__gitlabname__47__reponame.git.mdwn
+++ b/doc/bugs/_git-annex__58___bad_url_ssh__58____47____47__git__64__gitlab.com__58____126____47__gitlabname__47__reponame.git.mdwn
@@ -31,3 +31,6 @@ An encrypted remote is added to a working git annex repository (mind ":~/" in th
 
 # End of transcript or log.
 """]]
+
+> Fixed in [[!commit 2aa4fab62a69bb3fb5073d571d8ec110a61a144e]]
+> [[done]] --[[Joey]]

close
diff --git a/doc/bugs/how_to_copy_tp_multiple_remotes_simultaneously__63__.mdwn b/doc/bugs/how_to_copy_tp_multiple_remotes_simultaneously__63__.mdwn
index 8727fd9d14..555a8bce2d 100644
--- a/doc/bugs/how_to_copy_tp_multiple_remotes_simultaneously__63__.mdwn
+++ b/doc/bugs/how_to_copy_tp_multiple_remotes_simultaneously__63__.mdwn
@@ -40,3 +40,4 @@ What I have is a python script and its output which I can't imagine is that help
 Usng it regularly and like it - many thanks.  This is a wishlist item, not a bug, as far as I'm concerned.
 
 
+> [[done]], no response to my question... --[[Joey]] 

close
diff --git a/doc/bugs/special_remote_protocol__58___adding_local_state_variables___40__not_under_git__41__.mdwn b/doc/bugs/special_remote_protocol__58___adding_local_state_variables___40__not_under_git__41__.mdwn
index bb7345123c..9b87d77c7d 100644
--- a/doc/bugs/special_remote_protocol__58___adding_local_state_variables___40__not_under_git__41__.mdwn
+++ b/doc/bugs/special_remote_protocol__58___adding_local_state_variables___40__not_under_git__41__.mdwn
@@ -11,3 +11,7 @@ git-annex 6.20170101 on Debian stretch (from debian archives)
 
 ### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
 I'm using (and enjoying) it a lot :)
+
+> Seems like my comment is reasonable, so no change to git-annex is needed.
+> I would be open to being convinced otherwise though... [[wontfix|done]]
+> for now. --[[Joey]]

close
diff --git a/doc/bugs/Debian_and_MacOSX_cannot_read_eachother__39__s_S3_credentials.mdwn b/doc/bugs/Debian_and_MacOSX_cannot_read_eachother__39__s_S3_credentials.mdwn
index a351b0ae4e..b2be7e9fa2 100644
--- a/doc/bugs/Debian_and_MacOSX_cannot_read_eachother__39__s_S3_credentials.mdwn
+++ b/doc/bugs/Debian_and_MacOSX_cannot_read_eachother__39__s_S3_credentials.mdwn
@@ -61,3 +61,6 @@ Alternatively, create the S3 remote on Debian, sync and enable remote on MacOSX
 ### 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! git annex has been enormously helpful. Thanks so much for this tool.
 
+
+> I guess this was caused by an old gpg, and so no point in keeping it
+> open. [[done]] --[[Joey]]

close
diff --git a/doc/bugs/Hybrid_encryption_can__39__t_generate_the_right_key_after_moving_files.mdwn b/doc/bugs/Hybrid_encryption_can__39__t_generate_the_right_key_after_moving_files.mdwn
index b11da5e9da..e4b785a01a 100644
--- a/doc/bugs/Hybrid_encryption_can__39__t_generate_the_right_key_after_moving_files.mdwn
+++ b/doc/bugs/Hybrid_encryption_can__39__t_generate_the_right_key_after_moving_files.mdwn
@@ -113,3 +113,6 @@ git-annex: fsck: 1 failed
 ### 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)
 
 Yeah, it works great most of the time, and it was working great on the amazon remote before I tried to pull these migration shenanigans.
+
+> Closing this since there has been no response for information for 5
+> years. [[done]] --[[Joey]]

close
diff --git a/doc/bugs/sync_claims_data_loss_but_seems_to_just_lose_tracking.mdwn b/doc/bugs/sync_claims_data_loss_but_seems_to_just_lose_tracking.mdwn
index 3223c61c05..e596e8424b 100644
--- a/doc/bugs/sync_claims_data_loss_but_seems_to_just_lose_tracking.mdwn
+++ b/doc/bugs/sync_claims_data_loss_but_seems_to_just_lose_tracking.mdwn
@@ -213,3 +213,6 @@ To /Users/olaf/tmp/git-annex/play/bug-play/source
 
 Yes.  Love it.  Donated.  Have been using it for years.  Recommend it and get(/force) my collaborators to use it.  ;-)
 
+> Since this bug involved direct mode, and direct mode was known to be
+> rather buggy and has been removed, I think it's best to close it.
+> [[done]] --[[Joey]]

close
diff --git a/doc/bugs/Strange_case_of_data_loss__44___possibly_linked_to_git-annex_with_encrypted_rsync_remote.mdwn b/doc/bugs/Strange_case_of_data_loss__44___possibly_linked_to_git-annex_with_encrypted_rsync_remote.mdwn
index be0060c582..ffa9b5d676 100644
--- a/doc/bugs/Strange_case_of_data_loss__44___possibly_linked_to_git-annex_with_encrypted_rsync_remote.mdwn
+++ b/doc/bugs/Strange_case_of_data_loss__44___possibly_linked_to_git-annex_with_encrypted_rsync_remote.mdwn
@@ -53,3 +53,9 @@ caused this behavior? Or is there any other realistic explanation for this? In
 case this is an existing bug, is there any other evidence I can gather?
 Needless to say, the lesson here is to run `git annex fsck` regularly even if
 you have offsite backups...
+
+> My diagnosis is that the file got corrupted before it was copied to disk2
+> and disk3. What repository they reached them via does not matter much.
+> And indeed, 5 year old git-annex didn't verify the content of
+> files it transferred to/from a remote. Current git-annex does, so I guess 
+> this is [[done]]. --[[Joey]]

include filename in fsck warning
So it's available in --quiet mode. The same was already done in other
fsck warnings.
Sponsored-by: Noam Kremen on Patreon
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
index cae25dbfaa..eef6a4f214 100644
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -164,7 +164,7 @@ performRemote key afile backend numcopies remote =
 			Nothing -> go True Nothing
 			Just (Right verification) -> go True (Just (tmpfile, verification))
 			Just (Left _) -> do
-				warning "failed to download file from remote"
+				warning (decodeBS (actionItemDesc ai) ++ ": failed to download file from remote")
 				void $ go True Nothing
 				return False
 	dispatch (Right False) = go False Nothing
diff --git a/doc/bugs/git-annex_fsck_in_quiet_mode_don__39__t_report_the_files_it_failed_to_download.mdwn b/doc/bugs/git-annex_fsck_in_quiet_mode_don__39__t_report_the_files_it_failed_to_download.mdwn
index 2496715957..31da88a6ce 100644
--- a/doc/bugs/git-annex_fsck_in_quiet_mode_don__39__t_report_the_files_it_failed_to_download.mdwn
+++ b/doc/bugs/git-annex_fsck_in_quiet_mode_don__39__t_report_the_files_it_failed_to_download.mdwn
@@ -13,3 +13,6 @@ Just running 'git annex fsck --from remote --quiet' on some special remote with
 ### 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 very efficient to sync my usb key, laptops and servers together.
+
+> [[fixed|done]]. Sorry for the 4 year delay, seems I missed this bug
+> report. --[[Joey]]

this is fixed
diff --git a/doc/bugs/Adjust_--unlock_not_using_--reflink__63__.mdwn b/doc/bugs/Adjust_--unlock_not_using_--reflink__63__.mdwn
index 91a69a4ea3..ad2187ae8b 100644
--- a/doc/bugs/Adjust_--unlock_not_using_--reflink__63__.mdwn
+++ b/doc/bugs/Adjust_--unlock_not_using_--reflink__63__.mdwn
@@ -13,3 +13,8 @@ Run adjust --unlock with very large files.
 ### 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 I have! I've used it manage lots of video editing disks before, and am now migrating several slightly different copies of 15TB sized documentary footage from random USB3 disks and LTO tapes to a RAID server with BTRFS.
+
+> Yay, this got fixed some time ago, by making the smudge filter not output
+> the file content at all, but instead the link, and having a git hook
+> later run git-annex to populate the files, which it does use reflink for
+> when possible. [[done]] --[[Joey]]

close
diff --git a/doc/bugs/schedule.log_added_to_annex__63_____58____47____47____47__.mdwn b/doc/bugs/schedule.log_added_to_annex__63_____58____47____47____47__.mdwn
index 9982135827..eaf1157f95 100644
--- a/doc/bugs/schedule.log_added_to_annex__63_____58____47____47____47__.mdwn
+++ b/doc/bugs/schedule.log_added_to_annex__63_____58____47____47____47__.mdwn
@@ -53,3 +53,6 @@ Considering my two other issues »WHICH LOSE DATA«, I can only conclude it’s
 
 * https://git-annex.branchable.com/bugs/Data_loss_when_copying_files_with_running_assistant/
 * https://git-annex.branchable.com/bugs/Infinite_loop_when_synchronizing_between_many_machines/
+
+> I guess this was fixed in the intervening time. With no way to reproduce
+> it, it's hard to tell. [[done]] --[[Joey]]

close
diff --git a/doc/bugs/git_proxy_uses_ls-files_without_--exclude-standard.mdwn b/doc/bugs/git_proxy_uses_ls-files_without_--exclude-standard.mdwn
index dc0e140526..85bf7089ea 100644
--- a/doc/bugs/git_proxy_uses_ls-files_without_--exclude-standard.mdwn
+++ b/doc/bugs/git_proxy_uses_ls-files_without_--exclude-standard.mdwn
@@ -36,3 +36,5 @@ Version 6.20170520 on Mac OS 10.13.4.
 
 Today is my first day trying it out!  It's fabulous so far, but I'm at the beginning of the learning curve.
 
+> Yay, git-annex proxy is deprecated and just passes through to git, since
+> direct mode was eliminated. [[done]] --[[Joey]]

fix for removal of webdav build flag
diff --git a/doc/install/fromsource.mdwn b/doc/install/fromsource.mdwn
index c8a75779e2..33b8fabac4 100644
--- a/doc/install/fromsource.mdwn
+++ b/doc/install/fromsource.mdwn
@@ -83,8 +83,8 @@ to be broken from time to time.
 
 Get the git-annex source code, and inside the source tree, run:
 
-	cabal install -j -f"-assistant -webapp -webdav -pairing -dbus -magicmime" --only-dependencies
-	cabal configure -f"-assistant -webapp -webdav -pairing -dbus -magicmime"
+	cabal install -j -f"-assistant -webapp -pairing -dbus -magicmime" --only-dependencies
+	cabal configure -f"-assistant -webapp -pairing -dbus -magicmime"
 	cabal build -j
 
 To install the program and all associated files system-wide

fix build with assistant disabled and webapp enabled
The webapp modules cannot build with the assistant disabled, so make the
webapp be under the assistant build flag.
Sponsored-by: Jarkko Kniivilä on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 6810b10036..d48aaf1ad4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,8 @@ git-annex (10.20220625) UNRELEASED; urgency=medium
   * stack.yaml: Updated to lts-19.13
   * --backend is no longer a global option, and is only accepted by
     commands that actually need it.
+  * Fix building with the Assistant build flag disabled but the Webapp
+    build flag enabled.
 
  -- Joey Hess <id@joeyh.name>  Tue, 28 Jun 2022 14:49:17 -0400
 
diff --git a/Utility/Matcher.hs b/Utility/Matcher.hs
index 1772c230b3..567522b59e 100644
--- a/Utility/Matcher.hs
+++ b/Utility/Matcher.hs
@@ -34,6 +34,8 @@ module Utility.Matcher (
 
 import Common
 
+import Data.Kind
+
 {- A Token can be an Operation of an arbitrary type, or one of a few
  - predefined peices of syntax. -}
 data Token op = Operation op | And | Or | Not | Open | Close
@@ -136,7 +138,7 @@ matchM m v = matchMrun m $ \o -> o v
 {- More generic running of a monadic Matcher, with full control over running
  - of Operations. Mostly useful in order to match on more than one
  - parameter. -}
-matchMrun :: forall o (m :: * -> *). Monad m => Matcher o -> (o -> m Bool) -> m Bool
+matchMrun :: forall o (m :: Type -> Type). Monad m => Matcher o -> (o -> m Bool) -> m Bool
 matchMrun m run = go m
   where
 	go MAny = return True
diff --git a/doc/bugs/can__39__t_build_without_assistant.mdwn b/doc/bugs/can__39__t_build_without_assistant.mdwn
index 22171a2275..7cb17ece2a 100644
--- a/doc/bugs/can__39__t_build_without_assistant.mdwn
+++ b/doc/bugs/can__39__t_build_without_assistant.mdwn
@@ -30,3 +30,10 @@ I don't know haskell, but try to make optional building of Assistant, see [patch
 ### 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, use it for several years and have approx 100G repo with 10k+ files. Works fine.
 
+> You had the webapp flag enabled and the assistant disabled. 
+> That does not make sense; the assistant is part of the webapp.
+> That explains why it failed to build. Disabling both webapp and assistant
+> works, or only webapp and not assistant. 
+> 
+> Ok, fixed it to not build the webapp when the assistant flag is disabled.
+> [done]] --[[Joey]] 
diff --git a/git-annex.cabal b/git-annex.cabal
index 93edbdd94b..99e313f862 100644
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -514,6 +514,60 @@ Executable git-annex
             C-Sources: Utility/libkqueue.c
             Includes: Utility/libkqueue.h
             Other-Modules: Utility.DirWatcher.Kqueue
+  
+    if flag(Webapp)
+      Build-Depends:
+       yesod (>= 1.4.3), 
+       yesod-static (>= 1.5.1),
+       yesod-form (>= 1.4.8),
+       yesod-core (>= 1.6.0),
+       path-pieces (>= 0.2.1),
+       warp (>= 3.2.8),
+       warp-tls (>= 3.2.2),
+       wai,
+       wai-extra,
+       blaze-builder,
+       clientsession,
+       template-haskell,
+       shakespeare (>= 2.0.11)
+      CPP-Options: -DWITH_WEBAPP
+      Other-Modules:
+        Command.WebApp
+        Assistant.Threads.WebApp
+        Assistant.Threads.PairListener
+        Assistant.WebApp
+        Assistant.WebApp.Common
+        Assistant.WebApp.Configurators
+        Assistant.WebApp.Configurators.AWS
+        Assistant.WebApp.Configurators.Delete
+        Assistant.WebApp.Configurators.Edit
+        Assistant.WebApp.Configurators.Fsck
+        Assistant.WebApp.Configurators.IA
+        Assistant.WebApp.Configurators.Local
+        Assistant.WebApp.Configurators.Pairing
+        Assistant.WebApp.Configurators.Preferences
+        Assistant.WebApp.Configurators.Ssh
+        Assistant.WebApp.Configurators.Unused
+        Assistant.WebApp.Configurators.Upgrade
+        Assistant.WebApp.Configurators.WebDAV
+        Assistant.WebApp.Control
+        Assistant.WebApp.DashBoard
+        Assistant.WebApp.Documentation
+        Assistant.WebApp.Form
+        Assistant.WebApp.Gpg
+        Assistant.WebApp.MakeRemote
+        Assistant.WebApp.Notifications
+        Assistant.WebApp.OtherRepos
+        Assistant.WebApp.Page
+        Assistant.WebApp.Pairing
+        Assistant.WebApp.Repair
+        Assistant.WebApp.RepoId
+        Assistant.WebApp.RepoList
+        Assistant.WebApp.SideBar
+        Assistant.WebApp.Types
+        Assistant.MakeRepo
+        Utility.Yesod
+        Utility.WebApp
 
   if flag(Dbus)
     if (os(linux))
@@ -521,60 +575,6 @@ Executable git-annex
       CPP-Options: -DWITH_DBUS -DWITH_DESKTOP_NOTIFY -DWITH_DBUS_NOTIFICATIONS
       Other-Modules: Utility.DBus
 
-  if flag(Webapp)
-    Build-Depends:
-     yesod (>= 1.4.3), 
-     yesod-static (>= 1.5.1),
-     yesod-form (>= 1.4.8),
-     yesod-core (>= 1.6.0),
-     path-pieces (>= 0.2.1),
-     warp (>= 3.2.8),
-     warp-tls (>= 3.2.2),
-     wai,
-     wai-extra,
-     blaze-builder,
-     clientsession,
-     template-haskell,
-     shakespeare (>= 2.0.11)
-    CPP-Options: -DWITH_WEBAPP
-    Other-Modules:
-      Command.WebApp
-      Assistant.Threads.WebApp
-      Assistant.Threads.PairListener
-      Assistant.WebApp
-      Assistant.WebApp.Common
-      Assistant.WebApp.Configurators
-      Assistant.WebApp.Configurators.AWS
-      Assistant.WebApp.Configurators.Delete
-      Assistant.WebApp.Configurators.Edit
-      Assistant.WebApp.Configurators.Fsck
-      Assistant.WebApp.Configurators.IA
-      Assistant.WebApp.Configurators.Local
-      Assistant.WebApp.Configurators.Pairing
-      Assistant.WebApp.Configurators.Preferences
-      Assistant.WebApp.Configurators.Ssh
-      Assistant.WebApp.Configurators.Unused
-      Assistant.WebApp.Configurators.Upgrade
-      Assistant.WebApp.Configurators.WebDAV
-      Assistant.WebApp.Control
-      Assistant.WebApp.DashBoard
-      Assistant.WebApp.Documentation
-      Assistant.WebApp.Form
-      Assistant.WebApp.Gpg
-      Assistant.WebApp.MakeRemote
-      Assistant.WebApp.Notifications
-      Assistant.WebApp.OtherRepos
-      Assistant.WebApp.Page
-      Assistant.WebApp.Pairing
-      Assistant.WebApp.Repair
-      Assistant.WebApp.RepoId
-      Assistant.WebApp.RepoList
-      Assistant.WebApp.SideBar
-      Assistant.WebApp.Types
-      Assistant.MakeRepo
-      Utility.Yesod
-      Utility.WebApp
-
   if flag(Pairing)
     Build-Depends: network-multicast, network-info
     CPP-Options: -DWITH_PAIRING
diff --git a/stack.yaml b/stack.yaml
index 4b97f255f5..623080a477 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,10 +1,10 @@
 flags:
   git-annex:
     production: true
-    assistant: true
+    assistant: false
     pairing: true
     torrentparser: true
-    webapp: true
+    webapp: false
     magicmime: false
     dbus: false
     debuglocks: false

close
diff --git a/doc/bugs/git-annex-move_not_using_parallelism__63__.mdwn b/doc/bugs/git-annex-move_not_using_parallelism__63__.mdwn
index 5d0a3d1e2b..ad90d1468d 100644
--- a/doc/bugs/git-annex-move_not_using_parallelism__63__.mdwn
+++ b/doc/bugs/git-annex-move_not_using_parallelism__63__.mdwn
@@ -10,3 +10,6 @@ and there is only one thread with git-annex.
 git-annex version: 6.20180926-gc906aaf build flags: Assistant Webapp Pairing S3(multipartupload)(storageclasses) WebDAV Inotify ConcurrentOutput TorrentParser MagicMime Feed\ s Testsuite dependency versions: aws-0.17.1 bloomfilter-2.0.1.0 cryptonite-0.23 DAV-1.3.1 feed-0.3.12.0 ghc-8.0.2 http-client-0.5.7.0 persistent-s\ qlite-2.6.2 torrent-10000.1.1 uuid-1.3.13 yesod-1.4.5 key/value backends: SHA256E SHA256 SHA512E SHA512 SHA224E SHA224 SHA384E SHA384 SHA3_256E SHA3_256 SHA3_512E SHA3_512 SHA3_224E SHA3_2\ 24 SHA3_384E SHA3_384 SKEIN256E SKEIN256 SKEIN512E SKEIN512 BLAKE2B256E BLAKE2B256 BLAKE2B512E BLAKE2B512 BLAKE2B160E BLAKE2B160 BLAKE\ 2B224E BLAKE2B224 BLAKE2B384E BLAKE2B384 BLAKE2S256E BLAKE2S256 BLAKE2S160E BLAKE2S160 BLAKE2S224E BLAKE2S224 BLAKE2SP256E BLAKE2SP256\ BLAKE2SP224E BLAKE2SP224 SHA1E SHA1 MD5E MD5 WORM URL remote types: git gcrypt p2p S3 bup directory rsync web bittorrent webdav adb tahoe glacier ddar hook external operating system: linux x86_64 supported repository versions: 3 5 6 upgrade supported from repository versions: 0 1 2 3 4 5 local repository version: 5
 
 (master_env_py27_v28) [01:05 PM /data/ilya-work]$ uname -a Linux ip-172-31-87-156 4.14.72-68.55.amzn1.x86_64 #1 SMP Fri Sep 28 21:14:54 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
+
+> While this might be a bug, there is not enough information to know, and
+> so after 3 years, I'm going to close it. [[done]] --[[Joey]]

close
diff --git a/doc/bugs/git_keeps_refreshing_index.mdwn b/doc/bugs/git_keeps_refreshing_index.mdwn
index cd1f0e165d..c69844ccd1 100644
--- a/doc/bugs/git_keeps_refreshing_index.mdwn
+++ b/doc/bugs/git_keeps_refreshing_index.mdwn
@@ -1 +1,12 @@
 Since upgrading to git-annex 7.20190912, when doing `git status` I keep getting the message `Refresh index:`, and there is a delay until it turns into something like `Refresh index: 100% (601422/601422), done.`.  I don't recall this happening with earlier `git-annex` versions.  Have others seen this?  (Not a "bug" in terms of correctness, but posting here as it affects usability and speed.)
+
+> This was fixed in [[!commit faf84aa5c2b963a41e56d02d6bbd2fbda7da9ceb]],
+> as far as handling of unlocking of many files goes.
+> 
+> For other cases where git needs to smudge a lot of files, 
+> in v9 and above repos, `git-annex filter-process` is used, 
+> which can speed it up significantly.
+> 
+> While it seems that the bug submitter never followed up with
+> what they were doing that caused the problem, this seems likely enough
+> fixed to call it [[done]]. --[[Joey]]

remove --backend from global options
--backend is no longer a global option, and is only accepted by commands
that actually need it.
Three commands that used to support backend but don't any longer are
watch, webapp, and assistant. It would be possible to make them support it,
but I doubt anyone used the option with these. And in the case of webapp
and assistant, the option was handled inconsistently, only taking affect
when the command is run with an existing git-annex repo, not when it
creates a new one.
Also, renamed GlobalOption etc to AnnexOption. Because there are many
options of this type that are not actually global (any more) and get
added to commands that need them.
Sponsored-by: Kevin Mueller on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 959e2a03f2..6810b10036 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,8 @@ git-annex (10.20220625) UNRELEASED; urgency=medium
   * Improve handling of parallelization with -J when copying content
     from/to a git remote that is a local path.
   * stack.yaml: Updated to lts-19.13
+  * --backend is no longer a global option, and is only accepted by
+    commands that actually need it.
 
  -- Joey Hess <id@joeyh.name>  Tue, 28 Jun 2022 14:49:17 -0400
 
diff --git a/CmdLine.hs b/CmdLine.hs
index 1cb7953660..0b553b9b2f 100644
--- a/CmdLine.hs
+++ b/CmdLine.hs
@@ -53,14 +53,14 @@ dispatch' subcommandname args fuzzy cmds allargs allcmds fields getgitrepo progn
   where
 	go (Right g) = do
 		g' <- Git.Config.read g
-		(cmd, seek, globalsetter) <- parsewith False cmdparser
+		(cmd, seek, annexsetter) <- parsewith False cmdparser
 			(\a -> a (Just g'))
 			O.handleParseResult
-		state <- applyAnnexReadSetter globalsetter <$> Annex.new g'
+		state <- applyAnnexReadSetter annexsetter <$> Annex.new g'
 		Annex.eval state $ do
 			checkEnvironment
 			forM_ fields $ uncurry Annex.setField
-			prepRunCommand cmd globalsetter
+			prepRunCommand cmd annexsetter
 			startup
 			performCommandAction True cmd seek $
 				shutdown $ cmdnocommit cmd
@@ -101,7 +101,7 @@ dispatch' subcommandname args fuzzy cmds allargs allcmds fields getgitrepo progn
 			Just n -> n:args
 
 {- Parses command line, selecting one of the commands from the list. -}
-parseCmd :: String -> String -> CmdParams -> [Command] -> (Command -> O.Parser v) -> O.ParserResult (Command, v, GlobalSetter)
+parseCmd :: String -> String -> CmdParams -> [Command] -> (Command -> O.Parser v) -> O.ParserResult (Command, v, AnnexSetter)
 parseCmd progname progdesc allargs allcmds getparser = 
 	O.execParserPure (O.prefs O.idm) pinfo allargs
   where
@@ -114,7 +114,7 @@ parseCmd progname progdesc allargs allcmds getparser =
 	mkparser c = (,,) 
 		<$> pure c
 		<*> getparser c
-		<*> parserGlobalOptions (cmdglobaloptions c)
+		<*> parserAnnexOptions (cmdannexoptions c)
 	synopsis n d = n ++ " - " ++ d
 	intro = mconcat $ concatMap (\l -> [H.text l, H.line])
 		(synopsis progname progdesc : commandList allcmds)
@@ -141,14 +141,14 @@ subCmdName argv = (name, args)
 		| "-" `isPrefixOf` a = findname as (a:c)
 		| otherwise = (Just a, reverse c ++ as)
 
--- | Note that the GlobalSetter must have already had its annexReadSetter
+-- | Note that the AnnexSetter must have already had its annexReadSetter
 -- applied before entering the Annex monad to run this; that cannot be
 -- changed while running in the Annex monad.
-prepRunCommand :: Command -> GlobalSetter -> Annex ()
-prepRunCommand cmd globalsetter = do
+prepRunCommand :: Command -> AnnexSetter -> Annex ()
+prepRunCommand cmd annexsetter = do
 	when (cmdnomessages cmd) $
 		Annex.setOutput QuietOutput
-	annexStateSetter globalsetter
+	annexStateSetter annexsetter
 	whenM (Annex.getRead Annex.debugenabled) $
 		enableDebugOutput
 
@@ -186,7 +186,7 @@ mkAddonCommand p subcommandname = Command
 	, cmdparamdesc = "[PARAMS]"
 	, cmdsection = SectionAddOn
 	, cmddesc = "addon command"
-	, cmdglobaloptions = []
+	, cmdannexoptions = []
 	, cmdinfomod = O.forwardOptions
 	, cmdparser = parse
 	, cmdnorepo = Just parse
diff --git a/CmdLine/AnnexSetter.hs b/CmdLine/AnnexSetter.hs
new file mode 100644
index 0000000000..234faf3abe
--- /dev/null
+++ b/CmdLine/AnnexSetter.hs
@@ -0,0 +1,36 @@
+{- git-annex options that are stored in Annex
+ -
+ - Copyright 2015-2021 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU AGPL version 3 or higher.
+  -}
+  
+module CmdLine.AnnexSetter where
+
+import Common
+import Annex
+import Types.DeferredParse
+
+import Options.Applicative
+
+setAnnexState :: Annex () -> AnnexSetter
+setAnnexState a = AnnexSetter a id
+
+setAnnexRead :: (AnnexRead -> AnnexRead) -> AnnexSetter
+setAnnexRead f = AnnexSetter (return ()) f
+
+annexFlag :: AnnexSetter -> Mod FlagFields AnnexSetter -> AnnexOption
+annexFlag = flag'
+
+annexOption :: (v -> AnnexSetter) -> Parser v -> AnnexOption
+annexOption mk parser = mk <$> parser
+
+-- | Combines a bunch of AnnexOptions together into a Parser
+-- that returns a AnnexSetter that can be used to set all the options that
+-- are enabled.
+parserAnnexOptions :: [AnnexOption] -> Parser AnnexSetter
+parserAnnexOptions [] = pure mempty
+parserAnnexOptions l = mconcat <$> many (foldl1 (<|>) l)
+
+applyAnnexReadSetter :: AnnexSetter -> (AnnexState, AnnexRead) -> (AnnexState, AnnexRead)
+applyAnnexReadSetter gs (st, rd) = (st, annexReadSetter gs rd)
diff --git a/CmdLine/GitAnnex.hs b/CmdLine/GitAnnex.hs
index 63889f0b44..08c2bd832b 100644
--- a/CmdLine/GitAnnex.hs
+++ b/CmdLine/GitAnnex.hs
@@ -130,7 +130,7 @@ import qualified Command.TestRemote
 import qualified Command.Benchmark
 
 cmds :: Parser TestOptions -> TestRunner -> MkBenchmarkGenerator -> [Command]
-cmds testoptparser testrunner mkbenchmarkgenerator = map addGitAnnexGlobalOptions $
+cmds testoptparser testrunner mkbenchmarkgenerator = map addGitAnnexCommonOptions $
 	[ Command.Help.cmd
 	, Command.Add.cmd
 	, Command.Get.cmd
@@ -245,8 +245,8 @@ cmds testoptparser testrunner mkbenchmarkgenerator = map addGitAnnexGlobalOption
 		mkbenchmarkgenerator $ cmds testoptparser testrunner (\_ _ -> return noop)
 	]
 
-addGitAnnexGlobalOptions :: Command -> Command
-addGitAnnexGlobalOptions c = c { cmdglobaloptions = gitAnnexGlobalOptions ++ cmdglobaloptions c }
+addGitAnnexCommonOptions :: Command -> Command
+addGitAnnexCommonOptions c = c { cmdannexoptions = gitAnnexCommonOptions ++ cmdannexoptions c }
 
 run :: Parser TestOptions -> TestRunner -> MkBenchmarkGenerator -> [String] -> IO ()
 run testoptparser testrunner mkbenchmarkgenerator args = go envmodes
diff --git a/CmdLine/GitAnnex/Options.hs b/CmdLine/GitAnnex/Options.hs
index d57305440b..96d0b1872d 100644
--- a/CmdLine/GitAnnex/Options.hs
+++ b/CmdLine/GitAnnex/Options.hs
@@ -34,66 +34,66 @@ import qualified Limit
 import qualified Limit.Wanted
 import CmdLine.Option
 import CmdLine.Usage
-import CmdLine.GlobalSetter
+import CmdLine.AnnexSetter
 import qualified Backend
 import qualified Types.Backend as Backend
 import Utility.HumanTime
 import Utility.DataUnits
 import Annex.Concurrent
 
--- Global options that are accepted by all git-annex sub-commands,
+-- Options that are accepted by all git-annex sub-commands,
 -- although not always used.
-gitAnnexGlobalOptions :: [GlobalOption]
-gitAnnexGlobalOptions = commonGlobalOptions ++
-	[ globalOption setnumcopies $ option auto
+gitAnnexCommonOptions :: [AnnexOption]
+gitAnnexCommonOptions = commonOptions ++
+	[ annexOption setnumcopies $ option auto
 		( long "numcopies" <> short 'N' <> metavar paramNumber
 		<> help "override desired number of copies"
 		<> hidden
 		)
-	, globalOption setmincopies $ option auto
+	, annexOption setmincopies $ option auto
 		( long "mincopies" <> short 'N' <> metavar paramNumber
 		<> help "override minimum number of copies"
 		<> hidden
 		)
-	, globalOption (setAnnexState . Remote.forceTrust Trusted) $ strOption
+	, annexOption (setAnnexState . Remote.forceTrust Trusted) $ strOption
 		( long "trust" <> metavar paramRemote
 		<> help "deprecated, does not override trust setting"
 		<> hidden
 		<> completeRemotes
 		)
-	, globalOption (setAnnexState . Remote.forceTrust SemiTrusted) $ strOption
+	, annexOption (setAnnexState . Remote.forceTrust SemiTrusted) $ strOption
 		( long "semitrust" <> metavar paramRemote
 		<> help "override trust setting back to default"
 		<> hidden
 		<> completeRemotes
 		)
-	, globalOption (setAnnexState . Remote.forceTrust UnTrusted) $ strOption
+	, annexOption (setAnnexState . Remote.forceTrust UnTrusted) $ strOption
 		( long "untrust" <> metavar paramRemote
 		<> help "override trust setting to untrusted"

(Diff truncated)
update
diff --git a/doc/bugs/move_from_ssh_when_not_present_displays_misleading.mdwn b/doc/bugs/move_from_ssh_when_not_present_displays_misleading.mdwn
index 9d2afa2a58..a98e7e6114 100644
--- a/doc/bugs/move_from_ssh_when_not_present_displays_misleading.mdwn
+++ b/doc/bugs/move_from_ssh_when_not_present_displays_misleading.mdwn
@@ -16,3 +16,20 @@ that was still present.)
 
 IIRC there was a clear message displayed before git-annex-shell p2pstdio 
 got implemented. --[[Joey]]
+
+> Still a problem with 10.20220625, although the messages are
+> different and perhaps improved a bit:
+> 
+> 	move a (from b...) 
+> 
+>	  Transfer failed
+	failed
+> 	(from b...) 
+> 
+>	  Transfer failed
+> 	failed
+> 	(from b...) 
+> 
+>	  Transfer failed
+> 	failed
+> 	move: 3 failed

close
diff --git a/doc/bugs/git-annex_sometimes_messes_up___126____47__.git-credentials.mdwn b/doc/bugs/git-annex_sometimes_messes_up___126____47__.git-credentials.mdwn
index 3b3b2b78b5..819fff495d 100644
--- a/doc/bugs/git-annex_sometimes_messes_up___126____47__.git-credentials.mdwn
+++ b/doc/bugs/git-annex_sometimes_messes_up___126____47__.git-credentials.mdwn
@@ -35,3 +35,5 @@ I don't have a transcript that would illustrate the problem, but the behaviour i
 
 It works beautifully, other than occasionally clobbering `~/.git-credentials` :-)
 
+> [[done]] as this is apparently not a git-annex bug and the submitter
+> never followed up with any info about what caused it. --[[Joey]]

fix parallel copy from/to a local git repo
Improve handling of parallelization with -J when copying content from/to a
git remote that is a local path.
Sponsored-by: Nicholas Golder-Manning on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index b030fcdd81..959e2a03f2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
 git-annex (10.20220625) UNRELEASED; urgency=medium
 
+  * Improve handling of parallelization with -J when copying content
+    from/to a git remote that is a local path.
   * stack.yaml: Updated to lts-19.13
 
  -- Joey Hess <id@joeyh.name>  Tue, 28 Jun 2022 14:49:17 -0400
diff --git a/Remote/Git.hs b/Remote/Git.hs
index 1456fe4e51..0af5a3eb98 100644
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -609,12 +609,12 @@ repairRemote r a = return $ do
 		ensureInitialized
 		a `finally` stopCoProcesses
 
-data LocalRemoteAnnex = LocalRemoteAnnex Git.Repo (MVar (Maybe (Annex.AnnexState, Annex.AnnexRead)))
+data LocalRemoteAnnex = LocalRemoteAnnex Git.Repo (MVar [(Annex.AnnexState, Annex.AnnexRead)])
 
 {- This can safely be called on a Repo that is not local, but of course
  - onLocal will not work if used with the result. -}
 mkLocalRemoteAnnex :: Git.Repo -> Annex (LocalRemoteAnnex)
-mkLocalRemoteAnnex repo = LocalRemoteAnnex repo <$> liftIO (newMVar Nothing)
+mkLocalRemoteAnnex repo = LocalRemoteAnnex repo <$> liftIO (newMVar [])
 
 {- Runs an action from the perspective of a local remote.
  -
@@ -645,10 +645,13 @@ newLocal repo = do
 
 onLocal' :: LocalRemoteAnnex -> Annex a -> Annex a
 onLocal' (LocalRemoteAnnex repo mv) a = liftIO (takeMVar mv) >>= \case
-	Nothing -> do
+	[] -> do
+		liftIO $ putMVar mv []
 		v <- newLocal repo
 		go (v, ensureInitialized >> a)
-	Just v -> go (v, a)
+	(v:rest) -> do
+		liftIO $ putMVar mv rest
+		go (v, a)
   where
 	go ((st, rd), a') = do
 		curro <- Annex.getState Annex.output
@@ -657,7 +660,9 @@ onLocal' (LocalRemoteAnnex repo mv) a = liftIO (takeMVar mv) >>= \case
 		(ret, (st', _rd)) <- liftIO $ act `onException` cache (st, rd)
 		liftIO $ cache (st', rd)
 		return ret
-	cache = putMVar mv . Just
+	cache v = do
+		l <- takeMVar mv
+		putMVar mv (v:l)
 
 {- Faster variant of onLocal.
  -
diff --git a/doc/bugs/copy_-J_--to_local_git_remote_does_not_run_concurrently.mdwn b/doc/bugs/copy_-J_--to_local_git_remote_does_not_run_concurrently.mdwn
index 06146e938e..b3ead46927 100644
--- a/doc/bugs/copy_-J_--to_local_git_remote_does_not_run_concurrently.mdwn
+++ b/doc/bugs/copy_-J_--to_local_git_remote_does_not_run_concurrently.mdwn
@@ -24,3 +24,13 @@ would make sense to.)
 
 git-annex version: 8.20201103 --[[Joey]]
 
+> Reproduced with 10.20220625. 3 100 mb files and copy to a local git repo.
+> Also, `copy --from` and `get` do have the problem, at least they do now.
+> --[[Joey]]
+
+> The problem is that Remote.Git.onLocal uses a MVar for the remote state,
+> and the copy runs in that. So while one copy is running, the rest block.
+> So fixing this will need a pool of local remote states.
+> --[[Joey]]
+
+>> [[fixed|done]] --[[Joey]]

close mistaken bug report
diff --git a/doc/bugs/no_way_to_disable_repository_auto_upgrade.mdwn b/doc/bugs/no_way_to_disable_repository_auto_upgrade.mdwn
index e0423654b1..0e2db6464b 100644
--- a/doc/bugs/no_way_to_disable_repository_auto_upgrade.mdwn
+++ b/doc/bugs/no_way_to_disable_repository_auto_upgrade.mdwn
@@ -46,3 +46,4 @@ local repository version: 8
 I am using it since v5 and only v5 for repos like more 100k filecount and size ~800GB. Worked perfectly on Linux desktop, android, phone, tab, raspberry. The greatest sync tool I ever had. I am a little bit worried about the upgrade to v8 from v5 now :) 
 
 
+> seems like this was a mistake not a bug, so [[done]] --[[Joey]]

wontfix
diff --git a/doc/bugs/poor_choice_of_name_for_adjusted_branches.mdwn b/doc/bugs/poor_choice_of_name_for_adjusted_branches.mdwn
index 17a3ac14f6..ea9b009aa3 100644
--- a/doc/bugs/poor_choice_of_name_for_adjusted_branches.mdwn
+++ b/doc/bugs/poor_choice_of_name_for_adjusted_branches.mdwn
@@ -22,3 +22,5 @@ Result:
 The problem is easy to fix, I think, by choosing a different naming convention. I'm not sure how to deal with legacy-named branches going forward, though.
 
 Thank you very much for making git-annex available, much appreciated.
+
+> I disagree that this needs fixing, so [[wontfix]] --[[Joey]]

retitle
diff --git a/doc/bugs/mimeencoding_detection_is_not_working.mdwn b/doc/bugs/mimeencoding_detection_is_not_working.mdwn
index 1944fc0d13..e0f67d779a 100644
--- a/doc/bugs/mimeencoding_detection_is_not_working.mdwn
+++ b/doc/bugs/mimeencoding_detection_is_not_working.mdwn
@@ -39,3 +39,5 @@ Windows 11
 ### 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 just started using it and I love it. I like it more than git LFS
+
+[[!meta title="mimeencoding detection is not working on windows"]]

fix my comment format
diff --git a/doc/bugs/mimeencoding_detection_is_not_working/comment_1_dbcdb6ae6fb1d06f4d5cf27cba4c69be._comment b/doc/bugs/mimeencoding_detection_is_not_working/comment_1_dbcdb6ae6fb1d06f4d5cf27cba4c69be._comment
index 68257631cd..1e6e1a153d 100644
--- a/doc/bugs/mimeencoding_detection_is_not_working/comment_1_dbcdb6ae6fb1d06f4d5cf27cba4c69be._comment
+++ b/doc/bugs/mimeencoding_detection_is_not_working/comment_1_dbcdb6ae6fb1d06f4d5cf27cba4c69be._comment
@@ -1,4 +1,4 @@
-[!comment format=mdwn
+[[!comment format=mdwn
  username="joey"
  subject="""comment 1"""
  date="2022-05-05T14:50:13Z"

comment
diff --git a/doc/bugs/error__58___git-annex_filter-process_died_of_signal_15/comment_1_02da33e41e4f4471d1ae08e9fede229a._comment b/doc/bugs/error__58___git-annex_filter-process_died_of_signal_15/comment_1_02da33e41e4f4471d1ae08e9fede229a._comment
new file mode 100644
index 0000000000..df76b6d693
--- /dev/null
+++ b/doc/bugs/error__58___git-annex_filter-process_died_of_signal_15/comment_1_02da33e41e4f4471d1ae08e9fede229a._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-29T16:13:25Z"
+ content="""
+How big a commit are we talking here, in terms of number of files
+added/changed?
+
+I've tried with 10000 modified files and not seen a problem IIRC.
+
+Also, signal 15 is SIGTERM, which would be a strange signal to happen
+unless it were ctrl-c'd.
+
+Final weird thing is "fatal: the remote end hung up unexpectedly", since
+that would indicate a git fetch was running, and git-annex certianly does
+not fetch..
+"""]]

final readonly values moves to AnnexRead
At this point I've checked all AnnexState values and these were all that
remained that could move.
Pity that Annex.repo can't move, but it gets modified sometimes..
A couple of AnnexState values are set by options and could be AnnexRead,
but happen to use Annex when being set.
Sponsored-by: Max Thoursie on Patreon
diff --git a/Annex.hs b/Annex.hs
index e4fbd51152..81ecb1a833 100644
--- a/Annex.hs
+++ b/Annex.hs
@@ -126,6 +126,8 @@ data AnnexRead = AnnexRead
 	, forcenumcopies :: Maybe NumCopies
 	, forcemincopies :: Maybe MinCopies
 	, forcebackend :: Maybe String
+	, useragent :: Maybe String
+	, desktopnotify :: DesktopNotify
 	}
 
 newAnnexRead :: GitConfig -> IO AnnexRead
@@ -152,6 +154,8 @@ newAnnexRead c = do
 		, forcebackend = Nothing
 		, forcenumcopies = Nothing
 		, forcemincopies = Nothing
+		, useragent = Nothing
+		, desktopnotify = mempty
 		}
 
 -- Values that can change while running an Annex action.
@@ -190,14 +194,12 @@ data AnnexState = AnnexState
 	, fields :: M.Map String String
 	, cleanupactions :: M.Map CleanupAction (Annex ())
 	, sentinalstatus :: Maybe SentinalStatus
-	, useragent :: Maybe String
 	, errcounter :: Integer
 	, skippedfiles :: Bool
 	, adjustedbranchrefreshcounter :: Integer
 	, unusedkeys :: Maybe (S.Set Key)
 	, tempurls :: M.Map Key URLString
 	, existinghooks :: M.Map Git.Hook.Hook Bool
-	, desktopnotify :: DesktopNotify
 	, workers :: Maybe (TMVar (WorkerPool (AnnexState, AnnexRead)))
 	, cachedcurrentbranch :: (Maybe (Maybe Git.Branch, Maybe Adjustment))
 	, cachedgitenv :: Maybe (AltIndexFile, FilePath, [(String, String)])
@@ -245,14 +247,12 @@ newAnnexState c r = do
 		, fields = M.empty
 		, cleanupactions = M.empty
 		, sentinalstatus = Nothing
-		, useragent = Nothing
 		, errcounter = 0
 		, skippedfiles = False
 		, adjustedbranchrefreshcounter = 0
 		, unusedkeys = Nothing
 		, tempurls = M.empty
 		, existinghooks = M.empty
-		, desktopnotify = mempty
 		, workers = Nothing
 		, cachedcurrentbranch = Nothing
 		, cachedgitenv = Nothing
diff --git a/Annex/Url.hs b/Annex/Url.hs
index 303e98e798..0d74fcb20b 100644
--- a/Annex/Url.hs
+++ b/Annex/Url.hs
@@ -48,7 +48,7 @@ defaultUserAgent :: U.UserAgent
 defaultUserAgent = "git-annex/" ++ BuildInfo.packageversion
 
 getUserAgent :: Annex U.UserAgent
-getUserAgent = Annex.getState $ 
+getUserAgent = Annex.getRead $ 
 	fromMaybe defaultUserAgent . Annex.useragent
 
 getUrlOptions :: Annex U.UrlOptions
diff --git a/CmdLine/GitAnnex/Options.hs b/CmdLine/GitAnnex/Options.hs
index 6526d3632d..d57305440b 100644
--- a/CmdLine/GitAnnex/Options.hs
+++ b/CmdLine/GitAnnex/Options.hs
@@ -78,7 +78,7 @@ gitAnnexGlobalOptions = commonGlobalOptions ++
 		<> help "override git configuration setting"
 		<> hidden
 		)
-	, globalOption (setAnnexState . setuseragent) $ strOption
+	, globalOption setuseragent $ strOption
 		( long "user-agent" <> metavar paramName
 		<> help "override default User-Agent"
 		<> hidden
@@ -88,12 +88,12 @@ gitAnnexGlobalOptions = commonGlobalOptions ++
 		<> help "deprecated, does not trust Amazon Glacier inventory"
 		<> hidden
 		)
-	, globalFlag (setAnnexState $ setdesktopnotify mkNotifyFinish)
+	, globalFlag (setdesktopnotify mkNotifyFinish)
 		( long "notify-finish"
 		<> help "show desktop notification after transfer finishes"
 		<> hidden
 		)
-	, globalFlag (setAnnexState $ setdesktopnotify mkNotifyStart)
+	, globalFlag (setdesktopnotify mkNotifyStart)
 		( long "notify-start"
 		<> help "show desktop notification after transfer starts"
 		<> hidden
@@ -102,9 +102,9 @@ gitAnnexGlobalOptions = commonGlobalOptions ++
   where
 	setnumcopies n = setAnnexRead $ \rd -> rd { Annex.forcenumcopies = Just $ configuredNumCopies n }
 	setmincopies n = setAnnexRead $ \rd -> rd { Annex.forcemincopies = Just $ configuredMinCopies n }
-	setuseragent v = Annex.changeState $ \s -> s { Annex.useragent = Just v }
+	setuseragent v = setAnnexRead $ \rd -> rd { Annex.useragent = Just v }
+	setdesktopnotify v = setAnnexRead $ \rd -> rd { Annex.desktopnotify = Annex.desktopnotify rd <> v }
 	setgitconfig v = Annex.addGitConfigOverride v
-	setdesktopnotify v = Annex.changeState $ \s -> s { Annex.desktopnotify = Annex.desktopnotify s <> v }
 
 {- Parser that accepts all non-option params. -}
 cmdParams :: CmdParamsDesc -> Parser CmdParams
diff --git a/doc/todo/move_readonly_values_to_AnnexRead.mdwn b/doc/todo/move_readonly_values_to_AnnexRead.mdwn
index 70c94f00e7..8aabd70b37 100644
--- a/doc/todo/move_readonly_values_to_AnnexRead.mdwn
+++ b/doc/todo/move_readonly_values_to_AnnexRead.mdwn
@@ -4,4 +4,5 @@ anything that never needs to be modified while git-annex is running can be
 moved to AnnexRead for a performance win and also to make clean how it's
 used. --[[Joey]]
 
-Many things have been moved, but there are certainly others that can be.
+> [[done]]; all AnnexState fields have been checked and all that can be
+> made readonly are. --[[Joey]]

move several readonly values to AnnexRead
This improves performance to a small extent in several places.
Sponsored-by: Tobias Ammann on Patreon
diff --git a/Annex.hs b/Annex.hs
index 0856e7fc05..abe947444b 100644
--- a/Annex.hs
+++ b/Annex.hs
@@ -123,6 +123,11 @@ data AnnexRead = AnnexRead
 	, debugenabled :: Bool
 	, debugselector :: DebugSelector
 	, ciphers :: TMVar (M.Map StorableCipher Cipher)
+	, fast :: Bool
+	, force :: Bool
+	, forcenumcopies :: Maybe NumCopies
+	, forcemincopies :: Maybe MinCopies
+	, forcebackend :: Maybe String
 	}
 
 newAnnexRead :: GitConfig -> IO AnnexRead
@@ -144,6 +149,11 @@ newAnnexRead c = do
 		, debugenabled = annexDebug c
 		, debugselector = debugSelectorFromGitConfig c
 		, ciphers = cm
+		, fast = False
+		, force = False
+		, forcebackend = Nothing
+		, forcenumcopies = Nothing
+		, forcemincopies = Nothing
 		}
 
 -- Values that can change while running an Annex action.
@@ -159,8 +169,6 @@ data AnnexState = AnnexState
 	, remotes :: [Types.Remote.RemoteA Annex]
 	, output :: MessageState
 	, concurrency :: ConcurrencySetting
-	, force :: Bool
-	, fast :: Bool
 	, daemon :: Bool
 	, branchstate :: BranchState
 	, repoqueue :: Maybe (Git.Queue.Queue Annex)
@@ -168,11 +176,8 @@ data AnnexState = AnnexState
 	, hashobjecthandle :: Maybe HashObjectHandle
 	, checkattrhandle :: Maybe (ResourcePool CheckAttrHandle)
 	, checkignorehandle :: Maybe (ResourcePool CheckIgnoreHandle)
-	, forcebackend :: Maybe String
 	, globalnumcopies :: Maybe NumCopies
 	, globalmincopies :: Maybe MinCopies
-	, forcenumcopies :: Maybe NumCopies
-	, forcemincopies :: Maybe MinCopies
 	, limit :: ExpandableMatcher Annex
 	, timelimit :: Maybe (Duration, POSIXTime)
 	, sizelimit :: Maybe (TVar Integer)
@@ -220,8 +225,6 @@ newAnnexState c r = do
 		, remotes = []
 		, output = o
 		, concurrency = ConcurrencyCmdLine NonConcurrent
-		, force = False
-		, fast = False
 		, daemon = False
 		, branchstate = startBranchState
 		, repoqueue = Nothing
@@ -229,11 +232,8 @@ newAnnexState c r = do
 		, hashobjecthandle = Nothing
 		, checkattrhandle = Nothing
 		, checkignorehandle = Nothing
-		, forcebackend = Nothing
 		, globalnumcopies = Nothing
 		, globalmincopies = Nothing
-		, forcenumcopies = Nothing
-		, forcemincopies = Nothing
 		, limit = BuildingMatcher []
 		, timelimit = Nothing
 		, sizelimit = Nothing
diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs
index 27253016e7..4a55d4647a 100644
--- a/Annex/AdjustedBranch.hs
+++ b/Annex/AdjustedBranch.hs
@@ -207,7 +207,7 @@ enterAdjustedBranch adj = inRepo Git.Branch.current >>= \case
 	go currbranch = do
 		let origbranch = fromAdjustedBranch currbranch
 		let adjbranch = adjBranch $ originalToAdjusted origbranch adj
-		ifM (inRepo (Git.Ref.exists adjbranch) <&&> (not <$> Annex.getState Annex.force))
+		ifM (inRepo (Git.Ref.exists adjbranch) <&&> (not <$> Annex.getRead Annex.force))
 			( do
 				mapM_ (warning . unwords)
 					[ [ "adjusted branch"
diff --git a/Annex/CheckIgnore.hs b/Annex/CheckIgnore.hs
index 42e635b667..d3c03f210a 100644
--- a/Annex/CheckIgnore.hs
+++ b/Annex/CheckIgnore.hs
@@ -25,7 +25,7 @@ newtype CheckGitIgnore = CheckGitIgnore Bool
 checkIgnored :: CheckGitIgnore -> RawFilePath -> Annex Bool
 checkIgnored (CheckGitIgnore False) _ = pure False
 checkIgnored (CheckGitIgnore True) file =
-	ifM (Annex.getState Annex.force)
+	ifM (Annex.getRead Annex.force)
 		( pure False
 		, withCheckIgnoreHandle $ \h -> liftIO $ Git.checkIgnored h file
 		)
diff --git a/Annex/Content/LowLevel.hs b/Annex/Content/LowLevel.hs
index c686e462e2..0271fa65de 100644
--- a/Annex/Content/LowLevel.hs
+++ b/Annex/Content/LowLevel.hs
@@ -107,7 +107,7 @@ checkDiskSpace destdir key = checkDiskSpace' (fromMaybe 1 (fromKey keySize key))
 {- Allows specifying the size of the key, if it's known, which is useful
  - as not all keys know their size. -}
 checkDiskSpace' :: Integer -> Maybe RawFilePath -> Key -> Integer -> Bool -> Annex Bool
-checkDiskSpace' need destdir key alreadythere samefilesystem = ifM (Annex.getState Annex.force)
+checkDiskSpace' need destdir key alreadythere samefilesystem = ifM (Annex.getRead Annex.force)
 	( return True
 	, do
 		-- We can't get inprogress and free at the same
diff --git a/Annex/Ingest.hs b/Annex/Ingest.hs
index 6e5224b484..89dc8aceaa 100644
--- a/Annex/Ingest.hs
+++ b/Annex/Ingest.hs
@@ -325,7 +325,7 @@ addSymlink file key mcache = do
  - checked in, CheckGitIgnore True has no effect.
  -}
 gitAddParams :: CheckGitIgnore -> Annex [CommandParam]
-gitAddParams (CheckGitIgnore True) = ifM (Annex.getState Annex.force)
+gitAddParams (CheckGitIgnore True) = ifM (Annex.getRead Annex.force)
 	( return [Param "-f"]
 	, return []
 	)
diff --git a/Annex/NumCopies.hs b/Annex/NumCopies.hs
index d460856465..1c4fed1fcb 100644
--- a/Annex/NumCopies.hs
+++ b/Annex/NumCopies.hs
@@ -57,11 +57,11 @@ deprecatedNumCopies = annexNumCopies <$> Annex.getGitConfig
 
 {- Value forced on the command line by --numcopies. -}
 getForcedNumCopies :: Annex (Maybe NumCopies)
-getForcedNumCopies = Annex.getState Annex.forcenumcopies
+getForcedNumCopies = Annex.getRead Annex.forcenumcopies
 
 {- Value forced on the command line by --mincopies. -}
 getForcedMinCopies :: Annex (Maybe MinCopies)
-getForcedMinCopies = Annex.getState Annex.forcemincopies
+getForcedMinCopies = Annex.getRead Annex.forcemincopies
 
 {- NumCopies value from any of the non-.gitattributes configuration
  - sources. -}
diff --git a/Annex/YoutubeDl.hs b/Annex/YoutubeDl.hs
index 659a9adc91..e466f01eba 100644
--- a/Annex/YoutubeDl.hs
+++ b/Annex/YoutubeDl.hs
@@ -118,7 +118,7 @@ youtubeDl' url workdir p uo
 -- and any files in the workdir that it may have partially downloaded
 -- before.
 youtubeDlMaxSize :: FilePath -> Annex (Either String [CommandParam])
-youtubeDlMaxSize workdir = ifM (Annex.getState Annex.force)
+youtubeDlMaxSize workdir = ifM (Annex.getRead Annex.force)
 	( return $ Right []
 	, liftIO (getDiskFree workdir) >>= \case
 		Just have -> do
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
index 59f6922e3a..8a668f0db3 100644
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -57,7 +57,7 @@ checkCanWatch
 	| canWatch = do
 #ifndef mingw32_HOST_OS
 		liftIO Lsof.setup
-		unlessM (liftIO (inSearchPath "lsof") <||> Annex.getState Annex.force)
+		unlessM (liftIO (inSearchPath "lsof") <||> Annex.getRead Annex.force)
 			needLsof
 #else
 		noop
diff --git a/Backend.hs b/Backend.hs
index b85658e599..8e49bfce41 100644
--- a/Backend.hs
+++ b/Backend.hs
@@ -44,7 +44,7 @@ defaultBackend = maybe cache return =<< Annex.getState Annex.backend
   where
 	cache = do
 		n <- maybe (annexBackend <$> Annex.getGitConfig) (return . Just)
-			=<< Annex.getState Annex.forcebackend
+			=<< Annex.getRead Annex.forcebackend
 		b <- case n of
 			Just name | valid name -> lookupname name
 			_ -> pure (Prelude.head builtinList)
@@ -79,7 +79,7 @@ unknownBackendVarietyMessage v =
  - That can be configured on a per-file basis in the gitattributes file,
  - or forced with --backend. -}
 chooseBackend :: RawFilePath -> Annex (Maybe Backend)
-chooseBackend f = Annex.getState Annex.forcebackend >>= go
+chooseBackend f = Annex.getRead Annex.forcebackend >>= go
   where
 	go Nothing = maybeLookupBackendVariety . parseKeyVariety . encodeBS
 		=<< checkAttr "annex.backend" f
diff --git a/Backend/Hash.hs b/Backend/Hash.hs
index 4ffbcbbdee..550d8fc6c0 100644
--- a/Backend/Hash.hs
+++ b/Backend/Hash.hs
@@ -120,7 +120,7 @@ keyValueE hash source meterupdate =
 
 checkKeyChecksum :: Hash -> Key -> RawFilePath -> Annex Bool
 checkKeyChecksum hash key file = catchIOErrorType HardwareFault hwfault $ do
-	fast <- Annex.getState Annex.fast
+	fast <- Annex.getRead Annex.fast
 	exists <- liftIO $ R.doesPathExist file
 	case (exists, fast) of

(Diff truncated)
comment
diff --git a/doc/todo/add_--json-progress_to_fsck_--json/comment_2_2ce9c3c35ea5c4033bfa42e2f3365a64._comment b/doc/todo/add_--json-progress_to_fsck_--json/comment_2_2ce9c3c35ea5c4033bfa42e2f3365a64._comment
new file mode 100644
index 0000000000..7fb6967d69
--- /dev/null
+++ b/doc/todo/add_--json-progress_to_fsck_--json/comment_2_2ce9c3c35ea5c4033bfa42e2f3365a64._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2022-06-28T17:00:33Z"
+ content="""
+It seems problimatic to have json progress for both downloading the file
+and for checksumming it. I don't know how that json would look.
+
+In the meantime, though, git-annex has changed to checksum files while they
+are being downloaded from most remotes, and when it can do that, fsck
+--from avoids the separate checksumming step.
+
+While some special remotes do not support that, it's a very limited list:
+only bittorrent on linux, and adb, external, gcrypt, hook, and rsync on
+other OSs.
+
+This makes me think that complicating the json output to display two
+different kinds of progress is probably not worth the complication.
+"""]]

close
diff --git a/doc/todo/Fsck_remote_files_in-flight.mdwn b/doc/todo/Fsck_remote_files_in-flight.mdwn
index 1b88fb0b22..93bd87f2a0 100644
--- a/doc/todo/Fsck_remote_files_in-flight.mdwn
+++ b/doc/todo/Fsck_remote_files_in-flight.mdwn
@@ -3,3 +3,5 @@ When fsck'ing a remote repo, files seem to be copied from the remote to a local
 This is very time-inefficient and wastes precious SSD erase cycles which is especially problematic in the case of special remotes because they can only be fsck'd "remotely" (AFAIK).
 
 Instead, remote files should be directly piped into an in-memory checksum function and never written to disk on the machine performing the fsck.
+
+> [[done]] per my comments --[[Joey]] 

comment and retitle
diff --git a/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
index e4d8c5d7c8..7dbede3634 100644
--- a/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
+++ b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
@@ -44,3 +44,5 @@ Test image used in this bug report: https://wallpapercave.com/wp/wp8215486.jpg
 
 ### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
 I'm really loving git-annex. I've been reading up and I'm more than halfway through the devlog and I'm hoping to use git-annex for a lot and a long while. Mostly, I'm expecting to use it on Linux systems, but I'd love to be able to rely on it on my (Windows) work laptop as well. Thanks for looking at my report!
+
+[[!meta title="assistant on windows misses copy+rename of file"]]
diff --git a/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_1_c6e8e3e76185b6ca25067c2a7e5cd8fc._comment b/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_1_c6e8e3e76185b6ca25067c2a7e5cd8fc._comment
new file mode 100644
index 0000000000..4c25df1a09
--- /dev/null
+++ b/doc/bugs/copy+rename_of_file_on_windows_breaks_original/comment_1_c6e8e3e76185b6ca25067c2a7e5cd8fc._comment
@@ -0,0 +1,19 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-28T16:29:03Z"
+ content="""
+Well, this appears to be a case where the assistant somehow misses the
+rename of the file, so it has not gotten around to annexing it.
+
+You can get the same error message when not using the assistant, eg:
+
+	# touch foo
+	# git-annex info foo
+	fatal: Not a valid object name foo
+	info foo (not a directory or an annexed file or a treeish or a remote or a uuid) failed
+
+I guess that `git-annex add` would resolve your problem.
+The assistant should also notice the file the next time it starts up,
+and add it the same as it would any new file.
+"""]]

comment
diff --git a/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_6_8b18048e0ba4341ecef60787855cdac3._comment b/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_6_8b18048e0ba4341ecef60787855cdac3._comment
new file mode 100644
index 0000000000..c85f441df7
--- /dev/null
+++ b/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_6_8b18048e0ba4341ecef60787855cdac3._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2022-06-28T16:25:34Z"
+ content="""
+I'm sorry this caused you that trouble. But 6.20161212 is really extremely
+old, and supporting the quirks of old versions of git-annex forever in
+current versions is not something I want to take on.
+"""]]

comments
diff --git a/doc/forum/How_to_handle_cold_storage__63__/comment_1_ff49a65d4adaff3cf3902c2850227ea2._comment b/doc/forum/How_to_handle_cold_storage__63__/comment_1_ff49a65d4adaff3cf3902c2850227ea2._comment
new file mode 100644
index 0000000000..2c054ba4af
--- /dev/null
+++ b/doc/forum/How_to_handle_cold_storage__63__/comment_1_ff49a65d4adaff3cf3902c2850227ea2._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-28T16:08:38Z"
+ content="""
+This is what [[todo/do_not_count_trusted_repos_toward_mincopies]] is kind
+of about; setting a repo to trusted right now is a promise that you won't
+delete data from it, or will be careful when you do to sync that fact to
+everywhere. If that todo got implemented, you could set all the offline
+drives to trusted and git-annex drop would only need to verify 1 copy is
+present on an online drive.
+
+What I do right now is use trusted for cold storage and never drop
+anything from it except in cases where I want to get rid of all copies of
+some file.
+"""]]
diff --git a/doc/todo/do_not_count_trusted_repos_toward_mincopies/comment_2_058f521451ff38d2072af6c944143ab1._comment b/doc/todo/do_not_count_trusted_repos_toward_mincopies/comment_2_058f521451ff38d2072af6c944143ab1._comment
new file mode 100644
index 0000000000..b2d25c6abd
--- /dev/null
+++ b/doc/todo/do_not_count_trusted_repos_toward_mincopies/comment_2_058f521451ff38d2072af6c944143ab1._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2022-06-28T16:13:05Z"
+ content="""
+Perhaps it would be better to leave trusted working as it does now, and
+introduce a new intermediate trust level. It could be called eg, "offline".
+
+An offline repo would count toward numcopies when not available, but 
+would only count toward mincopies if it happened to be available at the
+time.
+
+It might then be that there's no real need for the less safe trusted,
+and users could be polled and it deprecated if so.
+
+(This might also be a good opportunity to improve the "semitrust" name,
+which would not make much sense if the list was untrust, semitrust,
+offline, trust. "normal" perhaps. Would need to still accept the old name
+for back-compat of course.)
+"""]]

add bug
diff --git a/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
new file mode 100644
index 0000000000..e4d8c5d7c8
--- /dev/null
+++ b/doc/bugs/copy+rename_of_file_on_windows_breaks_original.mdwn
@@ -0,0 +1,46 @@
+### Please describe the problem.
+Copying and renaming a file "breaks" the original. I'm not using git-annex long enough to use the correct terminology here. "Broken" means "git annex status" reports "fatal: Not a valid object name".
+
+### What steps will reproduce the problem?
+Using the Assistant on Windows, create a fresh repository. Place a file called starship.jpg in the root. Make two directories, called "pic" and "space". Using Explorer, move the picture into "space". Move "space" into "pic". Go into "pic>space". Copy the image and paste it to the root of the repo. Rename the image "wallpaper.jpg". 
+
+
+[[!format sh """
+$ git annex info wallpaper.jpg
+file: wallpaper.jpg
+size: 225.75 kilobytes
+key: SHA256E-s225747--e5cba8967f4bea1a971e7405169056852beb3efbbf7fa2d3758c61ba769296c1.jpg
+present: true
+
+$ git annex info pic/space/starship.jpg
+fatal: Not a valid object name pic/space/starship.jpg
+info pic/space/starship.jpg (not a directory or an annexed file or a treeish or a remote or a uuid) failed
+
+$ git annex status
+D .\space/starship.jpg
+? .\pic/space/starship.jpg
+"""]]
+
+### What version of git-annex are you using? On what operating system?
+[[!format sh """
+$ git annex version
+git-annex version: 10.20220526-gc6b112108
+build flags: Assistant Webapp Pairing TorrentParser MagicMime Feeds Testsuite S3 WebDAV
+dependency versions: aws-0.22 bloomfilter-2.0.1.0 cryptonite-0.29 DAV-1.3.4 feed-1.3.2.0 ghc-8.10.7 http-client-0.7.9 persistent-sqlite-2.13.0.3 torrent-10000.1.1 uuid-1.3.15 yesod-1.6.1.2
+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 9 10
+upgrade supported from repository versions: 2 3 4 5 6 7 8 9 10
+local repository version: 8
+"""]]
+
+OS: Windows 10 Pro, 21H1, 19043.1766
+
+### Please provide any additional information below.
+There's no .git/annex/daemon.log in my repo. Probably because after encountering this issue and reproducing it for this report, I made a new repo, and it seems that the Assistant's logs ended up in the original repo's logs.
+
+Test image used in this bug report: https://wallpapercave.com/wp/wp8215486.jpg
+
+### Have you had any luck using git-annex before? (Sometimes we get tired of reading bug reports all day and a lil' positive end note does wonders)
+I'm really loving git-annex. I've been reading up and I'm more than halfway through the devlog and I'm hoping to use git-annex for a lot and a long while. Mostly, I'm expecting to use it on Linux systems, but I'd love to be able to rely on it on my (Windows) work laptop as well. Thanks for looking at my report!

Added a comment: Same problem
diff --git a/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_5_a769702d5d203eb435677bbd423857a6._comment b/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_5_a769702d5d203eb435677bbd423857a6._comment
new file mode 100644
index 0000000000..781fce7a2f
--- /dev/null
+++ b/doc/bugs/Failed_move__47__get_over_ssh_in_new_version/comment_5_a769702d5d203eb435677bbd423857a6._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="Soxofaan"
+ avatar="http://cdn.libravatar.org/avatar/2083c64f7bc36bfd8809f15f0036ade1"
+ subject="Same problem"
+ date="2022-06-26T21:53:39Z"
+ content="""
+Same problem here: on my synology NAS I'm stuck at git-annex version 6.20161212  (later releases fail to run) and I recently (blindly) updated git-annex on my macbook with homebrew to 10.20220624 (I don't remember what previous version was installed).
+
+Because upgrading git-annex at the NAS-side is not an option at the moment, I had to downgrade git-annex locally to 8.20211011. This was quite a challenge unfortunately, and I documented it [on my blog](https://www.stefaanlippens.net/git-annex-homebrew-version-woes.html) (in case anybody else might face the same uphill battle)
+"""]]

Added a comment: Still useful
diff --git a/doc/todo/Specify_maximum_usable_space_per_remote/comment_6_bef44c8f571f2be177d6ee949c721a6a._comment b/doc/todo/Specify_maximum_usable_space_per_remote/comment_6_bef44c8f571f2be177d6ee949c721a6a._comment
new file mode 100644
index 0000000000..dc3faefeb5
--- /dev/null
+++ b/doc/todo/Specify_maximum_usable_space_per_remote/comment_6_bef44c8f571f2be177d6ee949c721a6a._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="Still useful"
+ date="2022-06-25T16:36:38Z"
+ content="""
+Implementing a hard limit concurrently isn't feasible but only providing a best-effort target size would still be useful.
+
+It should fully work in all non-concurrent use-cases which are the majority in my case.
+
+This would also open up the possibility of allowing me to know which drive I need to plug in when there is new data to be backed up because git-annex would know how much a repo is able to store and how much it currently holds.  
+This obviously isn't perfect either (there might be other data on that drive; other annex repos or entirely untracked data for example) but it'd still be good enough to make a manual decision on for me.
+"""]]

diff --git a/doc/forum/How_to_handle_cold_storage__63__.mdwn b/doc/forum/How_to_handle_cold_storage__63__.mdwn
new file mode 100644
index 0000000000..79ea77328a
--- /dev/null
+++ b/doc/forum/How_to_handle_cold_storage__63__.mdwn
@@ -0,0 +1,7 @@
+In my storage setup, I've got a NAS with a copy available to network devices, a copy among a few (mostly) offline USB drives near the NAS for backup and then a bunch of loose drives that are far away from the NAS for off-site backup which also hold a copy.
+
+Most of this is cold storage.
+
+In order to drop content from remotes (e.g. after watching a Movie on the laptop that as been copies from the NAS), I'd need physical access to drives out of the latter two categories in addition to the NAS and directly connect them to the laptop. This isn't feasible of course.
+
+I've resorted to setting all the cold drives as trusted but I feel like there should be a better way to go about this, given how well git-annex lends itself to managing cold storage (The Archivist use-case).

add news item for git-annex 10.20220624
diff --git a/doc/news/version_10.20220624.mdwn b/doc/news/version_10.20220624.mdwn
new file mode 100644
index 0000000000..f2a6ba5522
--- /dev/null
+++ b/doc/news/version_10.20220624.mdwn
@@ -0,0 +1,23 @@
+git-annex 10.20220624 released with [[!toggle text="these changes"]]
+[[!toggleable text="""  * init: Added --no-autoenable option.
+  * info: Added --autoenable option.
+  * initremote: Improve handling of type=git special remotes.
+    The location value no longer needs to match the url of an existing
+    git remote, and locations not using ssh:// will work now, including
+    both paths and host:/path
+  * Fix retrival of an empty file that is stored in a special remote with
+    chunking enabled.
+    (Fixes a reversion in 8.20201103)
+  * move: Improve resuming a move that succeeded in transferring the
+    content, but where dropping failed due to eg a network problem,
+    in cases where numcopies checks prevented the resumed
+    move from dropping the object from the source repository.
+  * add, fix, lock, rekey: When several files were being processed,
+    replacing an annex symlink of a file that was already processed
+    with a new large file could sometimes cause that large file to be
+    added to git. These races have been fixed.
+  * add: Also fix a similar race that could cause a large file be added
+    to git when a small file was modified or overwritten while it was
+    being added.
+  * add --batch: Fix handling of a file that is skipped due to being
+    gitignored."""]]
\ No newline at end of file

remove
diff --git a/doc/news/version_10.20220623.mdwn b/doc/news/version_10.20220623.mdwn
deleted file mode 100644
index 1abba1a087..0000000000
--- a/doc/news/version_10.20220623.mdwn
+++ /dev/null
@@ -1,23 +0,0 @@
-git-annex 10.20220623 released with [[!toggle text="these changes"]]
-[[!toggleable text="""  * init: Added --no-autoenable option.
-  * info: Added --autoenable option.
-  * initremote: Improve handling of type=git special remotes.
-    The location value no longer needs to match the url of an existing
-    git remote, and locations not using ssh:// will work now, including
-    both paths and host:/path
-  * Fix retrival of an empty file that is stored in a special remote with
-    chunking enabled.
-    (Fixes a reversion in 8.20201103)
-  * move: Improve resuming a move that succeeded in transferring the
-    content, but where dropping failed due to eg a network problem,
-    in cases where numcopies checks prevented the resumed
-    move from dropping the object from the source repository.
-  * add, fix, lock, rekey: When several files were being processed,
-    replacing an annex symlink of a file that was already processed
-    with a new large file could sometimes cause that large file to be
-    added to git. These races have been fixed.
-  * add: Also fix a similar race that could cause a large file be added
-    to git when a small file was modified or overwritten while it was
-    being added.
-  * add --batch: Fix handling of a file that is skipped due to being
-    gitignored."""]]
\ No newline at end of file

add news item for git-annex 10.20220623
diff --git a/doc/news/version_10.20220623.mdwn b/doc/news/version_10.20220623.mdwn
new file mode 100644
index 0000000000..1abba1a087
--- /dev/null
+++ b/doc/news/version_10.20220623.mdwn
@@ -0,0 +1,23 @@
+git-annex 10.20220623 released with [[!toggle text="these changes"]]
+[[!toggleable text="""  * init: Added --no-autoenable option.
+  * info: Added --autoenable option.
+  * initremote: Improve handling of type=git special remotes.
+    The location value no longer needs to match the url of an existing
+    git remote, and locations not using ssh:// will work now, including
+    both paths and host:/path
+  * Fix retrival of an empty file that is stored in a special remote with
+    chunking enabled.
+    (Fixes a reversion in 8.20201103)
+  * move: Improve resuming a move that succeeded in transferring the
+    content, but where dropping failed due to eg a network problem,
+    in cases where numcopies checks prevented the resumed
+    move from dropping the object from the source repository.
+  * add, fix, lock, rekey: When several files were being processed,
+    replacing an annex symlink of a file that was already processed
+    with a new large file could sometimes cause that large file to be
+    added to git. These races have been fixed.
+  * add: Also fix a similar race that could cause a large file be added
+    to git when a small file was modified or overwritten while it was
+    being added.
+  * add --batch: Fix handling of a file that is skipped due to being
+    gitignored."""]]
\ No newline at end of file

fix release version
diff --git a/CHANGELOG b/CHANGELOG
index 9d86a8f359..e13b15a96d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,4 @@
-git-annex (10.20220526) upstream; urgency=medium
+git-annex (10.20220623) upstream; urgency=medium
 
   * init: Added --no-autoenable option.
   * info: Added --autoenable option.
diff --git a/doc/news/version_10.20220526.mdwn b/doc/news/version_10.20220526.mdwn
deleted file mode 100644
index 35055fb51d..0000000000
--- a/doc/news/version_10.20220526.mdwn
+++ /dev/null
@@ -1,23 +0,0 @@
-git-annex 10.20220526 released with [[!toggle text="these changes"]]
-[[!toggleable text="""  * init: Added --no-autoenable option.
-  * info: Added --autoenable option.
-  * initremote: Improve handling of type=git special remotes.
-    The location value no longer needs to match the url of an existing
-    git remote, and locations not using ssh:// will work now, including
-    both paths and host:/path
-  * Fix retrival of an empty file that is stored in a special remote with
-    chunking enabled.
-    (Fixes a reversion in 8.20201103)
-  * move: Improve resuming a move that succeeded in transferring the
-    content, but where dropping failed due to eg a network problem,
-    in cases where numcopies checks prevented the resumed
-    move from dropping the object from the source repository.
-  * add, fix, lock, rekey: When several files were being processed,
-    replacing an annex symlink of a file that was already processed
-    with a new large file could sometimes cause that large file to be
-    added to git. These races have been fixed.
-  * add: Also fix a similar race that could cause a large file be added
-    to git when a small file was modified or overwritten while it was
-    being added.
-  * add --batch: Fix handling of a file that is skipped due to being
-    gitignored."""]]
\ No newline at end of file
diff --git a/git-annex.cabal b/git-annex.cabal
index bef8e20376..3e8c86469a 100644
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -1,5 +1,5 @@
 Name: git-annex
-Version: 10.20220525
+Version: 10.20220623
 Cabal-Version: >= 1.10
 License: AGPL-3
 Maintainer: Joey Hess <id@joeyh.name>

add news item for git-annex 10.20220526
diff --git a/doc/news/version_10.20220127.mdwn b/doc/news/version_10.20220127.mdwn
deleted file mode 100644
index 92e19aad81..0000000000
--- a/doc/news/version_10.20220127.mdwn
+++ /dev/null
@@ -1,29 +0,0 @@
-git-annex 10.20220127 released with [[!toggle text="these changes"]]
-[[!toggleable text="""  * New v10 repository version (with v9 as a stepping-stone to it).
-    v8 remains the default version for now.
-  * In v10, object files are locked using separate lock files. This allows
-    the object files to be kept non-writable even in repositories where
-    core.sharedRepository is set.
-  * The v10 upgrade will happen automatically, one year after the v9
-    upgrade, in order to allow time for any old git-annex processes that
-    are not aware of the locking change to finish. Or git-annex upgrade
-    can be used to upgrade to v10 immediately.
-  * In v9 upgrade, set filter.annex.process. This makes git add/checkout faster
-    when there are a lot of unlocked annexed files or non-annexed files, but can
-    also make git add of large files to the annex somewhat slower.
-    If this tradeoff does not work for your use case, you can still unset
-    filter.annex.process.
-  * export: When a non-annexed symlink is in the tree to be exported, skip it.
-  * import: When the previously exported tree contained a non-annexed symlink,
-    preserve it in the imported tree so it does not get deleted.
-  * enableremote, renameremote: Better handling of the unusual case where
-    multiple special remotes have been initialized with the same name.
-  * Recover from corrupted content being received from a git remote,
-    by deleting the temporary file when it fails to verify. This prevents
-    a retry from failing again.
-    (reversion introduced in version 8.20210903)
-  * adb: Added ignorefinderror configuration parameter.
-  * Avoid crashing when run in a bare git repo that somehow contains an
-    index file.
-  * Reject combinations of --batch (or --batch-keys) with options like
-    --all or --key or with filenames."""]]
\ No newline at end of file
diff --git a/doc/news/version_10.20220526.mdwn b/doc/news/version_10.20220526.mdwn
new file mode 100644
index 0000000000..35055fb51d
--- /dev/null
+++ b/doc/news/version_10.20220526.mdwn
@@ -0,0 +1,23 @@
+git-annex 10.20220526 released with [[!toggle text="these changes"]]
+[[!toggleable text="""  * init: Added --no-autoenable option.
+  * info: Added --autoenable option.
+  * initremote: Improve handling of type=git special remotes.
+    The location value no longer needs to match the url of an existing
+    git remote, and locations not using ssh:// will work now, including
+    both paths and host:/path
+  * Fix retrival of an empty file that is stored in a special remote with
+    chunking enabled.
+    (Fixes a reversion in 8.20201103)
+  * move: Improve resuming a move that succeeded in transferring the
+    content, but where dropping failed due to eg a network problem,
+    in cases where numcopies checks prevented the resumed
+    move from dropping the object from the source repository.
+  * add, fix, lock, rekey: When several files were being processed,
+    replacing an annex symlink of a file that was already processed
+    with a new large file could sometimes cause that large file to be
+    added to git. These races have been fixed.
+  * add: Also fix a similar race that could cause a large file be added
+    to git when a small file was modified or overwritten while it was
+    being added.
+  * add --batch: Fix handling of a file that is skipped due to being
+    gitignored."""]]
\ No newline at end of file

convert bug to todo
diff --git a/doc/bugs/worktree_overwrite_races.mdwn b/doc/bugs/worktree_overwrite_races.mdwn
deleted file mode 100644
index 6a5d0aad34..0000000000
--- a/doc/bugs/worktree_overwrite_races.mdwn
+++ /dev/null
@@ -1,31 +0,0 @@
-Similar to [[add_overwrite_race]], several callers of replaceWorkTreeFile
-are susceptable to a race. They do some check of the current content of the
-file, and then they call that to replace it with something else. But in
-between, the file could be overwritten with other content. And that content
-then gets replaced, which is not expected behavior.
-
-(Some other commands may modify the worktree without using it (oops)
-and also be susceptable to a race?)
-
-[[!commit 5ef79125ad0eddd5467b6bec451fdcdbd748b96f]] fixed one of these
-races, but not in an ideal way.
-
-Better would probably be for replaceWorkTreeFile to be provided with a
-InodeCache of the content of the worktree file that is ok to replace.
-Then it can move the file to a temp directory, check that it's still
-unmodified, and replace it. --[[Joey]]
-
-> On second thought, I remember that I investigated git's behavior before
-> when a checkout or pull is updating the worktree and a file is changed.
-> git does not avoid races, and can overwrite user modifications. Last time
-> I looked at this, I remember I decided that if git did that, it was ok
-> for git-annex to also.
-> 
-> The difference with [[add_overwrite_race]] is it caused the wrong thing
-> to get added to git, which is a problem that `git add` does not have
-> (probably). And git-annex already took steps to deal with writes that
-> happened in the middle of a `git-annex add`. So it made sense to fix
-> those problems. But extending it to this broader case is not necessary, I
-> think. 
-> 
-> [[done]] --[[Joey]]
diff --git a/doc/todo/worktree_overwrite_races.mdwn b/doc/todo/worktree_overwrite_races.mdwn
new file mode 100644
index 0000000000..815e438a7d
--- /dev/null
+++ b/doc/todo/worktree_overwrite_races.mdwn
@@ -0,0 +1,34 @@
+Similar to [[bugs/add_overwrite_race]], several callers of
+replaceWorkTreeFile are susceptable to a race. They do some check of the
+current content of the file, and then they call that to replace it with
+something else. But in between, the file could be overwritten with other
+content. And that content then gets overwritten.
+
+This is probably not a bug, because `git checkout` actually has the same
+problem, IIRC. And if git does that, it's ok for git-annex to also.
+
+Still, this affects several git-annex commands, and it would be better to
+avoid it. To avoid it, replaceWorkTreeFile could be provided with a
+Maybe FileStatus of the content that was in the worktree that is ok to
+overwrite. Then atomically swap the current worktree file and the new 
+file, and afterwards check if the old worktree file is unmodifified,
+moving it back if it is modified.
+
+(Note that, this will not help with situations where the worktree file
+is opened for append, but gets replaced by git-annex before being written
+to. A later write will be to a deleted file.)
+
+The atomic swap would need a call to `renameat2()` with
+`RENAME_EXCHANGE`. There does not seem to be a binding for that
+in any haskell library. Also, it is linux-specific, though may also
+have reached freebsd?
+
+Hmm, but even this could lead to file corruption. Suppose that a process
+is opening the worktree file for append, writing a byte, closing it, and
+repeating. The bytes are `[1,2,3,...]`. The worktree file
+has 1 appended to it. Then renameat2 swaps the files. The new file gets
+2 appended to it. The worktree file was modified, so is moved back into
+place. It gets 3 appended to it. So the worktree file ends up containing
+`1,3`.
+
+So, perhaps there is really no good solution to this. --[[Joey]]

use replaceWorkTreeFile when fixing an annex symlink
This does not change any behavior, but it's useful for all worktree
changes to be made using this.
Sponsored-by: Graham Spencer on Patreon
diff --git a/Command/Fix.hs b/Command/Fix.hs
index 59b3a15289..6220d2fdff 100644
--- a/Command/Fix.hs
+++ b/Command/Fix.hs
@@ -97,11 +97,11 @@ fixSymlink file link = do
 	mtime <- liftIO $ catchMaybeIO $ Posix.modificationTimeHiRes
 		<$> R.getSymbolicLinkStatus file
 #endif
-	createWorkTreeDirectory (parentDir file)
-	liftIO $ R.removeLink file
-	liftIO $ R.createSymbolicLink link file
+	replaceWorkTreeFile (fromRawFilePath file) $ \tmpfile -> do
+		let tmpfile' = toRawFilePath tmpfile
+		liftIO $ R.createSymbolicLink link tmpfile'
 #if ! defined(mingw32_HOST_OS)
-	liftIO $ maybe noop (\t -> touch file t False) mtime
+		liftIO $ maybe noop (\t -> touch tmpfile' t False) mtime
 #endif
 	stageSymlink file =<< hashSymlink link
 	next $ return True
diff --git a/doc/bugs/worktree_overwrite_races.mdwn b/doc/bugs/worktree_overwrite_races.mdwn
index c0fa11683f..6a5d0aad34 100644
--- a/doc/bugs/worktree_overwrite_races.mdwn
+++ b/doc/bugs/worktree_overwrite_races.mdwn
@@ -4,9 +4,8 @@ file, and then they call that to replace it with something else. But in
 between, the file could be overwritten with other content. And that content
 then gets replaced, which is not expected behavior.
 
-Some other commands may modify the worktree without using it (oops)
-and also be susceptable to a race. `git-annex fix` in fixSymlink, for example,
-checks if the file is a symlink, and then deletes and recreates it.
+(Some other commands may modify the worktree without using it (oops)
+and also be susceptable to a race?)
 
 [[!commit 5ef79125ad0eddd5467b6bec451fdcdbd748b96f]] fixed one of these
 races, but not in an ideal way.

followup
diff --git a/doc/bugs/Fails_to_drop_key_on_windows___40__Access_denied__41__/comment_1_fdad38ba9decc434e0d04a446f0a02e5._comment b/doc/bugs/Fails_to_drop_key_on_windows___40__Access_denied__41__/comment_1_fdad38ba9decc434e0d04a446f0a02e5._comment
new file mode 100644
index 0000000000..db6c3dc959
--- /dev/null
+++ b/doc/bugs/Fails_to_drop_key_on_windows___40__Access_denied__41__/comment_1_fdad38ba9decc434e0d04a446f0a02e5._comment
@@ -0,0 +1,52 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-22T16:42:25Z"
+ content="""
+I tried to construct a test case that I can from from these transcripts.
+
+	key=XDLRA--refs
+	mkdir $base/repo
+	cd $base/repo
+	git -c diff.ignoreSubmodules=none -C $base/repo init --bare
+	git config -z -l --show-origin
+	git -c diff.ignoreSubmodules=none config annex.private true
+	git -c diff.ignoreSubmodules=none annex init
+	git config -z -l --show-origin
+	git annex version --raw
+	git -c diff.ignoreSubmodules=none annex initremote origin type=directory directory=$remote encryption=none -c annex.dotfiles=true
+	git config -z -l --show-origin
+	git -c diff.ignoreSubmodules=none cat-file blob git-annex:remote.log
+	git -c diff.ignoreSubmodules=none annex fsck -f origin --fast --key $key -c annex.dotfiles=true
+	git -c diff.ignoreSubmodules=none annex drop --force --key $key -c annex.dotfiles=true
+	git -c diff.ignoreSubmodules=none annex get --key $key -c annex.dotfiles=true
+	git -c diff.ignoreSubmodules=none annex contentlocation $key -c annex.dotfiles=true
+	git -c diff.ignoreSubmodules=none annex drop --force --all -c annex.dotfiles=true
+
+Now, this would normally fail at the fsck step, since no object file exists for
+XDLRA--refs. I suppose that you must have pre-populated the special remote with
+it, somehow, and left that part out. The fsck would then learn that the object
+does exist there.
+
+It seems to me that how you prepopulated it might be a crucial detail, since
+that object file gets copied from the special remote and perhaps something to
+do with its permissions etc are what is later preventing deleting the copy.
+
+You're also using an external backend for whatever reason, and it would
+simplify the test case if that external backend were not needed and it just
+used a built-in backend. Using key=WORM--refs seems like it should behave the
+same, unless your external backend is doing something very strange. If the
+external backend is necessary to reproduce it, I will need some kind of minimal
+version of it.
+
+So, I've tried pre-populating the file in the remote like this:
+
+	mkdir -p $remote/06b/f75/WORM--refs
+	echo hi > $remote/06b/f75/WORM--refs/WORM--refs
+
+With that, I've gotten as far as getting the test case to succeed on linux.
+
+But I don't think it's worth the bother of getting a windows environment set up
+and running these commands on it, without first knowing how you prepopulated
+the special remote with the object file.
+"""]]

Added a comment
diff --git a/doc/todo/Filter_a_tree_with_pattern/comment_2_e2b0b3eafdfdab1e315066ac57c8b838._comment b/doc/todo/Filter_a_tree_with_pattern/comment_2_e2b0b3eafdfdab1e315066ac57c8b838._comment
new file mode 100644
index 0000000000..d63812f750
--- /dev/null
+++ b/doc/todo/Filter_a_tree_with_pattern/comment_2_e2b0b3eafdfdab1e315066ac57c8b838._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="comment 2"
+ date="2022-06-22T12:34:45Z"
+ content="""
+My main use-case is actually the glob patterns; the first two use-cases. The other ones would just come as a bonus thanks to git-annex-matching-expressions.
+
+"""]]

Added a comment
diff --git a/doc/forum/How_to_make_this_workflow_possible__63__/comment_1_38d7c0531d68803b1d7910c598aab38c._comment b/doc/forum/How_to_make_this_workflow_possible__63__/comment_1_38d7c0531d68803b1d7910c598aab38c._comment
new file mode 100644
index 0000000000..1b0cf01f60
--- /dev/null
+++ b/doc/forum/How_to_make_this_workflow_possible__63__/comment_1_38d7c0531d68803b1d7910c598aab38c._comment
@@ -0,0 +1,35 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="comment 1"
+ date="2022-06-22T12:24:21Z"
+ content="""
+> The one caveat here is `git annex sync --content --no-pull` seems to ignore the no-pull option and tries to redownload all the raw files back to the laptop. How to do a one-way sync (laptop -> remote only with content)?
+
+AFAICT, pull only concerns `git` actions, not `git-annex` actions like transferring content.
+
+What your laptop's repo tries to copy depends on what content it wants and numcopies.  
+
+The latter probably isn't your problem since you should have at least 2 copies of everything distributed over the drives.  
+The former should ideally be solved with a proper customised groups setup but you can also set a wanted expression or use the built-in groups which is probably your best option. Your laptop's repo should be set to `client` or `manual`.
+
+See https://git-annex.branchable.com/git-annex-preferred-content/ and https://git-annex.branchable.com/preferred_content/standard_groups/
+
+> XMP sidecars that are produced are tracked in regular git
+
+Have you set `annex.largefiles` to anything? 
+
+Sidecar files are just XML AFAICT, so a filter that would only add files with binary mimeencoding to annex would exclude these and add them to regular git.
+
+If you haven't touched that setting (double check with `git config`), that's a bug.
+
+> Try to sync with any available remote (this is the part I can't seem to figure out. I wish git-annex could be detect and try to to push to all available remotes instead me having to track which remotes are available). 
+> `git annex [copy|move]` have the `--to=here` option which will copy/move files from remotes to local repo. It would be exceptionally useful to have a `--to=reachable` option to send files to any reachable remote instead of having to copy/moves to each remote individually.
+
+By default, sync syncs with *all* available remotes. 
+
+How exactly are your repo's remotes set up?
+
+What does it to when syncing that you don't want it to?
+
+"""]]

diff --git a/doc/forum/How_to_make_this_workflow_possible__63__.mdwn b/doc/forum/How_to_make_this_workflow_possible__63__.mdwn
new file mode 100644
index 0000000000..8295097714
--- /dev/null
+++ b/doc/forum/How_to_make_this_workflow_possible__63__.mdwn
@@ -0,0 +1,22 @@
+I have recently discovered git-annex and I'm interested in using it to manage my photo archive of about 10 years of raw photos. My existing workflow has worked well, but I'm trying to figure out how to automate it using git-annex. The archive is mirrored across 5-7 different external harddrives, each with a redundant copy of the entire archive (or as much as it can store since each drive capacity varies)
+
+Current workflow:
+
+1. Rename and organize photos on laptop
+2. Copy renamed photos to any two of the external harddrives
+  2a. if the two harddrives are out sync, resync the harddrives
+3. Use laptop as local cache for editing
+  3a. If photo is unavailable on laptop, plug in an external drive and create a local copy of desired photos.
+4. Resync laptop with external drives (this copies XMP sidecars to external harddrives and they will eventually propagate to other drives)
+
+I can get pretty close to this workflow git-annex except for one quirk.
+
+1. have a laptop repository in git-annex. Commit renamed raw photos in git-annex.
+2. Each harddrive have a git-annex repo and sync with laptop repo. The one caveat here is `git annex sync --content --no-pull` seems to ignore the no-pull option and tries to redownload all the raw files back to the laptop. How to do a one-way sync (laptop -> remote only with content)?
+3. XMP sidecars that are produced are tracked in regular git
+4. Try to sync with any available remote (this is the part I can't seem to figure out. I wish git-annex could be detect and try to to push to all available remotes instead me having to track which remotes are available). 
+`git annex [copy|move]` have the `--to=here` option which will copy/move files from remotes to local repo. It would be exceptionally useful to have a `--to=reachable` option to send files to any reachable remote instead of having to copy/moves to each remote individually.
+
+Things I need to figure out, but can't seem to grasp
+1. How to do a one-way sync? Or better yet, since each external drive needs to sync with any other reachable drive, can I exclude the laptop repo from the sync? The laptop repo is only a temporary staging area + local cache.
+2. If sync isn't the solution, how do move/copy to all reachable remotes instead of one-by-one?

removed
diff --git a/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment b/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment
deleted file mode 100644
index 7d16cf0078..0000000000
--- a/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment
+++ /dev/null
@@ -1,20 +0,0 @@
-[[!comment format=mdwn
- username="arshitha"
- avatar="http://cdn.libravatar.org/avatar/53bc5fe931c24a0e454a567f559618da"
- subject="comment 2"
- date="2022-06-17T09:44:28Z"
- content="""
-This command worked for me `brew install --build-from-source git-annex`
-
-`brew install git-annex` gave me the following error on macOS Big Sur v11.5.2 (MacBook Air (M1 2020))
-
-```bash
-Error: git-annex: no bottle available!
-You can try to install from source with:
-  brew install --build-from-source git-annex
-Please note building from source is unsupported. You will encounter build
-failures with some formulae. If you experience any issues please create pull
-requests instead of asking for help on Homebrew's GitHub, Twitter or any other
-official channels.
-```
-"""]]

Added a comment
diff --git a/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment b/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment
new file mode 100644
index 0000000000..7d16cf0078
--- /dev/null
+++ b/doc/install/OSX/Homebrew/comment_2_b8670d2745bc2b07b1b9b7e22cc6fde7._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="arshitha"
+ avatar="http://cdn.libravatar.org/avatar/53bc5fe931c24a0e454a567f559618da"
+ subject="comment 2"
+ date="2022-06-17T09:44:28Z"
+ content="""
+This command worked for me `brew install --build-from-source git-annex`
+
+`brew install git-annex` gave me the following error on macOS Big Sur v11.5.2 (MacBook Air (M1 2020))
+
+```bash
+Error: git-annex: no bottle available!
+You can try to install from source with:
+  brew install --build-from-source git-annex
+Please note building from source is unsupported. You will encounter build
+failures with some formulae. If you experience any issues please create pull
+requests instead of asking for help on Homebrew's GitHub, Twitter or any other
+official channels.
+```
+"""]]

decided this is not a problem
diff --git a/doc/bugs/worktree_overwrite_races.mdwn b/doc/bugs/worktree_overwrite_races.mdwn
index 78fdf1addd..c0fa11683f 100644
--- a/doc/bugs/worktree_overwrite_races.mdwn
+++ b/doc/bugs/worktree_overwrite_races.mdwn
@@ -15,3 +15,18 @@ Better would probably be for replaceWorkTreeFile to be provided with a
 InodeCache of the content of the worktree file that is ok to replace.
 Then it can move the file to a temp directory, check that it's still
 unmodified, and replace it. --[[Joey]]
+
+> On second thought, I remember that I investigated git's behavior before
+> when a checkout or pull is updating the worktree and a file is changed.
+> git does not avoid races, and can overwrite user modifications. Last time
+> I looked at this, I remember I decided that if git did that, it was ok
+> for git-annex to also.
+> 
+> The difference with [[add_overwrite_race]] is it caused the wrong thing
+> to get added to git, which is a problem that `git add` does not have
+> (probably). And git-annex already took steps to deal with writes that
+> happened in the middle of a `git-annex add`. So it made sense to fix
+> those problems. But extending it to this broader case is not necessary, I
+> think. 
+> 
+> [[done]] --[[Joey]]

fix overwrite race with small file that got large
When adding a small file, it does not get locked down, so can be modified
after git-annex checks that it's small. The use of queued git add made the
race window nice and wide too.
Fixed by checking if the file has changed, and by not using git add.
Instead, have to recapitulate git add's handling of things like symlinks
and executable files.
Sponsored-by: Jochen Bartl on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 3e39bbc3a9..02a9696d00 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -17,6 +17,9 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
     replacing an annex symlink of a file that was already processed
     with a new large file could sometimes cause that large file to be
     added to git. These races have been fixed.
+  * add: Also fix a similar race that could cause a large file be added
+    to git when a small file was modified or overwritten while it was
+    being added.
   * add --batch: Fix handling of a file that is skipped due to being
     gitignored.
 
diff --git a/Command/Add.hs b/Command/Add.hs
index 31c560e4b0..671f16799e 100644
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2022 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -17,16 +17,20 @@ import qualified Database.Keys
 import Annex.FileMatcher
 import Annex.Link
 import Annex.Tmp
+import Annex.HashObject
 import Messages.Progress
 import Git.FilePath
+import Git.Types
+import Git.UpdateIndex
 import Config.GitConfig
-import Config.Smudge
 import Utility.OptParse
 import Utility.InodeCache
 import Annex.InodeSentinal
 import Annex.CheckIgnore
 import qualified Utility.RawFilePath as R
 
+import System.PosixCompat.Files
+
 cmd :: Command
 cmd = notBareRepo $ 
 	withGlobalOptions opts $
@@ -80,20 +84,21 @@ seek o = startConcurrency commandStages $ do
 	addunlockedmatcher <- addUnlockedMatcher
 	annexdotfiles <- getGitConfigVal annexDotFiles 
 	let gofile includingsmall (si, file) = case largeFilesOverride o of
-		Nothing -> 
+		Nothing -> do
+			s <- liftIO $ R.getSymbolicLinkStatus file
 			ifM (pure (annexdotfiles || not (dotfile file))
 				<&&> (checkFileMatcher largematcher file 
 				<||> Annex.getState Annex.force))
-				( start o si file addunlockedmatcher
+				( start si file addunlockedmatcher
 				, if includingsmall
 					then ifM (annexAddSmallFiles <$> Annex.getGitConfig)
-						( startSmall o si file
+						( startSmall si file s
 						, stop
 						)
 					else stop
 				)
-		Just True -> start o si file addunlockedmatcher
-		Just False -> startSmallOverridden o si file
+		Just True -> start si file addunlockedmatcher
+		Just False -> startSmallOverridden si file
 	case batchOption o of
 		Batch fmt
 			| updateOnly o ->
@@ -121,64 +126,84 @@ seek o = startConcurrency commandStages $ do
 			go False withUnmodifiedUnlockedPointers
 
 {- Pass file off to git-add. -}
-startSmall :: AddOptions -> SeekInput -> RawFilePath -> CommandStart
-startSmall o si file =
+startSmall :: SeekInput -> RawFilePath -> FileStatus -> CommandStart
+startSmall si file s =
 	starting "add" (ActionItemTreeFile file) si $
-		next $ addSmall (checkGitIgnoreOption o) file
+		next $ addSmall file s
 
-addSmall :: CheckGitIgnore -> RawFilePath -> Annex Bool
-addSmall ci file = do
+addSmall :: RawFilePath -> FileStatus -> Annex Bool
+addSmall file s = do
 	showNote "non-large file; adding content to git repository"
-	addFile Small ci file
+	addFile Small file s
 
-startSmallOverridden :: AddOptions -> SeekInput -> RawFilePath -> CommandStart
-startSmallOverridden o si file = 
-	starting "add" (ActionItemTreeFile file) si $ next $ do
-		showNote "adding content to git repository"
-		addFile Small (checkGitIgnoreOption o) file
+startSmallOverridden :: SeekInput -> RawFilePath -> CommandStart
+startSmallOverridden si file = 
+	liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
+		Just s -> starting "add" (ActionItemTreeFile file) si $ next $ do
+			
+			showNote "adding content to git repository"
+			addFile Small file s
+		Nothing -> stop
 
 data SmallOrLarge = Small | Large
 
-addFile :: SmallOrLarge -> CheckGitIgnore -> RawFilePath -> Annex Bool
-addFile smallorlarge ci file = do
-	ps <- gitAddParams ci
-	cps <- case smallorlarge of
-		-- In case the file is being converted from an annexed file
-		-- to be stored in git, remove the cached inode, so that
-		-- if the smudge clean filter later runs on the file,
-		-- it will not remember it was annexed.
-		--
-		-- The use of bypassSmudgeConfig prevents the smudge
-		-- filter from being run. So the changes to the database
-		-- can be queued up and not flushed to disk immediately.
-		Small -> do
-			maybe noop Database.Keys.removeInodeCache
-				=<< withTSDelta (liftIO . genInodeCache file)
-			return bypassSmudgeConfig
-		Large -> return []
-	Annex.Queue.addCommand cps "add" (ps++[Param "--"])
-		[fromRawFilePath file]
-	return True
-
-start :: AddOptions -> SeekInput -> RawFilePath -> AddUnlockedMatcher -> CommandStart
-start o si file addunlockedmatcher = do
-	mk <- liftIO $ isPointerFile file
-	maybe go fixuppointer mk
+addFile :: SmallOrLarge -> RawFilePath -> FileStatus -> Annex Bool
+addFile smallorlarge file s = do
+	sha <- if isSymbolicLink s
+		then hashBlob =<< liftIO (R.readSymbolicLink file)
+		else if isRegularFile s
+			then hashFile file
+			else giveup $ fromRawFilePath file ++ " is not a regular file"
+	let treetype = if isSymbolicLink s
+		then TreeSymlink
+		else if intersectFileModes ownerExecuteMode (fileMode s) /= 0
+			then TreeExecutable
+			else TreeFile
+	s' <- liftIO $ catchMaybeIO $ R.getSymbolicLinkStatus file
+	if maybe True (changed s) s'
+		then do
+			warning $ fromRawFilePath file ++ " changed while it was being added"
+			return False
+		else do
+			case smallorlarge of
+				-- In case the file is being converted from 
+				-- an annexed file to be stored in git,
+				-- remove the cached inode, so that if the
+				-- smudge clean filter later runs on the file,
+				-- it will not remember it was annexed.
+				Small -> maybe noop Database.Keys.removeInodeCache
+					=<< withTSDelta (liftIO . genInodeCache file)
+				Large -> noop
+			Annex.Queue.addUpdateIndex =<<
+				inRepo (stageFile sha treetype (fromRawFilePath file))
+			return True
   where
-	go = ifAnnexed file addpresent add
-	add = liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
+	changed a b =
+		deviceID a /= deviceID b ||
+		fileID a /= fileID b ||
+		fileSize a /= fileSize b ||
+		modificationTime a /= modificationTime b ||
+		isRegularFile a /= isRegularFile b ||
+		isSymbolicLink a /= isSymbolicLink b
+
+start :: SeekInput -> RawFilePath -> AddUnlockedMatcher -> CommandStart
+start si file addunlockedmatcher = 
+	liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
 		Nothing -> stop
-		Just s 
+		Just s
 			| not (isRegularFile s) && not (isSymbolicLink s) -> stop
-			| otherwise -> 
-				starting "add" (ActionItemTreeFile file) si $
-					if isSymbolicLink s
-						then next $ addFile Small (checkGitIgnoreOption o) file
-						else perform file addunlockedmatcher
-	addpresent key = 
-		liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
-			Just s | isSymbolicLink s -> fixuplink key
-			_ -> add
+			| otherwise -> do
+				mk <- liftIO $ isPointerFile file
+				maybe (go s) (fixuppointer s) mk
+  where
+	go s = ifAnnexed file (addpresent s) (add s)
+	add s = starting "add" (ActionItemTreeFile file) si $
+		if isSymbolicLink s

(Diff truncated)
Added a comment: hold it... ;)
diff --git a/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_3_7fd5d3decc2d6b40d6d2f2d7e6dcabdd._comment b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_3_7fd5d3decc2d6b40d6d2f2d7e6dcabdd._comment
new file mode 100644
index 0000000000..73d8c4030e
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_3_7fd5d3decc2d6b40d6d2f2d7e6dcabdd._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="hold it... ;)"
+ date="2022-06-14T18:47:24Z"
+ content="""
+Thank you Joey!  I will try to gain better insight into our dance in that test of ours. I am also curious how it worked before that it doesn't work now.  FWIW with `datalad clone` we do try URL as is, and then also with `/.git` to make users' life easier and so they could just e.g. `datalad clone https://datasets.datalad.org`.
+"""]]

get rid of racy addLink
The remaining callers all did not rely on it checking gitignore, so were
easy to convert.
They were susceptable to the same overwrite race as add and fix,
although less likely to have it and a narrower window than add's race.
Command.Rekey in passing got an unncessary call to removeFile deleted.
addSymlink handles deleting any existing worktree file.
diff --git a/Annex/Ingest.hs b/Annex/Ingest.hs
index da9d4b5ae4..6e5224b484 100644
--- a/Annex/Ingest.hs
+++ b/Annex/Ingest.hs
@@ -1,6 +1,6 @@
 {- git-annex content ingestion
  -
- - Copyright 2010-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2022 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -16,7 +16,6 @@ module Annex.Ingest (
 	ingest',
 	finishIngestUnlocked,
 	cleanOldKeys,
-	addLink,
 	addSymlink,
 	makeLink,
 	addUnlocked,
@@ -38,7 +37,6 @@ import Annex.CurrentBranch
 import Annex.CheckIgnore
 import Logs.Location
 import qualified Annex
-import qualified Annex.Queue
 import qualified Database.Keys
 import Config
 import Utility.InodeCache
@@ -315,30 +313,7 @@ makeLink file key mcache = flip catchNonAsync (restoreFile file key) $ do
   where
 	file' = fromRawFilePath file
 
-{- Creates the symlink to the annexed content, and stages it in git.
- -
- - As long as the filesystem supports symlinks, we use
- - git add, rather than directly staging the symlink to git.
- - Using git add is best because it allows the queuing to work
- - and is faster (staging the symlink runs hash-object commands each time).
- - Also, using git add allows it to skip gitignored files, unless forced
- - to include them.
- -
- - FIXME: Using git add opens a race where the file can be changed
- - before git adds it, causing a large file to be added directly to git.
- - addSymlink avoids that, but does not check git ignore. Things need to
- - be converted to use it, first checking git ignore themselves.
- -}
-addLink :: CheckGitIgnore -> RawFilePath -> Key -> Maybe InodeCache -> Annex ()
-addLink ci file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig)
-	( do
-		_ <- makeLink file key mcache
-		ps <- gitAddParams ci
-		Annex.Queue.addCommand [] "add" (ps++[Param "--"])
-			[fromRawFilePath file]
-	, addSymlink file key mcache
-	)
-
+{- Creates the symlink to the annexed content, and stages it in git. -}
 addSymlink :: RawFilePath -> Key -> Maybe InodeCache -> Annex ()
 addSymlink file key mcache = do
 	linktarget <- makeLink file key mcache
@@ -384,8 +359,8 @@ addUnlocked matcher mi contentpresent =
  -
  - When the content of the key is not accepted into the annex, returns False.
  -}
-addAnnexedFile :: CheckGitIgnore -> AddUnlockedMatcher -> RawFilePath -> Key -> Maybe RawFilePath -> Annex Bool
-addAnnexedFile ci matcher file key mtmp = ifM (addUnlocked matcher mi (isJust mtmp))
+addAnnexedFile :: AddUnlockedMatcher -> RawFilePath -> Key -> Maybe RawFilePath -> Annex Bool
+addAnnexedFile matcher file key mtmp = ifM (addUnlocked matcher mi (isJust mtmp))
 	( do
 		mode <- maybe
 			(pure Nothing)
@@ -403,7 +378,7 @@ addAnnexedFile ci matcher file key mtmp = ifM (addUnlocked matcher mi (isJust mt
 				, writepointer mode >> return True
 				)
 	, do
-		addLink ci file key Nothing
+		addSymlink file key Nothing
 		case mtmp of
 			Just tmp -> moveAnnex key af tmp
 			Nothing -> return True
diff --git a/CHANGELOG b/CHANGELOG
index 6a8790b090..3e39bbc3a9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,10 +13,10 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
     content, but where dropping failed due to eg a network problem, 
     in cases where numcopies checks prevented the resumed
     move from dropping the object from the source repository.
-  * add, fix: When several files are being added, replacing an annex symlink
-    of a file that was already processed with a new large file could
-    sometimes cause that large file to be added to git. 
-    These races have been fixed.
+  * add, fix, lock, rekey: When several files were being processed, 
+    replacing an annex symlink of a file that was already processed
+    with a new large file could sometimes cause that large file to be
+    added to git. These races have been fixed.
   * add --batch: Fix handling of a file that is skipped due to being
     gitignored.
 
diff --git a/Command/AddUnused.hs b/Command/AddUnused.hs
index 828e53d8a3..5a7e412c72 100644
--- a/Command/AddUnused.hs
+++ b/Command/AddUnused.hs
@@ -30,9 +30,7 @@ start = startUnused "addunused" perform
 perform :: Key -> CommandPerform
 perform key = next $ do
 	logStatus key InfoPresent
-	-- Ignore the usual git ignores because the user has explictly
-	-- asked to add these files.
-	addLink (CheckGitIgnore False) file key Nothing
+	addSymlink file key Nothing
 	return True
   where
 	file = "unused." <> keyFile key
diff --git a/Command/AddUrl.hs b/Command/AddUrl.hs
index 9c35b01e51..ae1680a19d 100644
--- a/Command/AddUrl.hs
+++ b/Command/AddUrl.hs
@@ -476,13 +476,13 @@ addWorkTree _ addunlockedmatcher u url file key mtmp = case mtmp of
 		maybeShowJSON $ JSONChunk [("key", serializeKey key)]
 		setUrlPresent key url
 		logChange key u InfoPresent
-		ifM (addAnnexedFile noci addunlockedmatcher file key mtmp)
+		ifM (addAnnexedFile addunlockedmatcher file key mtmp)
 			( do
 				when (isJust mtmp) $
 					logStatus key InfoPresent
 			, maybe noop (\tmp -> pruneTmpWorkDirBefore tmp (liftIO . removeWhenExistsWith R.removeLink)) mtmp
 			)
-	
+
 	-- git does not need to check ignores, because that has already
 	-- been done, as witnessed by the CannAddFile.
 	noci = CheckGitIgnore False
diff --git a/Command/Lock.hs b/Command/Lock.hs
index e0d4b8f885..0e45d92001 100644
--- a/Command/Lock.hs
+++ b/Command/Lock.hs
@@ -60,8 +60,7 @@ start si file key = ifM (isJust <$> isAnnexLink file)
 perform :: RawFilePath -> Key -> CommandPerform
 perform file key = do
 	lockdown =<< calcRepo (gitAnnexLocation key)
-	addLink (CheckGitIgnore False) file key
-		=<< withTSDelta (liftIO . genInodeCache file)
+	addSymlink file key =<< withTSDelta (liftIO . genInodeCache file)
 	next $ return True
   where
 	lockdown obj = do
diff --git a/Command/ReKey.hs b/Command/ReKey.hs
index d00cad566f..f06bb62c53 100644
--- a/Command/ReKey.hs
+++ b/Command/ReKey.hs
@@ -123,8 +123,7 @@ cleanup file newkey = do
 	ifM (isJust <$> isAnnexLink file)
 		( do
 			-- Update symlink to use the new key.
-			liftIO $ removeFile (fromRawFilePath file)
-			addLink (CheckGitIgnore False) file newkey Nothing
+			addSymlink file newkey Nothing
 		, do
 			mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus file
 			liftIO $ whenM (isJust <$> isPointerFile file) $
diff --git a/doc/bugs/add_overwrite_race.mdwn b/doc/bugs/add_overwrite_race.mdwn
index b971672fcd..026f1e7c3d 100644
--- a/doc/bugs/add_overwrite_race.mdwn
+++ b/doc/bugs/add_overwrite_race.mdwn
@@ -33,9 +33,7 @@ Since adding a file to the annex also involves locking it down and
 detecting modifications made while generating the key, update-index is
 sufficient.
 
-> Update: This is done for `git-annex add`, using addSymlink. But addLink
-> is still in use elsewhere, and those other users might also be subject to
-> similar races.
+> Update: This is fixed.
 
 When it's adding a file unlocked, it already stages the pointer file using
 update-index instead so there is no overwrite problem there.
@@ -56,5 +54,4 @@ Unsure how to fix this case yet? Maybe it needs to cache the inode,
 hash the file content, then verifiy the inode did not change during
 hashing, and then also use update-index.
 
-
 --[[Joey]]

bug report
diff --git a/doc/bugs/worktree_overwrite_races.mdwn b/doc/bugs/worktree_overwrite_races.mdwn
new file mode 100644
index 0000000000..78fdf1addd
--- /dev/null
+++ b/doc/bugs/worktree_overwrite_races.mdwn
@@ -0,0 +1,17 @@
+Similar to [[add_overwrite_race]], several callers of replaceWorkTreeFile
+are susceptable to a race. They do some check of the current content of the
+file, and then they call that to replace it with something else. But in
+between, the file could be overwritten with other content. And that content
+then gets replaced, which is not expected behavior.
+
+Some other commands may modify the worktree without using it (oops)
+and also be susceptable to a race. `git-annex fix` in fixSymlink, for example,
+checks if the file is a symlink, and then deletes and recreates it.
+
+[[!commit 5ef79125ad0eddd5467b6bec451fdcdbd748b96f]] fixed one of these
+races, but not in an ideal way.
+
+Better would probably be for replaceWorkTreeFile to be provided with a
+InodeCache of the content of the worktree file that is ok to replace.
+Then it can move the file to a temp directory, check that it's still
+unmodified, and replace it. --[[Joey]]

fix add overwrite race with git-annex add to annex
This is not a complete fix for all such races, only the one where a
large file gets changed while adding and gets added to git rather than
to the annex.
addLink needs to go away, any caller of it is probably subject to the
same kind of race. (Also, addLink itself fails to check gitignore when
symlinks are not supported.)
ingestAdd no longer checks gitignore. (It didn't check it consistently
before either, since there were cases where it did not run git add!)
When git-annex import calls it, it's already checked gitignore itself
earlier. When git-annex add calls it, it's usually on files found
by withFilesNotInGit, which handles checking ignores.
There was one other case, when git-annex add --batch calls it. In that
case, old git-annex behaved rather badly, it would seem to add the file,
but git add would later fail, leaving the file as an unstaged annex symlink.
That behavior has also been fixed.
Sponsored-by: Brett Eisenberg on Patreon
diff --git a/Annex/Ingest.hs b/Annex/Ingest.hs
index ef34b3476b..7e3cbe25b4 100644
--- a/Annex/Ingest.hs
+++ b/Annex/Ingest.hs
@@ -145,19 +145,19 @@ checkLockedDownWritePerms file displayfile = checkContentWritePerm file >>= retu
 
 {- Ingests a locked down file into the annex. Updates the work tree and
  - index. -}
-ingestAdd :: CheckGitIgnore -> MeterUpdate -> Maybe LockedDown -> Annex (Maybe Key)
-ingestAdd ci meterupdate ld = ingestAdd' ci meterupdate ld Nothing
+ingestAdd :: MeterUpdate -> Maybe LockedDown -> Annex (Maybe Key)
+ingestAdd meterupdate ld = ingestAdd' meterupdate ld Nothing
 
-ingestAdd' :: CheckGitIgnore -> MeterUpdate -> Maybe LockedDown -> Maybe Key -> Annex (Maybe Key)
-ingestAdd' _ _ Nothing _ = return Nothing
-ingestAdd' ci meterupdate ld@(Just (LockedDown cfg source)) mk = do
+ingestAdd' :: MeterUpdate -> Maybe LockedDown -> Maybe Key -> Annex (Maybe Key)
+ingestAdd' _ Nothing _ = return Nothing
+ingestAdd' meterupdate ld@(Just (LockedDown cfg source)) mk = do
 	(mk', mic) <- ingest meterupdate ld mk
 	case mk' of
 		Nothing -> return Nothing
 		Just k -> do
 			let f = keyFilename source
 			if lockingFile cfg
-				then addLink ci f k mic
+				then addSymlink f k mic
 				else do
 					mode <- liftIO $ catchMaybeIO $
 						fileMode <$> R.getFileStatus (contentLocation source)
@@ -322,6 +322,11 @@ makeLink file key mcache = flip catchNonAsync (restoreFile file key) $ do
  - and is faster (staging the symlink runs hash-object commands each time).
  - Also, using git add allows it to skip gitignored files, unless forced
  - to include them.
+ -
+ - FIXME: Using git add opens a race where the file can be changed
+ - before git adds it, causing a large file to be added directly to git.
+ - addSymlink avoids that, but does not check git ignore. Things need to
+ - be converted to use it, first checking git ignore themselves.
  -}
 addLink :: CheckGitIgnore -> RawFilePath -> Key -> Maybe InodeCache -> Annex ()
 addLink ci file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig)
@@ -330,11 +335,14 @@ addLink ci file key mcache = ifM (coreSymlinks <$> Annex.getGitConfig)
 		ps <- gitAddParams ci
 		Annex.Queue.addCommand [] "add" (ps++[Param "--"])
 			[fromRawFilePath file]
-	, do
-		l <- makeLink file key mcache
-		addAnnexLink l file
+	, addSymlink file key mcache
 	)
 
+addSymlink :: RawFilePath -> Key -> Maybe InodeCache -> Annex ()
+addSymlink file key mcache = do
+	l <- makeLink file key mcache
+	addAnnexLink l file
+
 {- Parameters to pass to git add, forcing addition of ignored files.
  -
  - Note that, when git add is being run on an ignored file that is already
diff --git a/CHANGELOG b/CHANGELOG
index 83b62e8e82..a6219672c1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,6 +13,12 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
     content, but where dropping failed due to eg a network problem, 
     in cases where numcopies checks prevented the resumed
     move from dropping the object from the source repository.
+  * add: When several files are being added, replacing an annex symlink
+    of a file that was already processed with a new large file could
+    sometimes cause that large file to be added to git. This race has been
+    fixed.
+  * add --batch: Fix handling of a file that is skipped due to being
+    gitignored.
 
  -- Joey Hess <id@joeyh.name>  Wed, 01 Jun 2022 13:23:05 -0400
 
diff --git a/Command/Add.hs b/Command/Add.hs
index 1582fdfb88..a453f96690 100644
--- a/Command/Add.hs
+++ b/Command/Add.hs
@@ -24,6 +24,7 @@ import Config.Smudge
 import Utility.OptParse
 import Utility.InodeCache
 import Annex.InodeSentinal
+import Annex.CheckIgnore
 import qualified Utility.RawFilePath as R
 
 cmd :: Command
@@ -98,7 +99,11 @@ seek o = startConcurrency commandStages $ do
 			| updateOnly o ->
 				giveup "--update --batch is not supported"
 			| otherwise -> batchOnly Nothing (addThese o) $
-				batchFiles fmt (gofile True)
+				batchFiles fmt $ \v@(_si, file) -> 
+					ifM (checkIgnored (checkGitIgnoreOption o) file)
+						( stop
+						, gofile True v
+						)
 		NoBatch -> do
 			-- Avoid git ls-files complaining about files that
 			-- are not known to git yet, since this will add
@@ -169,7 +174,7 @@ start o si file addunlockedmatcher = do
 				starting "add" (ActionItemTreeFile file) si $
 					if isSymbolicLink s
 						then next $ addFile Small (checkGitIgnoreOption o) file
-						else perform o file addunlockedmatcher
+						else perform file addunlockedmatcher
 	addpresent key = 
 		liftIO (catchMaybeIO $ R.getSymbolicLinkStatus file) >>= \case
 			Just s | isSymbolicLink s -> fixuplink key
@@ -186,8 +191,8 @@ start o si file addunlockedmatcher = do
 				Database.Keys.addAssociatedFile key =<< inRepo (toTopFilePath file)
 				next $ addFile Large (checkGitIgnoreOption o) file
 
-perform :: AddOptions -> RawFilePath -> AddUnlockedMatcher -> CommandPerform
-perform o file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
+perform :: RawFilePath -> AddUnlockedMatcher -> CommandPerform
+perform file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 	lockingfile <- not <$> addUnlocked addunlockedmatcher
 		(MatchingFile (FileInfo file file Nothing))
 		True
@@ -199,7 +204,7 @@ perform o file addunlockedmatcher = withOtherTmp $ \tmpdir -> do
 	ld <- lockDown cfg (fromRawFilePath file)
 	let sizer = keySource <$> ld
 	v <- metered Nothing sizer Nothing $ \_meter meterupdate ->
-		ingestAdd (checkGitIgnoreOption o) meterupdate ld
+		ingestAdd meterupdate ld
 	finish v
   where
 	finish (Just key) = next $ cleanup key True
diff --git a/Command/Import.hs b/Command/Import.hs
index b514f44268..716fbcf63a 100644
--- a/Command/Import.hs
+++ b/Command/Import.hs
@@ -246,7 +246,7 @@ startLocal o addunlockedmatcher largematcher mode (srcfile, destfile) =
 				}
 			}
 		ifM (checkFileMatcher largematcher destfile)
-			( ingestAdd' (checkGitIgnoreOption o) nullMeterUpdate (Just ld') (Just k)
+			( ingestAdd' nullMeterUpdate (Just ld') (Just k)
 				>>= maybe
 					stop
 					(\addedk -> next $ Command.Add.cleanup addedk True)
diff --git a/doc/bugs/add_overwrite_race.mdwn b/doc/bugs/add_overwrite_race.mdwn
index d64028ad71..b971672fcd 100644
--- a/doc/bugs/add_overwrite_race.mdwn
+++ b/doc/bugs/add_overwrite_race.mdwn
@@ -29,6 +29,14 @@ be that it's faster. It also sometimes relies on git add to check gitignore,
 although sometimes redundandly, some of the callers of it may rely on that
 and have to be changed to check it first themselves.
 
+Since adding a file to the annex also involves locking it down and
+detecting modifications made while generating the key, update-index is
+sufficient.
+
+> Update: This is done for `git-annex add`, using addSymlink. But addLink
+> is still in use elsewhere, and those other users might also be subject to
+> similar races.
+
 When it's adding a file unlocked, it already stages the pointer file using
 update-index instead so there is no overwrite problem there.
 
@@ -44,5 +52,9 @@ added to the annex after all. Test case for this:
 	git-annex add
 	git diff --cached 1
 
-Unsure how to fix this case yet?
+Unsure how to fix this case yet? Maybe it needs to cache the inode,
+hash the file content, then verifiy the inode did not change during
+hashing, and then also use update-index.
+
+
 --[[Joey]]

bug report
Sponsored-by: Luke Shumaker on Patreon
diff --git a/doc/bugs/add_overwrite_race.mdwn b/doc/bugs/add_overwrite_race.mdwn
new file mode 100644
index 0000000000..d64028ad71
--- /dev/null
+++ b/doc/bugs/add_overwrite_race.mdwn
@@ -0,0 +1,48 @@
+I was running `git-annex add` on 5 gb of files, and accidentially overwrote
+some of the first ones, which it had already processed, while it was
+running. This caused binary files to get staged in git, rather than the
+annex pointers. 
+
+Test case: 
+
+	echo hi > 1
+	dd if=/dev/urandom of=2 bs=1M count=1000
+	(sleep 2s; rm 1; echo bye > 1) &
+	git-annex add
+	git diff --cached 1
+	diff --git a/1 b/1
+	new file mode 100644
+	index 0000000..b023018
+	--- /dev/null
+	+++ b/1
+	@@ -0,0 +1 @@
+	+bye
+
+This happens due to ingestAdd using addLink on the symlink, 
+which just queues a "git add" of the file for later. In the
+meantime, the symlink is replaced with something else, so git
+adds that.
+
+It seems that the solution will be to use update-index rather than git add.
+Note that addLink has a comment about why it uses git add, which seems to mostly
+be that it's faster. It also sometimes relies on git add to check gitignore,
+although sometimes redundandly, some of the callers of it may rely on that
+and have to be changed to check it first themselves.
+
+When it's adding a file unlocked, it already stages the pointer file using
+update-index instead so there is no overwrite problem there.
+
+But, there's a similar problem when it decides not to annex a file
+and adds it to git. If the file content is overwritten then, it will
+git add the new content. Which may be large enough that it should have been
+added to the annex after all. Test case for this:
+
+	git config annex.largefiles largerthan=3b
+	echo hi > 1
+	dd if=/dev/urandom of=2 bs=1M count=1000
+	(sleep 2s; rm 1; echo bye > 1) &
+	git-annex add
+	git diff --cached 1
+
+Unsure how to fix this case yet?
+--[[Joey]]

comment
diff --git a/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_2_23398e5403c3a9f40d3b544139ee9915._comment b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_2_23398e5403c3a9f40d3b544139ee9915._comment
new file mode 100644
index 0000000000..f0c481cfdc
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_2_23398e5403c3a9f40d3b544139ee9915._comment
@@ -0,0 +1,45 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2022-06-14T15:51:57Z"
+ content="""
+The change that broke your test uses the exact same url uuid detection code
+that git-annex usually uses. So if you were using an url without .git
+before, it seems like it would have failed to detect a uuid after enabling
+the special remote.
+
+If that reasoning does not hold, I'm going to need a test case to
+understand why. Unfortunately, your test case is hard for me to
+understand what it's doing. 
+
+I can't even get git to clone a non-bare git repo served over plain http
+without using .git in the url, so don't understand how your test case is
+doing that.
+
+When I manually make a git remote with a http url without .git
+(which git cannot pull from),
+I see the git-annex uuid detection code failing, as I would expect:
+
+	git remote add test http://localhost/~joey/tmp/foo/
+	git config annex.security.allowed-ip-addresses all
+	git-annex info
+	
+	  Remote test not usable by git-annex; setting annex-ignore
+	
+	  http://localhost/~joey/tmp/foo//config download failed: Not Found
+
+And the initremote behavior is consistent with that:
+
+	git-annex initremote bar type=git location=http://localhost/~joey/tmp/foo/
+	initremote bar
+	  http://localhost/~joey/tmp/foo//config download failed: Not Found
+	
+	git-annex: git repository does not have an annex uuid
+	failed
+
+(It's worth noting that the change was made to handle another datalad use
+case, [[[initremote_type__61__git_is_not_working_for_unkn_reason]].
+I'd consider that use case unusual, and marginally worth supporting.
+I could of course revert the change, but then I'd have to close that
+bug as wontfix.)
+"""]]

Added a comment
diff --git a/doc/forum/git-annex-addunused_with_original_path+filename/comment_2_cddedfd83ee154f44b83f2414eb103d0._comment b/doc/forum/git-annex-addunused_with_original_path+filename/comment_2_cddedfd83ee154f44b83f2414eb103d0._comment
new file mode 100644
index 0000000000..479c354206
--- /dev/null
+++ b/doc/forum/git-annex-addunused_with_original_path+filename/comment_2_cddedfd83ee154f44b83f2414eb103d0._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="KachoOji"
+ avatar="http://cdn.libravatar.org/avatar/ccc61e285ed2498e328a131b13cfe712"
+ subject="comment 2"
+ date="2022-06-14T15:32:48Z"
+ content="""
+Ok, thank you.
+"""]]

Added a comment
diff --git a/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_1_54456b2ce0964933db23257149b5a651._comment b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_1_54456b2ce0964933db23257149b5a651._comment
new file mode 100644
index 0000000000..80799bb01c
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__/comment_1_54456b2ce0964933db23257149b5a651._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 1"
+ date="2022-06-13T23:52:48Z"
+ content="""
+the \"heart\" of the issue is that in initremote we provide URL which is without `/.git` suffix.   That used to work just fine and [our test](https://github.com/datalad/datalad/blob/HEAD/datalad/distribution/tests/test_siblings.py#L560) testing `git annex get` on the clone accomplished the mission.
+"""]]

initial whining about type=git initremote stopped
diff --git a/doc/bugs/initremote_type__61__git_fresh___34__regression__34__.mdwn b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__.mdwn
new file mode 100644
index 0000000000..c05f1f1ee8
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_fresh___34__regression__34__.mdwn
@@ -0,0 +1,20 @@
+Our test started to fail on 20220610 daily tests, with smth like
+
+```
+2022-06-10T05:51:16.5860150Z >               **results,
+2022-06-10T05:51:16.5860330Z             )
+2022-06-10T05:51:16.5861310Z E           datalad.runner.exception.CommandError: CommandError: 'git -c diff.ignoreSubmodules=none annex initremote fresh-sr type=git location=http://127.0.0.1:50821/ autoenable=true -c annex.dotfiles=true' failed with exitcode 1 under /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/datalad_temp_test_as_common_datasourceerpclil1 [out: 'initremote fresh-sr 
+2022-06-10T05:51:16.5861910Z E           
+2022-06-10T05:51:16.5862300Z E           failed'] [err: 'http://127.0.0.1:50821//config download failed: File not found
+2022-06-10T05:51:16.5862730Z E           git-annex: git repository does not have an annex uuid
+2022-06-10T05:51:16.5863040Z E           initremote: 1 failed']
+2022-06-10T05:51:16.5863180Z 
+2022-06-10T05:51:16.5863540Z ../../../../hostedtoolcache/Python/3.7.13/x64/lib/python3.7/site-packages/datalad/runner/runner.py:205: CommandError
+
+```
+most likely due to  [10.20220525-64-g14584e7a3](https://git.kitenet.net/index.cgi/git-annex.git/commit/?id=14584e7a384be87e2fd613ef93a8393d91c0842f) "initremote type=git probe uuid" .
+
+yet to unravel details/change in behavior, but since changelog entry says only about "Improve handling of type=git special remotes" I would not expect negative change in the behavior. Joey -- anything immediate comes to mind?
+
+[[!meta author=yoh]]
+[[!tag projects/datalad]]

add rf
diff --git a/doc/related_software.mdwn b/doc/related_software.mdwn
index fd09e469fd..1548fd1f24 100644
--- a/doc/related_software.mdwn
+++ b/doc/related_software.mdwn
@@ -61,4 +61,8 @@ designed to interoperate with it.
   TypeScript interface to git-annex (and git) commands. It can also be used
   by JavaScript.
 
+* [rf](https://github.com/apuapaquola/rf) is a minimalist framework for
+  reproducible computational workflows in which version control is
+  done with git + git annex
+
 See also [[not]] for software that is *not* related to git-annex, but similar.

comment
diff --git a/doc/todo/Filter_a_tree_with_pattern/comment_1_17dac31227b8f95c5ca50f4a4cf5a425._comment b/doc/todo/Filter_a_tree_with_pattern/comment_1_17dac31227b8f95c5ca50f4a4cf5a425._comment
new file mode 100644
index 0000000000..fb2ddcdde1
--- /dev/null
+++ b/doc/todo/Filter_a_tree_with_pattern/comment_1_17dac31227b8f95c5ca50f4a4cf5a425._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-13T17:39:41Z"
+ content="""
+Most of this can be done with either `git-annex adjust --hide-missing` or views. 
+
+The only thing that cannot is limiting files to those matching a glob,
+though views can limit files to the contents of directories.
+
+Unfortunately, adjusted branches and views don't compose. There is a todo
+about that, [[todo/unify_adjust_with_view]]. I think that is what you're
+looking for, or very close.
+"""]]

comment
diff --git a/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_/comment_1_996a54315c21e1dcb94cca9576d1d5f7._comment b/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_/comment_1_996a54315c21e1dcb94cca9576d1d5f7._comment
new file mode 100644
index 0000000000..a4e66d94f6
--- /dev/null
+++ b/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_/comment_1_996a54315c21e1dcb94cca9576d1d5f7._comment
@@ -0,0 +1,20 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-13T17:14:42Z"
+ content="""
+Well, this is a case where `git add` is failing for some reason. It seems
+very likely that it output some error message to stderr, so I'd look for
+that in the log. It's not clear to me if your grep would show that.
+
+If `git add` was silent on stderr, then I'd move on to thinking it must
+have crashed  (or have a weird bug). The 123 exit code is from xargs and
+only indicates that `git add` exited nonzero, so we don't know what the
+actual exit code was. But it seems that `git add` did not die of a
+segfault; it if had the exit code from xargs would be 125.
+
+Since git-annex is batching up some number of files and sending them all to
+a single `git add`, the files that come last would fail to be added if any
+earlier file somehow caused `git add` to give up. So that could explain why
+it happens more often for some files.
+"""]]

add todo based on forum post
diff --git a/doc/forum/git-annex-addunused_with_original_path+filename/comment_1_b24b20d6351d0373f9517438c33f05c1._comment b/doc/forum/git-annex-addunused_with_original_path+filename/comment_1_b24b20d6351d0373f9517438c33f05c1._comment
new file mode 100644
index 0000000000..7fce7dcc2d
--- /dev/null
+++ b/doc/forum/git-annex-addunused_with_original_path+filename/comment_1_b24b20d6351d0373f9517438c33f05c1._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-13T17:06:21Z"
+ content="""
+There is not currently an easy way to do that. Although I suppose you could
+write a script to rename the files based on the filenames whereused
+outputs.
+
+I do think this would be a good enhancement. I've made a todo
+[[todo/git-annex-addunused-historical]].
+"""]]
diff --git a/doc/todo/git-annex-addunused-historical.mdwn b/doc/todo/git-annex-addunused-historical.mdwn
new file mode 100644
index 0000000000..17e66c2b2f
--- /dev/null
+++ b/doc/todo/git-annex-addunused-historical.mdwn
@@ -0,0 +1,7 @@
+`git-annex addunused` could have a --historical option that adds back to
+the last-seen filename, like whereused does.
+
+Something would need to be done to handle the case where several unused
+keys use the same filename, or a file by that name already exists. Probably
+the simplest thing would be to add the key to the end of the filename.
+--[[Joey]]

diff --git a/doc/todo/Filter_a_tree_with_pattern.mdwn b/doc/todo/Filter_a_tree_with_pattern.mdwn
new file mode 100644
index 0000000000..c32996446a
--- /dev/null
+++ b/doc/todo/Filter_a_tree_with_pattern.mdwn
@@ -0,0 +1,55 @@
+Git-annex is able to create trees that are a perspective of the "real" tree via adjustment and metadata-driven views. 
+
+It would be useful if git-annex was also able to create trees that are the same as the main branch but only include a subset of the files; decided based on internal and external metadata.
+
+Use-cases:
+
+* Only check out subtitles in a specific language to reduce clutter
+* Only check out movies in one format
+* Only check out images with a certain metadata tag
+* Only check out files that are present
+
+Some of these are already possible but would require manual metadata tagging or would result in a totally different tree. This can be confusing to both users and software.
+
+UX examples:
+
+```
+$ ls Movies/
+movie.mkv movie.iso movie.mp4 movie.en_US.ass movie.de_DE.ass
+$ git annex filterview include=*.mkv
+$ ls Movies/
+movie.mkv
+$ git annex filterview include=*.mp4 or include=*.enUS.ass
+$ ls Movies/
+movie.mp4 movie.en_US.ass
+$ git annex filterview reset
+$ ls Movies/
+movie.mkv movie.iso movie.mp4 movie.en_US.ass movie.de_DE.ass
+$ git annex get Movies
+$ git annex filterview in=here
+$ ls Movies/
+movie.mkv movie.iso movie.mp4 movie.en_US.ass movie.de_DE.ass
+$ git annex drop Movies/*.ass
+$ ls Movies/
+movie.mkv movie.iso movie.mp4
+$ ls Photos/Cats/
+very_cute.jpg awwwww.jpg :3.jpg
+$ git annex filterview year=2022
+$ ls Photos/Cats/
+very_cute.jpg :3.jpg
+```
+
+Discussion points:
+
+* Should filters be additive or replace the old one?
+* Should it be possible to apply a filter to just a subtree?
+
+***
+
+Another useful feature that is tangentially related would be something like this:
+
+```
+$ git annex filterview root=Movies/
+$ ls
+movie.mkv movie.iso movie.mp4 movie.en_US.ass movie.de_DE.ass
+```

diff --git a/doc/forum/git-annex-addunused_with_original_path+filename.mdwn b/doc/forum/git-annex-addunused_with_original_path+filename.mdwn
new file mode 100644
index 0000000000..351f194f75
--- /dev/null
+++ b/doc/forum/git-annex-addunused_with_original_path+filename.mdwn
@@ -0,0 +1,11 @@
+I lost/misplaced some files on two disks (about 5000+7000 files), which are now showing up as unused files.
+
+"git annex whereused --historical --unused" shows the original paths and filenames. 
+these are however not retained when restoring via "git-annex-addunused"
+
+Is there a way/option to restore these to the original paths + filenames?
+I don't see myself renaming all these files manually...*
+
+\* I have a backup, but that leaves a small window for dataloss as the disks are full, so I must dropunused first. So I would prefer a addunused if possible (will also be faster I presume).
+
+Thanks

Added a comment
diff --git a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_3_aa083a54e0c77cdba9939a061b509488._comment b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_3_aa083a54e0c77cdba9939a061b509488._comment
new file mode 100644
index 0000000000..af5678741d
--- /dev/null
+++ b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_3_aa083a54e0c77cdba9939a061b509488._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 3"
+ date="2022-06-09T21:53:05Z"
+ content="""
+
+> fixed in [10.20220525-73-g13fc6a9b6](https://git.kitenet.net/index.cgi/git-annex.git/commit/?id=13fc6a9b6ad4f0ee0a783c91825ab7ad35dd2b64) --[[yarikoptic]]
+
+PS I wish there was a similar but automated annotation of those `[done]` to know actually \"when/with what\"
+"""]]

avoid cleaning up move log when drop from remote fails
move: Improve resuming a move that succeeded in transferring the content,
but where dropping failed due to eg a network problem, in cases where
numcopies checks prevented the resumed move from dropping the object from
the source repository.
This was earlier done for moves that got interrupted during the drop stage.
Sponsored-by: Svenne Krap on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 24b3c6cb5b..83b62e8e82 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,10 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
   * Fix retrival of an empty file that is stored in a special remote with 
     chunking enabled.
     (Fixes a reversion in 8.20201103)
+  * move: Improve resuming a move that succeeded in transferring the
+    content, but where dropping failed due to eg a network problem, 
+    in cases where numcopies checks prevented the resumed
+    move from dropping the object from the source repository.
 
  -- Joey Hess <id@joeyh.name>  Wed, 01 Jun 2022 13:23:05 -0400
 
diff --git a/Command/Move.hs b/Command/Move.hs
index 31f6d05a79..bc009d9177 100644
--- a/Command/Move.hs
+++ b/Command/Move.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2022 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -150,6 +150,7 @@ toPerform dest removewhen key afile fastcheck isthere = do
 				then finish deststartedwithcopy $
 					Remote.logStatus dest key InfoPresent
 				else do
+					logMoveCleanup deststartedwithcopy
 					when fastcheck $
 						warning "This could have failed because --fast is enabled."
 					stop
@@ -162,10 +163,11 @@ toPerform dest removewhen key afile fastcheck isthere = do
 	finish deststartedwithcopy setpresentremote = case removewhen of
 		RemoveNever -> do
 			setpresentremote
+			logMoveCleanup deststartedwithcopy
 			next $ return True
 		RemoveSafe -> lockContentForRemoval key lockfailed $ \contentlock -> do
 			srcuuid <- getUUID
-			willDropMakeItWorse srcuuid destuuid deststartedwithcopy key afile >>= \case
+			r <- willDropMakeItWorse srcuuid destuuid deststartedwithcopy key afile >>= \case
 				DropAllowed -> drophere setpresentremote contentlock "moved"
 				DropCheckNumCopies -> do
 					(numcopies, mincopies) <- getSafestNumMinCopies afile key
@@ -176,6 +178,8 @@ toPerform dest removewhen key afile fastcheck isthere = do
 						 (drophere setpresentremote contentlock . showproof)
 						 (faileddrophere setpresentremote)
 				DropWorse -> faileddrophere setpresentremote
+			logMoveCleanup deststartedwithcopy
+			return r
 	showproof proof = "proof: " ++ show proof
 	drophere setpresentremote contentlock reason = do
 		fastDebug "Command.Move" $ unwords
@@ -240,35 +244,42 @@ fromPerform src removewhen key afile = do
 	get = notifyTransfer Download afile $
 		download src key afile stdRetry
 	
-	dispatch _ _ False = stop -- failed
-	dispatch RemoveNever _ True = next $ return True -- copy complete
+	dispatch _ deststartedwithcopy False = do
+		logMoveCleanup deststartedwithcopy
+		stop -- copy failed
+	dispatch RemoveNever deststartedwithcopy True = do
+		logMoveCleanup deststartedwithcopy
+		next $ return True -- copy complete
 	dispatch RemoveSafe deststartedwithcopy True = lockContentShared key $ \_lck -> do
 		destuuid <- getUUID
 		willDropMakeItWorse srcuuid destuuid deststartedwithcopy key afile >>= \case
-			DropAllowed -> dropremote "moved"
+			DropAllowed -> dropremote deststartedwithcopy "moved"
 			DropCheckNumCopies -> do
 				(numcopies, mincopies) <- getSafestNumMinCopies afile key
 				(tocheck, verified) <- verifiableCopies key [Remote.uuid src]
 				verifyEnoughCopiesToDrop "" key Nothing numcopies mincopies [Remote.uuid src] verified
-					tocheck (dropremote . showproof) faileddropremote
-			DropWorse -> faileddropremote		
+					tocheck (dropremote deststartedwithcopy . showproof) (faileddropremote deststartedwithcopy)
+			DropWorse -> faileddropremote deststartedwithcopy
 	
 	srcuuid = Remote.uuid src
 	
 	showproof proof = "proof: " ++ show proof
 	
-	dropremote reason = do
+	dropremote deststartedwithcopy reason = do
 		fastDebug "Command.Move" $ unwords
 			[ "Dropping from remote"
 			, show src
 			, "(" ++ reason ++ ")"
 			]
 		ok <- Remote.action (Remote.removeKey src key)
+		when ok $
+			logMoveCleanup deststartedwithcopy
 		next $ Command.Drop.cleanupRemote key src (Command.Drop.DroppingUnused False) ok
 	
-	faileddropremote = do
+	faileddropremote deststartedwithcopy = do
 		showLongNote "(Use --force to override this check, or adjust numcopies.)"
 		showLongNote $ "Content not dropped from " ++ Remote.name src ++ "."
+		logMoveCleanup deststartedwithcopy
 		next $ return False
 
 {- Moves (or copies) the content of an annexed file from reachable remotes
@@ -312,7 +323,7 @@ toHereStart removewhen afile key ai si =
  - repository already had a copy of the file before the move began.
  -}
 willDropMakeItWorse :: UUID -> UUID -> DestStartedWithCopy -> Key -> AssociatedFile -> Annex DropCheck
-willDropMakeItWorse srcuuid destuuid (DestStartedWithCopy deststartedwithcopy) key afile =
+willDropMakeItWorse srcuuid destuuid (DestStartedWithCopy deststartedwithcopy _) key afile =
 	ifM (Command.Drop.checkRequiredContent (Command.Drop.PreferredContentChecked False) srcuuid key afile)
 		( if deststartedwithcopy
 			then unlessforced DropCheckNumCopies
@@ -334,10 +345,18 @@ willDropMakeItWorse srcuuid destuuid (DestStartedWithCopy deststartedwithcopy) k
 
 data DropCheck = DropWorse | DropAllowed | DropCheckNumCopies
 
-newtype DestStartedWithCopy = DestStartedWithCopy Bool
+data DestStartedWithCopy = DestStartedWithCopy Bool (Annex ())
+
+{- This should be called once the move has succeeded, or if it failed
+ - without doing anything. It should not be called if the move transferred
+ - the content but failed to drop due to eg a network error. In such a
+ - case, the move can be restarted later, so the move log should be
+ - preserved. -}
+logMoveCleanup :: DestStartedWithCopy -> Annex ()
+logMoveCleanup (DestStartedWithCopy _ a) = a
 
 {- Runs an action that performs a move, and logs the move, allowing an
- - interrupted move to be restarted later.
+ - failed or interrupted move to be re-done later.
  -
  - This deals with the situation where dest did not start with a copy,
  - but the move downloaded it, and was then interrupted before dropping
@@ -346,7 +365,7 @@ newtype DestStartedWithCopy = DestStartedWithCopy Bool
  - DestStartedWithCopy, this avoids that annoyance.
  -}
 logMove :: UUID -> UUID -> Bool -> Key -> (DestStartedWithCopy -> Annex a) -> Annex a
-logMove srcuuid destuuid deststartedwithcopy key a = bracket setup cleanup go
+logMove srcuuid destuuid deststartedwithcopy key a = go =<< setup
   where
 	logline = L.fromStrict $ B8.unwords
 		[ fromUUID srcuuid
@@ -378,8 +397,9 @@ logMove srcuuid destuuid deststartedwithcopy key a = bracket setup cleanup go
 			wasnocopy <- checkLogFile (fromRawFilePath logf) gitAnnexMoveLock
 				(== logline)
 			if wasnocopy
-				then go' False
-				else go' deststartedwithcopy
-		| otherwise = go' deststartedwithcopy
+				then go' logf False
+				else go' logf deststartedwithcopy
+		| otherwise = go' logf deststartedwithcopy
 
-	go' = a . DestStartedWithCopy
+	go' logf deststartedwithcopy' = a $
+		DestStartedWithCopy deststartedwithcopy' (cleanup logf)
diff --git a/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn b/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn
index b70c81ff8c..437f140be0 100644
--- a/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn
+++ b/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn
@@ -10,3 +10,5 @@ let the drop succeed.
 The problem is that logMove runs its cleanup action in this case.
 So the move.log gets the record of the move cleaned from it. It seems that
 the cleanup action should avoid doing that when the move failed. --[[Joey]]
+
+> [[fixed|done]] --[[Joey]]

fix to use 1 chunk for empty file
Fix retrival of an empty file that is stored in a special remote with
chunking enabled.
The speculative chunk stuff caused a reversion by adding an empty list for
the empty file. Which is just wrong; the empty file is still stored on the
remote, and should be retrieved like any other file. It uses 1 chunk, so
`max 1` is the simple fix.
Sponsored-by: Noam Kremen on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index ae14d55acd..24b3c6cb5b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -6,6 +6,9 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
     The location value no longer needs to match the url of an existing
     git remote, and locations not using ssh:// will work now, including
     both paths and host:/path
+  * Fix retrival of an empty file that is stored in a special remote with 
+    chunking enabled.
+    (Fixes a reversion in 8.20201103)
 
  -- Joey Hess <id@joeyh.name>  Wed, 01 Jun 2022 13:23:05 -0400
 
diff --git a/Remote/Helper/Chunked.hs b/Remote/Helper/Chunked.hs
index a8d928c597..72e33fe77e 100644
--- a/Remote/Helper/Chunked.hs
+++ b/Remote/Helper/Chunked.hs
@@ -554,7 +554,7 @@ chunkKeys' onlychunks u chunkconfig k = do
 			Nothing -> l
 			Just keysz -> 
 				let (d, m) = keysz `divMod` fromIntegral chunksz
-				    chunkcount = d + if m == 0 then 0 else 1
+				    chunkcount = max 1 (d + if m == 0 then 0 else 1)
  				    v = (FixedSizeChunks chunksz, chunkcount)
 				in if v `elem` recorded
 					then l
diff --git a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn
index b2358f32a3..a2ad892628 100644
--- a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn
+++ b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn
@@ -86,3 +86,5 @@ so I do not think it is an issue of the remote (which might also have some size
 
 
 tried both 8.20211123 and  10.20220504 from debian 
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_2_1d78459554bc12d0423a317bd9f466d1._comment b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_2_1d78459554bc12d0423a317bd9f466d1._comment
new file mode 100644
index 0000000000..a37c6f1d06
--- /dev/null
+++ b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_2_1d78459554bc12d0423a317bd9f466d1._comment
@@ -0,0 +1,17 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2022-06-09T18:05:15Z"
+ content="""
+This was broken by [[!commit dad4be97c2057db1ef3a13bb983d1701a90c9069]].
+
+For a key of size zero, `addspeculative` adds on a `[]` to the list of chunk
+key. But that causes retrieveChunks to think that it's already retrieved
+all the chunks, so it avoids doing any retrieval, so the file is not
+written. It needs to retrieve the empty key even though it's empty,
+so it can decrypt it when it's encrypted; git-annex does not special case
+empty file retrieval.
+
+Odd that testremote did not detect this. It does test with empty keys, and
+with chunking.
+"""]]

comment
diff --git a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_1_6af7ba08e93349b2c6176f91ba25a06f._comment b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_1_6af7ba08e93349b2c6176f91ba25a06f._comment
new file mode 100644
index 0000000000..a2ac6c2d01
--- /dev/null
+++ b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote/comment_1_6af7ba08e93349b2c6176f91ba25a06f._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-09T17:34:31Z"
+ content="""
+I can reproduce this with a directory special remote, initialized with
+
+	git annex initremote d type=directory directory=../d encryption=shared chunk=100MiB
+
+The chunking is what causes the problem.
+"""]]

comment
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_4_ea413a9dec0243688cbfcaf8a5998ac5._comment b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_4_ea413a9dec0243688cbfcaf8a5998ac5._comment
new file mode 100644
index 0000000000..d764a5c68f
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_4_ea413a9dec0243688cbfcaf8a5998ac5._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 4"""
+ date="2022-06-09T17:28:19Z"
+ content="""
+Re file:// urls, it does now work to use them in location=. I don't know if
+I'd consider using them any better than absolute paths though. YMMV.
+"""]]

close as not a bug
diff --git a/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn
index 2ecf1db6d0..499a732e69 100644
--- a/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn
+++ b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn
@@ -23,3 +23,4 @@ That however, is substantially more complex than looking at the exit code -- giv
 99.9% are just splendid. Wondering here whether this particular consequence of the change was intentional and avoidable?
 
 
+> [[notabug|done]] --[[Joey]]
diff --git a/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key/comment_1_e35bef8fb0b9a40938022c43c87df597._comment b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key/comment_1_e35bef8fb0b9a40938022c43c87df597._comment
new file mode 100644
index 0000000000..5df59eb22b
--- /dev/null
+++ b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key/comment_1_e35bef8fb0b9a40938022c43c87df597._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-09T17:19:07Z"
+ content="""
+That was a fix for an old reversion, so trying to use fsck for that would
+have also failed before 8.20201129.
+
+Fsck is not the right tool for the job. Fscking can fail for any of a
+variety of reasons, including probably reasons that have not been thought
+of or implemented yet, so taking failure to mean only one possible thing is
+doomed.
+
+A better tool exists: `git-annex checkpresentkey`
+"""]]

initial report on difficulty with 0 length file
diff --git a/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn
new file mode 100644
index 0000000000..b2358f32a3
--- /dev/null
+++ b/doc/bugs/error_out_on_file_size_0_from_external_spec_remote.mdwn
@@ -0,0 +1,88 @@
+### Please describe the problem.
+
+Here is a reproducer using the place where I encountered it -- using git-annex-remote-rclone (tiny bash script; needs fixing though for recent rclone -- take from [my fork where I am tuning testing etc](https://github.com/yarikoptic/git-annex-remote-rclone/blob/enh-tests/git-annex-remote-rclone)) [https://github.com/DanielDent/git-annex-remote-rclone/](https://github.com/DanielDent/git-annex-remote-rclone/)
+
+<details>
+<summary>this is a reproducer which creates repo and commits to annex of size 0</summary> 
+
+```bash
+#!/bin/bash
+
+cd "$(mktemp -d ${TMPDIR:-/tmp}/dl-XXXXXXX)"
+
+set -eux
+
+# provide versioning information to possibly ease troubleshooting
+git annex version
+rclone --version
+
+export HOME=$PWD
+echo -e '[local]\ntype = local\nnounc =' > ~/.rclone.conf
+# to pacify git/git-annex
+git config --global user.name Me
+git config --global user.email me@example.com
+git config --global init.defaultBranch master
+
+# Prepare rclone remote local store
+mkdir rclone-local
+export RCLONE_PREFIX=$PWD/rclone-local
+
+git-annex version
+mkdir testrepo
+cd testrepo
+git init .
+git-annex init
+git-annex initremote GA-rclone-CI type=external externaltype=rclone target=local prefix=$RCLONE_PREFIX chunk=100MiB encryption=shared mac=HMACSHA512
+
+# Rudimentary test, spaces in the filename must be ok, 0 length files should be ok
+
+touch "test 0"
+
+#echo 1 > "test 1"
+
+git-annex add *
+git-annex copy * --to GA-rclone-CI
+git-annex drop *
+git-annex get --debug *
+
+```
+</details> 
+
+where it ends with
+
+```
++ git-annex get --debug 'test 0'
+[2022-06-09 13:16:20.668338168] (Utility.Process) process [1022440] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","ls-files","--stage","-z","--error-unmatch","--","test 0"]
+[2022-06-09 13:16:20.668784836] (Utility.Process) process [1022441] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","cat-file","--batch-check=%(objectname) %(objecttype) %(objectsize)","--buffer"]
+[2022-06-09 13:16:20.669111286] (Utility.Process) process [1022442] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","cat-file","--batch=%(objectname) %(objecttype) %(objectsize)","--buffer"]
+[2022-06-09 13:16:20.66965775] (Utility.Process) process [1022443] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","show-ref","git-annex"]
+[2022-06-09 13:16:20.670318546] (Utility.Process) process [1022443] done ExitSuccess
+[2022-06-09 13:16:20.67074703] (Utility.Process) process [1022444] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","show-ref","--hash","refs/heads/git-annex"]
+[2022-06-09 13:16:20.671504449] (Utility.Process) process [1022444] done ExitSuccess
+[2022-06-09 13:16:20.672125442] (Utility.Process) process [1022445] read: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","log","refs/heads/git-annex..0da4aa78e4397feb9906400d1e08aaebccac1be1","--pretty=%H","-n1"]
+[2022-06-09 13:16:20.673019668] (Utility.Process) process [1022445] done ExitSuccess
+[2022-06-09 13:16:20.673440353] (Utility.Process) process [1022446] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","cat-file","--batch=%(objectname) %(objecttype) %(objectsize)","--buffer"]
+get test 0 [2022-06-09 13:16:20.674594171] (Utility.Process) process [1022448] chat: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","cat-file","--batch"]
+(from GA-rclone-CI...) 
+[2022-06-09 13:16:20.682205613] (Annex.Perms) freezing content directory .git/annex/objects/pX/ZJ/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+
+git-annex: .git/annex/tmp/SHA256E-s0--e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855: rename: does not exist (No such file or directory)
+failed
+[2022-06-09 13:16:20.682296739] (Utility.Process) process [1022446] done ExitSuccess
+[2022-06-09 13:16:20.682333234] (Utility.Process) process [1022442] done ExitSuccess
+[2022-06-09 13:16:20.682384256] (Utility.Process) process [1022441] done ExitSuccess
+[2022-06-09 13:16:20.682408227] (Utility.Process) process [1022440] done ExitSuccess
+[2022-06-09 13:16:20.682701178] (Utility.Process) process [1022448] done ExitSuccess
+get: 1 failed
+
+```
+
+- you can see that there is not even an attempt to talk to the remote. git-annex crashes before then with that " rename: does not exist (No such file or directory)"
+
+so I do not think it is an issue of the remote (which might also have some size 0 issues from looking at some conditions)
+
+
+### What version of git-annex are you using? On what operating system?
+
+
+tried both 8.20211123 and  10.20220504 from debian 

initremote type=git probe uuid
rather than matching path of an existing remote to find the uuid.
The main benefit of this is that locations not using ssh:// will work
now, including both paths and host:/path
The other benefit is that it's a simpler interface, no need to have an
existing remote with the same url and some other name. Although that
will still work of course.
This does rely on tryGitConfigRead working when given a Git.Repo that is
not a remote. Luckily, it works fine that way.
Also, tryGitConfigRead will auto-init a local repo that has a git-annex
branch. I did not enable auto-init of ssh repos though.
The uuid discovery actually happens twice; initremote discovers it,
and uses it to store the special remote config, but does not set it in the
git remote it creates. So the next run of git-annex does uuid discovery
again, and caches it that time. This could be improved for a tiny
speedup, but I didn't want to complicate things for that in this
commit.
Sponsored-by: Dartmouth College's DANDI project
diff --git a/CHANGELOG b/CHANGELOG
index 8e571fe8d9..ae14d55acd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,10 @@ git-annex (10.20220526) UNRELEASED; urgency=medium
 
   * init: Added --no-autoenable option.
   * info: Added --autoenable option.
+  * initremote: Improve handling of type=git special remotes.
+    The location value no longer needs to match the url of an existing
+    git remote, and locations not using ssh:// will work now, including
+    both paths and host:/path
 
  -- Joey Hess <id@joeyh.name>  Wed, 01 Jun 2022 13:23:05 -0400
 
diff --git a/Remote/Git.hs b/Remote/Git.hs
index a902032bd7..856d992f69 100644
--- a/Remote/Git.hs
+++ b/Remote/Git.hs
@@ -107,11 +107,11 @@ list autoinit = do
 				Git.Construct.remoteNamed n $
 					Git.Construct.fromRemoteLocation (Git.fromConfigValue url) g
 
-{- Git remotes are normally set up using standard git command, not
+{- Git remotes are normally set up using standard git commands, not
  - git-annex initremote and enableremote.
  -
- - For initremote, the git remote must already be set up, and have a uuid.
- - Initremote simply remembers its location.
+ - For initremote, probe the location to find the uuid.
+ - and set up a git remote.
  -
  - enableremote simply sets up a git remote using the stored location.
  - No attempt is made to make the remote be accessible via ssh key setup,
@@ -119,17 +119,16 @@ list autoinit = do
  -}
 gitSetup :: SetupStage -> Maybe UUID -> Maybe CredPair -> RemoteConfig -> RemoteGitConfig -> Annex (RemoteConfig, UUID)
 gitSetup Init mu _ c _ = do
-	let location = fromMaybe (giveup "Specify location=url") $
-		Url.parseURIRelaxed . fromProposedAccepted
-			=<< M.lookup locationField c
-	rs <- Annex.getGitRemotes
-	u <- case filter (\r -> Git.location r == Git.Url location) rs of
-		[r] -> getRepoUUID r
-		[] -> giveup "could not find existing git remote with specified location"
-		_ -> giveup "found multiple git remotes with specified location"
-	if isNothing mu || mu == Just u
-		then return (c, u)
-		else error "git remote did not have specified uuid"
+	let location = maybe (giveup "Specify location=url") fromProposedAccepted $
+		M.lookup locationField c
+	r <- inRepo $ Git.Construct.fromRemoteLocation location
+	r' <- tryGitConfigRead False r False
+	let u = getUncachedUUID r'
+	if u == NoUUID
+		then gitveup "git repository does not have an annex uuid"
+		else if isNothing mu || mu == Just u
+			then enableRemote (Just u) c
+			else giveup "git repository does not have specified uuid"
 gitSetup (Enable _) mu _ c _ = enableRemote mu c
 gitSetup (AutoEnable _) mu _ c _ = enableRemote mu c
 
@@ -142,7 +141,7 @@ enableRemote (Just u) c = do
 		, Param $ maybe (giveup "no location") fromProposedAccepted (M.lookup locationField c)
 		]
 	return (c, u)
-enableRemote Nothing _ = error "unable to enable git remote with no specified uuid"
+enableRemote Nothing _ = giveup "unable to enable git remote with no specified uuid"
 
 {- It's assumed to be cheap to read the config of non-URL remotes, so this is
  - done each time git-annex is run in a way that uses remotes, unless
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn
index 08d6821edd..fad43f305c 100644
--- a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn
@@ -66,3 +66,5 @@ above
 
 [[!meta author=yoh]]
 [[!tag projects/dandi]]
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_0baadee49b21451a99a38306a98e3e56._comment b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_0baadee49b21451a99a38306a98e3e56._comment
new file mode 100644
index 0000000000..c77c55a34f
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_0baadee49b21451a99a38306a98e3e56._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 2"""
+ date="2022-06-09T17:04:23Z"
+ content="""
+Implemented probing of the uuid of the repo location. Which may change
+how you use this feature. Although the old roundabout method of having an
+existing git remote and running initremote with the same location will
+work too, it's not neccessary to do that anymore.
+"""]]
diff --git a/doc/special_remotes/git.mdwn b/doc/special_remotes/git.mdwn
index 0a7558d5d1..3c61b750fe 100644
--- a/doc/special_remotes/git.mdwn
+++ b/doc/special_remotes/git.mdwn
@@ -7,29 +7,12 @@ git-annex and git will use the remote the same as any normal git remote,
 but its url will be recorded in the repository. One benefit of doing this
 is it allows [[git-annex init|git-annex-init]] to autoenable the remote.
 
-First you need a regular git remote with the url that you want to use for
-the special remote.
-
-	git remote add tmpremote ssh://...
-
-Then, to set up the special remote:
+To set up such a special remote:
 
 	git annex initremote myremote type=git location=ssh://... autoenable=true
 
-The location must be the same url as the existing git remote.
-
 Now `git annex init` in each clone of the repository will autoenable myremote.
 
-Note that the name of the git remote (`tmpremote` above) has to be
-different than the name you later use for the special remote,
-since [[git-annex initremote|git-annex-initremote]] will refuse to use the name of an existing
-remote. To work around that, you could finish by removing `tmpremote` and 
-enable the special remote:
-
-	git remote remove tmpremote
-	git annex enableremote myremote
-
 This only works for git repositories that have a git-annex uuid set,
-because git-annex thinks about special remotes in terms of their uuid. So
-it cannot be used with a git remote hosted somewhere that does not have
-git-annex installed.
+because special remotes have to have a uuid. So it cannot be used
+with git repositories that are not git-annex repositories.

bug report
diff --git a/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn b/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn
new file mode 100644
index 0000000000..b70c81ff8c
--- /dev/null
+++ b/doc/bugs/move_that_fails_at_drop_stage_does_not_resume_correctly.mdwn
@@ -0,0 +1,12 @@
+git-annex move takes care to handle resuming an interrupted move, where the
+dest received the object, but the drop did not happen before it was
+interrupted.
+
+But, the more common case than being interrupted with ctrl-c or power loss
+is the drop failing due to a network issue. In this case, re-running move
+does not let the drop succeed in cases where the original move would have
+let the drop succeed.
+
+The problem is that logMove runs its cleanup action in this case.
+So the move.log gets the record of the move cleaned from it. It seems that
+the cleanup action should avoid doing that when the move failed. --[[Joey]]

diff --git a/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn
new file mode 100644
index 0000000000..2ecf1db6d0
--- /dev/null
+++ b/doc/bugs/10.20220525_cannot_probe_for_untrusted_remote_key.mdwn
@@ -0,0 +1,25 @@
+### Please describe the problem.
+10.20220525 forcefully untrusts `exporttree=yes`, causing `fsck` to unconditionally fail when probing for a key that only exists on such a remote.
+
+The full story is at https://github.com/datalad/datalad-next/issues/72
+
+The summary is that an application needs to know whether a particular key is on such a remote, and it can only be on that remote (there is no other). Because the availability info from `whereis` cannot be trusted (same rational as the one causing the change in git-annex implementation) it uses `annex fsck --fast --key -f ...` which now fails.
+
+Parsing the JSON output of the failing `fsck` call seems to be the only way left to accomplish this goal.
+
+```
+% git -C myclone/.git/dl-repoannex/origin/repoannex -c diff.ignoreSubmodules=none annex fsck -f origin --fast --key XDLRA--refs -c annex.dotfiles=true --json
+  Only these untrusted locations may have copies of XDLRA--refs
+        d8735795-663e-4702-8f7e-8c684264a9df -- [origin]
+  Back it up to trusted locations with git-annex copy.
+{"dead":[],"command":"fsck","note":"(Avoid this check by running: git annex dead --key )","success":false,"input":[],"untrusted":[{"here":false,"uuid":"d8735795-663e-4702-8f7e-8c684264a9df","description":"[origin]"}],"key":"XDLRA--refs","error-messages":[],"file":null}
+fsck: 1 failed
+```
+
+That however, is substantially more complex than looking at the exit code -- given that the entire call is about a single key.
+
+### 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)
+
+99.9% are just splendid. Wondering here whether this particular consequence of the change was intentional and avoidable?
+
+

Added a comment
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_d51bb87dc5ed0f4ede808c5a9594240d._comment b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_d51bb87dc5ed0f4ede808c5a9594240d._comment
new file mode 100644
index 0000000000..085d00ea50
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_2_d51bb87dc5ed0f4ede808c5a9594240d._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="jkniiv"
+ avatar="http://cdn.libravatar.org/avatar/05fd8b33af7183342153e8013aa3713d"
+ subject="comment 2"
+ date="2022-06-09T02:32:18Z"
+ content="""
+Wouldn't it be possible to support (absolute) file:// urls, eg. something similar to
+`file:///home/jkniiv/test-VEfBrTZ/remote2`? In my mind they feel like a reasonable approximation
+of ssh:// urls and could be useful for getting a feel for git special remotes before setting
+up a bare git-repo/annex on an ssh-server. I know they are not the same thing implementation wise
+but I feel that being able to try this feature out on a least-effort basis would be useful
+from a pedagogical standpoint.
+"""]]

analysis
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_1_182b9cc42f7287c8b022422404fc74b7._comment b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_1_182b9cc42f7287c8b022422404fc74b7._comment
new file mode 100644
index 0000000000..3421df727b
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason/comment_1_182b9cc42f7287c8b022422404fc74b7._comment
@@ -0,0 +1,26 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-08T16:55:50Z"
+ content="""
+Hmm, I think this only works for ssh:// urls currently.
+
+Even the ssh url form host:/path does not work, because it gets
+normalized to a ssh:// url.
+
+The implementation does not support non-url's at all; the provided location
+is treated as an url (`Git.Url location`). And even if it were treated as a
+path, the path gets normalized to a relative path and an absolute path (or
+differently relavatized path) would not work.
+
+Using paths with this is rather problematic too, because if the repo is
+cloned to another machine, it would not find the repo at the recorded path.
+Similarly, relative paths are also problimatic. But it may as well support
+them to the extent it can.
+
+I think this needs changes to the core Git data structure, to store the
+original, unmodified git.remote.path. Or a different interface than the
+current, one that accepts any repo location and probes it to find the uuid.
+The latter idea seems better because it simplifies the UI rather than
+complicating the internal representation.
+"""]]

comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_24_054db0e62994590631737b9bd768d901._comment b/doc/bugs/get_is_busy_doing_nothing/comment_24_054db0e62994590631737b9bd768d901._comment
new file mode 100644
index 0000000000..f2a528db56
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_24_054db0e62994590631737b9bd768d901._comment
@@ -0,0 +1,7 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 24"""
+ date="2022-06-08T16:53:06Z"
+ content="""
+Right, DebugLocks is not needed. The current daily build should work now.
+"""]]

initial whining about type=git initremote
diff --git a/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn
new file mode 100644
index 0000000000..08d6821edd
--- /dev/null
+++ b/doc/bugs/initremote_type__61__git_is_not_working_for_unkn_reason.mdwn
@@ -0,0 +1,68 @@
+### Please describe the problem.
+
+
+[original question raised by John](https://github.com/dandi/dandisets/issues/139#issuecomment-1149948239) which lead me to the goose chase.
+
+Following reproducer
+
+```
+#!/bin/bash
+
+cd "$(mktemp -d ${TMPDIR:-/tmp}/dl-XXXXXXX)"
+set -eux
+
+git init --bare remote
+( cd remote; git annex init; cat config )
+rpath=$PWD/remote
+
+git init repo
+cd repo
+git annex init
+echo 'This is test text.' > file.txt
+git add file.txt
+git commit -m Init file.txt
+
+git remote add --fetch remote-git $rpath
+
+# without this -- there is no annex-uuid for  remote -- git-annex branch is not getting merged
+git annex info
+
+cat .git/config
+
+# but this still fails
+git annex initremote testremote type=git location=$rpath autoenable=true
+
+```
+
+ends with
+
+```
+[remote "remote-git"]
+	url = /home/yoh/.tmp/dl-VjO0aSF/remote
+	fetch = +refs/heads/*:refs/remotes/remote-git/*
+	annex-uuid = afdc6d54-cd6d-4a20-b639-a639f9c7ef09
++ git annex initremote testremote type=git location=/home/yoh/.tmp/dl-VjO0aSF/remote autoenable=true
+initremote testremote 
+git-annex: could not find existing git remote with specified location
+failed
+initremote: 1 failed
+
+```
+
+so
+
+- error "could not find existing git remote with specified location" seems not descriptive of the underlying problem since location matches the url. Underlying issue is still not clear why we can't initremote
+- as you could see in the script - need `annex info` to have annex-uuid populated and looking at [code ](https://git.kitenet.net/index.cgi/git-annex.git/tree/Remote/Git.hs?id=af0d854460c28230dc682faa7c6daf3d96698cb6#n110) comment -- it requires UUID to be known. If not known -- ideally should be a dedicated error message ("remote blah found but lacks uuid, check if remote is annex")
+- IMHO should not need manual `annex info` to merge git-annex branch
+
+
+### What steps will reproduce the problem?
+
+above
+
+### What version of git-annex are you using? On what operating system?
+
+10.20220504
+
+[[!meta author=yoh]]
+[[!tag projects/dandi]]

initial report on addurls issue
diff --git a/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_.mdwn b/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_.mdwn
new file mode 100644
index 0000000000..458bb84ff8
--- /dev/null
+++ b/doc/bugs/addurl_--batch_errors_with__git-annex__58___user_error_.mdwn
@@ -0,0 +1,71 @@
+### Please describe the problem.
+
+
+original report [https://github.com/dandi/dandisets/issues/168](https://github.com/dandi/dandisets/issues/168)
+
+- likely to happen due to dealing with lots of non-large files
+- happens only for non-large .json files (due to `* annex.largefiles=((mimeencoding=binary)and(largerthan=0))`)
+
+- our log for a sample file looks like
+
+```
+(base) dandi@drogon:/mnt/backup/dandi/dandisets/000108/.git/dandi/logs$ grep 'sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json' sync-20220608125450Z-1944539.log
+2022-06-08T09:34:27-0400 [INFO    ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: Syncing
+2022-06-08T09:34:27-0400 [INFO    ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: Not in dataset; will add
+2022-06-08T09:34:27-0400 [DEBUG   ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: Fetching bucket URL
+2022-06-08T09:34:27-0400 [DEBUG   ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: About to query S3
+2022-06-08T09:34:28-0400 [DEBUG   ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: Got bucket URL
+2022-06-08T09:34:28-0400 [INFO    ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: File is text; sending off for download from https://dandiarchive.s3.amazonaws.com/blobs/4a8/75e/4a875eb7-ab7c-4d54-b962-671b7c35a9d6?versionId=5dwackcdjuYtMeNZUh23k0rIpU7tRqbn
+2022-06-08T09:34:28-0400 [INFO    ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: Downloading from https://dandiarchive.s3.amazonaws.com/blobs/4a8/75e/4a875eb7-ab7c-4d54-b962-671b7c35a9d6?versionId=5dwackcdjuYtMeNZUh23k0rIpU7tRqbn
+2022-06-08T09:34:28-0400 [TRACE   ] backups2datalad Sending to addurl command: 'https://dandiarchive.s3.amazonaws.com/blobs/4a8/75e/4a875eb7-ab7c-4d54-b962-671b7c35a9d6?versionId=5dwackcdjuYtMeNZUh23k0rIpU7tRqbn sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json\n'
+2022-06-08T09:34:28-0400 [TRACE   ] backups2datalad Decoded line from addurl command: '{"command":"addurl","note":"to sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json\\nnon-large file; adding content to git repository","success":false,"input":["https://dandiarchive.s3.amazonaws.com/blobs/4a8/75e/4a875eb7-ab7c-4d54-b962-671b7c35a9d6?versionId=5dwackcdjuYtMeNZUh23k0rIpU7tRqbn sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json"],"error-messages":["git-annex: user error (xargs [\\"-0\\",\\"git\\",\\"--git-dir=.git\\",\\"--work-tree=.\\",\\"--literal-pathspecs\\",\\"-c\\",\\"filter.annex.smudge=\\",\\"-c\\",\\"filter.annex.clean=\\",\\"-c\\",\\"filter.annex.process=\\",\\"add\\",\\"-f\\",\\"--\\"] exited 123)"],"file":"sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json"}\n'
+2022-06-08T09:34:28-0400 [ERROR   ] backups2datalad sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json: download failed: git-annex: user error (xargs ["-0","git","--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","filter.annex.smudge=","-c","filter.annex.clean=","-c","filter.annex.process=","add","-f","--"] exited 123)
+
+```
+
+- that file is downloaded but not added to git (according to git status)
+- dedicated `git add` on that file after proceeds fine
+
+<details>
+<summary>seems to happen for some files more than for the others (grepping across logs across multiple runs) -- 38 different files failed, some up to 4 times</summary> 
+
+```shell
+(base) dandi@drogon:/mnt/backup/dandi/dandisets/000108/.git/dandi/logs$ awk '{print $5;}' < /tmp/108-download-fails.log | sort | uniq -c  | sort -n | nl | tail
+    29        2 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-LEC_run-1_chunk-7_SPIM.json:
+    30        2 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-1_SPIM.json:
+    31        2 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-5_SPIM.json:
+    32        2 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-YO_run-1_chunk-6_SPIM.json:
+    33        2 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-YO_run-1_chunk-7_SPIM.json:
+    34        2 sub-MITU01/ses-20220315h17m32s36/micr/sub-MITU01_ses-20220315h17m32s36_sample-25_stain-NN_run-1_chunk-1_SPIM.json:
+    35        2 sub-MITU01/ses-20220315h17m32s36/micr/sub-MITU01_ses-20220315h17m32s36_sample-25_stain-NN_run-1_chunk-7_SPIM.json:
+    36        3 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-LEC_run-1_chunk-4_SPIM.json:
+    37        4 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-6_SPIM.json:
+    38        4 sub-MITU01/ses-20220311h10m19s37/micr/sub-MITU01_ses-20220311h10m19s37_sample-22_stain-NN_run-1_chunk-7_SPIM.json:
+```
+</details>
+
+- ATM there is about 3k of staged "new file" .json files and 4 not staged (those errors)
+- invocation was `git-annex addurl --batch --with-files --jobs 5 --json --json-error-messages --json-progress --raw`
+
+### What steps will reproduce the problem?
+
+haven't tried to create reproducer yet.  may be I could just grep log for all those addurls and provide text file to feed annex addurls..
+
+### What version of git-annex are you using? On what operating system?
+
+`10.20220525+git57-ge796080f3-1~ndall+1` before that was some other recent -- same problem
+
+```
+(base) dandi@drogon:/mnt/backup/dandi/dandisets/000108$ /home/dandi/git-annexes/10.20220525+git57-ge796080f3-1~ndall+1/usr/lib/git-annex.linux/git --version
+git version 2.30.2
+(base) dandi@drogon:/mnt/backup/dandi/dandisets/000108$ /home/dandi/git-annexes/10.20220525+git57-ge796080f3-1~ndall+1/usr/lib/git-annex.linux/git-annex version
+git-annex version: 10.20220525+git57-ge796080f3-1~ndall+1
+...
+```
+
+
+Please advise on how to debug or share ideas on what could lead to it or what to try.
+
+
+[[!meta author=yoh]]
+[[!tag projects/dandi]]

Added a comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_23_3671d133d18f6722901ed01a1fa38164._comment b/doc/bugs/get_is_busy_doing_nothing/comment_23_3671d133d18f6722901ed01a1fa38164._comment
new file mode 100644
index 0000000000..fedf75d417
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_23_3671d133d18f6722901ed01a1fa38164._comment
@@ -0,0 +1,30 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="comment 23"
+ date="2022-06-07T12:07:29Z"
+ content="""
+> isn't https://github.com/kilobyte/compsize the one reporting number of fragments, thus degree of fragmentation?
+
+It is reporting the number of extents, just like `filefrag` does.
+
+Non-CoW filesystems overwrite data in-place, so there is never a need to create new fragments for (pre-allocated) in-file writes. New extents are only created when the free space at the file's location doesn't allow for further expansion and fragmentation is required.
+
+\# of extents is therefore indicative of fragmentation in non-CoW filesystems but this does not hold true in CoW filesystems like btrfs. If you wrote 1M to the first half of a 2M file and then 1M to the latter, you'll have 2 1M extents that might be right behind each other on-disk due to CoW.
+
+With transparent compression in the mix, this gets even worse because extent size caps out at 128K. A 1M incompressible file will have 8 extents/\"fragments\" at minimum despite its data possibly being layed out fully sequentially.
+
+> there is \"write silence\" for a while followed by a burst
+
+Does this burst occur at the same time the DB times out?
+
+If you take a look at `top`/`iotop`, which threads are using CPU time and writing? Look out for btrfs-transacti and btrfs-endio-wri.
+
+> disk write pressure (eg from getting annex objects) is causing sqlite writes to take a long time.
+
+The problem isn't exactly that. Sure, write activity will make DB operations take longer (larger queue -> higher latency) but this effect should stay within reasonable bounds. I think that the actual big problem is that the object writes are buffered and not written until they hit a deadline after which they *must* be written ASAP. I believe in such a situation, any other write operation must wait until those dirty pages are committed and a commit can take quite a while in btrfs due to integrity guarantees.
+
+I think it'd be better to avoid these dirty page deadline commits, start writing out data sooner (ideally immediately) and block the source if the target can't keep up.  
+IMO there's not much point in letting the target seem to \"write\" at a higher speed than it can actually take in a case of sequential writes like git-annex' object transfer. It'll only lead to wasted memory and erratic IO. (I guess a small buffer (something that could be flushed in a few seconds) could be helpful in smoothing over a little variance.)
+
+"""]]

Added a comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_22_711bf1ba8688bdc3bf33e7103340bc11._comment b/doc/bugs/get_is_busy_doing_nothing/comment_22_711bf1ba8688bdc3bf33e7103340bc11._comment
new file mode 100644
index 0000000000..c8b7cf29ff
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_22_711bf1ba8688bdc3bf33e7103340bc11._comment
@@ -0,0 +1,11 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 22"
+ date="2022-06-06T17:22:41Z"
+ content="""
+> So, I've added some debugging of when it commits to the database. @yarik, please get an updated build with commit 5da1a785086910c32559a46f9110779d765af0cd,
+
+it doesn't need custom build with DebugLocks right?  Then I will \"wait\" for daily build of datalad/git-annex to happen and test then... meanwhile also running `git clone https://github.com/dandizarrs/4ea5a47f-10d6-48a7-8c1e-ce6ce511f9a7; cd 4ea5a47f-10d6-48a7-8c1e-ce6ce511f9a7; git annex get -J25 . ` on smaug to see if reproduces there (but it is not busy IO so most likely would finish without whining; boosted to -J25 to \"make it harder\"). `smaug` is also BTRFS although underlying storage is setup differently (no LVM)
+
+"""]]

comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_21_eabe406ab0d6e71c3f5f30908016a592._comment b/doc/bugs/get_is_busy_doing_nothing/comment_21_eabe406ab0d6e71c3f5f30908016a592._comment
new file mode 100644
index 0000000000..2836a8287e
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_21_eabe406ab0d6e71c3f5f30908016a592._comment
@@ -0,0 +1,29 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""re: comment 16"""
+ date="2022-06-06T16:20:55Z"
+ content="""
+@Atemu, that seems like at least a plausible theory, that disk write
+pressure (eg from getting annex objects) is causing sqlite writes to take a
+long time. Increasing the wait time from the current 10 seconds would make
+sense if that's what's happening.
+
+The somewhat weird thing is that, with a single git-annex process, no
+matter the number of jobs, it only has a single database handle open,
+and so it should not be possible for one write to still be happening when
+a second write happens. Unless, of course, sqlite is doing something behind
+my back, like finishing the write in a separate thread? Or, of course,
+there might be a second process that has somehow not been visible..
+
+So, I've added some debugging of when it commits to the database. 
+@yarik, please get an updated build with commit
+5da1a785086910c32559a46f9110779d765af0cd,
+and try after setting:
+
+	git config annex.debug true
+	git config annex.debugfilter Database.Handle
+
+This should tell us if there are somehow multiple writers at the same time,
+and also will tell us the timing of how long it is taking to commit to the
+db.
+"""]]

comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_20_981cc786be798a612046d207c0f85955._comment b/doc/bugs/get_is_busy_doing_nothing/comment_20_981cc786be798a612046d207c0f85955._comment
new file mode 100644
index 0000000000..37f0889600
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_20_981cc786be798a612046d207c0f85955._comment
@@ -0,0 +1,22 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 20"""
+ date="2022-06-06T16:12:53Z"
+ content="""
+It does occur to me that one "solution" might be to have flushDbQueue
+not rethrow the exception, and just re-enqueue the write. Then, if a write
+fails in the middle of a long git-annex command, it will just queue up
+sqlite changes until later. And hopefully whatever is preventing writing to
+the database at that time would later resolve itself. At shutdown, it would
+need to make sure to flush the remaining queue and not ignore a write
+failure then.
+
+That has the potential problem that, if the write keeps failing, it might
+end up buffering a large number of writes in memory, and since the queue
+would be full, it would be trying to write every time. So it would both use
+an ever-growing amount of memory, and be slowed down by the write attempts.
+
+Still, it does give it something better to do while the write is failing
+than sleeping and retrying, eg to do the rest of the work it's been asked
+to do.
+"""]]

fix MVar deadlock when sqlite commit fails
The database queue was left empty, which caused subsequent calls to
flushDbQueue to deadlock.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/Database/Queue.hs b/Database/Queue.hs
index 46418ef1d8..f4882d2fa8 100644
--- a/Database/Queue.hs
+++ b/Database/Queue.hs
@@ -1,6 +1,6 @@
 {- Persistent sqlite database queues
  -
- - Copyright 2015 Joey Hess <id@joeyh.name>
+ - Copyright 2015-2022 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -20,6 +20,7 @@ module Database.Queue (
 import Utility.Monad
 import Utility.RawFilePath
 import Utility.DebugLocks
+import Utility.Exception
 import Database.Handle
 
 import Database.Persist.Sqlite
@@ -54,9 +55,11 @@ flushDbQueue :: DbQueue -> IO ()
 flushDbQueue (DQ hdl qvar) = do
 	q@(Queue sz _ qa) <- debugLocks $ takeMVar qvar
 	if sz > 0
-		then do
-			commitDb hdl qa
-			debugLocks $ putMVar qvar =<< emptyQueue
+		then tryNonAsync (commitDb hdl qa) >>= \case
+			Right () -> debugLocks $ putMVar qvar =<< emptyQueue
+			Left e -> do
+				debugLocks $ putMVar qvar q
+				throwM e
 		else debugLocks $ putMVar qvar q
 
 {- Makes a query using the DbQueue's database connection.
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_19_216aefa4a78d13cc0c46780161921a36._comment b/doc/bugs/get_is_busy_doing_nothing/comment_19_216aefa4a78d13cc0c46780161921a36._comment
new file mode 100644
index 0000000000..06bbb569ff
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_19_216aefa4a78d13cc0c46780161921a36._comment
@@ -0,0 +1,22 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 19"""
+ date="2022-06-06T15:55:15Z"
+ content="""
+So in flushDbQueue, which is waiting for all queued writes to complete.
+When the write failed with an exception, a previous flushDbQueue
+would have left the queue's MVar empty.
+
+So, now I understand how the original problem can lead to this MVar
+problem. And I've fixed that part of it. Now a write failing this way will
+refill the queue with what it failed to write. So it will try to write it
+again later.
+
+This flushDbQueue probably also explains the hang at
+"recording state in git" since there is also a final flushDbQueue at that
+point, and I guess it fails to detect a deadlock at that point so just
+hangs forever. So my fix should also avoid that.
+
+None of which means this is fixed, really, just the fallout from the
+write timeout problem will be less severe now.
+"""]]

Added a comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_18_0e5796c55c76dc8b37f9fd875d87eff0._comment b/doc/bugs/get_is_busy_doing_nothing/comment_18_0e5796c55c76dc8b37f9fd875d87eff0._comment
new file mode 100644
index 0000000000..29d86fdaf5
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_18_0e5796c55c76dc8b37f9fd875d87eff0._comment
@@ -0,0 +1,41 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 18"
+ date="2022-06-06T14:50:16Z"
+ content="""
+> I have made a standalone tarball built that way available here: https://downloads.kitenet.net/git-annex/linux/debuglocks/
+> It should display a backtrace on stderr when the MVar deadlock happens.
+
+jinxing helped ... thought I thought to complain that I don't see any traceback but apparently it is due to `-J5` (I guess) and lines being ovrewritten, but I managed to `Ctrl-s` at a point showing
+
+```
+get 0/0/0/3/5/50 (from web...)
+MVar deadlock detected CallStack (from HasCallStack):
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+  thread blocked indefinitely in an MVar operation
+(Delaying 1s before retrying....)
+```
+
+which when I let it go became smth like
+
+```
+get 0/0/0/3/0/98 (from web...)
+  thread blocked indefinitely in an MVar operation
+(Delaying 1s before retrying....)
+ok
+get 0/0/0/3/1/111 (from web...) ok
+```
+
+and reconfirmed for those in screenlog I quickly collected:
+
+```
+(base) dandi@drogon:~$ grep debugLocks screenlog.3
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+  debugLocks, called at ./Database/Queue.hs:55:30 in main:Database.Queue
+```
+
+"""]]

Added a comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_17_f687c46fb89009712247fe480f0f9566._comment b/doc/bugs/get_is_busy_doing_nothing/comment_17_f687c46fb89009712247fe480f0f9566._comment
new file mode 100644
index 0000000000..a834809b47
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_17_f687c46fb89009712247fe480f0f9566._comment
@@ -0,0 +1,41 @@
+[[!comment format=mdwn
+ username="yarikoptic"
+ avatar="http://cdn.libravatar.org/avatar/f11e9c84cb18d26a1748c33b48c924b4"
+ subject="comment 17"
+ date="2022-06-06T14:00:05Z"
+ content="""
+FWIW4Joey:
+
+> I have made a standalone tarball built that way available here: https://downloads.kitenet.net/git-annex/linux/debuglocks/
+
+I have tried that version.  Placing similar load I just got some `move`s to fail ([our issue+idea to just call multiple times](https://github.com/dandi/dandisets/issues/176)) and some other oddities (not new, yet to figure out) but overall  -- I have not spotted similar MVar messages :-/ (filing a comment to \"jinx\" it, rerunning again -- seeing some `get`'s going ;))
+
+4Atemu:
+
+>  I'm not aware of other means of measuring fragmentation
+
+isn't https://github.com/kilobyte/compsize the one reporting number of fragments, thus degree of fragmentation?  As I summarized in [comment above](http://git-annex.branchable.com/bugs/get_is_busy_doing_nothing/#comment-9c647c8d9837d46e45675de30ebfeefc) I have used it + `btrfs fi defrag`.
+
+> It might be worth looking into the timings of git annex get's write operations using iostat --human 1.
+
+well -- it confirms that there is \"write silence\" for a while followed by a burst, e.g.:
+
+```
+Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
+dm-0             50.00       800.0k         0.0k         0.0k     800.0k       0.0k       0.0k
+dm-0              9.00       144.0k         0.0k         0.0k     144.0k       0.0k       0.0k
+dm-0              0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k
+dm-0              0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k
+dm-0              0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k
+dm-0              0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k
+dm-0              0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k
+dm-0             17.00         0.0k       207.3M         0.0k       0.0k     207.3M       0.0k
+dm-0             50.00         4.0M       319.9M         0.0k       4.0M     319.9M       0.0k
+dm-0            111.00        12.6M       365.8M         0.0k      12.6M     365.8M       0.0k
+dm-0             99.00         2.9M       685.5M         0.0k       2.9M     685.5M       0.0k
+dm-0            107.00         1.6M        10.4M         0.0k       1.6M      10.4M       0.0k
+dm-0            236.00         3.7M         0.0k         0.0k       3.7M       0.0k       0.0k
+dm-0            330.00         5.2M         0.0k         0.0k       5.2M       0.0k       0.0k
+dm-0            323.00         5.0M         0.0k         0.0k       5.0M       0.0k       0.0k
+```
+"""]]

Added a comment
diff --git a/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_6_991bf6846513818c00b75b7962b0fb2e._comment b/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_6_991bf6846513818c00b75b7962b0fb2e._comment
new file mode 100644
index 0000000000..f88671cbe7
--- /dev/null
+++ b/doc/todo/whishlist__58___GPG_alternatives_like_AGE/comment_6_991bf6846513818c00b75b7962b0fb2e._comment
@@ -0,0 +1,13 @@
+[[!comment format=mdwn
+ username="aurelia@b44312a63326710de6cea9c43290e5debbd55607"
+ nickname="aurelia"
+ avatar="http://cdn.libravatar.org/avatar/818bf579caf9992f9123bd9b58321b2b"
+ subject="comment 6"
+ date="2022-06-06T12:38:26Z"
+ content="""
+The biggest reason to use age over PGP seems to be in the simplicity / attack surface. It deliberately does not include options to combat complexity and insecure configurations. It also has a lot less baggage and complexity than PGP: obscure packet-based format, web of trust, subkeys - age does a single thing, and it does it well. I do have a use case for hybrid encryption, but I'd rather not touch GPG ever again if I don't need to. Just the squabble about importing keys without identities makes me want to stay far far away. Age keys handle like SSH keys, so if you have a strategy for those age fits into your workflow very easily.
+
+Age also supports passphrase derived keys now, so the \"shared\" use case is covered.
+
+
+"""]]

update
diff --git a/doc/thanks/list b/doc/thanks/list
index 8119ac92c1..f57ca05658 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -127,3 +127,4 @@ Michael K.,
 Tobias Ammann, 
 David Oates, 
 k0ld, 
+Johannes Söderlund, 

Added a comment
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_16_4d375d35a0d05d99c7becf591823d6b8._comment b/doc/bugs/get_is_busy_doing_nothing/comment_16_4d375d35a0d05d99c7becf591823d6b8._comment
new file mode 100644
index 0000000000..0cb20ebbd6
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_16_4d375d35a0d05d99c7becf591823d6b8._comment
@@ -0,0 +1,25 @@
+[[!comment format=mdwn
+ username="Atemu"
+ avatar="http://cdn.libravatar.org/avatar/d1f0f4275931c552403f4c6707bead7a"
+ subject="comment 16"
+ date="2022-06-05T12:55:04Z"
+ content="""
+Nodatacow is not an option. It disables most safetly guarentees of btrfs (also w.r.t. RAID I believe) and is essentially a hack. As pointed out, it's also difficult to enable retroactively.
+
+Autodefrag is fundamentally broken and might result in overall worse performance. Also not an option.
+
+If a file is highly fragmented (which it might not be, amount of extents isn't an accurate representation fragmentation on btrfs), the correct measure is to `btrfs filesystem defragment` it.  
+If git-annex db fragmentation really is a common problem on btrfs (again, you'd have to do actual performance tests, I'm not aware of other means of measuring fragmentation), perhaps an automatic defrag similar to git's automatic gc would be appropriate. Keys DB files range in the Megabytes IME, so re-writing them every now and then shouldn't be a huge load.
+
+A file's fragmentation on-disk also shouldn't cause issues on DB writes because btrfs is CoW; new writes don't go where the old data was. It can only impact read performance.  
+Free space fragmentation might impact write performance but that's not something a userspace program can or should solve. @yarikoptic try `btrfs balance start -dusage=10`.
+
+I'm not sure I understand the situation 100% but poor DB write performance with hangouts and stalls can also be caused by long filesystem commits.  
+Writing a ton of file data to the filesystem only puts it in the cache and it gets written later. When that \"later\" occurs (usually after 30s), as I understand it, all of that data (potentially megabytes-gigabytes) needs to be written to disk *before* a DB's synchronous write returns.  
+
+This is essentially what git-annex does when getting data from a remote; it \"writes\" a file's data while downloading (without committing, so no disk activity whatsoever) and then does a syncs at the end (lots of disk activity) before starting the next download. A DB commit at the end can take for as long as (up to) 30s worth of transferred data takes to write. With a fast source/pipe, that could potentially be a lot of data.  
+Btrfs itself also has issues with insane commit times under some conditions (dedup especially), compounding the issue.
+
+It might be worth looking into the timings of `git annex get`'s write operations using `iostat --human 1`.
+
+"""]]

add debugLocks around database operations
to track down a blocked indefinitely on MVar that seems to occur after
sqlite throws ErrorBusy but that I have not been able to reproduce when
I made commits synthetically throw ErrorBusy.
Sponsored-by: Dartmouth College's Datalad project
diff --git a/Database/Handle.hs b/Database/Handle.hs
index 9eb32b4e27..cc3d7c35f9 100644
--- a/Database/Handle.hs
+++ b/Database/Handle.hs
@@ -19,6 +19,7 @@ module Database.Handle (
 
 import Utility.Exception
 import Utility.FileSystemEncoding
+import Utility.DebugLocks
 
 import Database.Persist.Sqlite
 import qualified Database.Sqlite as Sqlite
@@ -57,7 +58,7 @@ openDb db tablename = do
  - auto-close. -}
 closeDb :: DbHandle -> IO ()
 closeDb (DbHandle worker jobs) = do
-	putMVar jobs CloseJob
+	debugLocks $ putMVar jobs CloseJob
 	wait worker
 
 {- Makes a query using the DbHandle. This should not be used to make
@@ -74,8 +75,8 @@ queryDb :: DbHandle -> SqlPersistM a -> IO a
 queryDb (DbHandle _ jobs) a = do
 	res <- newEmptyMVar
 	putMVar jobs $ QueryJob $
-		liftIO . putMVar res =<< tryNonAsync a
-	(either throwIO return =<< takeMVar res)
+		debugLocks $ liftIO . putMVar res =<< tryNonAsync a
+	debugLocks $ (either throwIO return =<< takeMVar res)
 		`catchNonAsync` (\e -> error $ "sqlite query crashed: " ++ show e)
 
 {- Writes a change to the database.
@@ -101,8 +102,8 @@ commitDb' :: DbHandle -> SqlPersistM () -> IO (Either SomeException ())
 commitDb' (DbHandle _ jobs) a = do
 	res <- newEmptyMVar
 	putMVar jobs $ ChangeJob $
-		liftIO . putMVar res =<< tryNonAsync a
-	takeMVar res
+		debugLocks $ liftIO . putMVar res =<< tryNonAsync a
+	debugLocks $ takeMVar res
 
 data Job
 	= QueryJob (SqlPersistM ())
diff --git a/Database/Keys/Handle.hs b/Database/Keys/Handle.hs
index 801a9ed530..ed7cc6e6c8 100644
--- a/Database/Keys/Handle.hs
+++ b/Database/Keys/Handle.hs
@@ -16,6 +16,7 @@ module Database.Keys.Handle (
 
 import qualified Database.Queue as H
 import Utility.Exception
+import Utility.DebugLocks
 
 import Control.Concurrent
 import Control.Monad.IO.Class (liftIO, MonadIO)
@@ -42,16 +43,16 @@ withDbState
 	-> (DbState -> m (v, DbState))
 	-> m v
 withDbState (DbHandle mvar) a = do
-	st <- liftIO $ takeMVar mvar
-	go st `onException` (liftIO $ putMVar mvar st)
+	st <- liftIO $ debugLocks $ takeMVar mvar
+	go st `onException` (liftIO $ debugLocks $ putMVar mvar st)
   where
 	go st = do
 		(v, st') <- a st
-		liftIO $ putMVar mvar st'
+		liftIO $ debugLocks $ putMVar mvar st'
 		return v
 
 flushDbQueue :: DbHandle -> IO ()
-flushDbQueue (DbHandle mvar) = go =<< readMVar mvar
+flushDbQueue (DbHandle mvar) = go =<< debugLocks (readMVar mvar)
   where
 	go (DbOpen qh) = H.flushDbQueue qh
 	go _ = return ()
diff --git a/Database/Queue.hs b/Database/Queue.hs
index 7793904365..46418ef1d8 100644
--- a/Database/Queue.hs
+++ b/Database/Queue.hs
@@ -19,6 +19,7 @@ module Database.Queue (
 
 import Utility.Monad
 import Utility.RawFilePath
+import Utility.DebugLocks
 import Database.Handle
 
 import Database.Persist.Sqlite
@@ -51,12 +52,12 @@ closeDbQueue h@(DQ hdl _) = do
 {- Blocks until all queued changes have been written to the database. -}
 flushDbQueue :: DbQueue -> IO ()
 flushDbQueue (DQ hdl qvar) = do
-	q@(Queue sz _ qa) <- takeMVar qvar
+	q@(Queue sz _ qa) <- debugLocks $ takeMVar qvar
 	if sz > 0
 		then do
 			commitDb hdl qa
-			putMVar qvar =<< emptyQueue
-		else putMVar qvar q
+			debugLocks $ putMVar qvar =<< emptyQueue
+		else debugLocks $ putMVar qvar q
 
 {- Makes a query using the DbQueue's database connection.
  - This should not be used to make changes to the database!
@@ -95,10 +96,10 @@ queueDb
 	-> SqlPersistM ()
 	-> IO ()
 queueDb (DQ hdl qvar) commitchecker a = do
-	Queue sz lastcommittime qa <- takeMVar qvar
+	Queue sz lastcommittime qa <- debugLocks $ takeMVar qvar
 	let !sz' = sz + 1
 	let qa' = qa >> a
-	let enqueue = putMVar qvar
+	let enqueue = debugLocks . putMVar qvar
 	ifM (commitchecker sz' lastcommittime)
 		( do
 			r <- commitDb' hdl qa'
diff --git a/Makefile b/Makefile
index 73dffe3847..834a0a701f 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ install-home:
 tmp/configure-stamp: Build/TestConfig.hs Build/Configure.hs
 	if [ "$(BUILDER)" = ./Setup ]; then $(GHC) --make Setup; fi
 	if [ "$(BUILDER)" != stack ]; then \
-		$(BUILDER) configure $(BUILDERCOMMONOPTIONS) --ghc-options="$(shell Build/collect-ghc-options.sh)"; \
+		$(BUILDER) configure -f debuglocks $(BUILDERCOMMONOPTIONS) --ghc-options="$(shell Build/collect-ghc-options.sh)"; \
 	else \
 		$(BUILDER) setup $(BUILDERCOMMONOPTIONS); \
 	fi
diff --git a/doc/bugs/get_is_busy_doing_nothing/comment_13_bdfbcb8b6052a3c904e46678fce6adeb._comment b/doc/bugs/get_is_busy_doing_nothing/comment_13_bdfbcb8b6052a3c904e46678fce6adeb._comment
new file mode 100644
index 0000000000..781d182d41
--- /dev/null
+++ b/doc/bugs/get_is_busy_doing_nothing/comment_13_bdfbcb8b6052a3c904e46678fce6adeb._comment
@@ -0,0 +1,14 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 13"""
+ date="2022-06-03T17:52:12Z"
+ content="""
+To hopefully find out what MVar operation is blocking, I have added lock
+debugging instrumentation to all database calls. This will need a special
+build of git-annex with the DebugLocks build flag enabled. 
+
+I have made a standalone tarball built that way available here:
+<https://downloads.kitenet.net/git-annex/linux/debuglocks/>
+
+It should display a backtrace on stderr when the MVar deadlock happens.
+"""]]

comment
diff --git a/doc/forum/Change_annex_id_after_harddrive_clone/comment_1_2e8ba624f404edf2923d08ae27d12a60._comment b/doc/forum/Change_annex_id_after_harddrive_clone/comment_1_2e8ba624f404edf2923d08ae27d12a60._comment
new file mode 100644
index 0000000000..51ecb623c1
--- /dev/null
+++ b/doc/forum/Change_annex_id_after_harddrive_clone/comment_1_2e8ba624f404edf2923d08ae27d12a60._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2022-06-03T18:01:30Z"
+ content="""
+It's sufficient to just change the annex uuid to a new one you generate.
+
+You may want to also run `git-annex describe here` to give the new 
+repository some description.
+"""]]