A git alias to accept both main and master branch names?

Viewed 1065

I work on several projects with some having main and some master as default branch. I can't rename all the projects with one or other name because not all are managed by me. I know the .gitconfig alias system is very wide so I'm wondering if there is a way to accept both names for every command where there are involved. So far I only found this but it involves modify each individual command and using !sh -c which is not very 'pretty'. The ideal would be to also testing if both branches actually exist in the repo to avoid mistakes.
I tried adding master = main at the beginning of .gitconfig [alias] section but it don't works in either way.

5 Answers

To find the default branch of a remote repository, regardless of name, you can look at .git/refs/remote/<remote name>/HEAD.

For instance:

$ cat .git/refs/remotes/origin/HEAD
ref: refs/remotes/origin/master
$ awk -F "/" '{print $NF}' .git/refs/remotes/origin/HEAD
master

Then you can switch to the local branch of the same name:

git switch $(awk -F "/" '{print $NF}' .git/refs/remotes/origin/HEAD)

I don't think there's a practical way to do that. You could approximate that by renaming the branch locally; this doesn't affect the remote or any other user, so it doesn't matter whether it's "your" project to manage.

git checkout master
git branch -m main

But I don't really recommend it. The downside to this (or to your original "alias" idea if you could get it to work) is that you will likely reinforce your lack of knowledge about which repos use which name. For those cases when you need the remote tracking ref name (origin/master or origin/main), and for those cases where you're communicating with other users of the repo, this is likely to increase confusion.

A more robust version of IMSoP's answer is

awk -F "/" '{print $NF}' \
  $(find ./.git/refs/remotes/ -type f -name 'HEAD' | head -1)

It finds the HEAD first, and thus the branch name, regardless of the name of the remote(s, or their number).


Edit: My below, initial answer is more brittle than the above.

testing if both branches actually exist in the repo to avoid mistakes.

Specifically for switching to the default branch, I use this bash function during this… transition period.

gsm () {  # Switch to main or master branch
  git branch --list | grep ' master$'
  if   [[ $? -eq 0 ]]; then git switch master
  elif [[ $? -ne 0 ]]; then git branch --list | grep ' main$'
    if [[ $? -eq 0 ]]; then git switch main; fi
  else  # neither exists
    git switch -c main
  fi
}

I haven't tested this for each combination of master and/or main being present and/or absent, though.

From https://stackoverflow.com/a/62397081/10095231:

git rev-parse --abbrev-ref origin/HEAD will print origin/<default-branch-name>.

To strip the remote part (i.e.: "origin") from it and only print what is to the right of (the last) /:

basename "$(git rev-parse --abbrev-ref origin/HEAD)"

To support multiple remotes, something like the following could be done (inside a script or a function, for example):

test -n "$1" && r="$1" || r=origin
basename "$(git rev-parse --abbrev-ref "$r"/HEAD)"

Now inside a git alias, called "defbranch":

[alias]
    defbranch = "!f() { test -n \"$1\" && r=\"$1\" || r=origin; \
      basename \"$(git rev-parse --abbrev-ref \"$r\"/HEAD)\"; }; f"

It should be portable enough to work on any POSIX-compatible shell.

Add the above code to your git config file (e.g.: ~/.gitconfig) and use it like so:

$ git defbranch
master

To print the default branch of another remote, just specify its name as the first argument:

$ git defbranch origin2
main

Examples of using the alias with other git commands:

git checkout "$(git defbranch)"
git merge "$(git defbranch)"

For increased ergonomics, you could also store the default branch name in a variable and then use just the variable:

db="$(git defbranch)"
git checkout "$db"
git merge "$db"

Here's a simple way to do it in an alias in your .gitconfig:

[alias]
    com="!f() { git checkout master 2>/dev/null || git checkout main; }; f"

com stands for "checkout master/main". Just easier to type git com.
Note: only works if the main branch names in the repos you work on are either master or main.
Why anyone would make different main branch names I have no idea since there really isn't any programmatic advantage.

Related