Recent comments posted to this site:
It's fine to force-push your git-annex branch to the server in this situation.
Your log shows that you did not have refs/remotes/hub-con/git-annex at the point where git-annex sync would have merged it:
may be because it was not yet created by forgejo+aneksjo or I have not fetched yet... oh, well
If your local git-annex branch contains a git-annex forget transition, git-annex necessarily cannot merge unrelated remote git-annex branches into it, because they may contain exactly the historical information that was requested to be forgotten. Instead, the new information is extracted from the remote git-annex branch and put into the local git-annex branch.
oh, what how that magical addition has happened! I still have transition information
❯ git ls-tree git-annex | grep transitions
100644 blob 891a754697d1602294fe8619878aeb9bb22b1586 transitions.log
❯ git show git-annex:transitions.log
ForgetGitHistory 1789701492s
ForgetDeadRemotes 1789701492s
but when should it get gone -- any step I could do to fascilitate besides just going and killing that file? Or it to remain there forever to be sure not pick up from "old history"? (here new forgejo though was newer AFAIK)
Maybe I was mistaken about whether you have refs/remotes/hub-con/git-annex.
I don't think it matters; as far as I can see there's nothing here that
would be unexpected when there's a git-annex forget transition involved.
But what I wonder now is if the server ever replaced its git-annex branch with the synced/git-annex branch that got pushed to it. (Update: Based on comment #5 it has not.)
That needs git-annex to run on the server, and it may be that nothing
causes forgejo+aneksajo to run git-annex in this situation, and so it
leaves its git-annex branch at the initial git-annex init value.
The same could happen with a git remote that doesn't have git-annex installed.
And that would leave synced/git-annex as the only place where new git-annex branch information is being communicate. The problem with that being that multiple clients can force push that, which can result in information sent by one getting overwritten by another. While the clients would eventually converge on a single merged git-annex branch via only synced/git-annex, it might take some time.
I think that git-annex sync could deal with this better, by
checking if the remote git-annex branch's sha is in gitAnnexIgnoredRefs.
And if so, forcing a push to the remote git-annex branch,
using --force-with-lease to avoid overwriting any changes that might get
made to it at the same time.
continuing transition ["forget git history","forget dead remotes"]
If your local git-annex branch contains a git-annex forget transition,
git-annex necessarily cannot merge unrelated remote git-annex branches into it,
because they may contain exactly the historical information that was requested
to be forgotten. Instead, the new information is extracted from the remote
git-annex branch and put into the local git-annex branch.
Once the local git-annex branch is pushed to the remote as synced/git-annex, and git-annex is run on the server, it will replace the unrelated git-annex branch.
Your log shows that you did not have refs/remotes/hub-con/git-annex at the point where
git-annex sync would have merged it:
[2026-10-07 13:09:57.61588097] (Utility.Process) process [2221134] call: git ["--git-dir=.git","--work-tree=.","--literal-pathspecs","-c","annex.debug=true","show-ref","--verify","-q","refs/remotes/hub-con/git-annex"]
[2026-10-07 13:09:57.615954038] (Utility.Process) process [2221131] done ExitFailure 1
you do not have a remotes/hub-con/git-annex You do have a local branch named hub-con/git-annex
hm, did I cut paste something incorrectly somewhere? I do not think it is the case:
❯ git br -a
git-annex
* master
remotes/con@falkor/HEAD -> con@falkor/master
remotes/con@falkor/git-annex
remotes/con@falkor/master
remotes/con@falkor/synced/git-annex
remotes/con@falkor/synced/master
remotes/falkor/HEAD -> falkor/master
remotes/falkor/git-annex
remotes/falkor/master
remotes/falkor/synced/git-annex
remotes/falkor/synced/master
remotes/hub-con/HEAD -> hub-con/master
remotes/hub-con/git-annex
remotes/hub-con/master
remotes/hub-con/synced/git-annex
remotes/hub-con/synced/master
remotes/typhon/HEAD -> typhon/master
remotes/typhon/git-annex
remotes/typhon/master
remotes/typhon/synced/git-annex
remotes/typhon/synced/master
❯ ls -l .git/refs/heads .git/refs/remotes/hub-con
.git/refs/heads:
total 8
-rw-rw-r-- 1 yoh yoh 41 Oct 7 12:49 git-annex
-rw-rw-r-- 1 yoh yoh 41 Oct 7 12:49 master
.git/refs/remotes/hub-con:
total 12
-rw-rw-r-- 1 yoh yoh 33 Oct 7 12:49 HEAD
-rw-rw-r-- 1 yoh yoh 41 Oct 7 12:48 git-annex
-rw-rw-r-- 1 yoh yoh 41 Oct 7 12:50 master
drwxrwsr-x 1 yoh yoh 30 Oct 7 15:11 synced/
since it has already sent the same information to the remote in a successful force-push of the synced/git-annex branch. So that is all a red herring.
indeed! it seems that somehow, in that most recent merge it did add that UUID, and that was done in a commit TODAY catching up on a "transition" (done as AFAIK finished days ago! and IIRC on a different host but I could be mistaken and may be it was done on this) when this repo was produced by splitting off the tree of a larger "original repo".
❯ git show hub-con/git-annex:uuid.log | grep e9147e49-a1d5-49b1-bd50-74010d401463
e9147e49-a1d5-49b1-bd50-74010d401463 git@b16fdd44ad3a:/var/lib/gitea/git/repositories/con/liab-falkor-hub.git timestamp=1791391708s
❯ git show git-annex^1:uuid.log | grep e9147e49-a1d5-49b1-bd50-74010d401463
e9147e49-a1d5-49b1-bd50-74010d401463 git@b16fdd44ad3a:/var/lib/gitea/git/repositories/con/liab-falkor-hub.git timestamp=1791391708s
❯ git show git-annex^1
commit 09524451b82e929afe3eaacedc906033311382c0
Author: Yaroslav Halchenko <debian@onerussian.com>
Date: Wed Oct 7 12:48:56 2026 -0400
continuing transition ["forget git history","forget dead remotes"]
diff --git a/uuid.log b/uuid.log
index f8582b9..5c4adc9 100644
--- a/uuid.log
+++ b/uuid.log
@@ -5,4 +5,5 @@
60ab5caa-8d02-4a25-ae23-80e6cfcf8955 split-falkor-hub timestamp=1789701480s
885697c4-a371-484f-8332-ebdce2f5cce6 yoh@falkor:~/hardware/washoe/2026-revamp timestamp=1773954781s
adc21e14-c327-4051-bc7c-a95b706ea64d yoh@bilena:~/hardware/washoe/2026-revamp timestamp=1773928564s
+e9147e49-a1d5-49b1-bd50-74010d401463 git@b16fdd44ad3a:/var/lib/gitea/git/repositories/con/liab-falkor-hub.git timestamp=1791391708s
f0af65a3-fef7-4127-9fb6-910e266ebc09 yoh@typhon:~/hardware/washoe/2026-revamp/deployments/falkor-hub timestamp=1788183516s
so indeed info is there! but how that continuing transition came up again?
since info is there -- may be I could just force push git-annex branch (if would be allowed) or manually merge directly and push, correct?
What's weird about your situation and I don't understand is you do not have a remotes/hub-con/git-annex, which would be the usual name of the remote tracking branch that git-annex would normally automatically merge.
You do have a local branch named hub-con/git-annex. I don't understand where that came from. Maybe that ought to be merged and isn't being merged, but you're clearly doing something unusual to have that rather than the regular remote tracking branch.
Anyway, to dispell some of the other confusion in this bug report:
git-annex push does not treat a failure to push the git-annex branch as
an error since it has already sent the same information to the remote
in a successful force-push of the synced/git-annex branch. So that is all a
red herring.
And when you ran "git annex merge hub-con", that is the wrong command to merge the git-annex branch from the remote. That command merges a branch named "hub-con" into the current branch.
I think the solution will involve depedency injection, so ifWanted
can access wantGet from a structure passed into that module from the
caller. Maybe by converting it to a wrapper monad around Annex.
The patch below implements that. Unfortunately, it fails to build due to several import cycles. I spent a while trying to resolve those and could not, so I think it would need HS boot files to get this to build.
diff --git a/Annex/AdjustedBranch.hs b/Annex/AdjustedBranch.hs
index a9ff9849c0..ec885e13c1 100644
--- a/Annex/AdjustedBranch.hs
+++ b/Annex/AdjustedBranch.hs
@@ -1,6 +1,6 @@
{- adjusted branch
-
- - Copyright 2016-2024 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2026 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@@ -60,6 +60,7 @@ import Annex.CatFile
import Annex.Link
import Annex.Content.Presence
import Annex.CurrentBranch
+import Annex.Wanted
import Types.CleanupActions
import qualified Database.Keys
import Config
@@ -87,10 +88,16 @@ instance AdjustTreeItem Adjustment where
Nothing -> return Nothing
Just t' -> adjustTreeItem l t'
adjustTreeItem (LockUnlockPresentAdjustment l) t = adjustTreeItem l t
+ adjustTreeItem (WantedAdjustment p Nothing) t = adjustTreeItem p t
+ adjustTreeItem (WantedAdjustment p (Just l)) t =
+ adjustTreeItem p t >>= \case
+ Nothing -> return Nothing
+ Just t' -> adjustTreeItem l t'
adjustmentIsStable (LinkAdjustment l) = adjustmentIsStable l
adjustmentIsStable (PresenceAdjustment p _) = adjustmentIsStable p
adjustmentIsStable (LockUnlockPresentAdjustment l) = adjustmentIsStable l
+ adjustmentIsStable (WantedAdjustment p _) = adjustmentIsStable p
instance AdjustTreeItem LinkAdjustment where
adjustTreeItem UnlockAdjustment =
@@ -127,6 +134,15 @@ instance AdjustTreeItem LockUnlockPresentAdjustment where
adjustmentIsStable UnlockPresentAdjustment = False
adjustmentIsStable LockPresentAdjustment = True
+instance AdjustTreeItem WantedAdjustment where
+ adjustTreeItem HideUnwantedAdjustment =
+ ifWanted noAdjust hideAdjust
+ adjustTreeItem ShowUnwantedAdjustment =
+ noAdjust
+
+ adjustmentIsStable HideUnwantedAdjustment = False
+ adjustmentIsStable ShowUnwantedAdjustment = True
+
ifSymlink
:: (TreeItem -> Annex a)
-> (TreeItem -> Annex a)
@@ -143,7 +159,21 @@ ifPresent
-> Annex (Maybe TreeItem)
ifPresent ispresent notpresent ti@(TreeItem _ _ s) =
catKey s >>= \case
- Just k -> ifM (inAnnex k) (ispresent ti, notpresent ti)
+ Just k -> ifM (inAnnex k)
+ (ispresent ti, notpresent ti)
+ Nothing -> return (Just ti)
+
+ifWanted
+ :: (TreeItem -> Annex (Maybe TreeItem))
+ -> (TreeItem -> Annex (Maybe TreeItem))
+ -> TreeItem
+ -> Annex (Maybe TreeItem)
+ifWanted iswanted notwanted ti@(TreeItem topf _ s) =
+ catKey s >>= \case
+ Just k -> do
+ f <- inRepo $ fromTopFilePath topf
+ ifM (wantGet NoLiveUpdate True (Just k) f)
+ (iswanted ti, notwanted ti)
Nothing -> return (Just ti)
noAdjust :: TreeItem -> Annex (Maybe TreeItem)
diff --git a/Annex/AdjustedBranch/Name.hs b/Annex/AdjustedBranch/Name.hs
index 5a85b6ef9a..41ed48936a 100644
--- a/Annex/AdjustedBranch/Name.hs
+++ b/Annex/AdjustedBranch/Name.hs
@@ -1,6 +1,6 @@
{- adjusted branch names
-
- - Copyright 2016-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2026 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@@ -39,6 +39,10 @@ instance SerializeAdjustment Adjustment where
serializeAdjustment p <> "-" <> serializeAdjustment l
serializeAdjustment (LockUnlockPresentAdjustment l) =
serializeAdjustment l
+ serializeAdjustment (WantedAdjustment p Nothing) =
+ serializeAdjustment p
+ serializeAdjustment (WantedAdjustment p (Just l)) =
+ serializeAdjustment p <> "-" <> serializeAdjustment l
deserializeAdjustment s =
(LinkAdjustment <$> deserializeAdjustment s)
<|>
@@ -47,6 +51,10 @@ instance SerializeAdjustment Adjustment where
(PresenceAdjustment <$> deserializeAdjustment s <*> pure Nothing)
<|>
(LockUnlockPresentAdjustment <$> deserializeAdjustment s)
+ <|>
+ (WantedAdjustment <$> deserializeAdjustment s1 <*> pure (deserializeAdjustment s2))
+ <|>
+ (WantedAdjustment <$> deserializeAdjustment s <*> pure Nothing)
where
(s1, s2) = separate' (== (fromIntegral (ord '-'))) s
@@ -75,6 +83,13 @@ instance SerializeAdjustment LockUnlockPresentAdjustment where
deserializeAdjustment "lockpresent" = Just LockPresentAdjustment
deserializeAdjustment _ = Nothing
+instance SerializeAdjustment WantedAdjustment where
+ serializeAdjustment HideUnwantedAdjustment = "hideunwanted"
+ serializeAdjustment ShowUnwantedAdjustment = "showunwanted"
+ deserializeAdjustment "hideunwanted" = Just HideUnwantedAdjustment
+ deserializeAdjustment "showunwanted" = Just ShowUnwantedAdjustment
+ deserializeAdjustment _ = Nothing
+
newtype AdjBranch = AdjBranch { adjBranch :: Branch }
originalToAdjusted :: OrigBranch -> Adjustment -> AdjBranch
diff --git a/CHANGELOG b/CHANGELOG
index ea4832f783..207c68d8fd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+git-annex (10.20261007) UNRELEASED; urgency=medium
+
+ * adjust --wanted: New option to make an adjusted branch containing only
+ wanted files.
+
+ -- Joey Hess <id@joeyh.name> Tue, 06 Oct 2026 11:03:57 -0400
+
git-annex (10.20261006) upstream; urgency=medium
* Fix stack build.
diff --git a/Types/AdjustedBranch.hs b/Types/AdjustedBranch.hs
index 3c73d5e06c..422fe7da5c 100644
--- a/Types/AdjustedBranch.hs
+++ b/Types/AdjustedBranch.hs
@@ -1,6 +1,6 @@
{- adjusted branch types
-
- - Copyright 2016-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2026 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@@ -11,6 +11,7 @@ data Adjustment
= LinkAdjustment LinkAdjustment
| PresenceAdjustment PresenceAdjustment (Maybe LinkAdjustment)
| LockUnlockPresentAdjustment LockUnlockPresentAdjustment
+ | WantedAdjustment WantedAdjustment (Maybe LinkAdjustment)
deriving (Show, Eq)
data LinkAdjustment
@@ -30,6 +31,11 @@ data LockUnlockPresentAdjustment
| LockPresentAdjustment
deriving (Show, Eq)
+data WantedAdjustment
+ = HideUnwantedAdjustment
+ | ShowUnwantedAdjustment
+ deriving (Show, Eq)
+
-- Adjustments have to be able to be reversed, so that commits made to the
-- adjusted branch can be reversed to the commit that would have been made
-- without the adjustment and applied to the original branch.
@@ -43,6 +49,8 @@ instance ReversableAdjustment Adjustment where
PresenceAdjustment (reverseAdjustment p) (fmap reverseAdjustment ml)
reverseAdjustment (LockUnlockPresentAdjustment l) =
LockUnlockPresentAdjustment (reverseAdjustment l)
+ reverseAdjustment (WantedAdjustment l ml) =
+ WantedAdjustment (reverseAdjustment l) (fmap reverseAdjustment ml)
instance ReversableAdjustment LinkAdjustment where
reverseAdjustment UnlockAdjustment = LockAdjustment
@@ -59,6 +67,11 @@ instance ReversableAdjustment LockUnlockPresentAdjustment where
reverseAdjustment UnlockPresentAdjustment = LockPresentAdjustment
reverseAdjustment LockPresentAdjustment = UnlockPresentAdjustment
+instance ReversableAdjustment WantedAdjustment where
+ reverseAdjustment HideUnwantedAdjustment = ShowUnwantedAdjustment
+ reverseAdjustment ShowUnwantedAdjustment = HideUnwantedAdjustment
+
adjustmentHidesFiles :: Adjustment -> Bool
adjustmentHidesFiles (PresenceAdjustment HideMissingAdjustment _) = True
+adjustmentHidesFiles (WantedAdjustment HideUnwantedAdjustment _) = True
adjustmentHidesFiles _ = False
diff --git a/doc/git-annex-adjust.mdwn b/doc/git-annex-adjust.mdwn
index eccdf843cf..693a4285a2 100644
--- a/doc/git-annex-adjust.mdwn
+++ b/doc/git-annex-adjust.mdwn
@@ -4,7 +4,7 @@ git-annex adjust - enter an adjusted branch
# SYNOPSIS
-git annex adjust `--unlock|--lock|--fix|--hide-missing [--unlock|--lock|--fix]|--unlock-present`
+git annex adjust `--unlock|--lock|--fix|--hide-missing|--unlock-present|--wanted`
# DESCRIPTION
@@ -37,7 +37,7 @@ To avoid those problems, use `git annex merge otherbranch`.
Re-running this command with the same options
while inside the adjusted branch will update the adjusted branch
-as necessary (eg for `--hide-missing` and `--unlock-present`),
+as necessary (eg for `--hide-missing`, `--unlock-present`, and `--wanted`),
and will also propagate commits back to the original branch.
# OPTIONS
@@ -119,6 +119,21 @@ 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.
+* `--wanted`
+
+ Only include annexed files in the adjusted branch when they are preferred
+ content of the repository, as configured by `git-annex wanted`.
+
+ Note that when the preferred content expression uses `present`, files are
+ only wanted when their content is present. The adjusted branch is not
+ immediately changed when content availability changes. To update the
+ adjusted branch to reflect changes to content availability, run
+ `git-annex adjust --wanted` again. Or, to automate updates,
+ set the `annex.adjustedbranchrefresh` config.
+
+ The `--wanted` option can be combined with
+ `--unlock`, `--lock`, or `--fix`.
+
* `--force`
Allow overwriting an existing adjusted branch with a new version.
@@ -145,6 +160,8 @@ and will also propagate commits back to the original branch.
git-annex-view(1)
+git-annex-wanted(1)
+
# AUTHOR
Joey Hess <id@joeyh.name>
diff --git a/doc/todo/Filter_a_tree_with_pattern.mdwn b/doc/todo/Filter_a_tree_with_pattern.mdwn
index c32996446a..de1c478d4c 100644
--- a/doc/todo/Filter_a_tree_with_pattern.mdwn
+++ b/doc/todo/Filter_a_tree_with_pattern.mdwn
@@ -53,3 +53,5 @@ $ git annex filterview root=Movies/
$ ls
movie.mkv movie.iso movie.mp4 movie.en_US.ass movie.de_DE.ass
```
+
+> `git-annex adjust --wanted` done --Joey
git-annex export filters the exported tree to the preferred content of
the remote.
That is not quite what's being asked for here, which is a new feature to generate a filtered tree. But clearly the code to create such a tree is all present in git-annex already.
In fact, an export to a special remote does write such a tree to a git ref. So a dummy special remote that didn't write any files would be one way to implement this. (Not a good way.)
I think the question is, what command would it make sense to put this
feature in? And this seems like a good candiate: git-annex adjust --wanted
That would adjust the tree to only contain files that are preferred content
of the local repository.
That seems broadly useful. Although similarly to --hide-missing, with
some preferred content expressions (ones using present), the adjusted
branch sometimes needs to be updated when content availability changes,
eg after a git-annex drop.