Recent changes to this wiki:

done
diff --git a/doc/todo/BLAKE3_multithreading.mdwn b/doc/todo/BLAKE3_multithreading.mdwn
index e096343e74..75d1ce79ea 100644
--- a/doc/todo/BLAKE3_multithreading.mdwn
+++ b/doc/todo/BLAKE3_multithreading.mdwn
@@ -36,3 +36,5 @@ Supporting BLAKE3 multithreading would not be trivial. It needs mmaped file acce
 and the C++ library oneTBB, which would need to be somehow linked into the haskell library.
 
 Calling out to b3sum when in PATH would be easier.. --[[Joey]]
+
+> [[done]]

When b3sum is in PATH, use it for faster blake3 hashing
b3sum is so much faster that it's worth the pain.
The pain could be avoided if this is implemented:
https://github.com/k0001/hs-blake3/issues/9
There is no progress display possible when using b3sum. But it's so much
faster that it's still worth using.
Disabled incremental verification for blake3. b3sum is not able to use
multithreading when reading from stdin. And piping to it would surely be
slower.
The ugly use of unsafePerformIO in b3sumAvailable is the second place where
a Backend needs to use it. This suggests that there should be a constuctor
in IO for Backend.
Note that incremetal verification is disabled even when b3sum is not in
PATH. It would be possible to check b3sumAvailable, but again that needs
a constructor in IO for Backend. Anyway, probably users of this backend
will install b3sum.
Sponsored-by: Brock Spratlen
diff --git a/Backend/Hash.hs b/Backend/Hash.hs
index 6834229f15..f159b4d2be 100644
--- a/Backend/Hash.hs
+++ b/Backend/Hash.hs
@@ -179,7 +179,7 @@ sameCheckSum key hash
 	backslash = fromIntegral (ord '\\')
 
 checkKeyChecksumIncremental :: HashType -> Maybe (Key -> Annex IncrementalVerifier)
-checkKeyChecksumIncremental hash = case snd (hasher hash) of
+checkKeyChecksumIncremental hash = case hashIncremental (hasher hash) of
 	Just iv -> Just (liftIO . iv)
 	Nothing -> Nothing
 
@@ -238,15 +238,25 @@ trivialMigrate' oldkey newbackend afile maxextlen maxexts
 	newvariety = backendVariety newbackend
 
 hashFile :: HashType -> OsPath -> MeterUpdate -> Annex Hash
-hashFile hash file meterupdate = 
-	liftIO $ withMeteredFile file meterupdate $ \b -> do
-		let h = (fst $ hasher hash) b
+hashFile hashtype file meterupdate = case hashFileFast o of
+	Nothing -> liftIO hashpure
+	Just a -> liftIO $ a file >>= \case
+		Just h -> return h
+		Nothing -> hashpure
+  where
+	o = hasher hashtype
+	hashpure = withMeteredFile file meterupdate $ \b -> do
+		let h = (hashPure o) b
 		-- Force full evaluation of hash so whole file is read
 		-- before returning.
 		evaluate (rnf h)
 		return h
 
-type Hasher = (L.ByteString -> Hash, Maybe (Key -> IO IncrementalVerifier))
+data Hasher = Hasher
+	{ hashPure :: L.ByteString -> Hash
+	, hashFileFast :: Maybe (OsPath -> IO (Maybe Hash))
+	, hashIncremental :: Maybe (Key -> IO IncrementalVerifier)
+	}
 
 hasher :: HashType -> Hasher
 hasher MD5Hash = md5Hasher
@@ -266,10 +276,12 @@ hasher XXH3Hash = xxh3Hasher
 #endif
 
 mkHasher :: (L.ByteString -> HashDigest) -> IO IncrementalHasher -> Hasher
-mkHasher h i = 
-	( digestToHash . h
-	, Just $ mkIncrementalVerifier i descChecksum . sameCheckSum
-	)
+mkHasher h i = Hasher
+	{ hashPure = digestToHash . h
+	, hashFileFast = Nothing
+	, hashIncremental = Just $
+		mkIncrementalVerifier i descChecksum . sameCheckSum
+	}
 
 sha2Hasher :: HashSize -> Hasher
 sha2Hasher (HashSize hashsize)
@@ -322,12 +334,22 @@ blake2spHasher (HashSize hashsize)
 
 #ifdef WITH_BLAKE3
 blake3Hasher :: Hasher
-blake3Hasher = (blake3, Just $ blake3IncrementalVerifier descChecksum . sameCheckSum)
+blake3Hasher = Hasher
+	{ hashPure = blake3
+	, hashFileFast = Just blake3File
+	-- No incremental verification for blake3 because
+	-- blake3File is much faster.
+	, hashIncremental = Nothing
+	}
 #endif
 
 #ifdef WITH_XXH3
 xxh3Hasher :: Hasher
-xxh3Hasher = (digestToHash . xxh3, xxh3Incremental)
+xxh3Hasher = Hasher
+	{ hashPure = digestToHash . xxh3
+	, hashFileFast = Nothing
+	, hashIncremental = xxh3Incremental
+	}
 #endif
 
 sha1Hasher :: Hasher
@@ -367,6 +389,6 @@ testKeyHash = SHA2Hash (HashSize 256)
 genTestKey :: L.ByteString -> Key
 genTestKey content = addTestE $ mkKey $ \kd -> kd
 	{ keyName = S.toShort $ hashByteString $
-		(fst $ hasher testKeyHash) content
+		(hashPure $ hasher testKeyHash) content
 	, keyVariety = backendVariety testKeyBackend
 	}
diff --git a/CHANGELOG b/CHANGELOG
index ebb6c708ac..d54f23e8b0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,7 @@ git-annex (10.20260602) UNRELEASED; urgency=medium
   * Added Blake3 build flag, which is the fastest available
     cryptographically secure hash.
     Thanks to edef for implementing this.
+  * When b3sum is in PATH, use it for faster blake3 hashing.
   * Added XXH3 build flag, which is the fastest available
     non-cryptographically secure hash, and is a smaller hash
     than MD5 or SHA1.
diff --git a/Utility/Hash/Blake3.hs b/Utility/Hash/Blake3.hs
index 99eb510cd9..5b6c6659d5 100644
--- a/Utility/Hash/Blake3.hs
+++ b/Utility/Hash/Blake3.hs
@@ -1,4 +1,4 @@
-{- Blake3 convenience wrapper.
+{- Blake3 convenience wrapper with b3hash support.
  -
  - Copyright 2026 Joey Hess <id@joeyh.name>
  - Copyright 2022 edef <edef@edef.eu>
@@ -12,16 +12,25 @@
 module Utility.Hash.Blake3 (
 	blake3,
 	blake3IncrementalVerifier,
+	blake3File,
 ) where
 
 import Utility.Hash.Types
 import Utility.Hash.Incremental
 import Utility.FileSystemEncoding
+import Utility.OsPath
+import Utility.Path
+import Utility.FileSize
+import Utility.Process
+import Utility.Exception
+import Utility.Monad
 
 import qualified BLAKE3
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Lazy as L
 import Data.IORef
+import Control.Concurrent.MVar
+import GHC.IO (unsafePerformIO)
 
 finalize :: BLAKE3.Hasher -> BLAKE3.Digest BLAKE3.DEFAULT_DIGEST_LEN
 finalize = BLAKE3.finalize
@@ -46,3 +55,49 @@ blake3IncrementalVerifier desc samechecksum = do
 		, positionIncrementalVerifier = fmap snd <$> readIORef v
 		, descIncrementalVerifier = desc
 		}
+
+{- When b3sum is in the path, this uses it to hash a file. For large
+ - files, that is much faster due to supporting parallelism.
+ -
+ - This returns Nothing when b3sum is not in the path, or exits nonzero, 
+ - or when the file is too small to be worth running it.
+ -}
+blake3File :: OsPath -> IO (Maybe Hash)
+blake3File file = takeMVar b3sumAvailable >>= \case
+	Just True -> do
+		putMVar b3sumAvailable (Just True)
+		runb3sum
+	Just False -> do
+		putMVar b3sumAvailable (Just False)
+		return Nothing
+	Nothing -> ifM (inSearchPath "b3sum")
+		( do
+			putMVar b3sumAvailable (Just True)
+			runb3sum
+		, do
+			putMVar b3sumAvailable (Just False)
+			return Nothing
+		)
+  where
+	runb3sum = ifM filelargeenough
+		( tryNonAsync runb3sum' >>= return . \case
+			Right output -> case lines output of
+				(hash:[]) | length hash == 64 ->
+					Just $ Hash $ encodeBS hash
+				_ -> Nothing
+			Left _ ->  Nothing
+		, return Nothing
+		)
+	runb3sum' = readProcess "b3sum" ["--no-names", "--", fromOsPath file]
+	
+	-- A file needs to be about 3mb in size before the overhead of
+	-- starting a b3sum process is worthwhile.
+	filelargeenough = tryNonAsync (getFileSize file) >>= return . \case
+		Right sz -> sz > 3000000
+		Left _ -> False
+
+{- Used to avoid needing to check the PATH for b3sum each time
+ - blake3File is called. -}
+{-# NOINLINE b3sumAvailable #-}
+b3sumAvailable :: MVar (Maybe Bool)
+b3sumAvailable = unsafePerformIO $ newMVar Nothing
diff --git a/debian/control b/debian/control
index 321eb45783..e368394333 100644
--- a/debian/control
+++ b/debian/control
@@ -121,6 +121,7 @@ Recommends:
 	git-remote-gcrypt (>= 0.20130908-6),
 	nocache,
 	aria2,
+	b3sum,

(Diff truncated)
update
diff --git a/doc/todo/BLAKE3_multithreading.mdwn b/doc/todo/BLAKE3_multithreading.mdwn
index de60122a4f..e096343e74 100644
--- a/doc/todo/BLAKE3_multithreading.mdwn
+++ b/doc/todo/BLAKE3_multithreading.mdwn
@@ -29,6 +29,9 @@ file with SHA256SUM, git-annex fsck:
 	11.18user 2.11system 0:13.41elapsed 99%CPU (0avgtext+0avgdata 53792maxresident)k
 	7816912inputs+288outputs (2major+8615minor)pagefaults 0swaps
 
+Using -J with enough concurrent files might approach b3sum's speed,
+but often a user wants to add or fsck 1 file as fast as possible.
+
 Supporting BLAKE3 multithreading would not be trivial. It needs mmaped file access,
 and the C++ library oneTBB, which would need to be somehow linked into the haskell library.
 

todo
diff --git a/doc/todo/BLAKE3_multithreading.mdwn b/doc/todo/BLAKE3_multithreading.mdwn
new file mode 100644
index 0000000000..de60122a4f
--- /dev/null
+++ b/doc/todo/BLAKE3_multithreading.mdwn
@@ -0,0 +1,35 @@
+git-annex using BLAKE3 is significantly slower than b3sum.
+The latter uses BLAKE3's multithreading, which is not available in the
+haskell library.
+
+<https://github.com/k0001/hs-blake3/issues/9>
+
+On a 4 gb file:
+
+git-annex fsck:
+
+	1.25user 2.36system 0:07.81elapsed 46%CPU (0avgtext+0avgdata 57676maxresident)k
+	5539480inputs+208outputs (2major+9161minor)pagefaults 0swaps
+
+b3sum:
+
+	1.60user 3.03system 0:02.98elapsed 155%CPU (0avgtext+0avgdata 3158216maxresident)k
+	7013984inputs+0outputs (15345major+57763minor)pagefaults 0swaps
+
+b3sum --num-threads 1:
+
+	1.24user 1.62system 0:07.31elapsed 39%CPU (0avgtext+0avgdata 2326392maxresident)k
+	5902456inputs+0outputs (3262major+43283minor)pagefaults 0swaps
+
+A massive amount of performance is being left on the table.
+
+Although even so, BLAKE3 is still signficantly faster than SHA2, eg on the same
+file with SHA256SUM, git-annex fsck:
+
+	11.18user 2.11system 0:13.41elapsed 99%CPU (0avgtext+0avgdata 53792maxresident)k
+	7816912inputs+288outputs (2major+8615minor)pagefaults 0swaps
+
+Supporting BLAKE3 multithreading would not be trivial. It needs mmaped file access,
+and the C++ library oneTBB, which would need to be somehow linked into the haskell library.
+
+Calling out to b3sum when in PATH would be easier.. --[[Joey]]

done
diff --git a/doc/todo/add_xxHash_backend.mdwn b/doc/todo/add_xxHash_backend.mdwn
index cf9143ea09..065d41ade1 100644
--- a/doc/todo/add_xxHash_backend.mdwn
+++ b/doc/todo/add_xxHash_backend.mdwn
@@ -1 +1,3 @@
 From https://cyan4973.github.io/xxHash/ , xxHash seems much faster than md5 with comparable quality.  There's a Haskell implementation.
+
+> [[done]] (XX3) --[[Joey]]

XXH3
This is the fastest available non-cryptographically secure hash,
and is a smaller hash than MD5 or SHA1.
Had to work around a number of issues with the haskell library.
And it is not available on 32 bit systems until one of those issues is
fixed.
Sponsored-by: Jack Hill
diff --git a/Backend/Hash.hs b/Backend/Hash.hs
index 80b223f838..50eadc1bb2 100644
--- a/Backend/Hash.hs
+++ b/Backend/Hash.hs
@@ -41,6 +41,13 @@ import Control.Exception (evaluate)
 import Data.IORef
 import qualified BLAKE3
 #endif
+#ifdef WITH_XXH3
+import qualified Data.Digest.XXHash.FFI as XXH3
+import qualified Data.Hashable as Hashable
+import Data.Word
+import Data.Bits
+import Data.ByteString.Builder
+#endif
 
 data HashType
 	= MD5Hash
@@ -55,6 +62,9 @@ data HashType
 #ifdef WITH_BLAKE3
 	| Blake3Hash
 #endif
+#ifdef WITH_XXH3
+	| XXH3Hash
+#endif
 
 cryptographicallySecure :: HashType -> Bool
 cryptographicallySecure (SHA2Hash _) = True
@@ -67,6 +77,9 @@ cryptographicallySecure (Blake2spHash _) = True
 #ifdef WITH_BLAKE3
 cryptographicallySecure Blake3Hash = True
 #endif
+#ifdef WITH_XXH3
+cryptographicallySecure XXH3Hash = False
+#endif
 cryptographicallySecure SHA1Hash = False
 cryptographicallySecure MD5Hash = False
 
@@ -85,6 +98,11 @@ hashes = concat
 	, map (Blake2spHash . HashSize) [256, 224]
 #ifdef WITH_BLAKE3
 	, [Blake3Hash]
+#endif
+#ifdef WITH_XXH3
+	, if xxH3Supported
+		then [XXH3Hash]
+		else []
 #endif
 	, [SHA1Hash]
 	, [MD5Hash]
@@ -99,7 +117,7 @@ genBackend hash = Backend
 	{ backendVariety = hashKeyVariety hash (HasExt False)
 	, genKey = Just (keyValue hash)
 	, verifyKeyContent = Just $ checkKeyChecksum sameCheckSum hash
-	, verifyKeyContentIncrementally = Just $ checkKeyChecksumIncremental hash
+	, verifyKeyContentIncrementally = checkKeyChecksumIncremental hash
 	, canUpgradeKey = Just needsUpgrade
 	, fastMigrate = Just trivialMigrate
 	, isStableKey = const True
@@ -127,6 +145,9 @@ hashKeyVariety (Blake2spHash size) he = Blake2spKey size he
 #ifdef WITH_BLAKE3
 hashKeyVariety Blake3Hash he = Blake3Key he
 #endif
+#ifdef WITH_XXH3
+hashKeyVariety XXH3Hash he = XXH3Key he
+#endif
 
 {- A key is a hash of its contents. -}
 keyValue :: HashType -> KeySource -> MeterUpdate -> Annex Key
@@ -169,8 +190,10 @@ sameCheckSum key hash
 	expected = keyHash key
 	backslash = fromIntegral (ord '\\')
 
-checkKeyChecksumIncremental :: HashType -> Key -> Annex IncrementalVerifier
-checkKeyChecksumIncremental hash key = liftIO $ (snd $ hasher hash) key
+checkKeyChecksumIncremental :: HashType -> Maybe (Key -> Annex IncrementalVerifier)
+checkKeyChecksumIncremental hash = case snd (hasher hash) of
+	Just iv -> Just (liftIO . iv)
+	Nothing -> Nothing
 
 keyHash :: Key -> S.ByteString
 keyHash = fst . splitKeyNameExtension
@@ -235,7 +258,7 @@ hashFile hash file meterupdate =
 		evaluate (rnf h)
 		return h
 
-type Hasher = (L.ByteString -> Hash, Key -> IO IncrementalVerifier)
+type Hasher = (L.ByteString -> Hash, Maybe (Key -> IO IncrementalVerifier))
 
 hasher :: HashType -> Hasher
 hasher MD5Hash = md5Hasher
@@ -250,9 +273,15 @@ hasher (Blake2spHash hashsize) = blake2spHasher hashsize
 #ifdef WITH_BLAKE3
 hasher Blake3Hash = blake3Hasher
 #endif
+#ifdef WITH_XXH3
+hasher XXH3Hash = xxh3Hasher
+#endif
 
 mkHasher :: (L.ByteString -> HashDigest) -> IO IncrementalHasher -> Hasher
-mkHasher h i = (digestToHash . h, mkIncrementalVerifier i descChecksum . sameCheckSum)
+mkHasher h i = 
+	( digestToHash . h
+	, Just $ mkIncrementalVerifier i descChecksum . sameCheckSum
+	)
 
 sha2Hasher :: HashSize -> Hasher
 sha2Hasher (HashSize hashsize)
@@ -305,7 +334,7 @@ blake2spHasher (HashSize hashsize)
 
 #ifdef WITH_BLAKE3
 blake3Hasher :: Hasher
-blake3Hasher = (hash, incremental) where
+blake3Hasher = (hash, Just incremental) where
 	finalize :: BLAKE3.Hasher -> BLAKE3.Digest BLAKE3.DEFAULT_DIGEST_LEN
 	finalize = BLAKE3.finalize
 
@@ -331,6 +360,31 @@ blake3Hasher = (hash, incremental) where
 			}
 #endif
 
+#ifdef WITH_XXH3
+xxh3Hasher :: Hasher
+xxh3Hasher = (hash, incremental) where
+	hash = convcanonical . Hashable.hashWithSalt 0 . XXH3.XXH3
+	-- The haskell library does not support incremental verification
+	-- (without going too low-level to be appropriate here).
+	-- https://github.com/haskell-haskey/xxhash-ffi/issues/7
+	incremental = Nothing
+	-- Convert to XXH3 canonical representation.
+	-- This is unfortunately not exposed by xxhash-ffi so has to
+	-- be re-implemented here.
+	-- See https://github.com/haskell-haskey/xxhash-ffi/issues/8
+	convcanonical = digestToHash . HashDigest 
+		. L.toStrict . toLazyByteString 
+		. word64BE . fromint
+	fromint :: Int -> Word64
+	fromint = fromIntegral
+
+-- Due to use of Int, the xxhash-ffi library is currently only suitable
+-- for use on 64 bit (or higher) systems. 
+-- https://github.com/haskell-haskey/xxhash-ffi/issues/6
+xxH3Supported :: Bool
+xxH3Supported = finiteBitSize (0 :: Int) >= 64
+#endif
+
 sha1Hasher :: Hasher
 sha1Hasher = mkHasher sha1 sha1_hasher
 
diff --git a/BuildFlags.hs b/BuildFlags.hs
index e8f8361b7c..e618492b95 100644
--- a/BuildFlags.hs
+++ b/BuildFlags.hs
@@ -74,6 +74,11 @@ buildFlags = filter (not . null)
 	, "Blake3"
 #else
 #warning Building without Blake3 support.
+#endif
+#ifdef WITH_XXH3
+	, "XXH3"
+#else
+#warning Building without XXH3 support.
 #endif
 	]
 
diff --git a/CHANGELOG b/CHANGELOG
index 731f9b615b..a5da5c0f25 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,9 +2,12 @@ git-annex (10.20260602) UNRELEASED; urgency=medium
 
   * Added Botan build flag, which speeds up checksumming significantly
     for most existing hash backends.
-  * Added Blake3 build flag, which is the fastest available hash in
-    git-annex now.
+  * Added Blake3 build flag, which is the fastest available
+    cryptographically secure hash.
     Thanks to edef for their patches.
+  * Added XXH3 build flag, which is the fastest available
+    non-cryptographically secure hash, and is a smaller hash
+    than MD5 or SHA1.
   * Improve handling of synced/master and similar branches, by
     only creating such branches when necessary to push to a non-bare
     remote, and by removing the local synced branch once its changes
diff --git a/Types/Key.hs b/Types/Key.hs
index f7e9bc5a04..06396415a2 100644
--- a/Types/Key.hs
+++ b/Types/Key.hs
@@ -216,6 +216,9 @@ data KeyVariety
 	| Blake2spKey HashSize HasExt
 #ifdef WITH_BLAKE3
 	| Blake3Key HasExt
+#endif
+#ifdef WITH_XXH3
+	| XXH3Key HasExt
 #endif
 	| SHA1Key HasExt
 	| MD5Key HasExt
@@ -256,6 +259,9 @@ hasExt (Blake2spKey _ (HasExt b)) = b
 #ifdef WITH_BLAKE3

(Diff truncated)
comment
diff --git a/doc/todo/add_xxHash_backend/comment_7_d8b95d234c09d58be8b60bd0f6b4d767._comment b/doc/todo/add_xxHash_backend/comment_7_d8b95d234c09d58be8b60bd0f6b4d767._comment
new file mode 100644
index 0000000000..77894032ea
--- /dev/null
+++ b/doc/todo/add_xxHash_backend/comment_7_d8b95d234c09d58be8b60bd0f6b4d767._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 7"""
+ date="2026-06-22T15:22:32Z"
+ content="""
+This would still be worth doing despite git-annex supporting BLAKE3 now.
+XXHASH is even faster, due to not needing to do the work to be
+cryptographically secure.
+"""]]

comment
diff --git a/doc/backends/comment_36_d903eb4b0207b848a1d92010f7868bb1._comment b/doc/backends/comment_36_d903eb4b0207b848a1d92010f7868bb1._comment
new file mode 100644
index 0000000000..33d4d91de2
--- /dev/null
+++ b/doc/backends/comment_36_d903eb4b0207b848a1d92010f7868bb1._comment
@@ -0,0 +1,16 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Re: combine worm with partial hash"""
+ date="2026-06-22T15:17:46Z"
+ content="""
+It lowers the probability to only files that happen to start with the same
+prefix. There are many cases where 2 versions of a file start with the same
+data and diverge later, think eg a filesystem image where one file has been
+changed. 
+
+So I think this would give a false sense of protection, kind of like wearing
+a steel toed boot but with a gun still pointed at your foot.
+
+It's better to just not use WORM. Modern computers can hash really fast,
+especially if you use BLAKE3.
+"""]]

