WebSafe 3.7github.com
|
|
🏠
Skip to content

revisions: add @{primary} shorthand for primary branch#2183

Open
HaraldNordgren wants to merge 1 commit intogit:masterfrom
HaraldNordgren:default_shorthand
Open

revisions: add @{primary} shorthand for primary branch#2183
HaraldNordgren wants to merge 1 commit intogit:masterfrom
HaraldNordgren:default_shorthand

Conversation

@HaraldNordgren
Copy link
Contributor

@HaraldNordgren HaraldNordgren commented Jan 29, 2026

cc: "Kristoffer Haugsbakk" kristofferhaugsbakk@fastmail.com
cc: Phillip Wood phillip.wood123@gmail.com
cc: "D. Ben Knoble" ben.knoble@gmail.com

@HaraldNordgren
Copy link
Contributor Author

/submit

@gitgitgadget-git
Copy link

Submitted as pull.2183.git.git.1769700352081.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2183/HaraldNordgren/default_shorthand-v1

To fetch this version to local tag pr-git-2183/HaraldNordgren/default_shorthand-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2183/HaraldNordgren/default_shorthand-v1
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> Git already has shorthands like @{upstream} and @{push} to refer to
> tracking branches, but there is no convenient way to refer to the
> default branch of a repository (typically "main" or "master").
>
> Users often want to switch to the default branch regardless of its
> name, especially when working across repositories with different
> default branch names. Currently they must either hardcode the branch
> name or query it via configuration, which is cumbersome.
>
> Add a new @{default} shorthand that resolves to the default branch
> as determined by init.defaultBranch (or falls back to "main" or
> "master" depending on Git version). This allows users to write:
>
>   git checkout @{default}
>
> instead of having to know or look up the default branch name.
>
> The implementation follows the same pattern as @{upstream} and @{push},
> using a new branch_get_default() function that queries the default
> branch name and verifies it exists in the repository.

But @{upstream} and @{push} are inherently very different from what
you are adding, aren't they?  Asking for topic1@{upstream} and
topic2@{upstream} makes quite a lot of sense, because the meaning of
@{upstream} depends on "which branch's upstream are you talking
about???".  But I suspect that asking for topic1@{default} and
expect it would be different from topic2@{default} is nonsense, as
"the default" is not per branch but is an attribute of a repository.
In other words, <branch>@{default} may by itself be a nonsense
query.  Are you rejecting a non-empty <branch> that may appear
before @{default} as an error?

After cloning an upstream project, those who dislike the local
branch name 'master' often rename it to something else, like 

    $ git branch -m master main

and be happy, without configuring "init.defaultbranch".  After all,
that configuration variable affects newly created repositories, so
after renaming 'master' to 'main', it is too late anyway.  In such a
repository, if you say @{default}, what should happen?  As 'master'
branch no longer exist, even though it is the @{default}, should it
error out?  Does your implementation error out?

Also I do not quite see how this would be useful in practice.  Given
that the names of local branches are under control of the local end
user and not upstream projects, I would imagine that the primary
branch used by a user is of per-user nature, not per repository.  In
other words, instead of having to do "git branch -m" after cloning,
you may do "git config --global init.defaultBranch" just once and
keep using the same default name.  Under that condition, "can I ask
what default branch name this repository uses, so that I can work on
that branch" is rarely needed, if you are writing a script to use in
many of your repositories, isn't it?

So, I am not sure.  I wouldn't mind too terribly if <name>@{default}
is rejected, but I do not imagine many people using it.
@OneShot74
Copy link

Mute/Pause indefinitely

@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

Thanks for your comments!

> But @{upstream} and @{push} are inherently very different from what
> you are adding, aren't they?  Asking for topic1@{upstream} and
> topic2@{upstream} makes quite a lot of sense, because the meaning of
> @{upstream} depends on "which branch's upstream are you talking
> about???".  But I suspect that asking for topic1@{default} and
> expect it would be different from topic2@{default} is nonsense, as
> "the default" is not per branch but is an attribute of a repository.
> In other words, <branch>@{default} may by itself be a nonsense
> query.  Are you rejecting a non-empty <branch> that may appear
> before @{default} as an error?

I will update the code to treat 'new-branch@{default}' as nonsense, it's
not a case I thought about, and would never use 😅

> After cloning an upstream project, those who dislike the local
> branch name 'master' often rename it to something else, like 
> 
>     $ git branch -m master main

I have never heard about anyone doing that. Isn't it more expected that
people keep whatever branch is on the remote? But regardless, I hope
there is a way to still make @{default} map to whatever your renamed your
default branch to.

> Given
> that the names of local branches are under control of the local end
> user and not upstream projects, I would imagine that the primary
> branch used by a user is of per-user nature, not per repository.  In
> other words, instead of having to do "git branch -m" after cloning,
> you may do "git config --global init.defaultBranch" just once and
> keep using the same default name.

My ratio on cloning other people's repo vs. create new repos is likely
999/1, so I'm given the default names that maintainer chose. I have
default branches called 'master', 'main' and 'develop'.

Yes it's possible to rename, but what this feature does is open up the
convenience of not having to bother with that.

