Although Git is a really powerful distributed source control system, it is far from intuitive. Getting the most out of it often means learning obscure Terminal commands and option flags. These tips distill some of the weird incantations that have helped me to improve my Git workflow over the years.
If you don’t know what Git is or want to understand it better, I would highly recommend checking out Alice Bartlett’s excellent Git for humans slides.
Warning: Terminal required
These tips are aimed at folks who work with Git using the command line. That said I would like to think that those who use a graphical interaface may also find them useful.
1. Run the latest version of Git
This one seems almost too obvious to mention. That said, Git is constantly being updated with new features, bug fixes and performance enhancements and it pays to be running the latest version. At the very least, make sure you are running version 2 or above, as this introduced a number of important improvements and enhancements to the default behaviour. Download the latest Git from the Git website, or update it using your package manager of choice.
2. Git Completion in your Terminal
There’s a great bash script that adds tab-completion to Git commands, options, tags, and branch. I’m always creating branches with long names, and being able to tab-complete them is very handy. There’s versions for Z shell and tsch available too.
3. Interactively stage changes with --patch
Keeping a clean and atomic commit history is a good habit to get into. It makes reviewing code simpler, rolling back changes less painful, and writing focused, useful commit messages easier. But sometimes our fingers get ahead of us and we end up with a bunch of unrelated changes in a file that really should be added as separate commits.
In these situations, the
git add --patch command (or
git add -p for
short) is incredibly useful. It moves through “hunks” of changes one by one,
allowing you to make a decision as to whether to stage each change in turn:
y will stage the displayed change; hitting
n will skip it, leaving
it unstaged ready for a future commit.
There are additional options you can choose for each hunk and hitting
show them all. For example, the
s option can be used to split the current
hunk into even smaller ones.
4. Amend the most recent commit with --amend
If you ever find yourself having to amend the most recent commit, then you
git commit --amend useful. Rather than creating a new commit, it
merges the staged changes into the previous commit. It opens your editor with
the previous commit message so you can also edit it.
I often find that I’m amending the same commit as I refactor my code and don’t
need to change the commit message. In these situations, there’s an additional
flag that is useful:
-C for short, which lets you
specify a commit SHA for the message you want to use. So the following command
will amend the previous commit in one step, keep the same commit message:
$ git commit --amend -C HEAD
I do this so often that I’ve created a git alias (see tip 7) for it called
$ git config --global alias.amend 'commit --amend -C HEAD'
5. Rewriting history with interactive rebase
This is possibly my favourite git trick and is a really powerful way to keep a clean git history. Say you’ve spotted a typo or found a bug that was introduced in a recent commit on your local branch and you want to fix it.
The problem is, you’ve made additional commits since, so you can’t use
commit --amend as the commit you want to change isn’t the most recent one.
Now you could simply create a new commit that fixes the issue and move on. But that wouldn’t make for a very clean git history.
As long as the commits are only on your local branch and haven’t been remotely
shared with others, it makes sense to rewrite these commits so that the typo
never existed. To do this more complex commit rewriting, you can use
rebase --interactive, or
git rebase -i for short.
The interactive rebase command lets you to interactively rewrite your commits,
reordering and merging them as you see fit. To use it, call the command with
the revision you want to rebase to. For example, if you wanted to change the
last three commits, you can call
git rebase -i HEAD~3. I often
find it easiest to simply rebase as far back as the branch I branched from,
which is usually master:
git rebase -i master. Running this command will
give you a list of the commits in your editor that looks something like this:
To rewrite this commit history, we can edit the file, reordering the commits
and changing the commands as necessary. So in this case, we can move the line
representing most recent commit with the typo fix so that it sits below the
first commit, then change the command to
fixup, telling Git to squash the
typo commit into the previous one. We end up with a file that looks like this:
Saving and closing the file will trigger the rebase command to rewrite history, leaving us with a clean two-commit history minus the “fix typo” commit. You can learn more about rewriting history in the Rewriting History section of the Pro Git book.
6. Rewriting history redux: --fixup & --autosquash
Fixing a previous commit in this way is something I find myself doing often.
Turns out there is an even easier way to perform this kind of commit
rewriting using the
Firstly, when make the commit that fixes the issue, include the
option and pass in the SHA reference for the commit you want to fix against:
$ git commit --fixup f682d4c [git-demo 7643c3b] fixup! Add a default page meta title 1 file changed, 1 insertion(+), 1 deletion(-)
This creates a special commit with a commit message prefixed with “fixup!”,
which gives Git a clue that this is a fixup commit. Then when we call rebase,
we call it with the
$ git rebase -i --autosquash HEAD~3
This time when the editor is opened, the commits will already be reordered and
the fixup commit will already be marked as
fixup. You can simply save
the file and close the editor without having to make any changes and the
rebase will complete as before.
There’s even a way to configure your Git so that it will
default if it sees the “fixup!” prefix when you interactively rebase:
$ git config --global rebase.autosquash true
7. My favourite Git aliases
Git aliases are a great way to make helpful command and option combinations easier to type and remember. Here are some I find useful:
$ git config --global alias.co checkout
A simple one that saves you a few key strokes; now you can type
$ git config --global alias.cm 'commit --verbose'
--verbose option makes Git include the diff of the current set of
changes at the bottom of the commit message template, which is a useful way to
sanity check the changes and also helps me write better commit messages as I
can see the changes right there in my editor as I write.
$ git config --global alias.amend 'commit --amend -C HEAD'
As mentioned in Tip 5, this lets you update the previous commit with the currently staged changes in a single command.
$ git config --global alias.pretty 'log --pretty=oneline'
git pretty outputs a simpler one-line-per-commit log output.
$ git config --global alias.upstream \ '!git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)'
This is probably my favourite – it means you can push your local branch to the
remote origin and set the remote as upstream with
8. Get yourself a global Git ignore file
A helpful thing to do is configure yourself a global
.gitignore file. You
can use this file to list ignore rules that will apply across all your Git
repositories. This is a good way to globally ignore files that are specific
only to you and your system, for example Vim swap files, Pow
configuration files or Guard files. That way
you can avoid littering your application’s
.gitignore file with entries that
might not be relevant to other developers or to the application itself.
.gitigore file in your home directory and register it with the
git config --global core.excludesfile ~/.gitignore
9. Configure your colours
Whilst Git colours some of its outputs by default, it’s possible to configure
the colours to suite your tastes. I like to tweak the output of the
command to give a more intuitive colour output by adding the following to
[color "status"] changed = yellow added = green untracked = cyan
Frustratingly there isn’t currently a way to distinguished between new untracked files from deleted ones in the colour options. If there was then I would most definitely configure deleted files to be coloured red.
10. Read flippin’ the manual
I’ve only really scratched the surface with these tips and I can thoroughly recommend browsing through the official documentation website. Not only is it a complete reference of Git, it also includes discussions and additional information that you won’t find in the man pages. You’ll also find the entirety of the Pro Git book by Scott Chacon and Ben Straub on the website, which is a fantastic way to level up your Git skills.
Bonus tip: jumping between branches
A quick bonus tip: in the same way
cd - will take you to the previous
directory you were in,
git co - will checkout the previous branch you were
on. I find this useful for jumping back and forth between two branches.
Bonus tip 2: Using reflog to recover lost commits
That’s all folks
Hopefully I’ve shared at least one thing that was new to you and will help you improve your Git workflow. I’m always looking to get better at Git. If you have anything interesting that you’d like to share, hit me up on Twitter.