Chapter 9. Viewing History

The primary command for examining the commit history of your repository is git log. The documentation for this command, git-log(1), is about 30 pages long, and we shall not repeat all that detail here. We will cover its main modes of operation, as well as a selection of the most common and useful options and techniques.

Command Format

The format of the command is:

$ git log [options] [commits] [[--] path ...]

The commits parameter specifies the commits Git should list for you, using the notation discussed in Naming Sets of Commits; for example:

git log
The default for commits is HEAD, so this lists the commits reachable from the current HEAD commit. This is generally a branch, but may not be if you have checked out an arbitrary commit and are in “detached HEAD” mode (see Branches).
git log topic
Lists the commits in the topic branch, even if you are on another branch.
git log alvin simon
Lists all commits on either of the branches alvin or simon.
git log alvin..simon
Lists all commits in simon that are not in alvin; this is often those commits on simon that have occurred since you last merged it with alvin.

Here, the names topic, alvin, and simon could also be tags, or expressions such as master~3 or 780ae563. You can also use patterns to indicate sets of refs instead of listing them all individually, with these options:

--{branches,tags,remotes}[=pattern]

Behave as if all branches, tags, or remotes were given on the command line as the commits argument, optionally limiting the refs matched with a glob pattern. Match refs directly with --glob=pattern; a leading refs/ is implied if not given. A trailing /* is implied if the pattern does not contain * or ?.

Synonyms: --all = --glob='*'

Thus, git log --branches='foo*' lists all branches whose names begin with “foo”: refs/heads/foobar, refs/heads/foodie, etc.

The optional list of file pathnames or glob patterns further limits the commits listed, to those that touched matching paths by either adding, deleting, or modifying the named files. Use the -- separator in case there’s some ambiguity with the preceding options or commit names.

Output Formats

The default output format is fairly detailed, including the author timestamp and commit message:

$ git log
commit 86815742
Author: Richard E. Silverman <res@oreilly.com>
Date:   Tue Sep 18 14:36:00 2012 -0700

   reduce annoyance

   Fix this software so that it is slightly less
   annoying than it was before, though less annoyance
   would still be good.

commit 72e4d8e8
Merge: 5ac81f5f af771c39
Author: Witch King of Angmar <nazgul@barad-dur.org>
Date:   Tue Sep 18 14:35:54 2012 -0700

   Merge branch 'hobbits'

   Some scholars are of the opinion that "nazgûl" is
   exclusively plural, so that one does not speak of
   "a Nazgûl." Of course, it's best not to speak of
   them at all, regardless.

git log --oneline gives more compact output, including just the ID and message subject for each commit:

$ git log --oneline
86815742 reduce annoyance
72e4d8e8 Merge branch 'hobbits'
...

Note that this is one reason to format your commit messages in the conventional way, with a subject line: it makes this sort of summary readable, as opposed to just showing the beginning of a sentence trailing off to the right (see Commit Messages).

The --oneline option is actually short for --format=oneline --abbrev-commit, and the default is --format=medium. There are a number of predefined formats; the following table shows the full list, along with some commit elements they contain (they all show the commit ID).

format author author date committer commit date subject message

oneline

✓

short

✓

✓

medium

✓

✓

✓

✓

full

✓

✓

✓

✓

fuller

✓

✓

✓

✓

✓

✓

email

✓

✓

✓

✓

raw

✓

✓

✓

✓

✓

✓

The email format produces output in traditional Unix “mbox” style, with one email message per commit (and here’s yet another reason for the standard commit message format: commit subject lines become the email subject headers of each message). You can use it to prepare a set of email messages describing some commits, which can be easily read, manipulated, and sent with most Unix-based mail programs.

The raw format shows all the information in the commit in full detail and uninterpreted format, including the full 40-digit IDs of the parent commits and content tree object.

Defining Your Own Formats

You can also customize the display format, with git log --format ="format:string". You can give a format using a set of substitutions similar in usage to the printf function in the C standard library (and widely copied in other languages). The full set of substitutions is in the PRETTY FORMATS section of git-log(1); here are some examples:

# committer, commit ID, relative timestamp, subject
$ git log --date=relative --format='%an, %h, %ar, "%s"
Richard E. Silverman, 86815742, 6 hours ago, "reduce …
Witch King of Angmar, 72e4d8e8, 7 hours ago, "Merge b…
...

This example uses color and underlining to distinguish the different fields on the line. The colors may not show here depending on the medium in which you’re reading this text, but give it a try (it assumes your terminal is set up to handle color, of course):

# commit ID, subject, committer, date
$ git log --date=short --format=\
"%C(blue)%h %C(reset)%s %C(magenta)%aN %C(green ul)\
%ad%C(reset)"
86815742 reduce annoyance Richard E. Silverman 2012-1…
72e4d8e8 Merge branch 'hobbits' Witch King of Angmar …
...

Make sure to use %Creset at the end of such a format; otherwise, if the output is going directly to a terminal rather than through a pager, you’ll leave the terminal stuck in whatever color or mode you last used. You can add a format you use frequently to your configuration in ~/.gitconfig or elsewhere:

[pretty]
 colorful = "%C(blue)%h %C(reset)%s %C(magenta)%aN
 %C(green ul)%ad%C(reset)"

and then refer to it by name:

$ git log --format=colorful

Notes

  • To use a double quote in a format string given in a Git configuration file, escape it with backslash.
  • --pretty is a synonym for --format (from the term “pretty printing”).
  • A format given as format:template places a newline between each log item; there is no newline after the final item. Use tformat:template instead to get a final newline (“t” for “terminator”).
  • If the --format argument contains a percent sign (%), then Git assumes tformat:, as in the previous example.
  • You can change the default format for git log by setting format.pretty; this affects git show as well.

Limiting Commits to Be Shown

There are many options for further limiting the commits to be shown beyond the commits expression given as an argument to git log; here is a selection of common ones:

-n (-n n, --max-count=n)
Only show the first n commits.
--skip=n
Skip n leading commits before starting output.
--{before,after}=date
Show commits made before or after a specific date (synonyms: --{until,since}). Note that this refers to the commit timestamp; there is no analogous simple way to refer to the author timestamp.
--{author,committer}=regexp
Show only commits whose author or committer header (name <email>) matches the given regular expression. Multiple instances of a given constraint are combined with logical “or,” but (as usual) use of both types counts as logical “and”; thus, git log --author=Richard --author=Booboo --committer=Felix shows commits made by Felix, whose author is either Richard or Booboo.
--grep=regexp
Show only commits whose log messages match the given regular expression. Multiple instances are combined with logical “or”; change this to “and” with --all-match. Use --grep-reflog to match reflog entries instead, when using git log -g to examine the reflog instead of the commit graph (--grep still matches the commit message, even though the commits examined are found via the reflog; it does not match the reflog comment instead).
--{min,max}-parents=n

Show only commits with a matching number of parent commits. Synonyms:

  • --merges = --min-parents=2
  • --no-merges = --max-parents=1
--first-parent
Follow only the first parent of a merge commit, rather than all of them. This can give a more useful history of a topic branch into which you periodically merge from a more central branch, keeping it up to date with the main development. This shows only the activity on the topic branch itself, rather than commits brought in from the main branch by merging.
--diff-filter=[A|C|D|M|R|T]

Show commits containing files with any of the statuses given by the following one-letter codes. The “copied” and “renamed” statuses will only be effective if copy and rename detection are enabled as described:

  • A: Added
  • C: Copied
  • D: Deleted
  • M: Modified
  • R: Renamed
  • T: Type change (e.g., a file replaced by a symbolic link)

Regular Expressions

A number of options affect the interpretation of regular expressions:

-i (--regexp-ignore-case)
Ignore case differences (e.g., hello and HELLO will both match “Hello”).
-E (--extended-regexp)
Use extended regular expressions; the default type is basic.
-F (--fixed-strings)
Consider the limiting patterns as literal strings to be matched; that is, don’t interpret them as regular expressions at all.
--perl-regexp
Use Perl-style regular expressions. This will not be available if Git is not built with the --with-libpcre option, which is not on by default.

Reflog

git log --walk-reflogs (-g) shows a completely different log: the reflog. This is a log of actions you’ve taken in your repository, and it can be very helpful in recovering from mistakes; see Double Oops!.

Decoration

git log --decorate={no,short,full} shows refs pointing to the listed commits:

$ git log --decorate
commit feca033e (HEAD, master)
Author: Richard E. Silverman <res@oreilly.com>
Date:   Thu Dec 20 00:38:51 2012 -0500

    demo

commit 6faac5df (u/master, origin/master, origin/HEAD)
Author: Richard E. Silverman <res@oreilly.com>
Date:   Mon Dec 3 03:18:43 2012 -0500

    working on ch09

commit 110dac65
Author: Richard E. Silverman <res@oreilly.com>
Date:   Mon Dec 3 03:18:09 2012 -0500

    minor editing on earlier chapters

Note the inclusion in parentheses of various local and remote branch names. The default is short; full uses the full ref name (e.g., refs/heads/master instead of just master).

Date Style

git log --date= {local,relative,default,iso,rfc,short,raw}
This option affects how dates are rendered in formatted log output, as long as the format has not explicitly given a date style. For example, using this format:
[pretty]
    compact = %h %ad, \"%s\"

$ git log -1 --format=compact --date=local
6faac5df Mon Dec 3 03:18:43 2012, "working on ch09"

$ git log -1 --format=compact --date=relative
6faac5df 2 weeks ago, "working on ch09"

$ git log -1 --format=compact --date=iso
6faac5df 2012-12-03 03:18:43 -0500, "working on ch09"

$ git log -1 --format=compact --date=rfc
6faac5df Mon, 3 Dec 2012 03:18:43 -0500, "working on …

$ git log -1 --format=compact --date=short
6faac5df 2012-12-03, "working on ch09"

$ git log -1 --format=compact --date=raw
6faac5df 1354522723 -0500, "working on ch09"
default
Original time zone of author or committer
local
Local time zone
relative
How far in the past
iso
ISO 8601 format
rfc
RFC 2822 format (as found in email)
raw
Internal Git format

Listing Changed Files

git log --name-status summarizes which files changed in a given commit (relative to its predecessor), and the nature of the changes:

$ git log --name-status
commit bc0ba0f7
Author: Richard E. Silverman <res@oreilly.com>
Date:   Wed Dec 19 23:31:49 2012 -0500

    fix directory; misc diffs with older ghc

M       keepmeta.hs

commit f6a96775
Author: Richard E. Silverman <res@oreilly.com>
Date:   Wed Dec 19 21:48:26 2012 -0500

    rename keepmeta

D       .gitfoo
A       Makefile
D       commit.hs
A       keepmeta.hs

The single-letter codes to the left of the filenames, indicating the change status of that file in the commit, are the same as listed for the --diff-filter option for added, deleted, modified, and so on.

git log --name-only lists only filenames without the status codes, and --stat gives an ASCII-art graph (“diffstat”) representing the amount and kind of change in each file:

$ git log --stat
commit ddcd718b
Author: Richard E. Silverman <res@oreilly.com>
Date:   Sun Dec 9 23:47:50 2012 -0500

   add KDC default referral feature

   Two new realm configuration parameters:

   * default_referral_realm (string, none)
   * cross_realm_default_referral (boolean, false)

   If default_referral_realm is set, then the KDC
   will issue referrals to the specified realm for
   TGS requests otherwise qualifying for a referral
   but lacking a static realm mapping, as long as the
   presented TGT is not cross-realm (setting
   cross_realm_default_referral omits that check).

src/config-files/kdc.conf.M  | 12 +
src/include/adm.h           |  4 +
src/include/k5-int.h        |  2 
src/kdc/do_tgs_req.c         | 52 +----------
src/kdc/extern.h            |  4 +
src/kdc/main.c              | 12 
src/lib/kadm5/admin.h        |  5 -
src/lib/kadm5/alt_prof.c    | 15 +
8 files changed, 95 insertions(+), 11 deletions(-)

git log --dirstat summarizes the amount of change in subdirectories (it can take a number of parameters controlling how the summarization is done):

$ git log --dirstat
commit 4dd1530f (tag: mit-krb5-1.10.3, origin/MIT)
Author: Richard E. Silverman <res@oreilly.com>
Date:   Mon Jan 9 15:03:23 2012 -0500

    import MIT Kerberos 1.10.3

  52.1% doc/
   6.0% src/lib/
  12.4% src/windows/identity/doc/
   3.7% src/windows/identity/ui/
   8.8% src/windows/identity/
   3.5% src/windows/leash/htmlhelp/
   3.4% src/windows/leash/
   9.5% src/

Showing and Following Renames or Copies

Ordinary git log does not show file renaming, because it takes longer to do this and often you’re not interested. To enable renaming detection, use --find-renames[=n] (-M[n]). The optional integer n is an index of similarity: consider a delete/add pair to be a rename if the before/after files are at least n% identical (the default is 100%):

$ git log --name-status
commit 4a933304 (HEAD, master)
Author: Richard E. Silverman <res@qoxp.net>
Date:   Thu Dec 20 01:08:14 2012 -0500

    Rename foo; wouldn’t bar be better?

D       foo
A       bar

$ git log --name-status -M
commit 4a933304 (HEAD, master)
Author: Richard E. Silverman <res@qoxp.net>
Date:   Thu Dec 20 01:08:14 2012 -0500

    Rename foo; wouldn’t bar be better?

R100    foo     bar

To have Git follow a file past a rename, use git log --follow; this only works when you give a single file to follow:

$ git log bar
commit 4a933304 (HEAD, master)
Author: Richard E. Silverman <res@oreilly.com>
Date:   Thu Dec 20 01:08:14 2012 -0500

    Rename foo; wouldn’t bar be better?

$ git log --follow bar
commit 4a933304 (HEAD, master)
Author: Richard E. Silverman <res@oreilly.com>
Date:   Thu Dec 20 01:08:14 2012 -0500

    Rename foo; wouldn’t bar be better?

commit 4e286d96
Author: Richard E. Silverman <res@oreilly.com>
Date:   Tue Dec 18 04:57:55 2012 -0500

    Add “foo” in its glorious fooness!

Detecting Copies

A “copied” file is a new path appearing in a commit with identical or similar contents to an existing one (one already in a prior commit). git log --find-copies[=n] (-C[n]) does the same for detecting copies as -M does for renames. -CC (or --find-copies-harder) will consider all files in a commit as potential sources of copying, while plain -C considers only files that changed in that commit.

Rewriting Names and Addresses: The “mailmap”

The same person’s name or email address as embedded in commits may vary in a single repository history, depending on settings she had at various times as she was working. Git has a facility to normalize these for display and collation, called the mailmap. A mailmap file may be named .mailmap at the top of the working tree, or have any name given by the mailmap.file configuration option, and has lines in any of the following formats:

Correct Name <user@foo.com>
This collates by address and rewrites names: entries of this form with the same address identify commits marked with those addresses as being by the same person, with the specified name replacing the names given in those commits.
<desired@email.address> <random@other.address>
This collates by address and rewrites addresses: entries of this form with the same desired address, but differing “random other” addresses, identify commits by the varying addresses as being by the same person, with the specified address replacing those appearing in the commits (but leaving the names alone).
Correct Name <desired@email.com> <random@other.org>
This collates by address and rewrites both name and address: entries of this form, with the same desired address but differing names and other addresses, identify those commits as being by the same person, with the specified name and desired address replacing those appearing in the commits.
Correct Name <desired@email.com> Other Name <random@other.org>
This collates by both name and address, and rewrites both as well: entries of this form with the same “correct name” and desired address identify commits marked with the given combinations of “other” name/address pairs as being by the same person, with the specified name and address replacing those appearing in the commits.

For example, this mailmap entry:

Richard E. Silverman <res@oreilly.com>

coalesces all commits marked with the address res@oreilly.com and presents my name consistently as “Richard E. Silverman,” even if some say “Richard Silverman” or “Rich S.” These entries:

Richard E. Silverman <res@qoxp.net> <res@oreilly.com>
Richard E. Silverman <res@qoxp.net> <slade@shore.net>
Richard E. Silverman <res@qoxp.net> <rs@wesleyan.edu>

identify commits marked with the three addresses appearing on the right, and rewrite both name and address to be “Richard E. Silverman” and res@qoxp.net.

The coalescing and rewriting features are used by the command git shortlog, which summarizes history using the commit subjects (or other format given by --format) and grouping by author. The rewriting feature alone is used by git log and git blame if the format specifies it. Particular escapes for committer and author info, given in the PRETTY FORMATS section of git-log(1), take the mailmap (if any) into account; for example, this version of the “compact” format defined earlier:

[pretty]
        compact = %aN (%h) %aD, \"%s\"

(note the capital N and D) shows the author name and address as rewritten by the mailmap.

Shortening Names

Another use for the mailmap is shortening names for compact display. Full names can be truncated and difficult to read in a short format, such as the common git log --oneline. You can maintain a mailmap rewriting full names to your organization’s computer account names, for example, which are typically shorter. You can then define log formats that use them as above; you could place these in the system-level Git configuration used by everyone (usually /etc/gitconfig), or have a smaller group explicitly include a shared file via the include.path variable. This mailmap:

res <res@example.com>
res <rsilverman@example.com>
john <jpreston@example.com>
john <john@example.com>

together with the second “compact” log format above causes the author name and addresses for Richard Silverman and John Preston to appear as “res” and “john” instead, also taking into account two different email address formats.

Searching for Changes: The “pickaxe”

The Git “pickaxe,” git log -S string, lists commits that changed the number of occurrences of string in at least one file. Note that this is slightly different from string appearing in the commit diff at all: if a commit removed one occurrence and added another one elsewhere, the pickaxe will not show it. Nonetheless, this is a useful method of looking for changes. For example, if you want to know when a particular feature was added, using this command with the name of a function or variable specific to the feature will turn it up, as the earliest commit that introduced that term. git log -G pattern does the same with a regular expression.

If you combine the pickaxe with a git log option that lists files, such as --name-status, Git shows only those files that triggered the listing (those in which the number of string or pattern occurrences changed). If you add --pickaxe-all, then Git shows all files touched by the listed commits. This allows you to see the entire changeset associated with any commit that matched the pattern you’re interested in.

Showing Diffs

git log -p shows the “patch” or “diff” associated with each commit (illustrating the actual changes made to the files, only for text files, naturally), after the usual commit information as indicated by the log format in use. Normally, no diff is shown for merge commits, however you can use these options:

-m
Shows each pairwise diff between the merge and its parents.
-c
Shows the differences with all parents simultaneously in a merged format (a generalization of the traditional “unified diff”), rather than serially as with -m, and only for files that were modified in all branches.
--cc
Implies -c and further simplifies the diff by showing only conflicts; change regions with only two variants of which the merge picked one unmodified are not shown.

Color

The option --color[={always,auto,never}] uses color to help distinguish difference regions; additions are in green and deletions in red. The default is never, --color means --color=always, and --color=auto means to use color when standard output is a terminal.

Word Diff

The option --word-diff[={plain,color,none}] shows word-level changes within lines, rather than entire changed lines. For example, this:

- I changed a word.
+ I altered a word.

becomes this:

I [-changed-]{+altered+} a word.

with --word-diff=plain. This is often more useful than line diffs if the content is English prose rather than software code. The color option uses color instead of the markers shown earlier to indicate the additions and deletions, again using green and red. It is possible to change the regular expression Git uses to determine word boundaries with --word-diff-regex; see git-log(1) for details.

Comparing Branches

Often we are interested in understanding the relationship between the content of two branches, particularly in how they have diverged. As discussed in Naming Sets of Commits, a basic tool for this is the symmetric difference A...B, which shows those commits in either branches A or B but not in both (i.e., those commits added to either branch since they last diverged). Sometimes this isn’t enough, though. For example, git cherry-pick creates a new commit based on an existing one, by reapplying the changes introduced by the original commit at a different place in the history. It is useful in situations where incorporating changes by merging is inconvenient or impossible due to repository organization. If a commit has been cherry-picked from one branch to another, then it will be included in their symmetric difference anyway, since they are distinct commits that just happen to represent the same changeset. git log --cherry-pick takes this into account by omitting commits that have identical diffs. Consider the commit graph in Figure 9-1, in which commit 2 was produced with git cherry-pick D on the other branch, and so it and D have the same changeset.

git log and cherry-picking
Figure 9-1. git log and cherry-picking

Assuming all the other commits have distinct changesets, we will see something like:

$ git log master...other
e5feb479 E
070e87e5 D
9b0e3dc5 C
6f70a016 3
0badfe94 2
15f47204 1

Whereas this omits the patch-equivalent commits, showing just content differences:

$ git log --cherry-pick master...other
e5feb479 E
9b0e3dc5 C
6f70a016 3
15f47204 1

The variation --cherry-mark will mark duplicate commits with an equal sign, instead of omitting them:

$ git log --cherry-mark master...other
+ e5feb479 E
= 070e87e5 D
+ 9b0e3dc5 C
+ 6f70a016 3
= 0badfe94 2
+ 15f47204 1

Displaying Sides

git log master..other (with just two dots) shows one side of this situation: those commits on other that are not on master. If you want cherry-pick detection, you have to consider both sides as before, but then you are no longer viewing just one side. You can recover this by adding --{left,right}-only:

$ git log master..other
6f70a016 3
0badfe94 2
15f47204 1

$ git log --cherry-pick --right-only master...other
6f70a016 3
15f47204 1

This shows commits on other that are not contained in master or patch-equivalent to another commit in their difference, in this case, omitting commit 2 since it is equivalent to D. And similar to --cherry-mark, the related option --left-right displays the side of a commit with the symbols < and >:

$ git log --cherry-mark --left-right master...other
< e5feb479 E
< 070e87e5 D
= 9b0e3dc5 C
> 6f70a016 3
= 0badfe94 2
> 15f47204 1

The simple option --cherry is a synonym for --right-only --cherry-mark --no-merges, so that this:

$ git log --cherry HEAD@{upstream}...

shows the commits on your side of the current branch (ignoring possible merges with other branches), marking those that duplicate changes made by distinct commits on the other side (probable cherry-picks, either literally or by another means such as applying patches via email with git format-patch and git am).

Showing Notes

git log --notes[=ref] includes any notes on a commit after its message; see git notes for how this command and Git notes work in general.

Commit Ordering

Normally, git log displays commits in reverse chronological order according to the committer (not author) timestamps. You can alter this in three ways:

  • --date-order shows all children before their parents;
  • --topo-order (for “topological”) implies --date-order, and also groups commits from the same b ranch together; and
  • --reverse reverses the output list.

Warning

The --reverse option does not affect the selection of commits to list, but rather the final order in which they are listed; that is, it is applied last. You might expect the following command to show the root commit, by first listing all commits in reverse chronological order and then showing only the first one:

$ git log --reverse -n 1

Instead, however, it shows the latest commit, by picking the first one from the normal output of git log; the reversal applies to a list containing that single commit, and thus has no effect.

History Simplification

Git has a number of options for pruning sections of history according to various notions of equivalence between parent and child commits, documented in the “History Simplification” section of git-log(1). Since these are fairly specialized and abstruse, useful mostly with very large and complex histories, and well documented in the man page, we do not cover them here.

git cherry

git cherry [-v] [upstream [head [limit]]]

This command is similar to git log --cherry, but more specialized. It shows commits on a branch that are not in the upstream, marking those whose changes are duplicated by distinct upstream commits with a minus sign (while other commits have a plus sign). Using the same example as before, if we’re on the other branch for which master is upstream:

$ git cherry -v --abbrev
+ 6f70a016 3
- 0badfe94 2
+ 15f47204 1

This shows that we have three new commits on our side, but the changes from commit 2 are already applied upstream (the -v option includes the commit message subject line). As shown, you can give specific current (head) and upstream branches for comparison, and also a limit commit so that only commits in limit..head are eligible to be shown. The limit would be an earlier commit on the head branch, past which you are not interested in looking. The default is equivalent to git cherry HEAD@{upstream} HEAD (no limit).

git shortlog

As mentioned earlier, git shortlog summarizes commit history, grouping commits by author with the number of commits and their subjects, and applying a mailmap if available to rewrite author names or email addresses:

$ git shortlog
Ammon Riley (1):
     Make git-svn branch patterns match complete URL

Amos King (2):
     Do not name "repo" struct "remote" in push_http…
     http-push.c: use a faux remote to pass to http_…

Amos Waterland (6):
     tutorial note about git branch
     Explain what went wrong on update-cache of new …
     Do not create bogus branch from flag to git bra…
     git rebase loses author name/email if given bad…
     AIX compile fix for repo-config.c
     wcwidth redeclaration
...

This can be useful in preparing the release notes for a new version of a project, automatically collating the new features in this release. You could limit it to just the features since the last version by referring to the tags for the previous and current release (e.g., git shortlog v1.0..v1.1).

Get Git Pocket Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.