I have this script that I run many times a day. However it doesn't work
when remote is not called 'origin', so I have another version for
'upstream', etc:

    git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')

That's uneccessary overhead, that could now be replaced with:

    git checkout @{default}


Harald
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

I realize now when looping over all repos on my machine that quite a few of
them error out with

   error: pathspec '@{default}' did not match any file(s) known to git

I would expect all of them to have a default branch set. Maybe this is a
showstopper 🤔


Harald
@HaraldNordgren
Copy link
Contributor Author

/submit

@gitgitgadget-git
Copy link

Submitted as pull.2183.v2.git.git.1769779599196.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2183/HaraldNordgren/default_shorthand-v2

To fetch this version to local tag pr-git-2183/HaraldNordgren/default_shorthand-v2:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2183/HaraldNordgren/default_shorthand-v2
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Harald Nordgren <haraldnordgren@gmail.com> writes:

>> After cloning an upstream project, those who dislike the local
>> branch name 'master' often rename it to something else, like 
>> 
>>     $ git branch -m master main
>
> I have never heard about anyone doing that. Isn't it more expected that
> people keep whatever branch is on the remote? But regardless, I hope
> there is a way to still make @{default} map to whatever your renamed your
> default branch to.

But then that is what "default" is, isn't it?  The "default" branch
is what "git init" would create unless it is told otherwise.  The
'main' branch that the above example user renamed to to use because
they did not like the name 'master' is their primary branch that is
not the "default".

In a sense, I think what you are after _is_ "what the user considers
the primary branch in this repository".  How init.defaultBranch is
configured in their global (i.e., per-user) configuration file may
be a good hint to help answering the question, but not necessarily.

For those who follow the naming the upstream decided to use in
cloned repositories, init.defaultBranch is probably the last thing
you want to take as a hint, as these people decided to _ignore_ the
preference of their own and instead to follow what upstream uses.
For that, refs/remotes/origin/HEAD would be a lot more stronger
hint.  If they call their primary branch 'trunk', these people would
want to call theirs 'trunk'.  And for that, these people would not
do anything with init.defaultBranch.

>     git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
>
> That's uneccessary overhead, that could now be replaced with:
>
>     git checkout @{default}

That mirrors what I wrote in the previous paragraph.  Those who
follow the naming the upstream uses, refs/remotes/$remote/HEAD
(here, "origin" may not be the default remote) would be a better
hint than init.defaultBranch so calling it @{default} is misleading.

I am not good at naming things, so instead of calling it @{primary}
let's call it @{dumbo}.  With the realization that what branch
refs/remotes/$remote/HEAD points at is a good source of hint, I
actually think $branch@{dumbo} does make sense, and @{dumbo} should
be a short-hand for $branch@{dumbo} where the name of the current
branch is substituted for $branch (i.e. similar to @{push}, I
suppose).  As you may be interacting with two sets of branches that
go to two different remotes.

In any case, it is very different from what you implemented as the
@{default} in your patch.

Thanks.
@gitgitgadget-git
Copy link

"Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email):

On Fri, Jan 30, 2026, at 14:26, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> Git already has shorthands like @{upstream} and @{push} to refer to
> tracking branches, but there is no convenient way to refer to the
> default branch of a repository (typically "main" or "master").

I don’t use a lot of different repositories. But for the two I do use I
use `origin`. (Really `o` since I name the regular remote `o`.) Most of
the time I do not need to have the main *branch* as a branch. I am not
working on the main branch. Using the remote-tracking branch directly is
more convenient.

> Users often want to switch to the default branch regardless of its
> name, especially when working across repositories with different
> default branch names. Currently they must either hardcode the branch
> name or query it via configuration, which is cumbersome.

*Query it* sounds like git-config(1). I have found `git var
GIT_DEFAULT_BRANCH` useful for when I want to answer a question
without hardcodig `main` or `master`.

>
> Add a new @{default} shorthand that resolves to the default branch
> as determined by init.defaultBranch (or falls back to "main" or
> "master" depending on Git version). This allows users to write:
>
>   git checkout @{default}
>
> instead of having to know or look up the default branch name.
>
> The implementation follows the same pattern as @{upstream} and @{push},
> using a new branch_get_default() function that queries the default
> branch name and verifies it exists in the repository.
>
> Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
> ---
>[snip]
@gitgitgadget-git
Copy link

User "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> has been added to the cc: list.

@HaraldNordgren
Copy link
Contributor Author

/submit

@gitgitgadget-git
Copy link

Submitted as pull.2183.v3.git.git.1769805948018.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2183/HaraldNordgren/default_shorthand-v3

To fetch this version to local tag pr-git-2183/HaraldNordgren/default_shorthand-v3:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2183/HaraldNordgren/default_shorthand-v3
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

I pushed a WIP with some of these ideas now, not intended as the final
thing.


Harald
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Harald Nordgren <haraldnordgren@gmail.com> writes:

> I pushed a WIP with some of these ideas now, not intended as the final
> thing.
>
>
> Harald

Meaning we should feel free to ignore v3 and possibly a few later
versions, until we hear from you?

I was writing the following as v3 review, but I guess these are
comments on a version not for public consumption, so ...

--- >8 ---