mention annex.securehashesonly
diff --git a/doc/backends.mdwn b/doc/backends.mdwn
index 6fb6cc9638..9ff00f12be 100644
--- a/doc/backends.mdwn
+++ b/doc/backends.mdwn
@@ -76,7 +76,8 @@ in `.gitattributes`:
 ## non-cryptograpgically secure backends
 
 The backends below do not guarantee cryptographically that the
-content of an annexed file remains unchanged.
+content of an annexed file remains unchanged. git-annex can be configured
+to not allow these, by the `annex.securehashesonly` git-config setting.
 
 * `SHA1`, `SHA1E`, `MD5`, `MD5E` -- Smaller hashes than `SHA256`
    for those who want a checksum but are not concerned about security.

comment
diff --git a/doc/bugs/blake3_hash_support/comment_11_59b43eddd3f187440824ba36ee75ff57._comment b/doc/bugs/blake3_hash_support/comment_11_59b43eddd3f187440824ba36ee75ff57._comment
new file mode 100644
index 0000000000..b0c5a7d67c
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_11_59b43eddd3f187440824ba36ee75ff57._comment
@@ -0,0 +1,12 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 11"""
+ date="2026-06-22T14:56:37Z"
+ content="""
+It would be possible to add other blake3 hash lengths, but I have not
+investigated if there is any benefit (eg performance or security) in
+using anything other than `BLAKE3_256`. Will wait to see if anyone comes up
+with a use case for them.
+
+I don't think it will be a problem to adapt the existing code to use them.
+"""]]

fix
diff --git a/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment b/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment
index c5727fe205..8d730f85fe 100644
--- a/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment
+++ b/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment
@@ -3,7 +3,7 @@
  subject="""comment 9"""
  date="2026-06-22T14:25:51Z"
  content="""
-Updated to blake3 and it is indeed even faster: 6.80s
+Updated to blake3-0.3 and it is indeed even faster: 6.80s
 
 Impressive, that's twice as fast as botan's sha256.
 """]]

blake3 merged
diff --git a/doc/bugs/blake3_hash_support.mdwn b/doc/bugs/blake3_hash_support.mdwn
index 1485e79ac3..f5db2b9384 100644
--- a/doc/bugs/blake3_hash_support.mdwn
+++ b/doc/bugs/blake3_hash_support.mdwn
@@ -375,3 +375,5 @@ index 30614e156..0778948fb 100644
      Build-Depends: network-multicast, network-info
      CPP-Options: -DWITH_PAIRING
 """]]
+
+> [[merged|done]] --[[Joey]]
diff --git a/doc/bugs/blake3_hash_support/comment_10_5c3e349536dbb0c76d3df02e92a60c78._comment b/doc/bugs/blake3_hash_support/comment_10_5c3e349536dbb0c76d3df02e92a60c78._comment
new file mode 100644
index 0000000000..eb58ec2361
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_10_5c3e349536dbb0c76d3df02e92a60c78._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 10"""
+ date="2026-06-22T14:49:17Z"
+ content="""
+The above patches *did* have a memory leak.
+
+I've fixed that, and merged to master.
+"""]]

comment
diff --git a/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment b/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment
new file mode 100644
index 0000000000..c5727fe205
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_9_585f9d6f330c22a954b005cae0428e58._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 9"""
+ date="2026-06-22T14:25:51Z"
+ content="""
+Updated to blake3 and it is indeed even faster: 6.80s
+
+Impressive, that's twice as fast as botan's sha256.
+"""]]

