I get the impression that Git is still quite misunderstood, and newcomers to Git (myself included) struggle to pick up which commands are actually useful and which ones were just designed by Linus to invoke fear, uncertainty and doubt.
Over the last few years I've come up with (and stolen) a few Git command aliases that make my day-to-day usage of Git on the command line an intuitive/fluent experience.
If you've been using Git for a bit, but don't really grok it, I'd recommend watching Knowledge is Power: Getting out of trouble by understanding Git if you haven't already.
TL;DR
Here's my Git alias list from ~/.gitconfig
.
[alias]
co = checkout
cm = checkout master
cb = checkout -b
st = status
staged = diff --staged
unadd = reset HEAD
save = commit -m
amend = commit --amend
discard = checkout --
fp = !git fetch -a --tags && git pull
please = push --force-with-lease
graph = log --oneline --all --graph --decorate
oneline = log --oneline -n 20
hist = log -n 20 --date=iso --pretty=format:'%C(yellow)%h%Creset %cd %Cblue%an <%ae>%Creset %s'
refresh = "!f() { CBRANCH=$(git rev-parse --abbrev-ref HEAD); git cm && git fp && git co \"$CBRANCH\" && git rebase master; }; f"
put = "!f() { CBRANCH=$(git rev-parse --abbrev-ref HEAD); git push -u origin \"$CBRANCH\"; }; f"
Downloading a repository
git clone <repository url here>
We're literally cloning the entire contents of the remotely hosted repository.
In your terminal, switch into the directory that you want your repository downloaded into, then copy & paste the SSH or HTTPS url for the repository and run this command.
cd ~/Code
git clone git@github.com:facebook/react.git
cd react
Now you're in the directory for your local copy of the React project.
See git clone for more options.
Creating a branch
git checkout -b <your branch name here>
I always make changes on a feature branch, and we enforce branch naming conventions too, so my workflow looks like:
git checkout -b bugfix/PROJECTKEY-123-fix-all-the-things
However, checkout -b
is a lot of typing, so I use this alias in my ~/.gitconfig
file:
[alias]
cb = checkout -b
All further aliases in this blog post can be added below the [alias]
section of your ~/.gitconfig
file.
So I can type:
git cb feature/PROJECTKEY-456-awesome-new-sauce
See git checkout for more options.
Checking repository status
git status
I use this A LOT. This command will tell you useful information about the files in your local copy of the repository, like which files are modified, which files are staged ready to commit, etc.
It also prints out some useful commands as hints for what you might want to do next.
Obviously, status
is a lot of typing, so I use this alias:
st = status
So I can type:
git st
See git status for more options.
Switching branch
git checkout <branch name here>
If you have no unstaged changes (see below) in your directory, you can just 'checkout' the branch you're interested in.
I use these aliases:
co = checkout
cm = checkout master
So that I type:
git co bugfix/FOO-123-everything-is-on-fire
# now I'm on that fire related branch
git cm
# now I'm on the master branch
See git checkout for more options.
Stage changes ready for a commit
git add -p
This command allows you to pick and chose which changes you've made should be staged for a commit. It will show you a diff of each partial change and ask if you want to stage that change or not.
Press y for YES and n for NO or q for QUIT when it asks you if you want to Stage this hunk. If you just press enter it gives you a whole load more info about each response option.
I don't have an alias for this.
I usually run git st
(see above) after I've run git add -p
to sanity check I've staged the changes I think I have!
I also use git staged
(below) to complete my sanity check.
See git add for more options.
Reviewing staged changes before committing
git diff --staged
This shows a diff of all the changes I've staged for the commit.
I use this alias:
staged = diff --staged
So that I can type:
git staged
See git diff for more options.
Un-staging changes
git reset HEAD -p
I often stage a change to a file that I don't actually want to commit. I have fat fingers.
This handy command does the exact opposite to the git add -p
command listed above, it asks you bit by bit whether you
want to remove a change from staging.
I use this alias, because its like the synonym for git add
:
unadd = reset HEAD
And then type this command when I want to remove staged changes:
git unadd -p
NB - this does not remove those changes from disk, it just removes them from being staged for a commit.
See git reset for more options.
Making a commit
git commit -m "<your commit message here>"
Once I'm happy with the changes I've staged for a commit, this is how I make a commit.
In my pursuit of reduced typing I use this alias:
save = commit -m
So my commands look like:
git save "Updates..."
Unlike that example, I highly recommend you write good commit messages.
See git commit for more options.
Viewing commits
git log -n 10 --oneline
git log -n 10 --oneline -p
git log -n 10 --date=iso --pretty-format:'%C(yellow)%h%Creset %cd %Cblue%an <%ae>%Creset %s'
git log --oneline --all --graph --decorate
I use a combination of those commands or their respective aliases that I've set up in order to see what a mess I've made of my Git repository before pushing changes to a remote Git server.
The first command there is fairly simple, one-commit-per-line output containing just the hash and the commit message.
The second command shows the diff that the commit introduces.
The third command shows the author and commit timestamp as well as the hash and commit message.
The fourth command shows a pretty graph view of the repository and how the branches interact.
I use these aliases, in this order of frequency:
hist = log -n 10 --date=iso --pretty=format:'%C(yellow)%h%Creset %cd %Cblue%an <%ae>%Creset %s'
oneline = log --oneline -n 10
graph = log --oneline --all --graph --decorate
See git log for more options.
Amending commits
git commit --amend
When I'm not writing perfect code, I find a bug or typo or something after I've make a commit. This command allows me to stage a new change and then add that to the previous commit.
(This commit actually replaces the previous commit with a new one that is a merge of the new and previous changes.)
I use this alias:
amend = commit --amend
See git commit for more options.
Deleting changes I don't want
git checkout -- <file path here>
Once I've committed my changes, if there are edits I've made to files that I really don't need any more, I run this command to remove those changes from disk. This saves me opening each altered file and doing CTRL-Z or manually undoing those un-needed changes.
I use this alias:
discard = checkout --
Which can be used like this:
git discard src/main/java/com/acme/foo/bar/utils/DefaultServiceInstanceFactoryManagerBuilderImpl.java
See git checkout for more options.
Pushing commits to the remote Git server
New branches
git push -u origin <your branch name here>
This tells Git where on the remote Git server (referenced by the word 'origin' which is just the default name for the original remote server you cloned the repository from) to put your branch. It is possible to have a branch called one thing locally and another thing on the remote server.
I use this alias:
put = "!f() { CBRANCH=$(git rev-parse --abbrev-ref HEAD); git push -u origin \"$CBRANCH\"; }; f"
So that saves me copy and pasting the branch name.
Existing branches
git push
If your branch existing on the remote server already, just use this. No alias required.
Rebased branch or a branch with amended commits
git push --force-with-lease
If you've rebased (see below) or amended any commits that you've already pushed to a remote Git server, this tells Git to override whatever is already on the remote server.
I use this alias:
please = push --force-with-lease
See git push for more options.
Fetching changes from the remote Git server
git fetch -a --tags
git pull
The first command there just asks the remote server for the most up-to-date metadata about the repository e.g. which commits are on which branches, what branches and tags exist.
The second command there downloads the changes from the remote server that correspond to the branch you're currently on.
I use this alias:
fp = !git fetch -a --tags && git pull
See git fetch and git pull for more options.
Rebasing from master
If someone else has already checked out your branch or has made changes to it as well, DO NOT rebase the branch or amend commits because then you will have different and incompatible versions of the same branch which will require one of you to delete your local branch completely.
This requires a few steps:
git checkout master
git fetch -a --tags
git pull
git checkout -
git rebase master
Often, you'll be working on a branch and changes will happen on the master branch whilst your working. You have two options on how to reconcile those changes with the changes you have made:
- Merge master into your branch
- Rebase your branch off master
We typically prefer rebasing for a couple of reasons:
- it doesn't introduce lots of additional merge commits
- it avoids some edge-cases where commits can get lost
- it makes the Git history a bit more clear, even if it no longer reflects how/when the work was actually done
You can think of rebasing as taking some commits and re-applying the changes that those commits hold elsewhere, so that new commits get generated that look very similar to the original ones.
Whilst rebasing you may need to resolve conflicts in the same way that you would when merging.
I use this alias which uses a few of the other aliases too:
refresh = "!f() { CBRANCH=$(git rev-parse --abbrev-ref HEAD); git cm && git fp && git co \"$CBRANCH\" && git rebase master; }; f"
That alias will obtain the current branch name, switch to master, fetch the latest remote changes, download them for the master branch, switch back to the branch I was on and then rebase the branch off of the up-to-date master branch.
Summary
My typical workflow looks something like this:
git cm # Start from the master branch
git fp # Get the latest changes
git cb bugfix/PROJ-123-ui-is-broken # New branch please
# Edit files here
git st # What have I changed?
git add -p # Stage some changes
git staged # What have I staged?
git unadd -p # Oops, I shouldn't have staged that!
git st # What's the latest state of things?
git add -p # Stage some other change
git diff # What have I modified that I haven't staged?
git staged # Is this everything I need for this feature?
git st # Does this look about right?
git save "UI is not broken" # Make a commit
# Edit the test files I forgot about
git add -p # Stage changes to the tests, ready for commit
git amend # Pretend I fixed the tests as part of the original commit
git discard # Discard other changes I made but don't need
git put # Push the new branch to the remote server
git refresh # Rebase off and updated master branch
git please # Overwrite the branch contents I pushed a moment ago
And then I create a Pull Request in Bitbucket Server, ready for review!
- 09 Oct 2018 » A strange bug on AWS Lambda
- 17 Jan 2018 » How to run Karma tests in browsers in Docker
- 07 Dec 2017 » Switching from Javascript to Typescript
- 30 Oct 2017 » Fun with React event handlers
- 17 Jul 2017 » Switching from Groovy to Java
- 24 May 2017 » Useful Git Aliases
- 27 Mar 2017 » Practical Ratpack Promises
- 03 Nov 2016 » Custom Content in Forms for Confluence Connect
- 04 Oct 2016 » Checking user permissions from REST calls
- 30 Sep 2016 » Using the reflection API in Confluence
- 28 Sep 2016 » Creating a custom Confluence Blueprint
- 06 Sep 2016 » ReactJS in Forms for Confluence Connect
- 25 Apr 2016 » Migrating to ES6 in Atlassian Add-ons
- 17 Mar 2016 » All kinds of things I learnt trying to performance test against Fisheye/Crucible
- 24 Dec 2015 » Adaptavist’s Holiday Gift of Atlassian Deployment Automation
- 17 Dec 2015 » Getting a Custom Field value safely
- 07 Dec 2015 » Putting Google Analytics to work with plugins for Confluence
- 02 Dec 2015 » Devoxx Voting, A retrospective
- 25 Nov 2015 » Some things I've learnt about SingleSelect
- 15 Oct 2015 » Using SOY for JIRA actions
- 26 Sep 2015 » Object Reflection in Groovy
- 22 Sep 2015 » Introducing Adaptavist Labs