I'd rather not see you use "primary" for what init.defaultBranch
specifies, which already has a good name, "default".  If you are
using a different concept, like:

 * learn the remote @{upstream} for the current branch (for
   "@{primary}") or the named branch (for "$name@{primary}"), and
   then

 * look at refs/remotes/$remote/HEAD

then I would appreciate a good name to call that (which is a concept
that has no good name yet, as far as I can see) and "primary" might
be a good name for that new concept.

And from what I read as _your_ use case in an earlier message,
init.defaultBranch aka @{default} is not what you want 999/1, yet I
think what the patch implements is still that one.  Puzzled...

@HaraldNordgren
Copy link
Contributor Author

/submit

@gitgitgadget-git
Copy link

Submitted as pull.2183.v4.git.git.1769817987594.gitgitgadget@gmail.com

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2183/HaraldNordgren/default_shorthand-v4

To fetch this version to local tag pr-git-2183/HaraldNordgren/default_shorthand-v4:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2183/HaraldNordgren/default_shorthand-v4
@HaraldNordgren HaraldNordgren changed the title revisions: add @{default} shorthand for default branch revisions: add @{primary} shorthand for primary branch Jan 31, 2026
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> Meaning we should feel free to ignore v3 and possibly a few later
> versions, until we hear from you?

I pushed v4 now. Please feel free to review it.

> And from what I read as _your_ use case in an earlier message,
> init.defaultBranch aka @{default} is not what you want 999/1, yet I
>think what the patch implements is still that one.  Puzzled...

I'm agnostic with the regards to the implementation as long as it solves
my problem.

I changed my mind after realizing the other approach would have required
me to set 'init.defaultBranch' on many repos -- or worse, badger the
maintainers to set it. That does not scale.

