Reputation: 707
Is there a way to have a new branch automatically track the same remote as the existing branch it was created from?
Say I have a local branch foo
that tracks a remote origin/bar
. I can explicitly have a new branch baz
based on foo
also track origin/bar
by doing:
git checkout foo
git checkout -b baz origin/bar
But is there a generic way to create the branch baz
from foo
and have it track the same remote as foo
regardless of what that remote happens to be?
Upvotes: 2
Views: 1736
Reputation: 1329032
This is now (2022, 3 years later) supported.
With Git 2.35 (Q1 2022), git -c branch.autosetupmerge=inherit branch new old
makes new
to have the same upstream as the old
branch, instead of marking "old
" itself as its upstream.
Same for:
git switch --track=inherit -c new old
See commit 44f14a9, commit d311566, commit a3f40ec (20 Dec 2021) by Josh Steadmon (steadmon
).
(Merged by Junio C Hamano -- gitster
-- in commit 0669bdf, 10 Jan 2022)
branch
: add flags and config to inherit trackingSigned-off-by: Josh Steadmon
It can be helpful when creating a new branch to use the existing tracking configuration from the branch point.
However, there is currently not a method to automatically do so.Teach
git-
{branch
,checkout
,switch
} an "inherit
" argument to the "--track
" option.
When this is set, creating a new branch will cause the tracking configuration to default to the configuration of the branch point, if set.For example, if branch "
main
" tracks "origin/main
", and we rungit checkout --track=inherit -b feature main
(man), then branch "feature
" will track "origin/main
".Thus:
git status
(man) will show us how far ahead/behind we are fromorigin
, andgit pull
(man) will pull fromorigin
.This is particularly useful when creating branches across many submodules, such as with
git submodule foreach ...
(man) (or if running with a patch such as this one, which we use at$job
), as it avoids having to manually set tracking info for each submodule.Since we've added an argument to "
--track
", also add "--track=direct
" as another way to explicitly get the original "--track
" behavior ("--track
" without an argument still works as well).Finally, teach
branch.autoSetupMerge
a new "inherit" option.
When this is set, "--track=inherit
" becomes the default behavior.
git config
now includes in its man page:
local branch or remote-tracking branch;
inherit
-- if the starting point has a tracking configuration, it is copied to the new
git branch
now includes in its man page:
'git branch' [--track [direct|inherit] | --no-track] [-f] <branchname> [<start-point>]
git branch
now includes in its man page:
The exact upstream branch is chosen depending on the optional argument:
--track
or--track direct
means to use the start-point branch itself as the upstream;--track inherit
means to copy the upstream configuration of the start-point branch.
--track direct
is the default when the start point is a remote-tracking branch.
git branch
now includes in its man page:
start-point
is either a local or remote-tracking branch.Set it to
inherit
if you want to copy the tracking configuration from the branch point.See
git pull
andgit config
for additional discussion on how thebranch.<name>.remote
andbranch.<name>.merge
options are used.
git checkout
now includes in its man page:
--track [direct|inherit]
git switch
now includes in its man page:
--track [direct|inherit]
Upvotes: 3
Reputation: 60555
To copy all of one branch's settings to another:
git config --local --get-regexp ^branch\\.$one \
| sed "s,^,git config ,;s,$one,$another," \
# | sh -x
To do it for just the remote
and merge
settings you could get more specific,
git config --local --get-regexp "^branch\\.$one\\.(remote|merge)"
I tend to do these things by constructing command lists as above, others might prefer
git config --local --get-regexp "^branch\\.$one\\.(remote|merge)" \
| while read config value; do
git config ${config/.$one./.$another.} "$value"
done
Upvotes: 0
Reputation: 489828
But is there a generic way to create the branch
baz
fromfoo
and have it track the same remote asfoo
regardless of what that remote happens to be?
Yes, but it's a little complicated. Git being what it is, there are about twelve dozen1 different ways to do this, but we'll just pick one here.
First, let's look at all the important parts of this:
There are your own local branch names, foo
and baz
in your example.
There are remote-tracking names such as origin/bar
(full name refs/remotes/origin/bar
).
There's the commit hash ID to which each branch name or remote-tracking name points.
And, there's an optional upstream setting for any (local) branch name. Typically the upstream of some local branch is a remote-tracking name (Git calls this a "remote-tracking branch") using the same base name but prefixed with a remote name. For instance, master
typically has origin/master
set as its upstream. However, as in your example, the upstream of a branch need not match: the upstream of foo
can be origin/bar
.
(It's also possible to set a local branch as the upstream of another local branch; this behaves the way you would expect. Internally this is implemented by setting the remote
half of the two-part upstream setting to .
, which means your own repository. But we get to ignore these details here.)
As fphillipe mentioned, we have git branch --set-upstream-to
, which can set the upstream of any branch, at any time. If there is already some upstream set, this replaces it. If there was no upstream set, now there is.
We also have two different ways to create a new branch name:
git branch name [start-point]
creates the new (local) branch named name
. The initial hash ID stored in this name is given by start-point
; if you omit start-point
, the initial hash ID is whatever git rev-parse HEAD
produces.
git checkout -b name [start-point]
creates the new (local) branch named name
and then does a git checkout
of that name, all in one. It's essentially equivalent to running git branch
followed by git checkout
.
1"Ew, gross" đŸ˜€
In this case, you want to create a local branch named baz
, set its hash to match that of foo
, and set its upstream to foo
's upstream, which is origin/bar
. That is, after creating baz
you would like:
git rev-parse baz
to produce the same output as:
git rev-parse foo
—that means the same commit is the tip commit of each branch—but you want:
git rev-parse --symbolic-full-name baz@{upstream}
to produce refs/remotes/origin/bar
.
Both of the creation commands can, but don't always, also set the upstream of the newly created branch. You can use the --track
argument to force them to set it, and you can use the --no-track
argument to force them not to set it. The --track
flag takes the remote-tracking name to use, so if you first figure out that the upstream of foo
is origin/bar
, you can write:
git checkout -b baz origin/bar
(or the same with git branch
if you don't want to also check out the new branch). But this has two bugs:
foo
. You want to automate this.baz
pointing to the same commit as origin/bar
. If foo
points to a different commit, that violates one of the desires.Hence the job must be done in several parts. First, you will create the branch (and maybe switch to it if you like), using the local name foo
to set the starting-point. Both git branch
and git checkout
will not set the upstream of the new branch in this case, at least by default, but you can be totally explicit by using --no-track
, or you can turn foo
into a raw hash ID. Usually all of this is unnecessary,2 but if you wanted to go for raw hash ID, here's a shell fragment to do it:
name=baz
start=foo
hash=$(git rev-parse ${start}^{commit}) || exit
If git rev-parse
fails to parse foo
to a raw hash ID, or foo
does not identify a commit, git rev-parse
will produce an error message to stderr and exit nonzero, and the || exit
will have your shell fragment quit at this point.
Then:
git branch $name $hash || exit
will try to create the branch name $name
, pointing to the desired hash.
We also need to find the upstream name, for which git rev-parse
is again the command to use:
upstream=$(git rev-parse --symbolic-full-name ${start}@{upstream}) || exit
As before, we have our shell fragment quit if git rev-parse
has failed, letting git rev-parse
print the appropriate error. Now that we have the upstream, we can set it using git branch --set-upstream-to
:
git branch --set-upstream-to $name $upstream
2At the command line, you just run a command and observe: if it did what you want, fine, if not, you correct as you go. From a script, however, it's often hard to see what did or might happen. It tends to be best to use something that has as few side-effects as possible. So we break down each Git command into direct plumbing commands when possible. I don't carry this to its extreme, in this example, as that gets too annoying, but I will show the git rev-parse
operations.
Putting the pieces together in the right order, plus a little bit of argument checking and use of -e
to avoid all the || exit
s, gets us a full (but completely untested) shell script:
#! /bin/sh -e
usage() {
echo "usage: $0 branch-name start-point"
}
case "$#" in
2) ;;
*) usage 1>&2; exit 1;;
esac
name="$1"
start="$2"
hash=$(git rev-parse ${start}^{commit})
upstream=$(git rev-parse --symbolic-full-name ${start}@{upstream})
git branch $name $hash
git branch --set-upstream-to $name $upstream
After testing this, write it as an executable shell script somewhere in your $PATH
, calling it (e.g.) git-branch-with-upstream
, and then:
git branch-with-upstream baz foo
will invoke the script, which will create baz
pointing to the same commit as foo
and having foo
's upstream as baz
's upstream.
You now know how to write new Git commands!
Upvotes: 3
Reputation: 10054
Have a look at --set-upstream-to
of git branch
:
git branch (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
From the docs:
-u <upstream>, --set-upstream-to=<upstream>
Set up <branchname>'s tracking information so <upstream> is
considered <branchname>'s upstream branch. If no <branchname> is
specified, then it defaults to the current branch.
Upvotes: 0