[Updated 05/22: Added two further aliases that I've found useful.]
I've long known that you can set up custom Git commands (known as aliases) and configure other useful settings system-wide, but it has taken me far too long to actually act on that knowledge.
A few months ago, I started needing to constantly remember a particularly obtuse Git command to output my commit history to a text file (I won't go into why, just accept that I did 😁). I kept having to look up the actual sequence, which finally forced me to dig into how to create aliases. In brief, a Git alias is a shortcode or code snippet that is accessed directly from a custom command. For example, here's that Git-history-to-text-file snippet as an alias:
[alias] save = !git --no-pager log > log.txt
With that in place, all I have to do is open a terminal to my current repository and run git save
. Git notices a command it isn't aware of, checks my aliases, and runs the code snippet automatically. It certainly made outputting those log files much easier.
Of course, the beauty of Git aliases is that they accept any valid Git commands (including other aliases, which we'll return to later on) and can be assigned to any string that isn't an existing function (so you can't accidentally overwrite what commit
does, for example). As a result, you can use whatever term makes the most sense to you and that you find easiest to remember.
That flexibility makes aliases a particularly powerful tool, and once I'd realised how simple they are to create I began noticing other areas of my Git workflow that could be simplified or made easier. Initially, I got a bit carried away, created way too many aliases, and then promptly forgot most of them existed 😂 A few months down the line, however, I've genuinely come to rely on a handful of them, so I figured I'd write an article explaining what they do and how to set them up yourself.
For those already familiar with aliases, let's just get to the tl;dr: and show you my personal setup (feel free to copy and run 😉):
[alias] save = !git --no-pager log > log.txt last = log -1 HEAD trunk = !git checkout main && git pull new = !git trunk && git checkout -b $1 && git push --no-verify -u origin del = !git branch -d $1 include = commit --amend --no-edit amend = commit --amend undo = reset --soft HEAD~1 pupdate = !git trunk && git checkout $1 && git merge main -m ':twisted_rightwards_arrows: Align with main branch' [init] defaultBranch = trunk
For everyone else, follow me 🐇
ℹ I personally use trunk
as the name of my base Git branch; you can read why here, but feel free to switch that out for main
or release
or whatever else you like 👍
Where Are Git Aliases Stored
First things first, let's locate your global Git settings. I'm going to assume you've already got Git installed[1] which means you should already have a .gitconfig
file setup at your root directory. Exactly where your root directory is located depends on your operating system and how you installed Git in the first place, but for Windows[2] it defaults to:
C:\Users\<username>\.gitconfig
If you're unsure or can't find it, feel free to check out the official docs or run this command in your terminal:
$ git config --list --show-origin
👆 That should output a list of currently configured user settings, alongside the location of the .gitconfig
file.
Once you've located your file, open it up. It will likely contain a few settings already, including your default code editor under the [core]
heading and your user details under (surprise, surprise) the [user]
heading.
ℹ The root .gitconfig
file isn't the only place you can create aliases. If you need to set aliases across a local network or system for all users, you can do that in a system-level settings file. Alternatively, if an alias is only relevant for a specific project or should be shared across a distributed team, you can add aliases to a configuration file stored within the repository. Each level overrides the others; much like CSS, Git configurations have specificity. As a result – and because different people often have different opinions on what an alias should do – it's often best to just leave it up to individuals to configure for themselves, but the options are there if you need them (see the official getting started guide linked below for more details).
Oh, and one other thing! Git is designed as a command-line tool so, as you'd expect, you can create, manage, and check aliases directly from a terminal using the git config
command. I prefer to edit files – that's just my jam – so I won't mention the CLI command again, but the docs are linked below if you're interested.
Creating Your First Alias
Different people have different needs, but I want to start with the alias that I find the most broadly useful: git trunk
[3]. The first thing I do in pretty much every piece of work is create a new branch, and that branch should be based on the current "production code" i.e. the trunk. To do that, I want to checkout the base branch and then pull down any changes to my local development environment. In other words, I run these two terminal commands:
$ git checkout trunk $ git pull
That's exactly the kind of repetitive multistage task that aliases were made for!
In your root .gitconfig
create a new alias header (if it isn't already present) e.g:
// C:/Users/<username/.gitconfig [core] { ... } [user] { ... } [alias] [init] defaultBranch = trunk
Underneath, add a new alias that stitches together those two Git commands, like so:
// C:/Users/<username/.gitconfig { ... } [alias] trunk = !git checkout trunk && git pull [init] { ... }
Hopefully, that's pretty easy to understand. We're assigning a new alias with the name trunk
and passing an argument to it; that argument contains two separate commands chained with the &&
operator.
ℹ That leading !
is used to tell Git that we're running a full-fat command (technically known as a shell command), rather than a Git subcommand. Basically, if your alias wants to refer to the git
command directly, start it with a !
. You can read the full details in the Git docs or skip down to see some more examples of both types of alias below.
If you save your settings and open any old Git repository, you should now be able to run git trunk
and see the ✨ magic happen ✨[4]
Improving Your First Alias
Cool, so you've shortened two terminal commands into one – nice 👍 But we can do a bit better than that. Again, this is my personal workflow, but chances are good that your next step would be the same as mine: create a new branch. And if you're creating new branches, you probably want to push those branches to a remote repository at some point in the future (i.e. GitHub or GitLab).
So you've run git trunk
, created a branch, done your work, committed those changes, and now you're set to push on up for code review. What do you do next? Well, if you're me, you run git push
and get slapped down for being so impertinent – you forgot to link your local branch with the remote repository 🤦♂️ I probably do this on >80% of branches I create, or at least I used to.
Now, rather than begrudgingly typing in git push --set-upstream origin
and then scrabbling around to find my branch name, I just have one alias which does everything for me. It fetches any recent changes, creates a new branch from the trunk, and pushes it upstream, all in one smooth, automated workflow.
In order to make that work, we're going to extend our trunk
alias. We could literally just append some additional commands to the existing chain, but being able to browse your project's trunk is useful from time-to-time, so I want to keep the current functionality intact. Well, remember when I mentioned that alias commands can call other aliases? Yeah, let's do that instead and create a new alias (heh, you'll understand in a second):
// C:/Users/<username/.gitconfig { ... } [alias] trunk = !git checkout trunk && git pull new = !git trunk && git checkout -b $1 && git push --no-verify -u origin [init] { ... }
Right, let's break down what's going on here. We've added a new new
alias (see what I meant 😁). When run, that command first triggers our existing trunk
alias, checks out the base branch, and fetches any outstanding updates. Next, it creates a new branch and then immediately links that new branch with our remote repository. All that from a single Git command 👨🍳🤏
To stitch that all together, we're making use of a few handy Git shorthands that already exist. For instance, checkout -b
is equivalent to creating a new branch using the git branch
command, then checking it out using the git checkout
command. Similarly, we're using -u
as the shorthand for --set-upstream
when we push to the remote repository. Chances are you already know this stuff, but it never hurts to spell it out 😊
ℹ We're also using the --no-verify
flag on our push
command. This tells Git to bypass any pre-push checks that you might have configured, such as automated test runs or commit message requirements. That should be fine, because this is an empty commit; it's just a direct clone of the trunk. However, different code pipelines have different requirements, so you may need to remove or modify this command to fit your needs.
Finally, there's that mysterious $1
in the middle of the chain. If you know your RegEx, you'll recognise this as a variable or input token. What it does is take any string you type after git new
and stitches it into the argument at the token's location. That allows you to pass a name for your new branch directly into the Git command because, well, that part would be pretty hard to know ahead of time.
So let's test this out. Save your file and run something along the lines of:
$ git new my-fancy-branch
Fingers crossed, Git just automated the setup of a new branch called my-fancy-branch
right before your eyes 👀 The branch will be a direct copy of the latest version of your trunk and should be automatically linked to your remote repository, so when you're ready to push some changes all you need to do is git push
and away you go 🚀
Some Other Useful Aliases
The new
alias is easily my biggest productivity win when it comes to Git, letting me set up everything I need for a new piece of work in a single command. On top of new
, trunk
is a generally useful alias, but there are a few others shown in my settings above that are worth mentioning.
git last
last = log -1 HEAD
Not one I use a lot, but handy when needed. It prints out the details of the latest commit on a given branch to your terminal. Useful for double-checking commit messages or quick investigations into Git history.
git del
del = !git branch -d $1
A super simple alias, but a command I frequently find tricky to remember. The del
command takes a comma-separated list of branch names and then deletes them. For example:
$ git del branch-one, branch-two, test-branch
Will delete local branches with the names branch-one
, branch-two
, and test-branch
.
git include
include = commit --amend --no-edit
Have you just committed some changes, then remembered something else you needed to tweak or spotted a typo? Yeah, it happens 😉 Rather than rolling back the commit or adding a new, pretty pointless commit on top, you can use this alias to literally include any currently staged changes with the prior commit.
git amend
amend = commit --amend
Basically the same as include
under the hood, but I use it for a very different purpose. The amend
command lets me fix commit messages by reopening the prior commit in my text editor. As someone who uses gitmoji on a lot of personal projects, it's particularly useful for fixing misclicks or reorganising a commit. Plus typos – all the typos 😁
git undo
undo = reset --soft HEAD~1
An incredibly useful shorthand to reverse the previous commit, in case you staged a file you didn't intend to or just want to tweak the commit message.
git pupdate
pupdate = !git trunk && git checkout $1 && git merge main -m ":twisted_rightwards_arrows: Align with main branch"
This is definitely a more niche/personal alias, but for certain codebases, I find it invaluable. It effectively takes a branch name (e.g. git pupdate new-branch
) and brings it up to date with the main branch, though it does require the git trunk
command shown above[5]. I personally tend to use Gitmoji in commit messages, so this also automates that and ensures branch merges are consistently styled in the history, as an added bonus. Of course, if you rebase or just manage your repository differently, this may not be useful (or actively problematic), but each to their own 😉
Small wins, big impact
It sounds a bit silly, but Git aliases are one of my favourite discoveries of the last year. I don't use many of them, I frequently forget about some of the ones I do have (git del
seems to slip out of my brain about two-thirds of the time), and they probably don't save much time overall (if any)[6]. But I get a small spark of joy each time I do use one and, over the course of a day, that adds up quite a bit. There's something just inherently satisfying to making small efficiencies in a workflow; it makes me feel like I'm somehow beating the system, man ✌.
And I'm only just scratching the surface of what aliases can do. Because you can run them as full-blown shell commands, you can use them to integrate Git with other tools and services. You can hyper-optimise common functionality with your own abbreviations, such as reducing git commit
to just git c
(or even gc
if you want to fiddle with Bash configs as well). Or chain extremely lengthy, repetitive tasks into single commands.
Whatever you end up doing with them, I hope this article has helped you (and, likely, future me 👋) see some of the potential that aliases have.