With the new approach, things run smoothly for all the repos on my machine,
so my goal of convenience is achieved there. I ran this to check that:

    for x in */*; do (
      cd "$x" && \
      echo && \
      echo "$x" && \
      /Users/Harald/git-repos/github.com/git/git/git checkout @{primary}
    ); done


Harald
Git already has shorthands like @{upstream} and @{push} to refer to
tracking branches, but there is no convenient way to refer to the
primary branch of a repository (typically "main" or "master").

Users often want to switch to the primary branch regardless of its
name, especially when working across repositories with different
primary branch names. Currently they must either hardcode the branch
name or query it via configuration, which is cumbersome.

Add a new @{primary} shorthand that resolves to the primary branch
as determined by init.defaultBranch (or falls back to "main" or
"master" depending on Git version). This allows users to write:

  git checkout @{primary}

instead of having to know or look up the primary branch name.

The implementation follows the same pattern as @{upstream} and @{push},
using a new branch_get_primary_ref() function that queries the primary
branch name and verifies it exists in the repository.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Junio C Hamano <gitster@pobox.com> writes:

> For those who follow the naming the upstream decided to use in
> cloned repositories, init.defaultBranch is probably the last thing
> you want to take as a hint, as these people decided to _ignore_ the
> preference of their own and instead to follow what upstream uses.
> For that, refs/remotes/origin/HEAD would be a lot more stronger
> hint.  If they call their primary branch 'trunk', these people would
> want to call theirs 'trunk'.  And for that, these people would not
> do anything with init.defaultBranch.
>
>>     git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
>>
>> That's uneccessary overhead, that could now be replaced with:
>>
>>     git checkout @{default}
>
> That mirrors what I wrote in the previous paragraph.  Those who
> follow the naming the upstream uses, refs/remotes/$remote/HEAD
> (here, "origin" may not be the default remote) would be a better
> hint than init.defaultBranch so calling it @{default} is misleading.

Thinking about this more, while I can see how using the
remote-tracking branch that is pointed by refs/remotes/origin/HEAD
may make sense, I see no sensible reason why mapping that to a local
branch name by simply stripping refs/remotes/origin/ makes sense.

Stepping back a bit, the workflow that may be helped by being able
to learn the value of "refs/remotes/origin/HEAD", in addition to
@{upstream} and @{push} would be laid out like the following:

 * The project uses 'main' as its primary integration branch.  After
   you clone it, refs/remotes/origin/HEAD points at their 'main'
   (i.e., you have refs/remotes/origin/main keeping track of it),
   "git fetch" updates refs/remotes/origin/HEAD when they change
   their naming if you are using a recent enough version of Git.

 * The project uses topic based workflow.  The idea is each topic
   gets its own topic branch, e.g., 'feature', and participants join
   forces to bring it to perfection, after which 'main' merges the
   completed 'feature'.

 * You, as a participant of this project, fork your own 'feature'
   local branch from the remote-tracking branch 'origin/feature'.
   You publish the result into a separate repository of your own,
   different from where you cloned from.  If you fetch from there, a
   remote-tracking branch 'refs/remotes/publish/feature' may be
   created in your local clone as well (assuming your publishing
   repository is called 'publish').

Now, you have @{upstream} that is their 'feature' (that is kept
track of with refs/remotes/origin/feature remote-tracking branch
in your local clone), @{push} that is refs/remotes/publish/feature.
Comparing your local progress against these two are useful to see
where you are, how far you came, and how much others you see in
@{upstream} may have diverged.

In addition, the overall "progress" of the project is how far
@{upstream} has come relative to refs/remotes/origin/main, which is
the ultimate target that you and your fellow project participants
want to see @{upstream} gets merged, is a useful thing to keep track
of.

So in that sense, I do understand why somebody may find it useful if
there is a handy short-hand for refs/remotes/origin/main (or
whichever branch is pointed at by refs/remotes/origin/HEAD) in the
above picture.  And refs/remotes/origin/HEAD already does have a
handy short-hand, which is 'origin' ;-).

But step back and notice that there is no mention of local 'main' in
the above layout?

As Kristoffer said in another message [*1*], I would too expect that
people would not work on their 'main' (or have their 'main' track
the upstream's 'main').  So the utility of the piping to sed we saw
above is dubious, unless we are talking about quite different
workflow, but I do not think of what that other workflow would look
like that makes a neutral synonym for 'main' useful.

So, enough about refs/remotes/origin/HEAD.  Back to your original
idea of using init.defaultBranch.

In one of your other messages [*2*], you reported that you were
having trouble with repositories without init.defaultBranch
configuration variable cofigured.

Doesn't repo_default_branch_name() do the right thing without being
noisy at all even in a repository without that configured, as the
function will fall back to the built-in default?  While I do not
think of a workflow in which a handy access to the value the
function gives would be so useful that it deserves a short-hand, it
would be a reasonable candidate of what to be called "@{default}",
if it proves useful, I would think.

> I am not good at naming things, so instead of calling it @{primary}
> let's call it @{dumbo}.  With the realization that what branch
> refs/remotes/$remote/HEAD points at is a good source of hint, I
> actually think $branch@{dumbo} does make sense, and @{dumbo} should
> be a short-hand for $branch@{dumbo} where the name of the current
> branch is substituted for $branch (i.e. similar to @{push}, I
> suppose).  As you may be interacting with two sets of branches that
> go to two different remotes.
>
> In any case, it is very different from what you implemented as the
> @{default} in your patch.


[References]

*1* https://lore.kernel.org/git/7b62316f-a30a-4895-808d-baa20be0f3af@app.fastmail.com/

*2* https://lore.kernel.org/git/20260131000923.70152-1-haraldnordgren@gmail.com/
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> So in that sense, I do understand why somebody may find it useful if
> there is a handy short-hand for refs/remotes/origin/main (or
> whichever branch is pointed at by refs/remotes/origin/HEAD) in the
> above picture.  And refs/remotes/origin/HEAD already does have a
> handy short-hand, which is 'origin' ;-).

'git checkout origin' doesn't work without resulting in a detached head.

> As Kristoffer said in another message [*1*], I would too expect that
> people would not work on their 'main' (or have their 'main' track
> the upstream's 'main').  So the utility of the piping to sed we saw
> above is dubious, unless we are talking about quite different
> workflow, but I do not think of what that other workflow would look
> like that makes a neutral synonym for 'main' useful.

I don't work directly on the main branch.

However it serves and the only starting point for creating any new feature
branches. This is the command I use, and would be nice if it could be
simplified:

    git fetch --all
    git checkout $(git remote | rg '^(origin|upstream)$' | tail -n1)/HEAD -b new_branch

The main branch is used in my work frontend project for the app release
command, so there I do

    git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
    yarn release

I think me as a non-hardcore Git maintainer spend more time in different
repos than you two do, so maybe the pain of switching between systems is
more pronounced. That's my motivation for unifying stuff.

Just for reference, iterating all forked open-source repos on my machine
these are the different upstream names I work with:

    99designs/gqlgen
    refs/remotes/upstream/master

    amplitude/experiment-react-native-client
    refs/remotes/upstream/main

    Antonboom/testifylint
    refs/remotes/upstream/master

    cli/cli
    refs/remotes/upstream/trunk

    datastax/python-driver
    refs/remotes/origin/master

    dependabot/dependabot-core
    refs/remotes/origin/main

    derailed/k9s
    refs/remotes/origin/master

    elastic/go-elasticsearch
    refs/remotes/upstream/main

    git/git
    refs/remotes/upstream/master

    gitgitgadget/gitgitgadget
    refs/remotes/upstream/main

    github-linguist/linguist
    refs/remotes/origin/main

    go-redis/redis_rate
    refs/remotes/origin/v10

    golang-migrate/migrate
    refs/remotes/upstream/master

    golang/go
    refs/remotes/origin/master

    golangci/golangci-lint-action
    refs/remotes/upstream/main

    gradle/gradle
    refs/remotes/origin/master

    Homebrew/brew
    refs/remotes/origin/main

    jwalton/gh-docker-logs
    refs/remotes/upstream/master

    Khan/genqlient
    refs/remotes/upstream/main

    kubernetes-sigs/controller-tools
    refs/remotes/origin/main

    kubernetes/kompose
    refs/remotes/origin/main

    kubernetes/kubernetes
    refs/remotes/origin/master

    ldez/usetesting
    refs/remotes/origin/main

    liushuangls/go-anthropic
    refs/remotes/upstream/main

    matryer/moq
    refs/remotes/upstream/main

    mhemmings/revenuecat
    refs/remotes/origin/master

    ohmyzsh/ohmyzsh
    refs/remotes/upstream/master

    prettier/prettier
    refs/remotes/origin/main

    RevenueCat/docs
    refs/remotes/upstream/main

    RevenueCat/purchases-ios
    refs/remotes/origin/main

    RevenueCat/react-native-purchases
    refs/remotes/origin/main

    sashabaranov/go-openai
    refs/remotes/upstream/master

    stretchr/testify
    refs/remotes/origin/master

    vektah/gqlparser
    refs/remotes/upstream/master


> Doesn't repo_default_branch_name() do the right thing without being
> noisy at all even in a repository without that configured, as the
> function will fall back to the built-in default?  While I do not
> think of a workflow in which a handy access to the value the
> function gives would be so useful that it deserves a short-hand, it
> would be a reasonable candidate of what to be called "@{default}",
> if it proves useful, I would think.

I'll play around with this a bit and see how it works. Thanks for the tip!


Harald
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> Doesn't repo_default_branch_name() do the right thing without being
> noisy at all even in a repository without that configured, as the
> function will fall back to the built-in default?  While I do not
> think of a workflow in which a handy access to the value the
> function gives would be so useful that it deserves a short-hand, it
> would be a reasonable candidate of what to be called "@{default}",
> if it proves useful, I would think.

After looking a this, this is hard-coded. Not showing what is relevant for
each repo that exists:

```
char *repo_default_branch_name(struct repository *r, int quiet)
{
  const char *config_key = "init.defaultbranch";
  const char *config_display_key = "init.defaultBranch";
  char *ret = NULL, *full_ref;
  const char *env = getenv("GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME");

  if (env && *env)
    ret = xstrdup(env);
  if (!ret && repo_config_get_string(r, config_key, &ret) < 0)
    die(_("could not retrieve `%s`"), config_display_key);

  if (!ret) {
#ifdef WITH_BREAKING_CHANGES
    ret = xstrdup("main");
#else
    ret = xstrdup("master");
```


Harald
@gitgitgadget-git
Copy link

Phillip Wood wrote on the Git mailing list (how to reply to this email):

On 31/01/2026 20:22, Harald Nordgren wrote:
>> So in that sense, I do understand why somebody may find it useful if
>> there is a handy short-hand for refs/remotes/origin/main (or
>> whichever branch is pointed at by refs/remotes/origin/HEAD) in the
>> above picture.  And refs/remotes/origin/HEAD already does have a
>> handy short-hand, which is 'origin' ;-).
> > 'git checkout origin' doesn't work without resulting in a detached head.

That's expected because it refers to a remote tracking branch. Please correct me if I'm wrong but I think maybe what you're asking for is a shorthand for the branch "$b" where

	git push origin $b

would update the remote tracking branch pointed to by "origin/HEAD". I've not really thought this through but if that is what you want maybe we could add "@{local}" to give that branch. Then, with the default refspecs and with "origin/HEAD" pointing to "origin/master", "origin@{local}" would be "refs/heads/master". If you created a feature branch with

	git checkout -b feature origin

and you wanted to merge it into the local branch corresponding to the default branch on its upstream remote you could do

	git checkout feature@{upstream}@{local}
	git merge feature

I don't really understand what you're trying to achieve and I'm not sure if the suggestion above is a good idea but it might help understand what it is you're trying to do. Below you say you don't work directly on the main branch but then later on you're then creating a release from it. Is "main" just a mirror of "origin/main" or are you merging local work into it as well?

Thanks

Phillip

>> As Kristoffer said in another message [*1*], I would too expect that
>> people would not work on their 'main' (or have their 'main' track
>> the upstream's 'main').  So the utility of the piping to sed we saw
>> above is dubious, unless we are talking about quite different
>> workflow, but I do not think of what that other workflow would look
>> like that makes a neutral synonym for 'main' useful.
> > I don't work directly on the main branch.
> > However it serves and the only starting point for creating any new feature
> branches. This is the command I use, and would be nice if it could be
> simplified:
> >      git fetch --all
>      git checkout $(git remote | rg '^(origin|upstream)$' | tail -n1)/HEAD -b new_branch
> > The main branch is used in my work frontend project for the app release
> command, so there I do
> >      git checkout $(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')
>      yarn release
> > I think me as a non-hardcore Git maintainer spend more time in different
> repos than you two do, so maybe the pain of switching between systems is
> more pronounced. That's my motivation for unifying stuff.
> > Just for reference, iterating all forked open-source repos on my machine
> these are the different upstream names I work with:
> >      99designs/gqlgen
>      refs/remotes/upstream/master
> >      amplitude/experiment-react-native-client
>      refs/remotes/upstream/main
> >      Antonboom/testifylint
>      refs/remotes/upstream/master
> >      cli/cli
>      refs/remotes/upstream/trunk
> >      datastax/python-driver
>      refs/remotes/origin/master
> >      dependabot/dependabot-core
>      refs/remotes/origin/main
> >      derailed/k9s
>      refs/remotes/origin/master
> >      elastic/go-elasticsearch
>      refs/remotes/upstream/main
> >      git/git
>      refs/remotes/upstream/master
> >      gitgitgadget/gitgitgadget
>      refs/remotes/upstream/main
> >      github-linguist/linguist
>      refs/remotes/origin/main
> >      go-redis/redis_rate
>      refs/remotes/origin/v10
> >      golang-migrate/migrate
>      refs/remotes/upstream/master
> >      golang/go
>      refs/remotes/origin/master
> >      golangci/golangci-lint-action
>      refs/remotes/upstream/main
> >      gradle/gradle
>      refs/remotes/origin/master
> >      Homebrew/brew
>      refs/remotes/origin/main
> >      jwalton/gh-docker-logs
>      refs/remotes/upstream/master
> >      Khan/genqlient
>      refs/remotes/upstream/main
> >      kubernetes-sigs/controller-tools
>      refs/remotes/origin/main
> >      kubernetes/kompose
>      refs/remotes/origin/main
> >      kubernetes/kubernetes
>      refs/remotes/origin/master
> >      ldez/usetesting
>      refs/remotes/origin/main
> >      liushuangls/go-anthropic
>      refs/remotes/upstream/main
> >      matryer/moq
>      refs/remotes/upstream/main
> >      mhemmings/revenuecat
>      refs/remotes/origin/master
> >      ohmyzsh/ohmyzsh
>      refs/remotes/upstream/master
> >      prettier/prettier
>      refs/remotes/origin/main
> >      RevenueCat/docs
>      refs/remotes/upstream/main
> >      RevenueCat/purchases-ios
>      refs/remotes/origin/main
> >      RevenueCat/react-native-purchases
>      refs/remotes/origin/main
> >      sashabaranov/go-openai
>      refs/remotes/upstream/master
> >      stretchr/testify
>      refs/remotes/origin/master
> >      vektah/gqlparser
>      refs/remotes/upstream/master
> > >> Doesn't repo_default_branch_name() do the right thing without being
>> noisy at all even in a repository without that configured, as the
>> function will fall back to the built-in default?  While I do not
>> think of a workflow in which a handy access to the value the
>> function gives would be so useful that it deserves a short-hand, it
>> would be a reasonable candidate of what to be called "@{default}",
>> if it proves useful, I would think.
> > I'll play around with this a bit and see how it works. Thanks for the tip!
> > > Harald
> 
@gitgitgadget-git
Copy link

User Phillip Wood <phillip.wood123@gmail.com> has been added to the cc: list.

@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> I don't really understand what you're trying to achieve and I'm not sure 
> if the suggestion above is a good idea but it might help understand what 
> it is you're trying to do.
I didn't realize I was so bad at explaining 😅

What I want is a shorthand for switching to the local version of the
default branch of the repo. This but with less voodoo:

    git switch $(git rev-parse --abbrev-ref $(git remote | rg '^(origin|upstream)$' | tail -n1) | sed 's@.*/@@')


