512

Aside from writing an alias or script, is there a shorter command for getting the diff for a particular commit?

git diff 15dc8^..15dc8

If you only give the single commit id git diff 15dc8, it diffs that commit against HEAD.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian L
  • 10,757
  • 5
  • 19
  • 17
  • The coolest thing about this would be that this would work with "git difftool", using the tools to show the diff. – orip Jul 06 '10 at 11:38
  • For reference, the answer to this other question illustrates how you could set up a bash-powered alias to simplify the above: http://stackoverflow.com/questions/3321492/git-alias-with-positional-parameters-git-foo-aaa-bbb-ccc-foo-aaa-bar-bbb – Nick Sep 20 '12 at 11:40

8 Answers8

696

Use git show $COMMIT. It'll show you the log message for the commit, and the diff of that particular commit.

mipadi
  • 398,885
  • 90
  • 523
  • 479
  • 48
    Too bad it can't use difftool :( – orip Jul 06 '10 at 11:40
  • 1
    @orip you can always set GIT_EXTERNAL_DIFF to a script that does the same thing as your difftool. – slacy Aug 20 '12 at 20:31
  • 7
    I prefer JakubNarebski's answer, as the commit expression given there will work in many contexts: http://stackoverflow.com/a/449128/992887 – RichVel Apr 06 '13 at 07:58
  • 1
    If there is not diff shown, there probably are no actual changes, like for a merge commit – Devin Rhode Apr 29 '13 at 18:29
  • 1
    This answer is technically off-topic -- not sure why it has so many votes. The question was how to use `git diff` (and by implication `git difftool`), not `git show`. – PTWithy Jun 03 '14 at 13:10
  • 7
    @PTWithy: The question was, "Is there a shorter command for getting the diff for a particular commit?", which this question answers. – mipadi Jun 03 '14 at 23:11
  • What is "$COMMIT"? Is it literally "$COMMIT"? Or is it an SHA-1 hash value? – Peter Mortensen Jul 07 '21 at 17:40
  • @PeterMortensen: It's a commit hash, or the name of a commit (such as `HEAD`, `HEAD^`, `HEAD~3`, `some-branch-name`, etc.). – mipadi Jul 08 '21 at 18:33
  • It does not answerd the question as it use alias, but it may be helpfull to others : `[Alias] diff-commit="!f() (git log HEAD^${1}^! ; git difftool -d HEAD^${1}^! & ) ; f"` Use it like `git diff-commit 2` to show diff with difftool between HEAD^2 and HEAD^3 – Benjamin Jun 15 '22 at 07:41
497

Use:

git diff 15dc8^!

as described in the following fragment of git-rev-parse(1) man page (or in modern Git gitrevisions(7) man page):

Two other shorthands for naming a set that is formed by a commit and its parent commits exist. The r1^@ notation means all parents of r1. r1^! includes commit r1 but excludes all of its parents.

This means that you can use 15dc8^! as a shorthand for 15dc8^..15dc8 anywhere in Git where revisions are needed. For the diff command, the git diff 15dc8^..15dc8 is understood as git diff 15dc8^ 15dc8, which means the difference between parent of commit (15dc8^) and commit (15dc8).

Note: the description in git-rev-parse(1) man page talks about revision ranges, where it needs to work also for merge commits, with more than one parent. Then r1^! is "r1 --not r1^@" i.e. "r1 ^r1^1 ^r1^2 ..."