note about build flag
diff --git a/doc/backends.mdwn b/doc/backends.mdwn
index 767f1f7eb2..6fb6cc9638 100644
--- a/doc/backends.mdwn
+++ b/doc/backends.mdwn
@@ -62,7 +62,9 @@ in `.gitattributes`:
 * `BLAKE3_256`, `BLAKE3_256E`
   -- Very fast [Blake3 hash](https://github.com/BLAKE3-team/BLAKE3).
   Unlike the Blake2 family, Blake3 is a single variant optimised for many platforms.
-  It is still faster than Blake2 in most cases.
+  It is still faster than Blake2 in most cases.  
+  This is only supported when git-annex is built with the Blake3 build
+  flag.
 
 ### Other
 

update for blake3
Thanks to annexuser64623
diff --git a/doc/backends.mdwn b/doc/backends.mdwn
index 2d1328590b..767f1f7eb2 100644
--- a/doc/backends.mdwn
+++ b/doc/backends.mdwn
@@ -24,6 +24,8 @@ in `.gitattributes`:
 
 ## recommended backends to use
 
+### SHA-2 and SHA-3
+
 * `SHA256E` -- The default backend for new files, combines a 256 bit SHA-2
   hash of the file's content with the file's extension. This allows
   verifying that the file content is right, and can avoid duplicates of
@@ -39,6 +41,9 @@ in `.gitattributes`:
 * `SKEIN512`, `SKEIN512E`, `SKEIN256`, `SKEIN256E`
   -- [Skein hash](http://en.wikipedia.org/wiki/Skein_hash),
   a well-regarded SHA3 hash competition finalist.
+
+### Blake2 and Blake3
+
 * `BLAKE2B160`, `BLAKE2B224`, `BLAKE2B256`, `BLAKE2B384`, `BLAKE2B512`
   `BLAKE2B160E`, `BLAKE2B224E`, `BLAKE2B256E`, `BLAKE2B384E`, `BLAKE2B512E`
   -- Fast [Blake2 hash](https://blake2.net/) variants optimised for 64 bit
@@ -54,6 +59,13 @@ in `.gitattributes`:
   `BLAKE2SP224E`, `BLAKE2SP256E`
   -- Fast [Blake2 hash](https://blake2.net/) variants optimised for 
   8-way CPUs.
+* `BLAKE3_256`, `BLAKE3_256E`
+  -- Very fast [Blake3 hash](https://github.com/BLAKE3-team/BLAKE3).
+  Unlike the Blake2 family, Blake3 is a single variant optimised for many platforms.
+  It is still faster than Blake2 in most cases.
+
+### Other
+
 * `VURL` -- This is like an `URL` (see below) but the content can
   be verified with a cryptographically secure checksum that is
   recorded in the git-annex branch. It's generated when using 

comment
diff --git a/doc/bugs/blake3_hash_support/comment_8_9bc97dc175da01478fb6c39a6fdff538._comment b/doc/bugs/blake3_hash_support/comment_8_9bc97dc175da01478fb6c39a6fdff538._comment
new file mode 100644
index 0000000000..2ae60679cf
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_8_9bc97dc175da01478fb6c39a6fdff538._comment
@@ -0,0 +1,18 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 8"""
+ date="2026-06-22T14:05:49Z"
+ content="""
+blake3 does outperform botan's sha256. I benchmarked it at
+8.30s vs 0:14.36s adding a 4 gb file. That's with blake3-0.2. 
+Newer versions of the blake3 library may be faster yet.
+
+Since the above patches no longer cleanly apply, I have committed
+my update of them to the `blake3` branch. My above review items are not
+addressed yet in that branch. I did add Blake3 to BuildFlags.hs though,
+which was a problem I missed in my earlier review.
+
+Also, the haskell blake3 library has gotten a 0.3 release, which
+the code needs to be updated to build with. (I see no need to retain
+support for 0.2.)
+"""]]

Added a comment
diff --git a/doc/bugs/blake3_hash_support/comment_7_443a5e7eb16d1a859cad9b0d5506c7b1._comment b/doc/bugs/blake3_hash_support/comment_7_443a5e7eb16d1a859cad9b0d5506c7b1._comment
new file mode 100644
index 0000000000..1384caec3c
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_7_443a5e7eb16d1a859cad9b0d5506c7b1._comment
@@ -0,0 +1,48 @@
+[[!comment format=mdwn
+ username="annexuser64623"
+ avatar="http://cdn.libravatar.org/avatar/f2247695660ba216740a81d573e6a3cd"
+ subject="comment 7"
+ date="2026-06-19T10:18:02Z"
+ content="""
+Thanks for your work on Blake3.
+I am looking forward to use Blake3-256 as well since it's much faster than sha2-256 on my machine.
+
+Regarding support via Botan:
+I could not find any recent activity concerning Blake3 Support.
+Links:
+[Botan Discussion on Blake3](https://github.com/randombit/botan/discussions/4755)
+and
+[Botan Issue on Blake3](https://github.com/randombit/botan/issues/3054)
+
+Unfortunately, I'm unfamiliar with Haskell and cannot help with the suggested/requested implementation changes.
+Thus, I created a patch for updating the docs:
+
+```
+diff --git a/doc/backends.mdwn b/doc/backends.mdwn
+index 2d1328590b..fbcc9c87d6 100644
+--- a/doc/backends.mdwn
++++ b/doc/backends.mdwn
+@@ -26,2 +26,4 @@ in `.gitattributes`:
+
++### SHA-2 and SHA-3
++
+ * `SHA256E` -- The default backend for new files, combines a 256 bit SHA-2
+@@ -41,2 +43,5 @@ in `.gitattributes`:
+   a well-regarded SHA3 hash competition finalist.
++
++### Blake2 and Blake3
++
+ * `BLAKE2B160`, `BLAKE2B224`, `BLAKE2B256`, `BLAKE2B384`, `BLAKE2B512`
+@@ -56,2 +61,9 @@ in `.gitattributes`:
+   8-way CPUs.
++* `BLAKE3_256`, `BLAKE3_256E`
++  -- Very fast [Blake3 hash](https://github.com/BLAKE3-team/BLAKE3).
++  Unlike the Blake2 family, Blake3 is a single variant optimised for many platforms.
++  It is still faster than Blake2 in most cases.
++
++### Other
++
+ * `VURL` -- This is like an `URL` (see below) but the content can
+```
+
+"""]]

comment
diff --git a/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_4_49ad0811cab9f1565528b202c6b0d953._comment b/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_4_49ad0811cab9f1565528b202c6b0d953._comment
new file mode 100644
index 0000000000..b97a49c0ab
--- /dev/null
+++ b/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_4_49ad0811cab9f1565528b202c6b0d953._comment
@@ -0,0 +1,9 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""Re: annex-security-allow-unverified-downloads"""
+ date="2026-06-12T15:16:21Z"
+ content="""
+The reason downloading from the web special remote with just an URL key is
+allowed is because it avoids the specific security holes that
+annex-security-allow-unverified-downloads was added to deal with.
+"""]]

comment
diff --git a/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_3_cd276dcc85b3793e3b0bc2367e6081cf._comment b/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_3_cd276dcc85b3793e3b0bc2367e6081cf._comment
new file mode 100644
index 0000000000..285ed740a3
--- /dev/null
+++ b/doc/bugs/migrate_removes_associated_URLs_with_custom_scheme/comment_3_cd276dcc85b3793e3b0bc2367e6081cf._comment
@@ -0,0 +1,24 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 3"""
+ date="2026-06-12T15:01:37Z"
+ content="""
+The urls are actually being copied to the new key. But `whereis` only displays
+an url when it's located in a remote that contains a key. After the migration,
+git-annex does not think that your special remote contains the new key,
+so it does not display urls that are claimed by that special remote.
+
+For the web, there is a special case that handles this, recording that the
+new key is present in the web special remote. (setUrlPresent does it)
+
+I don't think it makes sense to make that be done for other types of
+special remotes generally. Consider that `git-annex get`
+would request the special remote get the new key. If the special remote
+is a key/value store, the new key is not located on it, so the get would
+fail. Similarly, `git-annex fsck --from specialremote` would detect a
+problem.
+
+Maybe there are other special remotes that are also url based in a way that
+it would make sense to update the location log when migrating a key.
+The bittorrent special remote comes to mind.
+"""]]

fixed earlier
diff --git a/doc/bugs/p2phttp_key_endpoints_stuck_after_2_head_requests.mdwn b/doc/bugs/p2phttp_key_endpoints_stuck_after_2_head_requests.mdwn
index ec02c06846..041623ecbb 100644
--- a/doc/bugs/p2phttp_key_endpoints_stuck_after_2_head_requests.mdwn
+++ b/doc/bugs/p2phttp_key_endpoints_stuck_after_2_head_requests.mdwn
@@ -83,3 +83,5 @@ icg149@icg1911:~/Playground/c1$ curl --head http://localhost:54321/git-annex/df5
 
 
 [[!tag projects/INM7]]
+
+> [[done]] --[[Joey]]

close old polls
being botspammed
diff --git a/doc/design/assistant/polls/goals_for_April.mdwn b/doc/design/assistant/polls/goals_for_April.mdwn
index 53132dd441..dc0e6a3d7a 100644
--- a/doc/design/assistant/polls/goals_for_April.mdwn
+++ b/doc/design/assistant/polls/goals_for_April.mdwn
@@ -3,7 +3,7 @@ features done in a month if I'm lucky. I have only 3 more funded months,
 and parts of one will be spent working on porting to Windows, so choose wisely!
 --[[Joey]]
 
-[[!poll open=yes expandable=yes 4 "upload and download rate limiting" 15 "get webapp working on Android" 5 "deltas: speed up syncing modified versions of existing files" 8 "encrypted git remotes using git-remote-gcrypt" 0 "add support for more cloud storage remotes" 19 "don't work on features, work on making it easier to install and use" 2 "Handle duplicate files" 6 "direct mode (aka real files instead of symlinks) [already done --joey]" 3 "start windows port now"]]
+[[!poll open=no expandable=yes 4 "upload and download rate limiting" 15 "get webapp working on Android" 5 "deltas: speed up syncing modified versions of existing files" 8 "encrypted git remotes using git-remote-gcrypt" 0 "add support for more cloud storage remotes" 19 "don't work on features, work on making it easier to install and use" 2 "Handle duplicate files" 6 "direct mode (aka real files instead of symlinks) [already done --joey]" 3 "start windows port now"]]
 
 References:
 
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 91fcb1e274..34ae925bee 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=no 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

close
diff --git a/doc/bugs/git-annex_is_slow_at_reading_file_content.mdwn b/doc/bugs/git-annex_is_slow_at_reading_file_content.mdwn
index f87496718d..2884e49447 100644
--- a/doc/bugs/git-annex_is_slow_at_reading_file_content.mdwn
+++ b/doc/bugs/git-annex_is_slow_at_reading_file_content.mdwn
@@ -53,3 +53,9 @@ NixOS 21.11
 
 
 [[!meta title="cryptonite does not use SHA instructions for sha256"]]
+
+> git-annex can now be built with the Botan build flag, which accellerates
+> SHA3 and also some other hashing. It will take a while for all builds
+> to have it, since the C++ library needs to be installed separately
+> from other dependencies. There is a build warning when building without
+> botab. [[done]] --[[Joey]]

correction
diff --git a/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment b/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment
index bfdc2c44d9..e7f1f8b7d6 100644
--- a/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment
+++ b/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment
@@ -3,7 +3,8 @@
  subject="""comment 6"""
  date="2026-06-09T19:44:56Z"
  content="""
-Another route might be to use botan, which supports blake3.
+Another route might be to use botan, once it supports blake3.
+
 There is some work toward using botan in
 [[git-annex_is_slow_at_reading_file_content]]
 and once it's used, adding blake3 would just be adding another hash.
diff --git a/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment
index f1e206356c..9b7493afb9 100644
--- a/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment
+++ b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment
@@ -3,6 +3,6 @@
  subject="""comment 15"""
  date="2026-06-09T19:46:55Z"
  content="""
-botan includes the blake3 hash, so [[blake3_hash_support]] could happen
+Once botan includes the blake3 hash, [[blake3_hash_support]] could happen
 more or less for free..
 """]]

comment
diff --git a/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment
new file mode 100644
index 0000000000..f1e206356c
--- /dev/null
+++ b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_15_6b7596c7a54c9d6d014998ec31478840._comment
@@ -0,0 +1,8 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 15"""
+ date="2026-06-09T19:46:55Z"
+ content="""
+botan includes the blake3 hash, so [[blake3_hash_support]] could happen
+more or less for free..
+"""]]

botan
diff --git a/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment b/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment
new file mode 100644
index 0000000000..bfdc2c44d9
--- /dev/null
+++ b/doc/bugs/blake3_hash_support/comment_6_86c121634d68400fffccd630dbce0b76._comment
@@ -0,0 +1,10 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 6"""
+ date="2026-06-09T19:44:56Z"
+ content="""
+Another route might be to use botan, which supports blake3.
+There is some work toward using botan in
+[[git-annex_is_slow_at_reading_file_content]]
+and once it's used, adding blake3 would just be adding another hash.
+"""]]

comment
diff --git a/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_14_badb9dcd455bf56a43c0035894084631._comment b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_14_badb9dcd455bf56a43c0035894084631._comment
new file mode 100644
index 0000000000..e5e6cf0d3b
--- /dev/null
+++ b/doc/bugs/git-annex_is_slow_at_reading_file_content/comment_14_badb9dcd455bf56a43c0035894084631._comment
@@ -0,0 +1,22 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 14"""
+ date="2026-06-09T19:34:42Z"
+ content="""
+Botan3 can also accellerate sha1 and sha3 (on machines with AVX-512).
+<https://botan.randombit.net/handbook/hardware_acceleration.html>
+
+I think it would make sense at this point to have a botan build flag
+that uses botan for all hashing. It may be that it's faster at other
+hashes than crypton too.
+
+The build flag would need to not be default, since it needs the C++ library
+to be installed. They seem to be moving toward vendoring the C++ library
+in the haskell package:
+<https://github.com/haskell-cryptography/botan/issues/98>
+At which point the build flag could become the default.
+
+I have some early work toward this in the `botan` branch. A proof of
+concept implementation for SHA256 keys benchmarked roughly  
+twice as fast on my laptop.
+"""]]

done
diff --git a/doc/todo/avoid_uncessary_synced_branches.mdwn b/doc/todo/avoid_uncessary_synced_branches.mdwn
index 9158ee8a9b..c0060a170f 100644
--- a/doc/todo/avoid_uncessary_synced_branches.mdwn
+++ b/doc/todo/avoid_uncessary_synced_branches.mdwn
@@ -14,3 +14,7 @@ distributed mode. Once those are merged, it should be deleted.
 And a bare repository should never have a synced/master branch.
 
 (synced/git-annex is another matter, and is fine as it is) --[[Joey]]
+
+> [[done]] --[[Joey]]
+
+[[!tag projects/INM7]]

reduce synced/ branch clutter in centralized git repo workflow
Avoid pushing synced/master branch when a remote accepts a push of
master.
Avoid creating a local synced/master branch when git-annex sync (or
related commands) is run.
Some code that assumed the existance of the local synced/master branch
had to be fixed.
This is to simplify workflows with centralized git repos, where a branch
might get rebased or rewound and force pushed. The existence of
synced/master (etc) branches complicates that because the user has to
clean them up.
This will mean that a decentralized situation, where pushes are to
non-bare git repos that don't allow updating their master, will
need a different workflow for such forced pushes. But, in a
decentralized situation, it's harder to rewrite history anyway.
So a difference in workflow is ok.
synced/git-annex is not affected.
diff --git a/CHANGELOG b/CHANGELOG
index 98636bc58e..d67b0b8930 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
 git-annex (10.20260602) UNRELEASED; urgency=medium
 
+  * Improve handling of synced/master and similar branches, by
+    only creating such branches when necessary to push to a non-bare
+    remote, and by removing the local synced branch once its changes
+    are merged.
   * put: New command.
   * move: Fix bug where an interrupted command other than move could
     be treated as an interrupted move, resulting in dropping content
diff --git a/Command/Sync.hs b/Command/Sync.hs
index b7332bcf16..b545c8cdd0 100644
--- a/Command/Sync.hs
+++ b/Command/Sync.hs
@@ -518,9 +518,6 @@ updateBranches forpull forpush (Just branch, madj) = do
 				when forpull $
 					updateadjustedbranch adj
 			Nothing -> noop
-	
-	-- Update the sync branch to match the new state of the branch
-	inRepo $ updateBranch (syncBranch branch) (fromViewBranch branch)
   where
 	-- The adjusted branch may need to be updated, if the adjustment
 	-- is not stable, and the usual configuration does not update it.
@@ -635,7 +632,7 @@ pullThirdPartyPopulated o remote
 	
 	wantpull = remoteAnnexPull (Remote.gitconfig remote)
 
-{- The remote probably has both a master and a synced/master branch.
+{- The remote often has both a master and a synced/master branch.
  - Which to merge from? Well, the master has whatever latest changes
  - were committed (or pushed changes, if this is a bare remote),
  - while the synced/master may have changes that some
@@ -646,20 +643,22 @@ mergeRemote remote currbranch mergeconfig o = ifM isBareRepo
 	, case currbranch of
 		(Nothing, _) -> do
 			branch <- inRepo Git.Branch.currentUnsafe
-			mergelisted (pure (branchlist branch))
-		(Just branch, _) -> do
-			inRepo $ updateBranch (syncBranch branch) branch
-			mergelisted (tomerge (branchlist (Just branch)))
+			domerge =<< branchlist branch
+		(Just branch, _) ->
+			domerge
+				=<< changedfrom branch
+				=<< branchlist (Just branch)
 	)
   where
-	mergelisted getlist =
-		merge currbranch mergeconfig o Git.Branch.ManualCommit
-			=<< map (remoteBranch remote) <$> getlist
-	tomerge = filterM (changed remote)
-	branchlist Nothing = []
+	domerge = merge currbranch mergeconfig o Git.Branch.ManualCommit
+	changedfrom branch = filterM (inRepo . Git.Branch.changed branch)
+	branchlist Nothing = pure []
 	branchlist (Just branch)
-		| is_branchView branch = []
-		| otherwise = [origBranch branch, syncBranch branch]
+		| is_branchView branch = pure []
+		| otherwise = filterM (inRepo . Git.Ref.exists)
+			[ remoteBranch remote (origBranch branch)
+			, remoteBranch remote (syncBranch branch)
+			]
 
 pushRemote :: SyncOptions -> Remote -> CurrBranch -> CommandStart
 pushRemote _o _remote (Nothing, _) = stop
@@ -685,9 +684,14 @@ pushRemote o remote (Just branch, _) = do
 	needpush mainbranch
 		| remoteAnnexReadOnly gc = return False
 		| not (remoteAnnexPush gc) = return False
-		| otherwise = anyM (newer remote) $ catMaybes
-			[ syncBranch <$> mainbranch
-			, Just (Annex.Branch.name)
+		| otherwise = anyM id $ concat
+			[ case mainbranch of
+				Just b ->
+					[ newer remote b b True
+					, newer remote (syncBranch b) b False
+					]
+				Nothing -> []
+			, [ newer remote Annex.Branch.name Annex.Branch.name True ]
 			]
 	-- Older remotes on crippled filesystems may not have a
 	-- post-receive hook set up, so when updateInstead emulation
@@ -716,7 +720,7 @@ pushRemote o remote (Just branch, _) = do
  - branch directly to it, so that cloning/pulling will get it.
  - On the other hand, if it's not bare, pushing to the checked out branch
  - will generally fail (except with receive.denyCurrentBranch=updateInstead),
- - and this is why we push to its syncBranch.
+ - and this is when it's useful to push to its syncBranch.
  -
  - Git offers no way to tell if a remote is bare or not, so both methods
  - are tried.
@@ -725,8 +729,9 @@ pushRemote o remote (Just branch, _) = do
  - github may treat the first branch pushed to a new repository as the
  - default branch for that repository.
  -
- - The sync push first sends the synced/master branch,
- - and then forces the update of the remote synced/git-annex branch.
+ - The sync push first sends the synced/master branch
+ - (when the direct push failed), and then forces the update of 
+ - the remote synced/git-annex branch.
  -
  - The forcing is necessary if a transition has rewritten the git-annex branch.
  - Normally any changes to the git-annex branch get pulled and merged before
@@ -755,16 +760,18 @@ pushBranch remote mbranch ms g = do
 				pushparams True
 					[ Git.fromRef $ Git.Ref.base $ origBranch branch ]
 			let p' = p { std_err = CreatePipe }
-			bracket (createProcess p') cleanupProcess $ \h -> do
+			exitcode <- bracket (createProcess p') cleanupProcess $ \h -> do
 				filterstderr [] (stderrHandle h) (processHandle h)
-				void $ waitForProcess (processHandle h)
-			return True
-		Nothing -> return False
-				
-	syncpush directpushed =  do
+				waitForProcess (processHandle h)
+			return (True, exitcode == ExitSuccess)
+		Nothing -> return (False, False)
+	
+	syncpush (directpushed, directbranchupdated) =  do
 		let p = flip Git.Command.gitCreateProcess g $
 			pushparams (not directpushed) $ catMaybes
-				[ (syncrefspec . origBranch) <$> mbranch
+				[ if not directbranchupdated
+					then (syncrefspec . origBranch) <$> mbranch
+					else Nothing
 				, Just $ Git.Branch.forcePush $ syncrefspec Annex.Branch.name
 				]
 		-- stderr is relayed through a pipe so that the push
@@ -832,20 +839,12 @@ mergeAnnex = do
 	void Annex.Branch.forceUpdate
 	stop
 
-changed :: Remote -> Git.Ref -> Annex Bool
-changed remote b = do
-	let r = remoteBranch remote b
-	ifM (inRepo $ Git.Ref.exists r)
-		( inRepo $ Git.Branch.changed b r
-		, return False
-		)
-
-newer :: Remote -> Git.Ref -> Annex Bool
-newer remote b = do
-	let r = remoteBranch remote b
+newer :: Remote -> Git.Ref -> Git.Ref -> Bool -> Annex Bool
+newer remote rb b dne = do
+	let r = remoteBranch remote rb
 	ifM (inRepo $ Git.Ref.exists r)
 		( inRepo $ Git.Branch.changed r b
-		, return True
+		, return dne
 		)
 
 {- Without --all, only looks at files in the work tree.
diff --git a/doc/git-annex-push.mdwn b/doc/git-annex-push.mdwn
index 6a1509a86e..886cf4ae6a 100644
--- a/doc/git-annex-push.mdwn
+++ b/doc/git-annex-push.mdwn
@@ -20,10 +20,8 @@ it's helpful to add remotes for nearby machines that you want
 to access the same annexed content. Pushing to a non-bare remote will
 not normally update the remote's current branch with changes from the local
 repository. (Unless the remote is configured with
-receive.denyCurrentBranch=updateInstead.)
-
-To make working with such non-bare remotes easier, this command pushes not
-only local `master` to remote `master`, but also to remote `synced/master`
+receive.denyCurrentBranch=updateInstead.) Instead, when it detects
+such a remote, this command pushes to the remote's `synced/master`
 (and similar with other branches). When `git-annex pull` (or `git-annex
 sync`) is later run on the remote, it will merge the `synced/` branches
 that were pushed to it.
@@ -118,14 +116,6 @@ See [[git-annex-preferred-content]](1).
   and pushed by `git-annex push` or `git-annex sync`. This option
   prevents all other activities.
 
-  This can come in handy when you've pushed a change to remotes and now
-  want to reset your master branch back before that change. So you
-  run `git reset` and force-push the master branch to remotes, only
-  to find that the next `git annex merge` or `git annex pull` brings the
-  changes back. Why? Because the `synced/master` branch is hanging
-  around and still has the change in it. Cleaning up the `synced/` branches
-  prevents that problem.
-
 * Also the [[git-annex-common-options]](1) can be used.
 
 # SEE ALSO
diff --git a/doc/sync.mdwn b/doc/sync.mdwn
index 2caae47c43..7b17ab5edf 100644
--- a/doc/sync.mdwn
+++ b/doc/sync.mdwn
@@ -20,8 +20,10 @@ When you run `git annex sync`, it merges the `synced/master` branch
 into `master`, receiving anything that's been pushed to it. (If there is a
 conflict in this merge, [[automatic_conflict_resolution]] is used to

(Diff truncated)
todo
diff --git a/doc/todo/avoid_uncessary_synced_branches.mdwn b/doc/todo/avoid_uncessary_synced_branches.mdwn
new file mode 100644
index 0000000000..9158ee8a9b
--- /dev/null
+++ b/doc/todo/avoid_uncessary_synced_branches.mdwn
@@ -0,0 +1,16 @@
+Sending synced/master to a bare git remote is unnecessary because master
+can be pushed directly to it.
+
+This would be good to avoid, because the existence of synced branches
+can complicate situations such as a force push of master that
+discards commits from the tip of the branch. In that case, the user needs
+to worry about cleaning up the synced/master branches, on remote and the
+remote tracking branches, and the local synced/master branch, 
+to avoid them getting merged back in.
+
+Ideally the local synced/master branch should only exist when there are
+un-merged changes that have been pushed from another repository in
+distributed mode. Once those are merged, it should be deleted.
+And a bare repository should never have a synced/master branch.
+
+(synced/git-annex is another matter, and is fine as it is) --[[Joey]]

remove out of date para about --content
diff --git a/doc/sync.mdwn b/doc/sync.mdwn
index eb86133f46..2caae47c43 100644
--- a/doc/sync.mdwn
+++ b/doc/sync.mdwn
@@ -40,10 +40,4 @@ The workflow for using `git annex sync` is simple:
 * Next time you're working on a different clone of that repository,
   run `git annex sync` to update it.
 
-Note that by default, `git annex sync` only synchronises the git
-repositories, but does not transfer the content of annexed files. If you
-want to fully synchronise two repositories content,
-you can use `git annex sync --content`. You can also configure
-[[preferred_content]] settings to make only some content be synced.
-
 See [[git-annex-sync]] for the command's man page.

document --force
diff --git a/doc/git-annex-adjust.mdwn b/doc/git-annex-adjust.mdwn
index bd2964779d..eccdf843cf 100644
--- a/doc/git-annex-adjust.mdwn
+++ b/doc/git-annex-adjust.mdwn
@@ -119,6 +119,16 @@ and will also propagate commits back to the original branch.
   set the `annex.adjustedbranchrefresh` config. Or use `git-annex sync
   --content`, which updates the branch after transferring content.
 
+* `--force`
+
+  Allow overwriting an existing adjusted branch with a new version.
+
+  For example, suppose you started with branch foo checked out
+  and used  `git-annex adjust --unlock`. But then foo was rebased,
+  and you checked out that new version of foo. Running `git-annex adjust
+  --unlock` again would fail because of the existing adjusted branch.
+  Using `--force` would overwrite that adjusted branch.
+
 * Also the [[git-annex-common-options]](1) can be used.
 
 # SEE ALSO

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..91fcb1e274 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

add
diff --git a/doc/security/p2phttp_denial_of_service.mdwn b/doc/security/p2phttp_denial_of_service.mdwn
new file mode 100644
index 0000000000..b8720a46bd
--- /dev/null
+++ b/doc/security/p2phttp_denial_of_service.mdwn
@@ -0,0 +1,9 @@
+The `git-annex p2phttp` server could be caused to hang by a simple HEAD
+request to it. This denial of service attack was fixed in version
+10.20260525.
+
+Before version 10.20251114, `p2phttp` had several other bugs that could
+cause it to stall. That version also fixed a file descriptor leak.
+
+Since p2phttp is still somewhat new, and the impact is only a DOS,
+no CVEs were assigned for these.

update
diff --git a/doc/thanks/list b/doc/thanks/list
index 0e8eb59c97..22e43a3de4 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -130,3 +130,4 @@ joshingly,
 Melody Tolly, 
 username, 
 Steffen Vogel, 
+Jan, 

Revert "update"
This reverts commit 2afeeebc41d447ad6f51d662ec9b3662f21c5c95.
script broken
diff --git a/doc/thanks/list b/doc/thanks/list
index 50f233b5ca..0e8eb59c97 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -130,101 +130,3 @@ joshingly,
 Melody Tolly, 
 username, 
 Steffen Vogel, 
-AlexS, 
-Amitai Schleier, 
-Andrew, 
-anon, 
-Anthony DeRobertis, 
-Anton Grensjö, 
-Anton Y, 
-Art S, 
-Arthur Lutz, 
-Ben, 
-Boyd Stephen Smith, 
-Bruno BEAUFILS, 
-Caleb Allen, 
-Calvin Beck, 
-Chris Lamb, 
-Christian Diller, 
-Christopher Baines, 
-Christopher Goes, 
-Dave Pifke, 
-don haraway, 
-DuncanConstruction, 
-encryptio, 
-Eric Drechsel, 
-ers35, 
-Evgeni Ku, 
-Farran, 
-Fernando Jimenez, 
-fiatjaf, 
-Francois Marier, 
-Gabriel Lee, 
-Greg Grossmeier, 
-HeartBreak KB Official, 
-Ignacio, 
-Ilya Baryshnikov, 
-James (purpleidea), 
-James Valleroy, 
-Jan, 
-Jason Woofenden, 
-Jeff Goeke-Smith, 
-Jim, 
-Jo, 
-Johannes Schlatow, 
-John Peloquin, 
-Jon D, 
-jose_d, 
-Josh Taylor, 
-Josh Tilles, 
-Lacnic, 
-Land Reaver, 
-Lee Hinman, 
-Lee-kai Wang, 
-Lukas Scheel-Platz, 
-Lukas Waymann, 
-Madison McGaffin, 
-Maggie Hess, 
-Matthew Willcockson, 
-Matthias Urlichs, 
-Matthieu, 
-Mattias J, 
-Mica, 
-Michal Politowski, 
-Mika Pflüger, 
-mo, 
-Mohit Munjani, 
-Nahum Shalman, 
-NinjaTrappeur, 
-Ole-Morten Duesund, 
-Paul Tötterman, 
-Pedro Luz, 
-Pig Monkey, 
-Renaud Casenave-Péré, 
-rjbl, 
-Ryan Newton, 
-Rémi Vanicat, 
-Sergey Karpukhin, 
-Shane-o, 
-Shawn Butts, 
-Stan Yamane, 
-Stephan Burkhardt, 
-Stephan Meister, 
-SvenDowideit, 
-sww, 
-Teremu HAMBLIN, 
-Thom May, 
-Thomas Ferris Nicolaisen, 
-Thomas Hochstein, 
-Thomas Schwinge, 
-Tim Howes, 
-tj, 
-Trent Lloyd, 
-Tyler Cipriani, 
-Valeria_, 
-Walltime, 
-wawatcz, 
-Will Hughes, 
-Willard Korfhage, 
-wzhd, 
-Zoé Cassiopée Gauthier, 

update
diff --git a/doc/thanks/list b/doc/thanks/list
index 0e8eb59c97..50f233b5ca 100644
--- a/doc/thanks/list
+++ b/doc/thanks/list
@@ -130,3 +130,101 @@ joshingly,
 Melody Tolly, 
 username, 
 Steffen Vogel, 
+AlexS, 
+Amitai Schleier, 
+Andrew, 
+anon, 
+Anthony DeRobertis, 
+Anton Grensjö, 
+Anton Y, 
+Art S, 
+Arthur Lutz, 
+Ben, 
+Boyd Stephen Smith, 
+Bruno BEAUFILS, 
+Caleb Allen, 
+Calvin Beck, 
+Chris Lamb, 
+Christian Diller, 
+Christopher Baines, 
+Christopher Goes, 
+Dave Pifke, 
+don haraway, 
+DuncanConstruction, 
+encryptio, 
+Eric Drechsel, 
+ers35, 
+Evgeni Ku, 
+Farran, 
+Fernando Jimenez, 
+fiatjaf, 
+Francois Marier, 
+Gabriel Lee, 
+Greg Grossmeier, 
+HeartBreak KB Official, 
+Ignacio, 
+Ilya Baryshnikov, 
+James (purpleidea), 
+James Valleroy, 
+Jan, 
+Jason Woofenden, 
+Jeff Goeke-Smith, 
+Jim, 
+Jo, 
+Johannes Schlatow, 
+John Peloquin, 
+Jon D, 
+jose_d, 
+Josh Taylor, 
+Josh Tilles, 
+Lacnic, 
+Land Reaver, 
+Lee Hinman, 
+Lee-kai Wang, 
+Lukas Scheel-Platz, 
+Lukas Waymann, 
+Madison McGaffin, 
+Maggie Hess, 
+Matthew Willcockson, 
+Matthias Urlichs, 
+Matthieu, 
+Mattias J, 
+Mica, 
+Michal Politowski, 
+Mika Pflüger, 
+mo, 
+Mohit Munjani, 
+Nahum Shalman, 
+NinjaTrappeur, 
+Ole-Morten Duesund, 
+Paul Tötterman, 
+Pedro Luz, 
+Pig Monkey, 
+Renaud Casenave-Péré, 
+rjbl, 
+Ryan Newton, 
+Rémi Vanicat, 
+Sergey Karpukhin, 
+Shane-o, 
+Shawn Butts, 
+Stan Yamane, 
+Stephan Burkhardt, 
+Stephan Meister, 
+SvenDowideit, 
+sww, 
+Teremu HAMBLIN, 
+Thom May, 
+Thomas Ferris Nicolaisen, 
+Thomas Hochstein, 
+Thomas Schwinge, 
+Tim Howes, 
+tj, 
+Trent Lloyd, 
+Tyler Cipriani, 
+Valeria_, 
+Walltime, 
+wawatcz, 
+Will Hughes, 
+Willard Korfhage, 
+wzhd, 
+Zoé Cassiopée Gauthier, 

close
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull.mdwn b/doc/todo/add_--json-progress_support_in_push_and_pull.mdwn
index 4e18897297..ed8c677093 100644
--- a/doc/todo/add_--json-progress_support_in_push_and_pull.mdwn
+++ b/doc/todo/add_--json-progress_support_in_push_and_pull.mdwn
@@ -1,3 +1,6 @@
 The pull and push commands do not have --json-progress support.  Please add.
 
 [[!tag projects/INM7]]
+
+> [[wontfix|done]] but see my comment at the end for alternatives.
+> --[[Joey]]

update my comment
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment
index 2bdeacf1cb..a19ac50449 100644
--- a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment
+++ b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment
@@ -1,46 +1,53 @@
 [[!comment format=mdwn
  username="joey"
  subject="""comment 10"""
- date="2026-05-29T13:59:25Z"
+ date="2026-06-04T13:59:25Z"
  content="""
-Summarizing the current status of this todo, if someone wants 
-the equivilant of `git-annex pull --json $someremote`, they can run:
+I have done some work adjacent to this todo, implmenting a `--wanted`
+option and `git-annex put` command.
+
+Now, if someone wants  the equivilant of `git-annex pull --json
+$someremote`, they can run:
 
 	git-annex pull --no-content $someremote
-	git-annex get --auto --json --from $someremote
-	git-annex drop --auto --json
+	git-annex get --wanted --json --from $someremote
+	git-annex drop --wanted --json
 
-The first command above does not have json output, but outputs the
+The `git-annex pull` above does not have json output, but outputs the
 usual `git pull` messages for the user to deal with as they see fit.
 
 And, if someone wants the equivilant of `git-annex push --json
 $someremote`, they can run:
 
-	git-annex copy --auto --json --to $someremote
-	git-annex drop --auto --json --from $someremote
+	git-annex copy --wanted --json --to $someremote
+	git-annex drop --wanted --json --from $someremote
 	git-annex push --no-content $someremote
 
-The last command above does not have json output, but outputs the
+The `git-annex push` above does not have json output, but outputs the
 usual `git push` messages for the user to deal with as they see fit.
 
-The argument for adding --json to pull/push now seems to be reduced.
-But not gone entirely I suppose. 
-
-For example, `git-annex push` without
-a remote pushes content to all remotes that want it. That needs
-multiple runs of `git-annex copy`, one per remote. Notice that
-in the `git-annex pull` case, it can be made to operate on all remotes:
+Similarly, the equivilant of `git-annex pull --json` with no remote
+specified:
 
 	git-annex pull --no-content
-	git-annex get --auto --json
-	git-annex drop --auto --json
+	git-annex get --wanted --json
+	git-annex drop --wanted --json
+
+And, the equivilant of `git-annex push` without a remote specified:
+
+	git-annex put --wanted --json
+	git-annex drop --wanted --json
+	git-annex push --no-content
+
+So, the argument for adding --json to pull/push now seems to be reduced.
+Here are all the arguments I can think of for still doing that:
 
-This seems like an argument for adding a `git-annex put`
-command that copies to all remotes that want content. Which is nicely
-symmetric with `git-annex get`.
+* These command sequences won't behave completely identically to pull/push
+  in all configurations, eg they don't look at `remote.<name>.annex-pull`
+  and `remote.<name>.annex-push` configs.
 
-Another difference is that a single `git-annex push` or `pull`
-(or `sync`) does less work than several git-annex commands.
-In the scripts above, git-annex has to traverse the tree twice.
-That is a pretty small difference in overhead.
+* A single `git-annex push` or `pull` (or `sync`) does less work than several
+  git-annex commands. In the command sequences above, git-annex has to
+  traverse the tree twice. That is a pretty small difference in overhead
+  though most of the time.
 """]]
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
deleted file mode 100644
index 727dd711d3..0000000000
--- a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
+++ /dev/null
@@ -1,17 +0,0 @@
-[[!comment format=mdwn
- username="joey"
- subject="""comment 11"""
- date="2026-05-29T15:09:31Z"
- content="""
-Hmm, `git-annex get --auto` and `git-annex copy --auto` don't just check
-preferred content, they will also make a copy if numcopies is not satisfied
-for the file.
-
-That's something that `git-annex pull` and `git-annex push` don't do.
-
-I think it would be a good idea to add a `--wanted` option to commands
-that support `--auto`. It would only operate on preferred content, avoiding
-making copies only to work toward satisfying numcopies.
-
-Update: Added `--wanted`
-"""]]

decided this is not actually a bug
diff --git a/doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn b/doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn
index 6458160415..e4da8e7d3f 100644
--- a/doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn
+++ b/doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn
@@ -1,4 +1,29 @@
 `git-annex put -J2` with the same file needing to go to 2 remotes only runs
-1 transfer at a time. Same with `git-annex push -J2`.
+1 transfer at a time. Same with `git-annex push -J2` and `git-annex copy
+-J2`.
 
 This is because of the use of OnlyActionOn. --[[Joey]]
+
+> That is the thing that prevents it, but there is a good reason for it to
+> be prevented in some cases. Consider `git-annex copy -J2 --auto`, where
+> there are 2 archive remotes. It should only send to 1 of them, not
+> to both. And if the first one it tries to send to fails, it ought to
+> send to the other one. So the second thread needs to block waiting on the
+> first thread and check again if the action ought to be run. Which is what
+> OnlyActionOn does.
+> 
+> So this seems to only make sense to do if preferred content is not being
+> checked, or if the remote does not have a preferred content expression,
+> or if the preferred content expression does not use "copies=" or
+> similar.
+> 
+> Also, this doesn't fully serialize eg `put -J2`, as long as there are
+> several files to put. The 1st file will be sent to the 1st remote
+> and concurrently the 2nd file to the 2nd remote, and once those transfers
+> complete it will move on to sending the 1st file to the 2nd remote and
+> the 2nd file to the 1st remote.
+> 
+> Is it worth supporting this where it can be supported? It might seem
+> like inconsistent behavior.
+> 
+> I think not. [[done]] --[[Joey]]

typo
diff --git a/doc/git-annex-put.mdwn b/doc/git-annex-put.mdwn
index 831fa146b1..13eb06515c 100644
--- a/doc/git-annex-put.mdwn
+++ b/doc/git-annex-put.mdwn
@@ -25,7 +25,7 @@ Paths of files or directories to operate on can be specified.
 * `--auto`
 
   Only send files to remotes that want them, or when there are not
-  yet have the desired number of copies.
+  yet the desired number of copies.
 
 * `--fast`
 

this is a bug
diff --git a/doc/todo/push_and_put_-J_OnlyActionOn_problem.mdwn b/doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn
similarity index 100%
rename from doc/todo/push_and_put_-J_OnlyActionOn_problem.mdwn
rename to doc/bugs/push_and_put_-J_OnlyActionOn_problem.mdwn

fix todo
diff --git a/doc/todo/push_and_put_-J_OnlyActionOn_problem.mdwn b/doc/todo/push_and_put_-J_OnlyActionOn_problem.mdwn
new file mode 100644
index 0000000000..6458160415
--- /dev/null
+++ b/doc/todo/push_and_put_-J_OnlyActionOn_problem.mdwn
@@ -0,0 +1,4 @@
+`git-annex put -J2` with the same file needing to go to 2 remotes only runs
+1 transfer at a time. Same with `git-annex push -J2`.
+
+This is because of the use of OnlyActionOn. --[[Joey]]
diff --git a/doc/todo/put_--json_lacks_uuid.mdwn b/doc/todo/put_--json_lacks_uuid.mdwn
index 387a780f6f..f60e04fac1 100644
--- a/doc/todo/put_--json_lacks_uuid.mdwn
+++ b/doc/todo/put_--json_lacks_uuid.mdwn
@@ -1,2 +1,4 @@
 `git annex put --json` does not include the uuid of the remote that content
 is sent to. It ought to, since it can send to multiple remotes. --[[Joey]]
+
+> [[fixed|done]] --[[Joey]]
diff --git a/doc/todo/put_-J_OnlyActionOn_problem.mdwn b/doc/todo/put_-J_OnlyActionOn_problem.mdwn
deleted file mode 100644
index b4f189c485..0000000000
--- a/doc/todo/put_-J_OnlyActionOn_problem.mdwn
+++ /dev/null
@@ -1,5 +0,0 @@
-`git-annex put -J2` with the same file needing to go to 2 remotes only runs
-1 transfer at a time. This is because of the use of OnlyActionOn in
-Command.Move. --[[Joey]]
-
-[[fixed|done]] --[[Joey]] 

include uuid in put --json
diff --git a/Command/Copy.hs b/Command/Copy.hs
index bbc08fda04..247584327b 100644
--- a/Command/Copy.hs
+++ b/Command/Copy.hs
@@ -64,16 +64,16 @@ seek' o fto = startConcurrency (Command.Move.stages fto) $ do
 	case batchOption o of
 		NoBatch -> withKeyOptions
 			(keyOptions o) (autoMode o || wantedMode o) seeker
-			(commandAction . startKey o fto)
+			(commandAction . startKey o fto id)
 			(withFilesInGitAnnex ww seeker)
 			=<< workTreeItems ww (copyFiles o)
 		Batch fmt -> batchOnly (keyOptions o) (copyFiles o) $
-			batchAnnexed fmt seeker (startKey o fto)
+			batchAnnexed fmt seeker (startKey o fto id)
   where
 	ww = WarnUnmatchLsFiles "copy"
 	
 	seeker = AnnexedFileSeeker
-		{ startAction = startSingle $ const $ start o fto
+		{ startAction = startSingle $ const $ start o fto id
 		, checkContentPresent = case fto of
 			FromOrToRemote (FromRemote _) -> Just False
 			FromOrToRemote (ToRemote _) -> Just True
@@ -86,8 +86,15 @@ seek' o fto = startConcurrency (Command.Move.stages fto) $ do
 {- A copy is just a move that does not delete the source file.
  - However, auto mode avoids unnecessary copies, and avoids getting or
  - sending non-preferred content. -}
-start :: CopyOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
-start o fto si file key = do
+start
+	:: CopyOptions
+	-> FromToHereOptions
+	-> (ActionItem -> ActionItem)
+	-> SeekInput
+	-> OsPath
+	-> Key
+	-> CommandStart
+start o fto fai si file key = do
 	ru <- case fto of
 		FromOrToRemote (ToRemote dest) -> getru dest
 		FromOrToRemote (FromRemote _) -> pure Nothing
@@ -95,13 +102,26 @@ start o fto si file key = do
 		FromRemoteToRemote _ dest -> getru dest
 		FromAnywhereToRemote dest -> getru dest
 	lu <- prepareLiveUpdate ru key AddingKey
-	start' lu o fto si file key
+
+	start' lu o fto afile si file key ai
   where
 	getru dest = Just . Remote.uuid <$> getParsed dest
+	
+	afile = AssociatedFile (Just file)
+	ai = fai $ mkActionItem (key, afile)
 
-start' :: LiveUpdate -> CopyOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
-start' lu o fto si file key = stopUnless shouldCopy $ 
-	Command.Move.start lu fto (moveAction o) si file key
+start'
+	:: LiveUpdate
+	-> CopyOptions
+	-> FromToHereOptions
+	-> AssociatedFile
+	-> SeekInput
+	-> OsPath
+	-> Key
+	-> ActionItem
+	-> CommandStart
+start' lu o fto afile si file key ai = stopUnless shouldCopy $ 
+	Command.Move.start' lu fto (moveAction o) afile si key ai
   where
 	shouldCopy
 		| autoMode o = want <||> numCopiesCheck file key (<)
@@ -119,5 +139,11 @@ start' lu o fto si file key = stopUnless shouldCopy $
 			wantGetBy lu False (Just key) (AssociatedFile (Just file))
 	checkwantget = wantGet lu False (Just key) (AssociatedFile (Just file))
 
-startKey :: CopyOptions -> FromToHereOptions -> (SeekInput, Key, ActionItem) -> CommandStart
-startKey o fto = Command.Move.startKey NoLiveUpdate fto (moveAction o)
+startKey
+	:: CopyOptions
+	-> FromToHereOptions
+	-> (ActionItem -> ActionItem)
+	-> (SeekInput, Key, ActionItem)
+	-> CommandStart
+startKey o fto fai (si, k, ai) = 
+	Command.Move.startKey NoLiveUpdate fto (moveAction o) (si, k, fai ai)
diff --git a/Command/Put.hs b/Command/Put.hs
index ed4b70d256..d32e9b9245 100644
--- a/Command/Put.hs
+++ b/Command/Put.hs
@@ -46,13 +46,13 @@ seek o = startConcurrency commandStages $ do
 	let seeker = AnnexedFileSeeker
 		{ startAction = \_ si p k ->
 			return $ flip map contentremotes $ \r ->
-				Command.Copy.start co (to r) si p k
+				Command.Copy.start co (to r) (fai r) si p k
 		, checkContentPresent = Just True
 		, usesLocationLog = True
 		}
 	let keyaction v = 
 		return $ flip map contentremotes $ \r ->
-			Command.Copy.startKey co (to r) v
+			Command.Copy.startKey co (to r) (fai r) v
 	case batchOption o of
 		NoBatch -> withKeyOptions
 			(keyOptions o) (autoMode o || wantedMode o) seeker
@@ -75,3 +75,9 @@ seek o = startConcurrency commandStages $ do
 		}
 	
 	to = FromOrToRemote . ToRemote . ReadyParse
+
+	-- Since put can send to remotes of its choosing, suppliment the
+	-- ActionItem with the uuid. This makes the json include the remote
+	-- uuid.
+	fai r ai = ActionItemForUUID (Remote.uuid r) ai
+
diff --git a/Types/ActionItem.hs b/Types/ActionItem.hs
index 53e7822a74..84e26b8e2f 100644
--- a/Types/ActionItem.hs
+++ b/Types/ActionItem.hs
@@ -1,6 +1,6 @@
 {- items that a command can act on
  -
- - Copyright 2016-2023 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2026 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -29,6 +29,7 @@ data ActionItem
 	| ActionItemUUID UUID StringContainingQuotedPath
 	-- ^ UUID with a description or name of the repository
 	| ActionItemOther (Maybe StringContainingQuotedPath)
+	| ActionItemForUUID UUID ActionItem
 	| OnlyActionOn Key ActionItem
 	-- ^ Use to avoid more than one thread concurrently processing the
 	-- same Key.
@@ -85,6 +86,7 @@ actionItemDesc (ActionItemTreeFile f) = QuotedPath f
 actionItemDesc (ActionItemUUID _ desc) = desc
 actionItemDesc (ActionItemOther Nothing) = mempty
 actionItemDesc (ActionItemOther (Just v)) = v
+actionItemDesc (ActionItemForUUID _ ai) = actionItemDesc ai
 actionItemDesc (OnlyActionOn _ ai) = actionItemDesc ai
 
 actionItemKey :: ActionItem -> Maybe Key
@@ -95,21 +97,26 @@ actionItemKey (ActionItemFailedTransfer t _) = Just (transferKey t)
 actionItemKey (ActionItemTreeFile _) = Nothing
 actionItemKey (ActionItemUUID _ _) = Nothing
 actionItemKey (ActionItemOther _) = Nothing
+actionItemKey (ActionItemForUUID _ ai) = actionItemKey ai
 actionItemKey (OnlyActionOn _ ai) = actionItemKey ai
 
 actionItemFile :: ActionItem -> Maybe OsPath
 actionItemFile (ActionItemAssociatedFile (AssociatedFile af) _) = af
 actionItemFile (ActionItemTreeFile f) = Just f
 actionItemFile (ActionItemUUID _ _) = Nothing
+actionItemFile (ActionItemForUUID _ ai) = actionItemFile ai
 actionItemFile (OnlyActionOn _ ai) = actionItemFile ai
 actionItemFile _ = Nothing
 
 actionItemUUID :: ActionItem -> Maybe UUID
 actionItemUUID (ActionItemUUID uuid _) = Just uuid
+actionItemUUID (ActionItemForUUID uuid _) = Just uuid
+actionItemUUID (OnlyActionOn _ ai) = actionItemUUID ai
 actionItemUUID _ = Nothing
 
 actionItemTransferDirection :: ActionItem -> Maybe Direction
 actionItemTransferDirection (ActionItemFailedTransfer t _) = Just $
 	transferDirection t
+actionItemTransferDirection (ActionItemForUUID _ ai) = actionItemTransferDirection ai
 actionItemTransferDirection (OnlyActionOn _ ai) = actionItemTransferDirection ai
 actionItemTransferDirection _ = Nothing
diff --git a/doc/todo/put_-J_OnlyActionOn_problem.mdwn b/doc/todo/put_-J_OnlyActionOn_problem.mdwn
index 88cb783723..b4f189c485 100644
--- a/doc/todo/put_-J_OnlyActionOn_problem.mdwn
+++ b/doc/todo/put_-J_OnlyActionOn_problem.mdwn
@@ -1,3 +1,5 @@
 `git-annex put -J2` with the same file needing to go to 2 remotes only runs
 1 transfer at a time. This is because of the use of OnlyActionOn in
 Command.Move. --[[Joey]]
+
+[[fixed|done]] --[[Joey]] 

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index a92964f743..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Google Drive)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index d1bdb81324..a92964f743 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (OpenStack SWIFT)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index be9e779b44..d1bdb81324 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Tahoe-LAFS)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..be9e779b44 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 0090d9b91d..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Box.com (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 859324f795..0090d9b91d 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon Glacier (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..859324f795 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index a92964f743..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Google Drive)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index d1bdb81324..a92964f743 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (OpenStack SWIFT)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index be9e779b44..d1bdb81324 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Tahoe-LAFS)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..be9e779b44 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 0090d9b91d..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Box.com (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 859324f795..0090d9b91d 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon Glacier (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..859324f795 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index a92964f743..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Google Drive)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index d1bdb81324..a92964f743 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (OpenStack SWIFT)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index be9e779b44..d1bdb81324 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Tahoe-LAFS)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index d1bdb81324..be9e779b44 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (OpenStack SWIFT)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..d1bdb81324 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 0090d9b91d..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Box.com (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 859324f795..0090d9b91d 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon Glacier (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..859324f795 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index be9e779b44..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Tahoe-LAFS)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..be9e779b44 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index a92964f743..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Google Drive)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index d1bdb81324..a92964f743 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 38 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (OpenStack SWIFT)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index be9e779b44..d1bdb81324 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 20 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Tahoe-LAFS)
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 05feed7d11..be9e779b44 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 30 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (My phone (or MP3 player))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 0090d9b91d..05feed7d11 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 82 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Box.com (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 859324f795..0090d9b91d 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 11 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon Glacier (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 1e5b6c172f..859324f795 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 18 "Amazon S3 (done)" 14 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

poll vote (Amazon S3 (done))
diff --git a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
index 2c6d62e9c2..1e5b6c172f 100644
--- a/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
+++ b/doc/design/assistant/polls/prioritizing_special_remotes.mdwn
@@ -6,7 +6,7 @@ locally paired systems, and remote servers with rsync.
 Help me prioritize my work: What special remote would you most like
 to use with the git-annex assistant?
 
-[[!poll open=yes 18 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
+[[!poll open=yes 19 "Amazon S3 (done)" 13 "Amazon Glacier (done)" 10 "Box.com (done)" 81 "My phone (or MP3 player)" 29 "Tahoe-LAFS" 19 "OpenStack SWIFT" 37 "Google Drive"]]
 
 This poll is ordered with the options I consider easiest to build
 listed first. Mostly because git-annex already supports them and they

put todos
diff --git a/doc/todo/put_--json_lacks_uuid.mdwn b/doc/todo/put_--json_lacks_uuid.mdwn
new file mode 100644
index 0000000000..387a780f6f
--- /dev/null
+++ b/doc/todo/put_--json_lacks_uuid.mdwn
@@ -0,0 +1,2 @@
+`git annex put --json` does not include the uuid of the remote that content
+is sent to. It ought to, since it can send to multiple remotes. --[[Joey]]
diff --git a/doc/todo/put_-J_OnlyActionOn_problem.mdwn b/doc/todo/put_-J_OnlyActionOn_problem.mdwn
new file mode 100644
index 0000000000..88cb783723
--- /dev/null
+++ b/doc/todo/put_-J_OnlyActionOn_problem.mdwn
@@ -0,0 +1,3 @@
+`git-annex put -J2` with the same file needing to go to 2 remotes only runs
+1 transfer at a time. This is because of the use of OnlyActionOn in
+Command.Move. --[[Joey]]

close
diff --git a/doc/todo/git-annex_put.mdwn b/doc/todo/git-annex_put.mdwn
index ca5a17b55f..449f112e0a 100644
--- a/doc/todo/git-annex_put.mdwn
+++ b/doc/todo/git-annex_put.mdwn
@@ -34,3 +34,5 @@ Another way to look at it is this tower has a missing leg:
 
 
 --[[Joey]]
+
+> [[done]]

put --fast like push --fast
Sponsored-by: Luke T. Shumaker
diff --git a/Command/Put.hs b/Command/Put.hs
index afa2ebbde2..ed4b70d256 100644
--- a/Command/Put.hs
+++ b/Command/Put.hs
@@ -8,6 +8,7 @@
 module Command.Put where
 
 import Command
+import qualified Annex
 import qualified Command.Copy
 import qualified Command.Move (MoveAction(..))
 import qualified Remote
@@ -36,8 +37,11 @@ optParser desc = PutOptions
 
 seek :: PutOptions -> CommandSeek
 seek o = startConcurrency commandStages $ do
+	fast <- Annex.getRead Annex.fast
+	let fastest = fromMaybe [] . headMaybe
 	contentremotes <- filter Remote.canPut
-		. concat . Remote.byCost 
+		. (if fast then fastest else concat)
+		. Remote.byCost 
 		<$> (Remote.contentRemotes =<< Remote.remoteList)
 	let seeker = AnnexedFileSeeker
 		{ startAction = \_ si p k ->
diff --git a/doc/git-annex-put.mdwn b/doc/git-annex-put.mdwn
index 554e705409..831fa146b1 100644
--- a/doc/git-annex-put.mdwn
+++ b/doc/git-annex-put.mdwn
@@ -27,6 +27,10 @@ Paths of files or directories to operate on can be specified.
   Only send files to remotes that want them, or when there are not
   yet have the desired number of copies.
 
+* `--fast`
+
+  Only send files to remotes with the lowest annex-cost value configured.
+
 * `--jobs=N` `-JN`
 
   Enables parallel transfers with up to the specified number of jobs

remove --fast option from put
put, like push, will always trust location logs
The main reason for doing this is that otherwise, put would always
do a lot of work, with ugly verbose output.
But also, best to leave copy --to a special case. It's useful in
some circumstances for copy to have a way to not be --fast, but no
need for two commands that both do it. And it's hard for users to understand.
Also, this leaves open the possibility of making put --fast behave like
push --fast.
Sponsored-by: Kevin Mueller
diff --git a/Command/Copy.hs b/Command/Copy.hs
index d483db6c6a..a550793c34 100644
--- a/Command/Copy.hs
+++ b/Command/Copy.hs
@@ -8,6 +8,7 @@
 module Command.Copy where
 
 import Command
+import qualified Annex
 import qualified Command.Move
 import qualified Remote
 import Annex.Wanted
@@ -37,7 +38,7 @@ optParser desc = CopyOptions
 	<*> parseAutoOption
 	<*> parseWantedOption
 	<*> parseBatchOption True
-	<*> pure Command.Move.Copy
+	<*> pure (Command.Move.Copy False)
 
 instance DeferredParseClass CopyOptions where
 	finishParse v = CopyOptions
@@ -52,7 +53,10 @@ instance DeferredParseClass CopyOptions where
 
 seek :: CopyOptions -> CommandSeek
 seek o = case fromToOptions o of
-	Just fto -> seek' o fto
+	Just fto -> do
+		fast <- Annex.getRead Annex.fast
+		let o' = o { moveAction = Command.Move.Copy fast }
+		seek' o' fto
 	Nothing -> giveup "Specify --from or --to"
 
 seek' :: CopyOptions -> FromToHereOptions -> CommandSeek
diff --git a/Command/Mirror.hs b/Command/Mirror.hs
index 67aac3f74b..42232f31c0 100644
--- a/Command/Mirror.hs
+++ b/Command/Mirror.hs
@@ -66,7 +66,7 @@ start o si file k = startKey o afile (si, k, ai)
 startKey :: MirrorOptions -> AssociatedFile -> (SeekInput, Key, ActionItem) -> CommandStart
 startKey o afile (si, key, ai) = case fromToOptions o of
 	ToRemote r -> checkFailedTransferDirection ai Upload $ ifM (inAnnex key)
-		( Command.Move.toStart NoLiveUpdate Command.Move.Copy afile key ai si =<< getParsed r
+		( Command.Move.toStart NoLiveUpdate (Command.Move.Copy False) afile key ai si =<< getParsed r
 		, do
 			(numcopies, mincopies) <- getSafestNumMinCopies afile key
 			Command.Drop.startRemote NoLiveUpdate pcc afile ai si numcopies mincopies key (Command.Drop.DroppingUnused False)
diff --git a/Command/Move.hs b/Command/Move.hs
index de01527832..1d7412b14c 100644
--- a/Command/Move.hs
+++ b/Command/Move.hs
@@ -57,12 +57,16 @@ instance DeferredParseClass MoveOptions where
 		<*> pure (keyOptions v)
 		<*> pure (batchOption v)
 
-data MoveAction = Move | Copy | Put | Get
+data MoveAction
+	= Move
+	| Copy { copyFast :: Bool }
+	| Put
+	| Get
 	deriving (Show, Eq)
 
 describeMoveAction :: MoveAction -> String
 describeMoveAction Move = "move"
-describeMoveAction Copy = "copy"
+describeMoveAction (Copy _) = "copy"
 describeMoveAction Put = "put"
 describeMoveAction Get = "get"
 
@@ -140,15 +144,16 @@ toStart lu moveaction afile key ai si dest = do
 		else toStart' lu dest moveaction afile key ai si
 
 toStart' :: LiveUpdate -> Remote -> MoveAction -> AssociatedFile -> Key -> ActionItem -> SeekInput -> CommandStart
-toStart' lu dest moveaction afile key ai si = do
-	fast <- Annex.getRead Annex.fast
-	if fast && moveaction /= Move
-		then ifM (expectedPresent dest key)
+toStart' lu dest moveaction afile key ai si =
+	case moveaction of
+		Move -> checkhaskey
+		Copy { copyFast = False } -> checkhaskey
+		_ -> ifM (expectedPresent dest key)
 			( stop
 			, go True (pure $ Right False)
 			)
-		else go False (Remote.hasKey dest key)
   where
+	checkhaskey = go False (Remote.hasKey dest key)
 	go fastcheck isthere =
 		starting (describeMoveAction moveaction) (OnlyActionOn key ai) si $
 			toPerform lu dest moveaction key afile fastcheck =<< isthere
@@ -364,11 +369,10 @@ fromToStart lu moveaction afile key ai si src dest =
   where
 	somethingtodo
 		| Remote.uuid src == Remote.uuid dest = return False
-		| otherwise = do
-			fast <- Annex.getRead Annex.fast
-			if fast && moveaction /= Move
-				then not <$> expectedPresent dest key
-				else return True
+		| otherwise = case moveaction of
+			Move -> return True
+			Copy { copyFast = False } -> return True
+			_ -> not <$> expectedPresent dest key
 
 fromAnywhereToStart :: LiveUpdate -> MoveAction -> AssociatedFile -> Key -> ActionItem -> SeekInput -> Remote -> CommandStart
 fromAnywhereToStart lu moveaction afile key ai si dest =
@@ -388,11 +392,10 @@ fromAnywhereToStart lu moveaction afile key ai si dest =
 						toStart lu moveaction afile key ai si dest 
 				next $ return True
   where
-	somethingtodo = do
-		fast <- Annex.getRead Annex.fast
-		if fast && moveaction /= Move
-			then not <$> expectedPresent dest key
-			else return True
+	somethingtodo = case moveaction of
+		Move -> return True
+		Copy { copyFast = False } -> return True
+		_ -> not <$> expectedPresent dest key
 
 {- When there is a local copy, transfer it to the dest, and drop from the src.
  -
@@ -432,7 +435,7 @@ fromToPerform lu src dest moveaction key afile = do
 		dropsrc <- fromsrc True
 		combinecleanups 
 			-- Send to dest, preserve local copy.
-			(todest Nothing Copy haskey)
+			(todest Nothing (Copy False) haskey)
 			(\senttodest -> if senttodest
 				then dropsrc moveaction
 				else stop
@@ -474,7 +477,7 @@ fromToPerform lu src dest moveaction key afile = do
 							-- Drop from src, checking
 							-- copies including dest.
 							combinecleanups
-								(cleanupfromsrc Copy)
+								(cleanupfromsrc (Copy False))
 								(\_ -> if senttodest
 									then dropfromsrc (\l -> UnVerifiedRemote dest : l)
 									else stop
diff --git a/doc/git-annex-put.mdwn b/doc/git-annex-put.mdwn
index 03a8620563..554e705409 100644
--- a/doc/git-annex-put.mdwn
+++ b/doc/git-annex-put.mdwn
@@ -34,12 +34,6 @@ Paths of files or directories to operate on can be specified.
 
   Setting this to "cpus" will run one job per CPU core.
 
-* `--fast`
-
-  Avoid doing round trips to check if a remote already has content.
-  This can be faster, but might skip sending content to a remote in
-  some cases.
-
 * `--all` `-A`
 
   Rather than specifying a filename or path to send, this option can be

git-annex put working
(Except it says it's copying, a small wart.)
Implementing this in terms of copy is a little bit weird, but copy came
first. And copy is in some senses lower-level, since the user has to
tell it which remote to copy to, while put picks where the content
should go.
In that way, put is similar to push, but it does not drop unwanted
content from remotes, or send git content. Which is why it supports
--json, unlike push.
Sponsored-by: Joshua Antonishen
diff --git a/CHANGELOG b/CHANGELOG
index 6687c63ad9..9497f783ed 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+git-annex (10.20260602) UNRELEASED; urgency=medium
+
+  * put: New command.
+
+ -- Joey Hess <id@joeyh.name>  Wed, 03 Jun 2026 14:49:39 -0400
+
 git-annex (10.20260601) upstream; urgency=medium
 
   * Avoid an error message displaying a password embedded in a git remote url.
diff --git a/CmdLine/GitAnnex.hs b/CmdLine/GitAnnex.hs
index 65b5fac680..b2adcfa6f1 100644
--- a/CmdLine/GitAnnex.hs
+++ b/CmdLine/GitAnnex.hs
@@ -104,6 +104,7 @@ import qualified Command.Sync
 import qualified Command.Assist
 import qualified Command.Pull
 import qualified Command.Push
+import qualified Command.Put
 import qualified Command.Satisfy
 import qualified Command.Mirror
 import qualified Command.AddUrl
@@ -161,6 +162,7 @@ cmds testoptparser testrunner mkbenchmarkgenerator = map addGitAnnexCommonOption
 	, Command.Assist.cmd
 	, Command.Pull.cmd
 	, Command.Push.cmd
+	, Command.Put.cmd
 	, Command.Satisfy.cmd
 	, Command.Mirror.cmd
 	, Command.AddUrl.cmd
diff --git a/Command/Put.hs b/Command/Put.hs
index 1c2b48108c..ba29cde2be 100644
--- a/Command/Put.hs
+++ b/Command/Put.hs
@@ -1,6 +1,6 @@
 {- git-annex command
  -
- - Copyright 2010-2026 Joey Hess <id@joeyh.name>
+ - Copyright 2026 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -8,22 +8,20 @@
 module Command.Put where
 
 import Command
-import qualified Command.Move
+import qualified Command.Copy
 import qualified Remote
-import Annex.Wanted
-import Annex.NumCopies
 
 cmd :: Command
 cmd = withAnnexOptions [jobsOption, jsonOptions, jsonProgressOption, annexedMatchingOptions] $
 	command "put" SectionCommon
 		"send content of files to other repositories"
-		paramPaths (seek <--< optParser)
+		paramPaths (seek <$$> optParser)
 
 data PutOptions = PutOptions
 	{ putFiles :: CmdParams
 	, keyOptions :: Maybe KeyOptions
-	, autoMode :: Bool
 	, wantedMode :: Bool
+	, autoMode :: Bool
 	, batchOption :: BatchMode
 	}
 
@@ -31,67 +29,43 @@ optParser :: CmdParamsDesc -> Parser PutOptions
 optParser desc = PutOptions
 	<$> cmdParams desc
 	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
-	<*> parseAutoOption
 	<*> parseWantedOption
+	<*> parseAutoOption
 	<*> parseBatchOption True
 
 seek :: PutOptions -> CommandSeek
 seek o = startConcurrency commandStages $ do
+	contentremotes <- filter Remote.canPut
+		. concat . Remote.byCost 
+		<$> (Remote.contentRemotes =<< Remote.remoteList)
+	let seeker = AnnexedFileSeeker
+		{ startAction = \_ si p k ->
+			return $ flip map contentremotes $ \r ->
+				Command.Copy.start co (to r) si p k
+		, checkContentPresent = Just True
+		, usesLocationLog = True
+		}
+	let keyaction v = 
+		return $ flip map contentremotes $ \r ->
+			Command.Copy.startKey (to r) v
 	case batchOption o of
 		NoBatch -> withKeyOptions
 			(keyOptions o) (autoMode o || wantedMode o) seeker
-			(commandAction . keyaction)
+			(\v -> commandActions =<< keyaction v)
 			(withFilesInGitAnnex ww seeker)
 			=<< workTreeItems ww (putFiles o)
 		Batch fmt -> batchOnly (keyOptions o) (putFiles o) $
-			batchAnnexed fmt seeker keyaction
+			batchAnnexed' fmt seeker keyaction
   where
 	ww = WarnUnmatchLsFiles "put"
-	
-	seeker = AnnexedFileSeeker
-		{ startAction = startSingle $ const $ start o
-		, checkContentPresent = case fto of
-			FromOrToRemote (FromRemote _) -> Just False
-			FromOrToRemote (ToRemote _) -> Just True
-			ToHere -> Just False
-			FromRemoteToRemote _ _ -> Nothing
-			FromAnywhereToRemote _ -> Nothing
-		, usesLocationLog = True
-		}
-	keyaction = Command.Move.startKey NoLiveUpdate fto Command.Move.RemoveNever
-
-{- A copy is just a move that does not delete the source file.
- - However, auto mode avoids unnecessary copies, and avoids getting or
- - sending non-preferred content. -}
-start :: PutOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
-start o fto si file key = do
-	ru <- case fto of
-		FromOrToRemote (ToRemote dest) -> getru dest
-		FromOrToRemote (FromRemote _) -> pure Nothing
-		ToHere -> pure Nothing
-		FromRemoteToRemote _ dest -> getru dest
-		FromAnywhereToRemote dest -> getru dest
-	lu <- prepareLiveUpdate ru key AddingKey
-	start' lu o fto si file key
-  where
-	getru dest = Just . Remote.uuid <$> getParsed dest
 
-start' :: LiveUpdate -> PutOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
-start' lu o fto si file key = stopUnless shouldCopy $ 
-	Command.Move.start lu fto Command.Move.RemoveNever si file key
-  where
-	shouldCopy
-		| autoMode o = want <||> numCopiesCheck file key (<)
-		| wantedMode o = want
-		| otherwise = return True
-	want = case fto of
-		FromOrToRemote (ToRemote dest) -> checkwantsend dest
-		FromOrToRemote (FromRemote _) -> checkwantget
-		ToHere -> checkwantget
-		FromRemoteToRemote _ dest -> checkwantsend dest
-		FromAnywhereToRemote dest -> checkwantsend dest
-
-	checkwantsend dest = 
-		(Remote.uuid <$> getParsed dest) >>=
-			wantGetBy lu False (Just key) (AssociatedFile (Just file))
-	checkwantget = wantGet lu False (Just key) (AssociatedFile (Just file))
+	co = Command.Copy.CopyOptions
+		{ Command.Copy.copyFiles = putFiles o
+		, Command.Copy.fromToOptions = Nothing
+		, Command.Copy.keyOptions = keyOptions o
+		, Command.Copy.autoMode = autoMode o
+		, Command.Copy.wantedMode = wantedMode o
+		, Command.Copy.batchOption = batchOption o
+		}
+	
+	to = FromOrToRemote . ToRemote . ReadyParse
diff --git a/doc/git-annex-put.mdwn b/doc/git-annex-put.mdwn
index fe14cfe45b..03a8620563 100644
--- a/doc/git-annex-put.mdwn
+++ b/doc/git-annex-put.mdwn
@@ -8,7 +8,7 @@ git annex put `[path ...]`
 
 # DESCRIPTION
 
-Copies the content of annexed files to other remotes. By default,
+Sends the content of annexed files to remotes. By default,
 files are sent to all remotes (except for certian special remotes
 that have to be populated using `git-annex export`).
 
@@ -17,9 +17,15 @@ Paths of files or directories to operate on can be specified.
 
 # OPTIONS
 
-* `--to=remote`
+* `--wanted` `-w`
+
+  Send files only to remotes that want them.
+  See [[git-annex-preferred-content]](1)
 
-  Only send the content of files to the specified remote.
+* `--auto`
+
+  Only send files to remotes that want them, or when there are not
+  yet have the desired number of copies.
 
 * `--jobs=N` `-JN`
 
@@ -28,18 +34,6 @@ Paths of files or directories to operate on can be specified.
 
   Setting this to "cpus" will run one job per CPU core.

(Diff truncated)
Merge branch 'master' into put
update
diff --git a/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment b/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment
index 7666345901..fccda0bc0d 100644
--- a/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment
+++ b/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment
@@ -7,15 +7,31 @@
 another remote if the first is not available, and of course does nothing
 if the content is present already. 
 
-It would be most symmetric if `git-annex put` picked one remote to send
-content to (ie, the fastest one that wants it), fell back to
-the next best remote if that one was not available, and avoided 
+It would be perhaps most symmetric with that if `git-annex put` picked one
+remote to send content to (ie, the lowest cost one that wants it), fell
+back to the next best remote if that one was not available, and avoided
 sending any content for files that are in some other repository already.
 
 As well as just being symmetric, that feels like a useful behavior that
-is not currently possible to get from any git-annex command. 
+is not currently possible to get from any git-annex command.
 
 That's in tension with the idea that `git-annex put --json` would
 send to the same remotes that `git-annex push` would. Maybe that
 behavior should be an option? Or maybe that belongs in yet another command.
+
+Just how useful would the 1 copy behavior be? One indication maybe is that
+noone has ever asked for that behavior. And it seems like it would be easy
+for the content to go to an unexpected place and break a workflow. Eg,
+suppose a user starts using `git-annex put`, which sends the content
+to `origin` and makes it available to others. But they also have a remote
+for a local USB drive, which has been disconnected all that time. When
+they one day reconnect that drive, it has a lower cost, and so their
+puts start going there, preventing others from accessing the files.
+
+Also it's worth noting that `pull` picks a remote to get to, but
+`push` sends to all remotes that want it. So this particular symmetry
+is not maintained all the way up. So perhaps it's not a useful symmetry.
+
+Overall, it seems like something that could be an option and not the
+default. If someone has a good use case.
 """]]