> you say you don't work directly on the main branch but then later on
> you're then creating a release from it. Is  "main" just a mirror of
> "origin/main" or are you merging local work into it as well?

My main is a mirror of upstream/main. I never commit to it, just do
'git pull' to create releases.

Also, I switch to it when I discover a bug on my branch, to try to
understand if the bug is already on main or not. It's the baseline all work
is compared against.

>>      99designs/gqlgen
>>      refs/remotes/upstream/master
>> 
>>      amplitude/experiment-react-native-client
>>      refs/remotes/upstream/main
>> 
>>      Antonboom/testifylint
>>      refs/remotes/upstream/master
>> 
>>      cli/cli
>>      refs/remotes/upstream/trunk

I want a shorthand so that when in any of these repos, I'm switching to the
default branch, I simply have to run

    git switch @{primary}

and I would end up with

      99designs/gqlgen
      Switched to branch 'master'
 
      amplitude/experiment-react-native-client
      Switched to branch 'main'
 
      Antonboom/testifylint
      Switched to branch 'main'
 
      cli/cli
      Switched to branch 'trunk'


Harald
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Harald Nordgren <haraldnordgren@gmail.com> writes:

>> Doesn't repo_default_branch_name() do the right thing without being
>> noisy at all even in a repository without that configured, as the
>> function will fall back to the built-in default?  While I do not
>> think of a workflow in which a handy access to the value the
>> function gives would be so useful that it deserves a short-hand, it
>> would be a reasonable candidate of what to be called "@{default}",
>> if it proves useful, I would think.
>
> After looking a this, this is hard-coded. Not showing what is relevant for
> each repo that exists:

