Git aliases: switch to mainster, etc

Saturday 26 October 2024

I use a lot of git aliases because I work in the terminal and aliases give me short commands for common operations. They are defined in my global git config file and range from simple to powerful but twisty.

First, some basic aliases for operations I do often:

[alias]
    br = branch
    co = checkout
    sw = switch
    d = diff
    di = diff
    s = status -s -b --show-stash

These are simple, but others could use some explanation.

Committing

I have a few aliases for committing code. The “ci” alias provides the default option “--edit” so that even if I provide a message on the command line with “git ci -m”, it will pop me into the editor to provide more detail. “git amend” is for updating the last commit with the latest file edits I’ve made, and “git edit” is for updating the commit message on the latest commit:

[alias]
    ci = commit --edit
    amend = commit --amend --no-edit
    edit = commit --amend --only

Returning home

I work in many repos. Many have a primary branch called “main” but in some it’s called “master”. I don’t want to have to remember which is which, so I have an alias “git ma” that returns me to the primary branch however it’s named. It uses a helper alias to find the name of the primary branch:

[alias]
    # Find the name of the primary branch, either "main" or "master".
    primary = "!f() { \
        git branch -a | \
        sed -n -E -e '/remotes.origin.ma(in|ster)$/s@remotes/origin/@@p'; \
    }; f"

If you haven’t seen this style of alias before, the initial exclamation point means it’s a shell command not a git command. Then we use shell f() {···}; f syntax to define a function and immediately invoke it. This lets us use shell commands in a pipeline, access arguments with $1, and so on. (Fetching GitHub pull requests has more about this technique.)

This alias uses the “git branch -a” command to list all the branches, then pipes it into the Unix sed command to find the remote one named either “main” or “master”.

With “git primary” defined, we can define the “ma” alias to switch to the primary branch and pull the latest code. I like “ma” because it’s short for both main and master, and because it feels like coming home (“Hi ma!”):

[alias]
    # Switch to main or master, whichever exists, and update it.
    ma = "!f() { \
        git checkout $(git primary) && \
        git pull; \
    }; f"

For repos with an upstream, I need to pull their latest code and also push to my fork to get everything in sync. For that I have “git mma” (like ma but more):

[alias]
    # Pull the upstream main/master branch and update our fork.
    mma = "!f() { \
        git ma && \
        git pull upstream $(git primary) --ff-only && \
        git push; \
    }; f"

Merging and finishing branches

For personal projects, I don’t use pull requests to make changes. I work on a branch and then merge it to main. The “brmerge” alias merges a branch and then deletes the merged branch:

[alias]
    # Merge a branch, and delete it here and on the origin.
    brmerge = "!f() { \
        : git show; \
        git merge $1 && \
        git branch -d $1 && \
        git push origin --delete $1; \
    }; f"

This shows another technique: the : git show; command does nothing but instructs zsh’s tab completion that this command takes the same arguments as “git show”. In other words, the name of a branch. That argument is available as $1 so we can use it in the aliased shell commands.

Often what I want to do is switch from my branch to main, then merge the branch. The “brmerge-” alias does that. The “-” is similar to “git switch -” which switches to the branch you last left:

[alias]
    # Merge the branch we just switched from.
    brmerge- = "!f() { \
        git brmerge $(git rev-parse --abbrev-ref @{-1}); \
    }; f"

Finally, “git brdone” is what I use from a branch that has already been merged in a pull request. I return to the main branch, and delete the work branch:

[alias]
    # I'm done with this merged branch, ready to switch back to another one.
    brdone = "!f() { \
        : git show; \
        local brname=\"$(git symbolic-ref --short HEAD)\" && \
        local primary=\"$(git primary)\" && \
        git checkout ${1:-$primary} && \
        git pull && \
        git branch -d $brname && \
        git push origin --delete $brname; \
    }; f"

This one is a monster, and uses “local” to define shell variables I can use in a few places.

There are other aliases in my git config file, some of which I’d even forgotten I had. Maybe you’ll find other useful pieces there.

» 4 reactions

Comments

[gravatar]

I have ‘ci’ aliased to ‘commit –verbose’, which shows the diff in the editor. I find it useful to remind me what I did, and as a last-chance code review.

[gravatar]

Nice, I didn’t know about --verbose. I’ve added it to my alias.

[gravatar]
William Higgins 2:41 PM on 27 Oct 2024

Thank you for sharing these. It gives me some keepers for my git shortcuts, but also clarifies some git operations I was fuzzy on.

So now I’ve added branch juggling to the balls and clubs!

[gravatar]

@Ned, you can just use -v for --verbose as well. I agree with Roger that it’s super handy. I almost always use -v when committing code.

I like using git diff --staged a lot. My personal alias for that is git diffs

For git-log, I’ll frequently use --stat to show what files are affected in the commit.

I also have an alias for --oneline to get a nice concise graph, which can help out a ton: log --graph --decorate --oneline

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.