improve wording
diff --git a/Command/Get.hs b/Command/Get.hs
index 073b6bbb6f..6688bdf42e 100644
--- a/Command/Get.hs
+++ b/Command/Get.hs
@@ -18,7 +18,7 @@ import Logs.Location
 cmd :: Command
 cmd = withAnnexOptions [jobsOption, jsonOptions, jsonProgressOption, annexedMatchingOptions] $ 
 	command "get" SectionCommon 
-		"make content of annexed files available"
+		"get content of annexed files"
 		paramPaths (seek <$$> optParser)
 
 data GetOptions = GetOptions
diff --git a/doc/git-annex-drop.mdwn b/doc/git-annex-drop.mdwn
index ea610d5b05..94c9dd4f45 100644
--- a/doc/git-annex-drop.mdwn
+++ b/doc/git-annex-drop.mdwn
@@ -1,6 +1,6 @@
 # NAME
 
-git-annex drop - remove content of files from repository
+git-annex drop - remove content of annexed files from repository
 
 # SYNOPSIS
 
diff --git a/doc/git-annex-get.mdwn b/doc/git-annex-get.mdwn
index 5c49c39669..9c45b0ece4 100644
--- a/doc/git-annex-get.mdwn
+++ b/doc/git-annex-get.mdwn
@@ -1,6 +1,6 @@
 # NAME
 