Yes.

Of course.  It was a suggestion to avoid getting failures in
repositories that do not override it with their own configuration
files.

So the @{default} we originally discussed was not something that is
"relevant for each repo", and where refs/remotes/origin/HEAD points
at has a better chance of closer to the relevant name?
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> So the @{default} we originally discussed was not something that is
> "relevant for each repo", and where refs/remotes/origin/HEAD points
> at has a better chance of closer to the relevant name?

This is a communication error on my side again, my goal was always to have
someting that is relevant for every repo.


Harald
@gitgitgadget-git
Copy link

"D. Ben Knoble" wrote on the Git mailing list (how to reply to this email):

On Mon, Feb 2, 2026 at 5:19 AM Harald Nordgren <haraldnordgren@gmail.com> wrote:
>
> > I don't really understand what you're trying to achieve and I'm not sure
> > if the suggestion above is a good idea but it might help understand what
> > it is you're trying to do.
> I didn't realize I was so bad at explaining 😅
>
> What I want is a shorthand for switching to the local version of the
> default branch of the repo. This but with less voodoo:
>
>     git switch $(git rev-parse --abbrev-ref $(git remote | rg '^(origin|upstream)$' | tail -n1) | sed 's@.*/@@')
>
>
> > you say you don't work directly on the main branch but then later on
> > you're then creating a release from it. Is  "main" just a mirror of
> > "origin/main" or are you merging local work into it as well?
>
> My main is a mirror of upstream/main. I never commit to it, just do
> 'git pull' to create releases.
>
> Also, I switch to it when I discover a bug on my branch, to try to
> understand if the bug is already on main or not. It's the baseline all work
> is compared against.
>
> >>      99designs/gqlgen
> >>      refs/remotes/upstream/master
> >>
> >>      amplitude/experiment-react-native-client
> >>      refs/remotes/upstream/main
> >>
> >>      Antonboom/testifylint
> >>      refs/remotes/upstream/master
> >>
> >>      cli/cli
> >>      refs/remotes/upstream/trunk
>
> I want a shorthand so that when in any of these repos, I'm switching to the
> default branch, I simply have to run
>
>     git switch @{primary}
>
> and I would end up with
>
>       99designs/gqlgen
>       Switched to branch 'master'
>
>       amplitude/experiment-react-native-client
>       Switched to branch 'main'
>
>       Antonboom/testifylint
>       Switched to branch 'main'
>
>       cli/cli
>       Switched to branch 'trunk'
>
>
> Harald

