Description
stg version
Stacked Git 2.5.0
Copyright (C) 2005-2024 StGit authors
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
SPDX-License-Identifier: GPL-2.0-only
git version 2.47.1
When creating a new branch from a remote reference stg branch --create
sets up the remote differently from git:
Creating the branch with git
> git checkout -b git-branch origin/master
branch 'git-branch' set up to track 'origin/master'.
Switched to a new branch 'git-branch'
> git config get --show-names --all --regexp 'branch\\.git-branch'
branch.git-branch.remote origin
branch.git-branch.merge refs/heads/master
Creating branch with stg
> stg branch -c stg-branch origin/master
info: Recording `origin/master` as parent branch
info: Using remote `origin/master` to pull parent from
> git config get --show-names --all --regexp 'branch\\.stg-branch'
branch.stg-branch.stgit.parentbranch origin/master
branch.stg-branch.remote origin
branch.stg-branch.merge master
Note that the remote tracking ref is unqualified: master
vs refs/heads/master
. It is also unresolved because technically the fetch refspec of the remote could rename the branch and the remote to something else.
Actual bug
Unfortunately, this means that stg rebase
and git rebase
both fail when called without an argument on such a branch:
> git checkout stg-branch
Switched to branch 'stg-branch'
> git reset --hard HEAD~1
HEAD is now at 2bc36869 chore: update windows-sys to 0.59.0
> git rebase
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details.
git rebase '<branch>'
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> stg-branch
> stg rebase
error: Could not get the remote reference to translate into the local tracking branch: The configured name of the remote ref to merge wasn't valid: Standalone references must be all uppercased, like 'HEAD'
Git behaves correctly
They do work correctly on the branches set up with git:
> git checkout git-branch
Switched to branch 'git-branch'
Your branch is up to date with 'origin/master'.
> stg init
> git reset --hard HEAD~1
HEAD is now at a0f39085 chore: update transitive deps
> git rebase
Successfully rebased and updated refs/heads/git-branch.
> git reset --hard HEAD~1
HEAD is now at a0f39085 chore: update transitive deps
> stg rebase
info: Rebasing to `10db7b5131d11b6bd27fb516fa13ecd2967b50cf` (git-branch2 pristine rebase-fixes-2 stg-branch2 test test2 origin/HEAD origin/master yet-another-origin/main)
HEAD is now at 10db7b51 chore: update changelog for 2.5.0
Weird remote branch example
> git remote add another-origin https://github.com/stacked-git/stgit.git
> git config remote.another-origin.fetch '+refs/heads/master:refs/remotes/yet-another-origin/main'
> git fetch another-origin
From https://github.com/stacked-git/stgit
* [new branch] master -> yet-another-origin/main
Even though remote branches like refs/heads/<branch name>
are conventionally fetched to refs/remotes/<remote name>/<branch name>
this isn't a requirement. What git
actually does internally is resoling yet-another-origin/main
to refs/remotes/yet-another-origin/main
and then walking through the remote.<name>.fetch
refspecs of all remotes until it finds exactly one match by reverse applying the refspec which results in refs/heads/master
of the another-origin
remote:
> git checkout -b git-branch2 yet-another-origin/main
branch 'git-branch2' set up to track 'another-origin/master'.
Switched to a new branch 'git-branch2'
> git config get --show-names --all --regexp 'branch\\.git-branch2'
branch.git-branch2.remote another-origin
branch.git-branch2.merge refs/heads/master
stg
doesn't do this and simply splits the shortened input ref by /
and treats the first part as the remote name and the second part as the branch name:
> stg branch -c stg-branch2 yet-another-origin/main
info: Recording `yet-another-origin/main` as parent branch
info: Using remote `yet-another-origin/main` to pull parent from
> git config get --show-names --all --regexp 'branch\\.stg-branch2'
branch.stg-branch2.stgit.parentbranch yet-another-origin/main
branch.stg-branch2.remote yet-another-origin
branch.stg-branch2.merge main
This means git rebase
and stg rebase
still work on the branch set up by git
:
> git checkout git-branch2
Switched to branch 'git-branch2'
Your branch is up to date with 'yet-another-origin/main'.
> git reset --hard HEAD~1
HEAD is now at a0f39085 chore: update transitive deps
> git rebase
Successfully rebased and updated refs/heads/git-branch2.
> git reset --hard HEAD~1
HEAD is now at a0f39085 chore: update transitive deps
> stg init
> stg rebase
info: Rebasing to `10db7b5131d11b6bd27fb516fa13ecd2967b50cf` (git-branch pristine rebase-fixes-2 stg-branch2 test test2 origin/HEAD origin/master yet-another-origin/main)
HEAD is now at 10db7b51 chore: update changelog for 2.5.0
They break on stg-branch2
with an error similar to the one already shown above:
> stg rebase
error: Could not get the remote reference to translate into the local tracking branch: The configured name of the remote ref to merge wasn't valid: Standalone references must be all uppercased, like 'HEAD'
> git rebase
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details.
git rebase '<branch>'
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=yet-another-origin/<branch> stg-branch2
git pull
/ stg pull
git pull
and stg pull
don't have a problem with the unqualified remote branch at least when the remote name matches. They interpret branch.<name>.merge
as a refspec and then merge or rebase from FETCH_HEAD
. git rebase
/ stg rebase
on the other hand, need to forward translate the branch.<name>.merge
name back to the remote tracking branch name, i.e. by convention something like refs/remotes/<remote name>/<branch name>
so they can find the ref to rebase to.
Running git pull
on the stg-branch
created like above works but it fails on the stg-branch2
because the remote name is incorrect:
fatal: 'yet-another-origin' does not appear to be a git repository
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Conclusion
I tried fixing this myself by looking at what git does. The logic is actually quite complicated and there doesn't appear to be an simple API in Gitoxide. Correctly implementing it would mean duplicating the git
logic in stg
. (IIUC it would involve building a gix_refspec::MatchGroup
for each remote and then calling match_remotes
on each of them, handling the case where there is no match or multiple matches as errors.) I think this is better implemented upstream in Gitoxide itself instead. The easy way out is likely just creating the branch without tracking and then calling git branch --set-upstream
on it. I've run out of time working on this for now but wanted to record my findings in case someone else might pick it up. Otherwise, I'll try to have a go at it at some point.
Finally, I think the setting branch.<name>.stgit.parentbranch
should also be resolved to a fully qualified ref (and maybe only set if the source branch actually has an StackedGit stack). I haven't run into a case where this is an issue yet, though.