-git-annex get - make content of annexed files available
+git-annex get - get content of annexed files
 
 # SYNOPSIS
 
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
index 3cb7e4f6d8..1dd8a4506b 100644
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -68,7 +68,7 @@ content from the key-value store.
 
 * `get [path ...]`
 
-  Makes the content of annexed files available in this repository.
+  Gets the content of annexed files.
   
   See [[git-annex-get]](1) for details.
 

comment
diff --git a/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment b/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment
new file mode 100644
index 0000000000..7666345901
--- /dev/null
+++ b/doc/todo/git-annex_put/comment_1_e71f2d3a103e84812977620706ee6542._comment
@@ -0,0 +1,21 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 1"""
+ date="2026-06-03T16:02:58Z"
+ content="""
+`git-annex get` picks which remote to use, and falls back as needed to
+another remote if the first is not available, and of course does nothing
+if the content is present already. 
+
+It would be most symmetric if `git-annex put` picked one remote to send
+content to (ie, the fastest one that wants it), fell back to
+the next best remote if that one was not available, and avoided 
+sending any content for files that are in some other repository already.
+
+As well as just being symmetric, that feels like a useful behavior that
+is not currently possible to get from any git-annex command. 
+
+That's in tension with the idea that `git-annex put --json` would
+send to the same remotes that `git-annex push` would. Maybe that
+behavior should be an option? Or maybe that belongs in yet another command.
+"""]]

add news item for git-annex 10.20260601
diff --git a/doc/news/version_10.20260213.mdwn b/doc/news/version_10.20260213.mdwn
deleted file mode 100644
index 873cee77a3..0000000000
--- a/doc/news/version_10.20260213.mdwn
+++ /dev/null
@@ -1,30 +0,0 @@
-git-annex 10.20260213 released with [[!toggle text="these changes"]]
-[[!toggleable text="""  * When used with git forges that allow Push to Create, the remote's
-    annex-uuid is re-probed after the initial push.
-  * addurl, importfeed: Enable --verifiable by default.
-  * fromkey, registerurl: When passed an url, generate a VURL key.
-  * unregisterurl: Unregister both VURL and URL keys.
-  * Fix behavior of local git remotes that have annex-ignore
-    set to be the same as ssh git remotes.
-  * Added annex.security.allow-insecure-https config, which allows
-    using old http servers that use TLS 1.2 without Extended Main
-    Secret support.
-  * p2phttp: Commit git-annex branch changes promptly.
-  * p2phttp: Fix a server stall by disabling warp's slowloris attack
-    prevention.
-  * p2phttp: Added --cpus option.
-  * Avoid ever starting more capabilities than the number of cpus.
-  * fsck: Support repairing a corrupted file in a versioned S3 remote.
-  * Fix incorrect transfer direction in remote transfer log when
-    downloading from a local git remote.
-  * Fix bug that prevented 2 clones of a local git remote
-    from concurrently downloading the same file.
-  * rsync: Avoid deleting contents of a non-empty directory when
-    removing the last exported file from the directory.
-  * unregisterurl: Fix display of action to not be "registerurl".
-  * The OsPath build flag requires file-io 0.2.0, which fixes several
-    issues.
-  * Remove deprecated commands direct, indirect, proxy, and transferkeys.
-  * Deprecate undo command.
-  * Remove undo action from kde and nautilus integrations.
-  * Fix build on BSDs. Thanks, Greg Steuck"""]]
\ No newline at end of file
diff --git a/doc/news/version_10.20260601.mdwn b/doc/news/version_10.20260601.mdwn
new file mode 100644
index 0000000000..ebf553e703
--- /dev/null
+++ b/doc/news/version_10.20260601.mdwn
@@ -0,0 +1,18 @@
+git-annex 10.20260601 released with [[!toggle text="these changes"]]
+[[!toggleable text="""  * Avoid an error message displaying a password embedded in a git remote url.
+    (Reversion introduced in 10.20260421)
+  * Fix use of annex.commitmessage
+    (Reversion introduced in 10.20250115)
+  * disableremote: Avoid (temporarily) clearing size tracking information
+    for other repos than the one being disabled.
+  * Fix bug that prevented remote.name.annex-web-options from always taking
+    effect, and sometimes caused it to be used in situations where it
+    should not be.
+  * map: Don't try to use dot -Tx11 to display the map, that no longer
+    works in commonly available builds of graphviz.
+  * push: Avoid updating a view branch or adjusted branch.
+  * pull: Avoid propagating adjusted branch changes to the original branch.
+  * get, copy: Added --wanted option, which only transfers files
+    that are preferred content.
+  * fsck, drop, whereis: Support --failed option.
+  * Display an introduction before other debug output."""]]
\ No newline at end of file

drop, whereis: support --failed option
Looked over the other commands and these seemed like the only ones left
that could usefully support it.
Sponsored-by: Leon Schuermann
diff --git a/CHANGELOG b/CHANGELOG
index 8c32fff47c..d9d16c2154 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -15,7 +15,7 @@ git-annex (10.20260526) UNRELEASED; urgency=medium
   * pull: Avoid propagating adjusted branch changes to the original branch.
   * get, copy: Added --wanted option, which only transfers files
     that are preferred content.
-  * fsck: Support --failed option. 
+  * fsck, drop, whereis: Support --failed option. 
   * Display an introduction before other debug output.
 
  -- Joey Hess <id@joeyh.name>  Thu, 28 May 2026 13:13:21 -0400
diff --git a/Command/Drop.hs b/Command/Drop.hs
index 95b6bf062a..a50921fb31 100644
--- a/Command/Drop.hs
+++ b/Command/Drop.hs
@@ -41,7 +41,7 @@ optParser desc = DropOptions
 	<$> cmdParams desc
 	<*> optional parseDropFromOption
 	<*> parseAutoOption
-	<*> optional parseKeyOptions
+	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
 	<*> parseBatchOption True
 
 parseDropFromOption :: Parser (DeferredParse Remote)
diff --git a/Command/Whereis.hs b/Command/Whereis.hs
index 919d96b322..a193232982 100644
--- a/Command/Whereis.hs
+++ b/Command/Whereis.hs
@@ -38,7 +38,7 @@ data WhereisOptions = WhereisOptions
 optParser :: CmdParamsDesc -> Parser WhereisOptions
 optParser desc = WhereisOptions
 	<$> cmdParams desc
-	<*> optional parseKeyOptions
+	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
 	<*> parseBatchOption True
 	<*> optional parseFormatOption
 
diff --git a/doc/git-annex-drop.mdwn b/doc/git-annex-drop.mdwn
index 3d1307700a..ea610d5b05 100644
--- a/doc/git-annex-drop.mdwn
+++ b/doc/git-annex-drop.mdwn
@@ -88,6 +88,13 @@ Paths of files or directories to drop can be specified.
   Note that this bypasses checking the .gitattributes annex.numcopies
   setting and required content settings.
 
+* `--failed`
+
+  Drop files that have recently failed to be transferred.
+  
+  Note that this bypasses checking the .gitattributes annex.numcopies
+  setting and required content settings.
+
 * `--key=keyname`
 
   Use this option to drop a specified key.
diff --git a/doc/git-annex-whereis.mdwn b/doc/git-annex-whereis.mdwn
index 84678709f0..8771afcbab 100644
--- a/doc/git-annex-whereis.mdwn
+++ b/doc/git-annex-whereis.mdwn
@@ -50,6 +50,10 @@ received from remotes.
 
   Show whereis information for files found by last run of git-annex unused.
 
+* `--failed`
+
+  Operate on files that have recently failed to be transferred.
+
 * `--batch`
 
   Enables batch mode, in which a file is read in a line from stdin,

fsck: Support --failed option
Example use: I was running a long git-annex copy and it failed on a few
files, which have apparently gotten corrupted. So I want to fsck those to
let it deal with them, but fscking the whole repo or specific files would
be too much bother. With --failed fsck will check just those files.
Sponsored-by: Graham Spencer on Patreon
diff --git a/CHANGELOG b/CHANGELOG
index 3f15c1baa3..8c32fff47c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -15,6 +15,7 @@ git-annex (10.20260526) UNRELEASED; urgency=medium
   * pull: Avoid propagating adjusted branch changes to the original branch.
   * get, copy: Added --wanted option, which only transfers files
     that are preferred content.
+  * fsck: Support --failed option. 
   * Display an introduction before other debug output.
 
  -- Joey Hess <id@joeyh.name>  Thu, 28 May 2026 13:13:21 -0400
diff --git a/Command/Fsck.hs b/Command/Fsck.hs
index a17ca0d2c3..645576c117 100644
--- a/Command/Fsck.hs
+++ b/Command/Fsck.hs
@@ -83,7 +83,7 @@ optParser desc = FsckOptions
 		<> completeRemotes
 		))
 	<*> optional parseincremental
-	<*> optional parseKeyOptions
+	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
   where
 	parseincremental =
 		flag' StartIncrementalO
diff --git a/doc/git-annex-fsck.mdwn b/doc/git-annex-fsck.mdwn
index 89760119d8..5873799af6 100644
--- a/doc/git-annex-fsck.mdwn
+++ b/doc/git-annex-fsck.mdwn
@@ -92,6 +92,10 @@ better format.
 
   Operate on files found by last run of git-annex unused.
 
+* `--failed`
+
+  Operate on files that have recently failed to be transferred.
+
 * `--key=keyname`
 
   Use this option to fsck a specified key.

wip git-annex put
diff --git a/Command/Put.hs b/Command/Put.hs
new file mode 100644
index 0000000000..f5aadc2ccb
--- /dev/null
+++ b/Command/Put.hs
@@ -0,0 +1,97 @@
+{- git-annex command
+ -
+ - Copyright 2010-2026 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU AGPL version 3 or higher.
+ -}
+
+module Command.Put where
+
+import Command
+import qualified Command.Move
+import qualified Remote
+import Annex.Wanted
+import Annex.NumCopies
+
+cmd :: Command
+cmd = withAnnexOptions [jobsOption, jsonOptions, jsonProgressOption, annexedMatchingOptions] $
+	command "put" SectionCommon
+		"put content of files to other repositories"
+		paramPaths (seek <--< optParser)
+
+data PutOptions = PutOptions
+	{ putFiles :: CmdParams
+	, keyOptions :: Maybe KeyOptions
+	, autoMode :: Bool
+	, wantedMode :: Bool
+	, batchOption :: BatchMode
+	}
+
+optParser :: CmdParamsDesc -> Parser PutOptions
+optParser desc = PutOptions
+	<$> cmdParams desc
+	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
+	<*> parseAutoOption
+	<*> parseWantedOption
+	<*> parseBatchOption True
+
+seek :: CopyOptions -> CommandSeek
+seek o = startConcurrency commandStages $ do
+	case batchOption o of
+		NoBatch -> withKeyOptions
+			(keyOptions o) (autoMode o || wantedMode o) seeker
+			(commandAction . keyaction)
+			(withFilesInGitAnnex ww seeker)
+			=<< workTreeItems ww (copyFiles o)
+		Batch fmt -> batchOnly (keyOptions o) (copyFiles o) $
+			batchAnnexed fmt seeker keyaction
+  where
+	ww = WarnUnmatchLsFiles "put"
+	
+	seeker = AnnexedFileSeeker
+		{ startAction = const $ start o
+		, checkContentPresent = case fto of
+			FromOrToRemote (FromRemote _) -> Just False
+			FromOrToRemote (ToRemote _) -> Just True
+			ToHere -> Just False
+			FromRemoteToRemote _ _ -> Nothing
+			FromAnywhereToRemote _ -> Nothing
+		, usesLocationLog = True
+		}
+	keyaction = Command.Move.startKey NoLiveUpdate fto Command.Move.RemoveNever
+
+{- A copy is just a move that does not delete the source file.
+ - However, auto mode avoids unnecessary copies, and avoids getting or
+ - sending non-preferred content. -}
+start :: CopyOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
+start o fto si file key = do
+	ru <- case fto of
+		FromOrToRemote (ToRemote dest) -> getru dest
+		FromOrToRemote (FromRemote _) -> pure Nothing
+		ToHere -> pure Nothing
+		FromRemoteToRemote _ dest -> getru dest
+		FromAnywhereToRemote dest -> getru dest
+	lu <- prepareLiveUpdate ru key AddingKey
+	start' lu o fto si file key
+  where
+	getru dest = Just . Remote.uuid <$> getParsed dest
+
+start' :: LiveUpdate -> CopyOptions -> FromToHereOptions -> SeekInput -> OsPath -> Key -> CommandStart
+start' lu o fto si file key = stopUnless shouldCopy $ 
+	Command.Move.start lu fto Command.Move.RemoveNever si file key
+  where
+	shouldCopy
+		| autoMode o = want <||> numCopiesCheck file key (<)
+		| wantedMode o = want
+		| otherwise = return True
+	want = case fto of
+		FromOrToRemote (ToRemote dest) -> checkwantsend dest
+		FromOrToRemote (FromRemote _) -> checkwantget
+		ToHere -> checkwantget
+		FromRemoteToRemote _ dest -> checkwantsend dest
+		FromAnywhereToRemote dest -> checkwantsend dest
+
+	checkwantsend dest = 
+		(Remote.uuid <$> getParsed dest) >>=
+			wantGetBy lu False (Just key) (AssociatedFile (Just file))
+	checkwantget = wantGet lu False (Just key) (AssociatedFile (Just file))
diff --git a/doc/git-annex-put.mdwn b/doc/git-annex-put.mdwn
new file mode 100644
index 0000000000..fe14cfe45b
--- /dev/null
+++ b/doc/git-annex-put.mdwn
@@ -0,0 +1,127 @@
+# NAME
+
+git-annex put - send content of files to other repositories
+
+# SYNOPSIS
+
+git annex put `[path ...]`
+
+# DESCRIPTION
+
+Copies the content of annexed files to other remotes. By default,
+files are sent to all remotes (except for certian special remotes
+that have to be populated using `git-annex export`).
+
+With no parameters, operates on all annexed files in the current directory.
+Paths of files or directories to operate on can be specified.
+
+# OPTIONS
+
+* `--to=remote`
+
+  Only send the content of files to the specified remote.
+
+* `--jobs=N` `-JN`
+
+  Enables parallel transfers with up to the specified number of jobs
+  running at once. For example: `-J10`
+
+  Setting this to "cpus" will run one job per CPU core.
+
+* `--wanted` `-w`
+
+  Rather than sending all specified files, only send those that
+  are preferred content of the destination repository.
+  See [[git-annex-preferred-content]](1)
+
+* `--auto`
+
+  Rather than sending all specified files, only send those that don't yet have
+  the desired number of copies, or that are preferred content of the
+  destination repository.
+
+* `--fast`
+
+  Avoid doing round trips to check if a remote already has content.
+  This can be faster, but might skip sending content to a remote in
+  some cases.
+
+* `--all` `-A`
+
+  Rather than specifying a filename or path to send, this option can be
+  used to send all available versions of all files.
+
+  This is the default behavior when running git-annex in a bare repository.
+
+* `--branch=ref`
+
+  Operate on files in the specified branch or treeish.
+
+* `--unused`
+
+  Operate on files found by last run of git-annex unused.
+
+* `--failed`
+
+  Operate on files that have recently failed to be transferred.
+
+* `--key=keyname`
+
+  Use this option to send a specified key.
+
+* matching options
+
+  The [[git-annex-matching-options]](1)
+  can be used to specify what to send.
+
+* `--batch`
+
+  Enables batch mode, in which lines containing names of files to send
+  are read from stdin.
+
+  As each specified file is processed, the usual progress output is
+  displayed. If a file's content does not need to be sent, or it does not
+  match specified matching options, or it is not an annexed file,
+  a blank line is output in response instead.
+
+  Since the usual output while sending a file is verbose and not
+  machine-parseable, you may want to use --json in combination with
+  --batch.
+
+* `--batch-keys`

(Diff truncated)
wording
diff --git a/doc/git-annex-copy.mdwn b/doc/git-annex-copy.mdwn
index 88a94fabdf..d837256e3b 100644
--- a/doc/git-annex-copy.mdwn
+++ b/doc/git-annex-copy.mdwn
@@ -8,7 +8,7 @@ git annex copy `[path ...] [--from=remote|--to=remote]`
 
 # DESCRIPTION
 
-Copies the content of files from or to another remote.
+Copies the content of files from or to another repository.
 
 With no parameters, operates on all annexed files in the current directory.
 Paths of files or directories to operate on can be specified.
diff --git a/doc/git-annex-move.mdwn b/doc/git-annex-move.mdwn
index c93953f985..03785bc910 100644
--- a/doc/git-annex-move.mdwn
+++ b/doc/git-annex-move.mdwn
@@ -8,7 +8,7 @@ git annex move `[path ...] [--from=remote|--to=remote|--to=here]`
 
 # DESCRIPTION
 
-Moves the content of files from or to another remote.
+Moves the content of files from or to another repository.
 
 With no parameters, operates on all annexed files in the current directory.
 Paths of files or directories to operate on can be specified.

typo
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
index 098d34ecee..727dd711d3 100644
--- a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
+++ b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
@@ -13,5 +13,5 @@ I think it would be a good idea to add a `--wanted` option to commands
 that support `--auto`. It would only operate on preferred content, avoiding
 making copies only to work toward satisfying numcopies.
 
-Update: Added `--wanted.
+Update: Added `--wanted`
 """]]