If you don't need to be on a branch, then "git switch -d origin" (or
upstream, or whatever your remote is) should work just fine.

That just makes discovering the name of the remote the "interesting" part…

-- 
D. Ben Knoble
@gitgitgadget-git
Copy link

User "D. Ben Knoble" <ben.knoble@gmail.com> has been added to the cc: list.

@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> If you don't need to be on a branch, then "git switch -d origin" (or
> upstream, or whatever your remote is) should work just fine.

Thanks, but it needs to be a branch, do you use detached heads for
anything? 🤗 For me, the only ever happen by accident.


Harald
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"D. Ben Knoble" <ben.knoble@gmail.com> writes:

> If you don't need to be on a branch, then "git switch -d origin" (or
> upstream, or whatever your remote is) should work just fine.
>
> That just makes discovering the name of the remote the "interesting" part…

The only thing that is different is if you need to _name_ a branch,
or the commit pointed at is sufficient.  In order to run something
like "git shortlog origin..", "git shortlog @{default}.." is not
needed.

Of course, checking out and to be on the branch requires you to name
a branch (otherwise when two branches point at the same commit, you
cannot tell which one you want to check out).

@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Harald Nordgren <haraldnordgren@gmail.com> writes:

> My main is a mirror of upstream/main. I never commit to it, just do
> 'git pull' to create releases.
>
> Also, I switch to it when I discover a bug on my branch, to try to
> understand if the bug is already on main or not. It's the baseline all work
> is compared against.

OK.

But for that kind of "I go there to see, but I never modify anything
there let alone committing to it" usage, detached HEAD is exactly
the tool invented for.  So while I can understand the allure of
always having my local 'main' be at the 'main' at the remote, I no
longer see this as a "must have, somebody would die unless we add
it" kind of thing anymore, even though I think it may be a nice
thing to have for some people.

Thanks.
@gitgitgadget-git
Copy link

"Kristoffer Haugsbakk" wrote on the Git mailing list (how to reply to this email):

On Mon, Feb 2, 2026, at 22:19, Harald Nordgren wrote:
>> If you don't need to be on a branch, then "git switch -d origin" (or
>> upstream, or whatever your remote is) should work just fine.
>
> Thanks, but it needs to be a branch, do you use detached heads for
> anything? 🤗 For me, the only ever happen by accident.

The remote-tracking branch itself is enough for me to do things like:

> > Also, I switch to it when I discover a bug on my branch, to try to
> > understand if the bug is already on main or not. It's the baseline all work
> > is compared against.

And to compare against with git-diff(1), use in a range to git-log(1),
and use in `git branch --set-upstream-to=origin`.
@gitgitgadget-git
Copy link

Ben Knoble wrote on the Git mailing list (how to reply to this email):

> Le 2 févr. 2026 à 16:33, Junio C Hamano <gitster@pobox.com> a écrit :
> 
> "D. Ben Knoble" <ben.knoble@gmail.com> writes:
> 
>> If you don't need to be on a branch, then "git switch -d origin" (or
>> upstream, or whatever your remote is) should work just fine.
>> 
>> That just makes discovering the name of the remote the "interesting" part…
> 
> The only thing that is different is if you need to _name_ a branch,
> or the commit pointed at is sufficient.  In order to run something
> like "git shortlog origin..", "git shortlog @{default}.." is not
> needed.
> 
> Of course, checking out and to be on the branch requires you to name
> a branch (otherwise when two branches point at the same commit, you
> cannot tell which one you want to check out)

