Conveniently handling non-master development/default branches in Git(/Hub)
Those who work on many open source projects will surely find irritating to handle the different naming of the development/default branch.
In this short article, I’ll show how conveniently handle it, and as usual, I’ll sprinkle it with a bit of Linux tooling.
Content:
- Conventions and general information
- Finding the default branch
- Integrating with git aliases
- Handling repositories with a development branch that is non-default
- Conclusion
Conventions and general information
I’ll assume that the remote name is origin
. If this isn’t the case, just search and replace the value.
This article is based on a StackOverflow post.
There are edge cases; for those, just use the manual configuration strategy (see section below).
Finding the default branch
There are two (main) ways of finding the default branch in a git repository; one offline, and one online.
Offline version
The offline version is less safe, since it won’t detect changes, and it won’t handle a few edge cases, but we can still use it for the vast majority of the cases:
$ git rev-parse --abbrev-ref origin/HEAD
origin/master
Now, there are different approaches to extracting the branch name. The smallest and still sensible (IMO) is via awk:
$ git rev-parse --abbrev-ref origin/HEAD | awk -F/ '{print $NF}'
master
What we’re doing here is very simple:
-F/
: sets the separator (which default to space), to/
;print $NF
: print the last token.
That was easy 😄
Online version
For completion, this is the online version; while more robust, it’s not convenient to use with aliases, due to the relatively slow speed:
$ git remote show origin
* remote origin
Fetch URL: git@github.com:saveriomiroddi/saveriomiroddi.github.io.git
Push URL: git@github.com:saveriomiroddi/saveriomiroddi.github.io.git
HEAD branch: master
... other information ...
This outputs much more information. Again, awk comes handy:
git remote show origin | awk '/^ HEAD branch:/ {print $NF}'
The logic here is also simple:
- We filter in the lines matching a condition, here defined by a regex (
/regex/
)- The line must start (
^
metacharacter) with ` HEAD branch:`
- The line must start (
- If condition is true, evaluate the block
- Again, we print the last token (
$NF
)
- Again, we print the last token (
Nice!
Integrating with git aliases
Let’s say we want an alias that displays, from a branch, the commits that are not in the development branch.
Traditionally, we’d use:
$ git cherry -v --abbrev=10 master
- 47a1f20690 Boing: Add `fastrand` dependency
+ 524ed12d99 Boing: Complete Ball
+ 0d1995feb4 Boing: Complete Bat
+ 93d9cd6647 Boing: Complete Game#update()
+ 3bfa3863fc Boing: Correct misinterpreted sound playback functionality
+ 5a0935e9db Boing: Add AudioEntity trait
+ 17b5e7a9c1 Boing: Add GraphicEntity trait
+ ee9ee13bd5 Boing: Implement GraphicEntity for Ball, Bat and Impact
+ f6eeecb974 Boing: Post-implementation removals
However, now we can’t assume the master
name. Let’s solve this.
git config --global alias.devbr "\!git rev-parse --abbrev-ref origin/HEAD | awk -F/ '{print \$NF}'"
This will set a global alias, devbr
, which prints the default branch. To note:
- the
!
prefix, which tells git that this is a full shell command, rather than a git one; - the
!
and$NF
escaping, which is required, since the outer quotes are double ones! without escaping, those tokens will be interpreted.
Now, we can write an alias for the cherry command:
git config --global alias.chm '!git cherry -v --abbrev=10 "$(git devbr)"'
Done. No more fears of non-master
branches!
$ git chm
- 47a1f20690 Boing: Add `fastrand` dependency
+ 524ed12d99 Boing: Complete Ball
+ 0d1995feb4 Boing: Complete Bat
+ 93d9cd6647 Boing: Complete Game#update()
+ 3bfa3863fc Boing: Correct misinterpreted sound playback functionality
+ 5a0935e9db Boing: Add AudioEntity trait
+ 17b5e7a9c1 Boing: Add GraphicEntity trait
+ ee9ee13bd5 Boing: Implement GraphicEntity for Ball, Bat and Impact
+ f6eeecb974 Boing: Post-implementation removals
Handling repositories with a development branch that is non-default
Some repositories have a development branch different from the default one.
For example, ggez uses devel
as development branch, and master
as default branch.
In this case, we can just manually configure the name, and store it somewhere. Where?
Interestingly, git allows to store arbitrary configuration values. Let’s use that!:
git config custom.development-branch devel
This will add to the repository configuration (since we didn’t specify --global
), a custom
group, with the key/value pair development-branch
/devel
. See the config change:
$ tail -n 2 .git/config
[custom]
development-branch = devel
The group and key names are arbitrary.
Now, in order to generalize this, we have a bit of a problem; we need to use the config value if exist, otherwise, find it. This is still easy to solve! Let’s try it first:
$ git config --get custom.development-branch || git rev-parse --abbrev-ref origin/HEAD | awk -F/ '{print $NF}'
devel
The logic is simple; it’s based on the fact that if config --get
will not find a value, it will exit with a non-success value:
$ git config --get this.is-not-found
$ echo $?
1
In Bash boolean logic, this evaluates to false; since we use a disjunction (“or” boolean operator = ||
), the second command will be executed.
Let’s replace the previous devbr
alias:
$ git config --global alias.devbr "\!git config custom.development-branch || git rev-parse --abbrev-ref origin/HEAD | awk -F/ '{print \$NF}'"
And… there we go!:
$ git devbr
devel
The cool thing is that other aliases/commands relying on the devbr
alias will now work accordingly, e.g.:
$ git checkout upstream/upgrade-glutin-oldschool-ext
HEAD is now at a42082a Heck you Windows.
$ git chm
+ efe0d12a7a Make sure window.make_current() is called.
+ bd95362714 Update graphics setting example
+ a1097816cb Rename `graphics::image` to disambiguate
+ a42082a95f Heck you Windows.
Conclusion
We’ve used the basic, convenient functionalities of standard tools - git, awk and bash - in order to automate, cleanly, workflows that would otherwise be manual and tedious.
Although this is a trivial example, it shows the Power Of The Unix Spirit™. Enjoy fiddling with Linux tools! 😎