--wanted
get, copy: Added --wanted option, which only transfers files that are
preferred content, not taking numcopies into account.
The only other command that supports --auto is drop. I considered
making it support --wanted. However, since drop always honors numcopies,
the behavior of --wanted would necessarily be the same as --auto. I
don't think it's worth adding a new option that behaves the same as an
existing option.
Sponsored-by: unqueued
diff --git a/CHANGELOG b/CHANGELOG
index 8d4bebd4d3..f68a512e9c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
 git-annex (10.20260526) UNRELEASED; urgency=medium
 
+  * get, copy: Added --wanted option, which only transfers files
+    that are preferred content.
   * Avoid an error message displaying a password embedded in a git remote url.
     (Reversion introduced in 10.20260421)
   * Fix use of annex.commitmessage
diff --git a/CmdLine/GitAnnex/Options.hs b/CmdLine/GitAnnex/Options.hs
index cb969c8c21..5bc6f0074e 100644
--- a/CmdLine/GitAnnex/Options.hs
+++ b/CmdLine/GitAnnex/Options.hs
@@ -129,6 +129,12 @@ parseAutoOption = switch
 	<> help "automatic mode"
 	)
 
+parseWantedOption :: Parser Bool
+parseWantedOption = switch
+	( long "wanted" <> short 'w'
+	<> help "operate on preferred content"
+	)
+
 mkParseRemoteOption :: RemoteName -> DeferredParse Remote
 mkParseRemoteOption = DeferredParse 
 	. (fromJust <$$> Remote.byNameWithUUID)