Also, you can use git show COMMIT to get the commit description and diff for a commit. If you want only the diff, you can use git diff-tree -p COMMIT.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 9
    This should be the accepted answer, it's much neater. However, the last sentence of the git-rev-parse extract is rather confusing - seems like it means 'range from this commit's parent to this commit'. – RichVel Apr 05 '13 at 16:49
  • 3
    @RichVel: it is a bit confusing because it tries to describe also the situation where commit has more than one parent (is a merge commit). r1^! works as expected also then. – Jakub Narębski Apr 05 '13 at 19:38
  • 1
    @JakubNarębski: good point, maybe you could edit your answer to summarise your understanding of the single-parent and multi-parent cases - separate statements on each might be easier to understand. – RichVel Apr 06 '13 at 07:56
  • 1
    @JakubNarębski: yes, much better! I now use this shortcut all the time - thanks. – RichVel Apr 09 '13 at 15:26
  • No need to specify the commit: `git difftool HEAD@{1}^!` works as well. – jmiserez Mar 16 '15 at 09:17
  • @jmiserez: `HEAD@{1}` specifies a commit. I'll explain that r1 can be any commit-ish. – Jakub Narębski Mar 16 '15 at 10:52
  • 1
    The `^!` parent shorthand notation works properly with difftool for normal commits but the diff is reversed for merge commits. Why so? – hIpPy Jul 27 '17 at 05:28
60

If you know how far back, you can try something like:

# Current branch vs. parent
git diff HEAD^ HEAD

# Current branch, diff between commits 2 and 3 times back
git diff HEAD~3 HEAD~2

Prior commits work something like this:

# Parent of HEAD
git show HEAD^1

# Grandparent
git show HEAD^2

There are a lot of ways you can specify commits:

# Great grandparent
git show HEAD~3

See this page for details.

Paul Vincent Craven
  • 2,027
  • 3
  • 19
  • 24
  • 4
    HEAD^2 is not the grandparent, if HEAD^1 is daddy, then HEAD^2 is mommy. Use HEAD~2 for daddy's daddy. – Binarian Jul 21 '17 at 10:21
12

As mipadi points out, you can use git show $COMMIT, but this also shows some headers and the commit message. If you want a straight diff, use git show --pretty=format:%b $COMMIT.

This is, obviously not a very short hand, so I'm keeping this alias in my .gitconfig

    [alias]
      sd = show --pretty=format:%b

This enables me to use git sd $COMMITto show diff.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Øystein Steimler
  • 1,579
  • 1
  • 11
  • 11
  • 1
    This alias can include --color which makes it easier to read: sd = show --color --pretty=format:%b – RichVel Apr 06 '13 at 08:02
  • @RichVel Indeed! Very good point. If you have colors enabled by default in git, you won't need this switch, though. That's what I normally do. – Øystein Steimler Apr 11 '13 at 12:18
6

Many of the mentioned examples (e.g. git diff 15dc8^!, or git diff 15dc8^..15dc8) don't work if you are using Z shell and have extendedglob option set. You can fix it by one of the following three ways:

  1. unsetopt extendedglob (and/or remove it from .zshrc)

  2. setopt NO_NOMATCH (and/or set it in .zshrc)

  3. escape the caret and bang every time with a backslash, e.g., git diff 15dc8\^\!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ville
  • 4,088
  • 2
  • 37
  • 38
3
git diff 15dc8 15dce~1

~1 means 'parent', ~2 'grandparent, etc.

John Lawrence Aspden
  • 17,124
  • 11
  • 67
  • 110
3

Paul's solution did what I was hoping it would.

$ git diff HEAD^1

Also, it's useful to add aliases like hobs mentioned. If you put the following in the [alias] section of your ~/.gitconfig file then you can use the shorthand to view diff between head and previous.

[alias]
    diff-last = diff HEAD^1

Then running $ git diff-last will get you your result. Note that this will also include any changes you've not yet committed as well as the diff between commits. If you want to ignore changes you've not yet committed, then you can use diff to compare the HEAD with its parent directly:

$ git diff HEAD^1 HEAD
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
0

This uses aliases, so it doesn't answer your question exactly, but I find these useful for doing what you intend...

alias gitdiff-1="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 2|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git diff"
alias gitdiff-2="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 3|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git diff"
alias gitdiff-3="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 4|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git diff"

alias gitlog-1="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 2|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git log --summary"
alias gitlog-2="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 3|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git log --summary"
alias gitlog-3="git log --reverse|grep commit|cut -d ' ' -f2|tail -n 4|head -n 2|xargs echo|sed -e 's/\s/../'|xargs -n 1 git log --summary"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hobs
  • 18,473
  • 10
  • 83
  • 106