Mastering Git Log

on git

I have been using git for a number of years now and have accumulated some awesome git aliases. In this blog post I want to share some of my favourite git log aliases. The log command is probably my most used command, it lets me view the latest changes, compare changes between branches, view just fetches changes and examine changes other team members made.

For those of you that don't know, Git is a free and open-source distributed version control system. The git log command is used to display code changes in chronological order.

Basic Log Commands

Running the usual git log will give you a list of all commits in chronological order. Use the usual vi bindings --gg, G, Ctrl-D, Ctrl-U-- to navigate swiftly around the log.

git log

commit b9326fa37b261dcfa92827e32ad1b0739b1a1005
Merge: a022731 ba40fbd
Author: Author1 <author1@hotmail.com>
Date:   Wed Jun 3 20:15:03 2015 +0100

    Merge pull request #1501 from ...

    Did something ...

commit ba40fbd68c1e7c3b497a03984b912f01431d7d58
Author: Author2 <author2@yandex.com>
Date:   Sun May 31 19:32:52 2015 -0400

    Restored some bits that were overwritten.

This looks ok but I almost never read the log in this format, it's takes up far too much screen with useless data. So I use the --oneline flag to condense each commit into one clean line.

git log --oneline

b9326fa Merge pull request #1501 from ...
ba40fbd Restored some bits that were overwritten.
a022731 Merge pull request #1505 from ...
7b17c1b type ...

This is my first alias l = git log --oneline.

View the latest changes with the -p flag--it stands for patch. This will display all commits with patch information appended to the bottom of each one. This is immensely useful for viewing the changes each commit introduced.

I use this all the time after a git pull.

git log -p

commit 923c4ec0e1756346bee5bea48392e16156692313
Author: Ben D'Angelo <ben@bendangelo.me>
Date:   Fri May 29 17:46:16 2015 -0400

    Updated gems

diff --git a/Gemfile.lock b/Gemfile.lock
index 3b1def6..ec90fea 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -65,7 +65,7 @@ GEM
       rb-fsevent (>= 0.9.3)
       rb-inotify (>= 0.9)
     mercenary (0.3.5)
-    mini_magick (4.2.4)
+    mini_magick (4.2.7)
     multi_json (1.11.0)
     parslet (1.5.0)
       blankslate (~> 2.0)

Most people don't know this, you can display the log of other branches by passing in a branch name or a commit hash. Remember a branch name essentially is a pointer to a hash.

git log dev
git log origin/dev

There are times when I want to view the changes a specific fix introduced but it happened maybe a week ago and is not easily accessible by scrolling. With the --grep flag and the issue number you can find the log.

This can even be coupled with the -p flag to display the patch.

git log --grep=#1234 -p

commit dcd60d87741eccc0323b3de3749c6da1042ad59d
Author: Ben D'Angelo <ben@bendangelo.me>
Date:   Sun May 31 13:04:44 2015 -0400

    Fixes #1234 Something
...

I made the alias lgr stands for log grep. I always prefix my log aliases with an l to show it uses the log command.

lgr = !sh -c 'git l --grep=$1 --max-count=20' -

I have personally never used this command but am itching to try it out. It lets you see changes of a function over time--yeah not kidding. There are two version of this command. One lets you specify a line range--which works well for scripting languages-- and the other a regex. I found the regex works well in C++.

git log -L :main:main.cpp

In both cases the file path has to be specified. You'll be amazed at what you learn by stepping through a functions history.

Comparing Commits in Branches

Comparing branches is something I do on a daily basis. Did this branch get merged into master? How many commits is development ahead? Are common questions.

Say your team has been pushing commits to the dev branch for some time. You know master is quite behind dev but you don't know by how much. So you want to log of all the commits in dev that master does not have.

Of course the easiest way is to compare the two branches.

git log dev --not master --oneline
# or
git log master..dev --oneline

923c4ec Updated gems
8afb047 Updated font sizing.

The second comparison might be confusing but it is quite simple.

git log master..dev

This means log all commits reachable by dev that are not reachable by master. You can also think of it as, ignore commits in master and display commits only in dev.

So, you now know that dev has two commits that master does not have but, master could have commits that dev does not have. What if you want to see commits that are not reachable by both dev and master? This brings us to the triple dot syntax.

git log --left-right master...dev --oneline

> 923c4ec Updated gems
> 8afb047 Updated font sizing.
< c9035a3 Hot fix

First thing you should look at is the arrow, this shows which branch the commit belongs to. The > is pointing to dev and < is pointing to master, the arrow direction is relative to construction of the command. Reversing the comparison will reverse the arrows. Hence the flag name--left-right-- master is on the left, dev is on the right.

