BetterBranch is a tiny Git helper that lists local branches with ahead/behind counts and the age of each branch tip. I rewrote it in Java and compiled it to a native binary so it stays fast and self-contained.
What does BetterBranch do
BetterBranch has just one job: It shows the list of local branches, but with a few extra pieces of information. It adds two columns »Ahead« and »Behind« in front of the branch name, plus the »last commit« date on that branch.
git bb alias$ git bb
Ahead Behind Branch Last Commit
----- ----- -------------------------------------------------- --------------------
0 0 main 3 days, 8 hours ago
0 3 zstd_dist 3 days, 9 hours ago
0 3 compress_for_size 3 days, 9 hours ago
0 7 fix/build-native-yaml-if 4 days, 6 hours ago
0 8 fix/workflow-archive-upload 4 days, 7 hours ago
0 12 fix/build-native-linux-upload 4 days, 7 hours ago
0 17 fix/early-access-binary-artifacts 4 days, 10 hours ago
0 19 fix/early-access-artifacts 4 days, 10 hours agoI find this super helpful to find branches easily and to see which ones to update or rebase next.
Compared to git branch --list
The command git branch --list gives you only one piece of information: which branches are locally known or checked out. However, the current branch is coloured in green (or whatever colour your terminal supports). It will also list linked worktree branches with a (+) sign, BetterBranch does not do that (yet!), but the invocation of BetterBranch DOES work in worktrees since this week.
Additionally, git branch --list supports a <pattern> argument, something else BetterBranch is still missing.
What BetterBranch does better is to give you an overview of branch relationship: Ahead/Behind and last commit date are very helpful pieces of information not shown when running git branch --list.3 It also sorts by latest tip commit date by default, so the most relevant branches stay near the top. This makes piping through head easy and feasible.
Why the rewrite
I used to use a shell script from a gist. But it was awfully slow.
Since I know Java best and I was looking for another excuse to create a GraalVM native binary, I rewrote it in Java. 😉
Tools used
Since this is a simple tool, I tried to keep the dependencies down as much as possible. However, I did not want to shell out to Git, so I needed Eclipse jGit as a bare minimum. Sadly, Eclipse jGit brought slf4j-api, but that is no big deal. No picocli needed for this tool.
Building with GraalVM
Supported Platforms
To not waste CPU cycles, I decided to adapt my native-build.yaml from jfmt: First, only the linux x64 binary is built. Only when that succeeds all the other platforms are built (Linux:aarch64, Mac:aarch64, Windows:x64).
Signed JARs / Signatures
I came across one problem, though: Signatures from jar archives (»signed JARs«) currently break GraalVM builds. As a workaround, I adapted Copilot’s suggestion to create a shaded jar with all the classes minus the signatures and use that as the only classpath input for GraalVM.
This is how you do it:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- other tags omitted for brevity -->
<build>
<plugins>
<!-- other plugins omitted for brevity -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<skip>${native.skip}</skip>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>native-image</shadedClassifierName>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/*.MF</exclude>
<exclude>META-INF/LICENSE.txt</exclude>
<exclude>META-INF/versions/*/*module-info*.*</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<id>shade-for-native-image</id>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<!-- force usage of the shaded jar -->
<classpath>
<param>${project.build.directory}/${project.build.finalName}-native-image.jar</param>
</classpath>
</configuration>
</plugin>
</plugins>
</build>
</project>Download
Download pre-releases of BetterBranch: BetterBranch early-access @ GitHub