diff --git a/CmdLine/Seek.hs b/CmdLine/Seek.hs
index c012811ac3..f8a981ee7a 100644
--- a/CmdLine/Seek.hs
+++ b/CmdLine/Seek.hs
@@ -233,10 +233,10 @@ withKeyOptions'
 	-> (WorkTreeItems -> CommandSeek)
 	-> WorkTreeItems
 	-> CommandSeek
-withKeyOptions' ko auto mkkeyaction fallbackaction worktreeitems = do
+withKeyOptions' ko autoorwanted mkkeyaction fallbackaction worktreeitems = do
 	bare <- fromRepo Git.repoIsLocalBare
-	when (auto && bare) $
-		giveup "Cannot use --auto in a bare repository"
+	when (autoorwanted && bare) $
+		giveup "Cannot use --auto or --wanted in a bare repository"
 	case (nospecifiedworktreeitems, ko) of
 		(True, Nothing)
 			| bare -> nofilename $ noauto runallkeys
@@ -251,7 +251,7 @@ withKeyOptions' ko auto mkkeyaction fallbackaction worktreeitems = do
 		(False, Just _) -> giveup "Can only specify one of file names, --all, --branch, --unused, --failed, --key, or --incomplete"
   where
 	noauto a
-		| auto = giveup "Cannot use --auto with --all or --branch or --unused or --key or --incomplete"
+		| autoorwanted = giveup "Cannot use --auto or --wanted with --all or --branch or --unused or --key or --incomplete"
 		| otherwise = a
 			
 	nofilename a = ifM (Limit.introspect matchNeedsFileName)