git log --left-right dev...master --oneline

< 923c4ec Updated gems
< 8afb047 Updated font sizing.
> c9035a3 Hot fix

So what does this tell you? There are two commits inside dev that master doesn't have and there is one commit--a hotfix--inside master which dev doesn't have. And you did this all commandline--what a time saver.

Remember you can even add the -p flag to view the changes.

Seriously, remember these two syntaxes, I use these on a daily basis.

Moving on, the log command can even be used to preview merges. Say you been working on a feature branch for some time. You can run this command to view the commits that have to be merged in.

git log --no-merges feature12..master

You can even preview incoming commits to your branch.

git fetch
git log master..origin/master --oneline

923c4ec Added something

# looks good, merge in
git merge origin/master

A git fetch fetches changes and puts them in the remote version of your branch, in this case origin/master. You can then compare the remote branch--origin/master-- with your local branch--master.

Or view the commits that just went in from a git pull or a git merge.

git log ORIG_HEAD.. --oneline

923c4ec Added something

ORIG_HEAD is the previous state of HEAD before it was changed by a command like git merge or git rebase. You might notice there is no argument on the right side--ORIG_HEAD..-- the right side will default to HEAD.

Understanding the Graph

I've shown most of the log commands that I use in my daily workflow, except one--the graph flag. It gives you such a nice overview of the branches history and the state of other branches as well.

git log --graph --oneline

* b7de477 Added feature 2
* f737f26 Added feature 1
*   b9326fa Merge pull request #1501
|\  
| * ba40fbd Cleaned up something
| * 486bd4a Fix 1
| * 63bf944 Fix 2
| * 23b2f3b Fix 3
| * 297802f Fix 4
| * 42c303c Fix 5
| * 3817be4 Fix 6
* |   a022731 Merge pull request #1505
|\ \  
| * | 7b17c1b Typo

This graph tells us not only the recent commits in the branch but also nicely displays the merges that took place. Two arrows pointing to an asterisk means it was a merge commit--it brought two separate commits together through a new commit, ie b9326fa.

So how is this useful? Being able to see merge commits and the commits they integrate allows for better decision making. For example I wouldn't want to interactively rebase ba40fbd because it'll lead to confusing conflicts, a better approach would be to do a soft reset.

My Aliases

Here is a big list of my favourite log aliases, do to them as you wish.

This aliases improves the graph functionality by adding relative dates, name of committer and branch names.

lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)[%an]%Creset' --abbrev-commit --date=relative --decorate

This is my default log commit, each commit is on one line and displays committer, relative date and branch names.

l = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cgreen\\ (%cr)\\ %Cblue\\ [%cn]" --decorate

923c4ec (HEAD, origin/dev, dev) Updated gems (5 days ago)  [Ben D'Angelo]
8afb047 Updated font sizing. (6 days ago)  [Ben D'Angelo]

This is a more specialized log, it displays commits on single lines and displays all file changes with stats on line removals and additions. I use this to find which file had the most work done on it.

ll = log --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cblue\\ [%cn]" --decorate --numstat

923c4ec (HEAD, origin/dev, dev) Updated gems [Ben D'Angelo]
1       1       Gemfile.lock

8afb047 Updated font sizing. [Ben D'Angelo]
1       11      _assets/stylesheets/highlight.css.sass
11      1       _assets/stylesheets/text.css
1       1       _assets/stylesheets/vendor/skeleton.scss
1       1       _config.yml

This alias has to be one of my favourites, it displays all commits on a single line and displays file changes on their own line. I use this to quickly find which files were modified to look for potential bugs or to pick up someone else's work.

ls = log --pretty=format:\"%C(yellow)%h %Cgreen(%cr)%Creset %s %C(bold blue)[%cn]%Creset\" --name-status

923c4ec (5 days ago) Updated gems [Ben D'Angelo]
M       Gemfile.lock

8afb047 (6 days ago) Updated font sizing. [Ben D'Angelo]
M       _assets/stylesheets/highlight.css.sass
M       _assets/stylesheets/text.css
M       _assets/stylesheets/vendor/skeleton.scss
M       _config.yml

This is used to see incoming commits after a git fetch. The @{u} is shorthand for grabbing the upstream of the branch. It's equal to origin/{branch_name}.

lf = log ..@{u} --stat

This displays all the commits added today.

lt = log --stat --since='1 Day Ago' --graph --pretty=oneline --abbrev-commit --date=relative

Thanks for reading. You can checkout all my configurations on my dotfiles repository.