Git Prom! My Favorite Git Alias

technosophos

Matt Butcher

Posted on January 5, 2024

Git Prom! My Favorite Git Alias

I work on a lot of GitHub repositories. In most of these, I make a fork of the repository, do my own coding, and then open PRs against the main repository.

Like any avid Git user, over time I have accumulated a number of Git aliases. But my favorite (and one of the most frequently used) is one I call git prom.

Since Git setups tend to differ, let me give a quick high-level about how I set up my clones and why.

How I Set Up My Local Clones

Typically, when working on an upstream repository at GitHub, I start with the main repo, clone it, and work on a clone. Then I open PRs to suggest merging my changes back into upstream.

So we're tracking two repos: the upstream and my own clone. I use the following naming convention:

  • My own repo is always called my (git clone my URL)
  • Upstream is always origin (git remote add origin URL)

For example, here's a snippet of my Git config for the spin repository:

[remote "origin"]
    url = git@github.com:fermyon/spin.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[remote "my"]
    url = git@github.com:technosophos/spin.git
    fetch = +refs/heads/*:refs/remotes/my/*
Enter fullscreen mode Exit fullscreen mode

I find this really useful because then origin is always the "real" project, and my is always my copy. Easy to remember.

Some of my friends call upstream upstream and their copy origin. I find that use of origin confusing, but if it works for you, it's totally fine. I'll point out where you need to make changes in the alias.

The Problem: Updating my branch to the last from upstream

For a longer-running PR, it is often the case that I need to periodically update my branch with the latest from the upstream repository.

The best way to do this is to:

  • Pull the upstream repo
  • Rebase my own work on top of it

I wanted a fast way to do this that didn't involve typing lots of flags (and hence introducing typo opportunities).

The Solution: The git prom alias

I wrote a quick Git alias that is in my main .gitconfig file (and is therefore global):

[alias]
    prom = "!git pull --rebase origin $(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)"
Enter fullscreen mode Exit fullscreen mode

First, let me explain what it does. Then I'll go into the specific command a little more.

The name gives it away; prom is shorthand for Pull Rebase Origin Main. So it pulls and rebases origin's (or upstream's) main branch.

Say I'm on a feature branch called feat/prom-example, and I've been working away on it. In the meantime, upstream's main branch has been merging new PRs. I need to re-sync. I'll use git prom to rebase my current branch on the latest main:

$ git prom
remote: Enumerating objects: 1180, done.
remote: Counting objects: 100% (327/327), done.
remote: Total 1180 (delta 327), reused 327 (delta 327), pack-reused 853
Receiving objects: 100% (1180/1180), 536.46 KiB | 2.87 MiB/s, done.
Resolving deltas: 100% (691/691), completed with 124 local objects.
From github.com:fermyon/spin
 * branch              main       -> FETCH_HEAD
   0405da75..74cd215a  main       -> origin/main
Updating 16dd1339..74cd215a
Fast-forward
 .devcontainer/Dockerfile                     |    3 -
 .devcontainer/devcontainer.json              |    1 -
 .envrc                                       |    1 +
# and more changes
Enter fullscreen mode Exit fullscreen mode

At the end, I have the latest version of the upstream's main branch pulled, and then my own changes rebased on top of that.

This is a very common part of workflow, so it's nice to have a nice mnemonic alias that is more typo-resistent than the usual full command.

How The Command Works

Even though the workflow is very common, the alias is surprisingly complex. Let's take a quick look at what it's doing.

prom = "!git pull --rebase origin $(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)"
Enter fullscreen mode Exit fullscreen mode

In essence, when I am on a branch feat/prom-example and I want to pull and rebase, the basic command is git pull --rebase origin main. And in most cases, the prom alias could have just been prom = "!git pull --rebase origin main".

But... it is not always the case that the upstream main branch is named main. Some still use the antiquated master. Others use their own systems for what the main branch is. (I worked on one that set the main branch name to whatever the current major release number was, e.g. v2.)

So to get around this limitation, we need to run a bit of ad hoc shell processing: $(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5)

This does the following:

  • Run git remote show origin to see the branches that are upstream (remember: for me, origin is the name of the upstream repository. If you use a different convention, adapt accordingly).
  • grep 'HEAD branch' gets us the name of the branch that upstream currently considers to be the "HEAD". For GitHub, this is the branch that is the default main branch.
  • And cut -d' ' -f5 says "split the returned line based on ' ' (space) characters and return me the 5th (-f5) field.

So git remote show origin | grep HEAD returns

  HEAD branch: main
Enter fullscreen mode Exit fullscreen mode

Note the leading whitespace. The cut command returns five fields:

  1. [EMPTY]
  2. [EMPTY]
  3. HEAD
  4. branch:
  5. main

And we just want field 5. If we were to run just this subcommand in a terminal, here's what the output would look like:

$ git remote show origin | grep HEAD | cut -d' ' -f5
main
Enter fullscreen mode Exit fullscreen mode

A Quick Note for Windows (non-WSL) Users

The alias I use works fine on macOS, Linux, WSL, and probably most UNIXy OSes. But it will not work on Windows outside of WSL. I don't happen to know how to do this in Powershell, so your best bet may be to just hard code in the default branch name with !git pull --rebase origin main.

If you know a Windows solution, please post it in the comments below. I'd love to know this.

Conclusion

My git prom alias abstracts away the specifics of git pull --rebase origin main. It still works with esoteric or unconventional upstream repos as long as I follow my origin and my conventions.

If you use a different convention (like upstream instead of origin), then all you need to do is change occurrences of origin to whatever convention you use.


Cover image generated using Bing's version of DALL-E using the prompt: "Using a blueprint illustration style, draw a terminal window used by a software developer."

💖 💪 🙅 🚩
technosophos
Matt Butcher

Posted on January 5, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related