diff --git a/Command/Copy.hs b/Command/Copy.hs
index dce01ddefe..59c476aba9 100644
--- a/Command/Copy.hs
+++ b/Command/Copy.hs
@@ -24,6 +24,7 @@ data CopyOptions = CopyOptions
 	, fromToOptions :: Maybe FromToHereOptions
 	, keyOptions :: Maybe KeyOptions
 	, autoMode :: Bool
+	, wantedMode :: Bool
 	, batchOption :: BatchMode
 	}
 
@@ -33,6 +34,7 @@ optParser desc = CopyOptions
 	<*> parseFromToHereOptions
 	<*> optional (parseKeyOptions <|> parseFailedTransfersOption)
 	<*> parseAutoOption
+	<*> parseWantedOption
 	<*> parseBatchOption True
 
 instance DeferredParseClass CopyOptions where
@@ -42,6 +44,7 @@ instance DeferredParseClass CopyOptions where
 			(fromToOptions v)
 		<*> pure (keyOptions v)
 		<*> pure (autoMode v)
+		<*> pure (wantedMode v)
 		<*> pure (batchOption v)
 
 seek :: CopyOptions -> CommandSeek
@@ -53,7 +56,7 @@ seek' :: CopyOptions -> FromToHereOptions -> CommandSeek
 seek' o fto = startConcurrency (Command.Move.stages fto) $ do
 	case batchOption o of
 		NoBatch -> withKeyOptions
-			(keyOptions o) (autoMode o) seeker
+			(keyOptions o) (autoMode o || wantedMode o) seeker
 			(commandAction . keyaction)
 			(withFilesInGitAnnex ww seeker)
 			=<< workTreeItems ww (copyFiles o)
@@ -96,6 +99,7 @@ start' lu o fto si file key = stopUnless shouldCopy $
   where
 	shouldCopy
 		| autoMode o = want <||> numCopiesCheck file key (<)
+		| wantedMode o = want
 		| otherwise = return True
 	want = case fto of
 		FromOrToRemote (ToRemote dest) -> checkwantsend dest
diff --git a/Command/Get.hs b/Command/Get.hs
index 880aa03198..073b6bbb6f 100644
--- a/Command/Get.hs
+++ b/Command/Get.hs
@@ -25,6 +25,7 @@ data GetOptions = GetOptions
 	{ getFiles :: CmdParams
 	, getFrom :: Maybe (DeferredParse Remote)
 	, autoMode :: Bool
+	, wantedMode :: Bool
 	, keyOptions :: Maybe KeyOptions
 	, batchOption :: BatchMode
 	}
@@ -34,6 +35,7 @@ optParser desc = GetOptions
 	<$> cmdParams desc
 	<*> optional (mkParseRemoteOption <$> parseFromOption)
 	<*> parseAutoOption
+	<*> parseWantedOption
 	<*> optional (parseIncompleteOption <|> parseKeyOptions <|> parseFailedTransfersOption)
 	<*> parseBatchOption True
 
@@ -46,7 +48,7 @@ seek o = startConcurrency transferStages $ do
 		, usesLocationLog = True
 		}
 	case batchOption o of
-		NoBatch -> withKeyOptions (keyOptions o) (autoMode o) seeker
+		NoBatch -> withKeyOptions (keyOptions o) (autoMode o || wantedMode o) seeker
 			(commandAction . startKeys from)
 			(withFilesInGitAnnex ww seeker)
 			=<< workTreeItems ww (getFiles o)
@@ -63,9 +65,10 @@ start o from si file key = do
 	afile = AssociatedFile (Just file)
 	ai = mkActionItem (key, afile)
 	expensivecheck lu
-		| autoMode o = numCopiesCheck file key (<)
-			<||> wantGet lu False (Just key) afile
+		| autoMode o = numCopiesCheck file key (<) <||> wantget lu
+		| wantedMode o = wantget lu
 		| otherwise = return True
+	wantget lu = wantGet lu False (Just key) afile
 
 startKeys :: Maybe Remote -> (SeekInput, Key, ActionItem) -> CommandStart
 startKeys from (si, key, ai) = checkFailedTransferDirection ai Download $
diff --git a/doc/git-annex-copy.mdwn b/doc/git-annex-copy.mdwn
index add7a96f06..88a94fabdf 100644
--- a/doc/git-annex-copy.mdwn
+++ b/doc/git-annex-copy.mdwn
@@ -56,11 +56,17 @@ Paths of files or directories to operate on can be specified.
   Note that when using --from with --to, twice this many jobs will
   run at once, evenly split between the two remotes.
 
+* `--wanted` `-w`
+
+  Rather than copying all specified files, only copy those that
+  are preferred content of the destination repository.
+  See [[git-annex-preferred-content]](1)
+
 * `--auto`
 
   Rather than copying all specified files, only copy those that don't yet have
   the desired number of copies, or that are preferred content of the
-  destination repository. See [[git-annex-preferred-content]](1)
+  destination repository.
 
 * `--fast`
 
diff --git a/doc/git-annex-get.mdwn b/doc/git-annex-get.mdwn
index 208188171f..5c49c39669 100644
--- a/doc/git-annex-get.mdwn
+++ b/doc/git-annex-get.mdwn
@@ -28,11 +28,17 @@ be specified.
 
 # OPTIONS
 
+* `--wanted`
+
+  Rather than getting all the specified files, get only those that
+  are preferred content of the repository.
+  See [[git-annex-preferred-content]](1)
+
 * `--auto`
 
   Rather than getting all the specified files, get only those that don't yet
   have the desired number of copies, or that are preferred content of the
-  repository. See [[git-annex-preferred-content]](1)
+  repository.
 
 * `--from=remote`
 
diff --git a/doc/git-annex-preferred-content.mdwn b/doc/git-annex-preferred-content.mdwn
index 6b9fc521ac..3de39961b8 100644
--- a/doc/git-annex-preferred-content.mdwn
+++ b/doc/git-annex-preferred-content.mdwn
@@ -7,8 +7,8 @@ git-annex-preferred-content - which files are wanted in a repository
 Each repository has a preferred content setting, which specifies content
 that the repository wants to have present. These settings can be configured
 using `git annex vicfg` or `git annex wanted`.
-They are used by the `--auto` option, by `git annex sync --content`,
-by clusters, and by the git-annex assistant.
+They are used by the `--wanted` and `--auto` options, 

(Diff truncated)
comment
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
new file mode 100644
index 0000000000..9e908771c3
--- /dev/null
+++ b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_11_ee87ba949945446dc324981bdf92329f._comment
@@ -0,0 +1,15 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 11"""
+ date="2026-05-29T15:09:31Z"
+ content="""
+Hmm, `git-annex get --auto` and `git-annex copy --auto` don't just check
+preferred content, they will also make a copy if numcopies is not satisfied
+for the file.
+
+That's something that `git-annex pull` and `git-annex push` don't do.
+
+I think it would be a good idea to add a `--wanted` option to commands
+that support `--auto`. It would only operate on preferred content, avoiding
+making copies only to work toward satisfying numcopies.
+"""]]

todo
diff --git a/doc/todo/git-annex_put.mdwn b/doc/todo/git-annex_put.mdwn
new file mode 100644
index 0000000000..ca5a17b55f
--- /dev/null
+++ b/doc/todo/git-annex_put.mdwn
@@ -0,0 +1,36 @@
+In [[todo/add_--json-progress_support_in_push_and_pull]]
+I noticed that it might be good to have a `git-annex put` command
+that is symmetric with `git-annex get`. That is, it picks which 
+remotes to send content to.
+
+One use case is emulating `git-annex push` but with json output.
+When operating on a single remote, that can be done using `copy --to`:
+
+	git-annex copy --auto --json --to $someremote
+	git-annex drop --auto --json --from $someremote
+	git-annex push --no-content $someremote
+
+But to operate on all remotes needs `git-annex put`:
+
+    git-annex put --auto --json
+    git-annex drop --auto --json
+    git-annex push --no-content
+
+So `git-annex put` would default to putting to all remotes,
+but with `--auto` would honor preferred content and numcopies.
+It's essentially `git-annex copy --to` run over each remote in turn.
+
+Another way to look at it is this tower has a missing leg:
+
+	           assist
+                  |
+	          --sync--
+              |      |
+           push      pull
+              |      |
+           copy --to copy --from
+              |      |
+                     get
+
+
+--[[Joey]]

current status update
diff --git a/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment
new file mode 100644
index 0000000000..2bdeacf1cb
--- /dev/null
+++ b/doc/todo/add_--json-progress_support_in_push_and_pull/comment_10_71fa338205298e0c929d16e470684e02._comment
@@ -0,0 +1,46 @@
+[[!comment format=mdwn
+ username="joey"
+ subject="""comment 10"""
+ date="2026-05-29T13:59:25Z"
+ content="""
+Summarizing the current status of this todo, if someone wants 
+the equivilant of `git-annex pull --json $someremote`, they can run:
+
+	git-annex pull --no-content $someremote
+	git-annex get --auto --json --from $someremote
+	git-annex drop --auto --json
+
+The first command above does not have json output, but outputs the
+usual `git pull` messages for the user to deal with as they see fit.
+
+And, if someone wants the equivilant of `git-annex push --json
+$someremote`, they can run:
+
+	git-annex copy --auto --json --to $someremote
+	git-annex drop --auto --json --from $someremote
+	git-annex push --no-content $someremote
+
+The last command above does not have json output, but outputs the
+usual `git push` messages for the user to deal with as they see fit.
+
+The argument for adding --json to pull/push now seems to be reduced.
+But not gone entirely I suppose. 
+
+For example, `git-annex push` without
+a remote pushes content to all remotes that want it. That needs
+multiple runs of `git-annex copy`, one per remote. Notice that
+in the `git-annex pull` case, it can be made to operate on all remotes:
+
+	git-annex pull --no-content
+	git-annex get --auto --json
+	git-annex drop --auto --json
+
+This seems like an argument for adding a `git-annex put`
+command that copies to all remotes that want content. Which is nicely
+symmetric with `git-annex get`.
+
+Another difference is that a single `git-annex push` or `pull`
+(or `sync`) does less work than several git-annex commands.
+In the scripts above, git-annex has to traverse the tree twice.
+That is a pretty small difference in overhead.
+"""]]