In Software Engieerning, we all have seen this meme:

GopherCon Check

It’s not only about writing the code, but also being able to be reviewed effectively by peers.

Therefore, pull requests with smaller diffs are always preferred.

However, developers face many challenges when actually practicing this approach, especially when doing it with as-of-now the most popular code collaboration mode, trunk-based development. In particular, smaller diffs means more pull requests for a serie of changes that are dependent on top of the other. We refer to them as stacked pull requests. Now, we have a dependency management issue with Git branches. Not only that, whenever the depended branch gets merged, there is a serie of “merges” or “rebases” for all the dependent pull requests.

It is not impossible to use vanilla Git commands for managing the dependencies between Git branches, but there is a more pleasant way.

Graphite, it seems to be built as a code review dashboard via its web UI, but what I found more interesting is, its CLI works pretty well in standalone for managing stacked pull requests. You can use its web UI to take the full advantage of features, but I still very much prefer the GitHub UI for doing my code reviews, so… sorry creators!

Alright, let’s get straight to the point. Once installed, the CLI is available as gt.

There are essentially four commands that I use to manage stacked pull requests with Graphite CLI in my workflow.

  1. gt create - To create a new Git branch
  2. gt sync - To sync branch updates from remote, and prompt for deleting merged branches, as well as automatically restack branches 🌟
  3. gt restack - To manually trigger a restack
  4. gt log short - To look at the dependency tree

The best part? The gt works seamlessly with all the native Git commands, it’s not vendor lock-in.

Here is a typical scenario at my work:

  1. gt create to create a new branch (feature/database) from the main branch
  2. Code something up, push it up, submit for review
  3. gt create another branch (feature/router) on top of the feature/database branch
  4. Code something up, push it up, submit for review. On GitHub UI, choose the feature/database branch as the “base” so the diff becomes correct
  5. New branches goes on, for an end-to-end feature development
  6. The pull request for feature/database is approved and merged to main
  7. gt sync to sync the updates, it prompts me to delete feature/database locally and automatically restacks all dependent branches

Great, there is only one problem left.

While I can always use gt log short to check for the dependency tree, it is not fast enough, I want to be able to know the dependencies way faster than that.

So, I came up with a stupid naming mechanism for my branches.

They all start with two digits, e.g. 00-feature-database, 01-feature-router.

The first digit represents a serie of changes, and the second digit is the sequence in the same serie, the number indicates the dependency. Yes, it can only support up to 10 changes in a serie, but in practice, that has never happened to me, and even when that happens, using 010- would be just fine.

That’s it, happy coding!