I oversimplified; thanks.
@gitgitgadget-git
Copy link

Ben Knoble wrote on the Git mailing list (how to reply to this email):

> Le 2 févr. 2026 à 16:19, Harald Nordgren <haraldnordgren@gmail.com> a écrit :
> 
> 
>> 
>> If you don't need to be on a branch, then "git switch -d origin" (or
>> upstream, or whatever your remote is) should work just fine.
> 
> Thanks, but it needs to be a branch, do you use detached heads for
> anything? 🤗 For me, the only ever happen by accident.

Yes, frequently :)

I run « git switch -d origin » a lot to avoid having to keep a local main branch up to date (if I don’t use it for anything, which is often the case). 
@gitgitgadget-git
Copy link

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

Phillip Wood <phillip.wood123@gmail.com> writes:

> ... Please 
> correct me if I'm wrong but I think maybe what you're asking for is a 
> shorthand for the branch "$b" where
>
> 	git push origin $b
>
> would update the remote tracking branch pointed to by "origin/HEAD". 
> I've not really thought this through but if that is what you want maybe 
> we could add "@{local}" to give that branch. Then, with the default 
> refspecs and with "origin/HEAD" pointing to "origin/master", 
> "origin@{local}" would be "refs/heads/master". If you created a feature 
> branch with
>
> 	git checkout -b feature origin
>
> and you wanted to merge it into the local branch corresponding to the 
> default branch on its upstream remote you could do
>
> 	git checkout feature@{upstream}@{local}
> 	git merge feature

I do not know if that is what Harald is looking for, but I did
wonder if we have use cases like that where we can string together
multiple @{modifier} after a branch name.  The @{local} thing that
takes a remote-tracking branch and gives the local branch that would
push to would be a "reverse" of @{push}; I wonder if three is need
for a similar concept for a reverse of @{upstream} and if so, it
would also be @{local-something-else}, and we may want to name this
one not just @{local} but @{local-something}.

That "feature@{upstream}@{local}" notation is a great food for
thought.

Thanks.
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> Yes, frequently :)
>
> I run « git switch -d origin » a lot to avoid having to keep a local main
> branch up to date (if I don’t use it for anything, which is often the
> case).

Very interesting! I'm gonna try this! I will also do this

    git config --global advice.detachedHead false

because that advice always looked to me like I was doing something very
wrong.



Harald
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> But for that kind of "I go there to see, but I never modify anything
> there let alone committing to it" usage, detached HEAD is exactly
> the tool invented for.  So while I can understand the allure of
> always having my local 'main' be at the 'main' at the remote, I no
> longer see this as a "must have, somebody would die unless we add
> it" kind of thing anymore, even though I think it may be a nice
> thing to have for some people.

This makes a lot of sense 👍


Harald
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

> That just makes discovering the name of the remote the "interesting" part…

Yeah, this is true! Discovering which is the upstream remote is still
non-trivial. It's aggravated because the GitHub 'gh' tool will rename
'origin' to 'upstream' when forking + creating PR from the CLI.

Which I why I do this becomes necessary

    git remote | rg '^(origin|upstream)$' | tail -n1


Harald
@gitgitgadget-git
Copy link

Harald Nordgren wrote on the Git mailing list (how to reply to this email):

I just ran into this issue when working from a detached HEAD. Maybe I can
get around it by adding "HEAD" as acceptable branches.

But it's an example that a lot of code out there requires real branches to
do things.

	$ release-it --ci
	ERROR Must be on branch release/*,hotfix/*,develop
	Documentation: https://git.io/release-it-git

	error Command failed with exit code 1.
	info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.



Harald
@gitgitgadget-git
Copy link

Phillip Wood wrote on the Git mailing list (how to reply to this email):

On 02/02/2026 10:14, Harald Nordgren wrote:
>> I don't really understand what you're trying to achieve and I'm not sure
>> if the suggestion above is a good idea but it might help understand what
>> it is you're trying to do.
>
> I want a shorthand so that when in any of these repos, I'm switching to the
> default branch, I simply have to run
> >      git switch @{primary}
> > and I would end up with
> >        99designs/gqlgen
>        Switched to branch 'master'
>   >        amplitude/experiment-react-native-client
>        Switched to branch 'main'
>   >        Antonboom/testifylint
>        Switched to branch 'main'
>   >        cli/cli
>        Switched to branch 'trunk'

I think I understand now. That sounds tricky to do in the general case because we don't know what the remote is called. "origin" and "upstream" are popular choices but the user can choose any name they want when they run "git clone" (or rename the remote after they clone). If there is only one remote then its simple because there is only one choice. It's also simple if there are multiple remotes and they all use the same default branch name and refspecs. If  remote.pushDefault is set we can probably rule that remote out. If there's a branch checked out with an upstream set we could use that remote but there's no guarantee that's the remote the user wants. I don't think there's a robust way to determine the remote the user wants in the general case.

With the "@{local}" thing I suggested yesterday the user would have to name the remote which makes everything well defined but I think you want to avoid having to do that.

Thanks

Phillip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants

Comments