139

When I do a git diff or a git log -p, how do I get line numbers of the source file(s) inlined with the output?

I tried to look it up man git-diff | grep "line numbers" and I tried googling but got nothing quickly.

0

12 Answers 12

128

You can't get human-readable line numbers with git diff

There aren't currently any options to get line-numbers displayed vertically on the side with git diff.

Unified-diff format

That information is available in the (c)hunk headers for each change in the diff though, it's just in unified-diff format:

@@ -start,count +start,count @@

The original state of the file is represented with -, and the new state is represented with + (they don't mean additions and deletions in the hunk header. start represents the starting line number of each version of the file, and count represents how many lines are included, starting from the start point.

Example

diff --git a/osx/.gitconfig b/osx/.gitconfig
index 4fd8f04..fcd220c 100644
--- a/osx/.gitconfig
+++ b/osx/.gitconfig
@@ -11,7 +11,7 @@ <== HERE!
 [color "branch"]
        upstream = cyan
 [color "diff"]
-       meta = yellow
+       meta = cyan
        plain = white dim
        old = red bold
        new = green bold

The hunk header

@@ -11,7 +11,7 @@

says that the previous version of the file starts at line 11, and includes 7 lines:

11  [color "branch"]
12         upstream = cyan
13  [color "diff"]
14 -       meta = yellow
14 +       meta = cyan
15         plain = white dim
16         old = red bold
17         new = green bold

while the next version of the file also starts at line 11, and also includes 7 lines.

Unified-diff format isn't really for human consumption

As you can probably tell, unified-diff format doesn't make it easy to figure out line numbers (at least if you're not a machine). If you really want line numbers that you can read, you'll need to use a diffing tool that will display them for you.

Additional Reading

6
  • 9
    Here is a grep to extract only line numbers in modified files, e.g. to be used to filter a checkstyle report git diff --unified=0 | grep -Po '^\+\+\+ ./\K.*|^@@ -[0-9]+(,[0-9]+)? \+\K[0-9]+(,[0-9]+)?(?= @@)' Commented Jul 6, 2018 at 12:57
  • 1
    @JakubBochenski's comment solved my problem quite nicely.
    – 0xbe5077ed
    Commented Sep 11, 2018 at 1:11
  • 2
    This is only really useful if you specify --unified=0 or -U0, though.
    – caw
    Commented Jun 7, 2019 at 21:36
  • I just finished git diffn, a drop-in replacement (wrapper) for git diff which shows line numbers and has full compatibility with all usage and options of git diff: stackoverflow.com/questions/24455377/… Commented May 25, 2020 at 7:30
  • 1
    @PeitiLi You probably already Googled it at this point, but for the record: -P specifies that the RegEx pattern is in Perl format, and -o tells grep to only print the exact match rather than the entire line where the match was found.
    – Shayna
    Commented Nov 17, 2021 at 21:56
28

Here's another solution, expanding on Andy Talkowski's code.

Plain text:

git diff | gawk '
  match($0,"^@@ -([0-9]+),([0-9]+) [+]([0-9]+),([0-9]+) @@",a){
    left=a[1]
    ll=length(a[2])
    right=a[3]
    rl=length(a[4])
  }
  /^(---|\+\+\+|[^-+ ])/{ print;next }
  { line=substr($0,2) }
  /^[-]/{ printf "-%"ll"s %"rl"s:%s\n",left++,""     ,line;next }
  /^[+]/{ printf "+%"ll"s %"rl"s:%s\n",""    ,right++,line;next }
        { printf " %"ll"s %"rl"s:%s\n",left++,right++,line }
'

Here's sample output:

diff --git a/.bashrc b/.bashrc
index b2b6d5f..51e0b8c 100644
--- a/.bashrc
+++ b/.bashrc
@@ -1,8 +1,26 @@
 1  1:#!/bin/bash
-2   :# ~/.bashrc: executed by bash(1) for non-login shells.
-3   :# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
-4   :# for examples
+   2:# 2020-03-06 14:54:25 From R S:
+   3:##export PATH="/usr/local/opt/ed/libexec/gnubin:$PATH"
+   4:#export PATH="/usr/local/opt/findutils/libexec/gnubin:$PATH"
+   5:#export PATH="/usr/local/opt/gnu-indent/libexec/gnubin:$PATH"
+   6:#export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
+   7:#export PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH"
+   8:#export PATH="/usr/local/opt/gnu-which/libexec/gnubin:$PATH"
 5  9:
+  10:export PATH="/usr/local/opt/sqlite/bin:$PATH"
+  11:export PATH="/usr/local/opt/file-formula/bin:$PATH"
+  12:export PATH="/usr/local/opt/unzip/bin:$PATH"
+  13:export PATH="/usr/local/opt/openssl/bin:$PATH"
+  14:export PATH="/usr/local/opt/wireshark/bin:$PATH"
+  15:
+  16:
+  17:#export PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"

9
  • I notice it breaks alignment (indentation) of the code, whereas native git diff carefully maintains alignment. This code is over my head at the moment, so would you be willing to fix it? In other words, when one line says +240:+ and the next line says (241,257):, you need to add some extra spaces to the top line to make its code maintain proper alignment and indentation with the code from the lower line. Maybe this could easily be done with print? Commented May 21, 2020 at 10:01
  • ...I mean with printf. Commented May 21, 2020 at 10:19
  • stackoverflow.com/questions/61932427/… Commented May 21, 2020 at 10:28
  • 1
    Nevermind; I got it! I just finished git diffn. See here: stackoverflow.com/a/61997003/4561887. Thanks @PFudd, for your answer. I studied it and used it to learn, then started from scratch and wrote git diffn. Once formatted so I could read your code (thanks @EdMorton), I was able to learn some great things from it which helped me. Commented May 25, 2020 at 7:27
  • Sorry, it's been 5+ years since I wrote that, I would have done a prettier job these days. :-)
    – PFudd
    Commented Mar 7, 2021 at 4:17
26

Tested and works in Ubuntu 18.04, 20.04, 22.04, and in the Git Bash terminal in Windows 10 and 11. It should work in any OS.

Quick summary

It's not part of git yet, but I wrote a wrapper tool in the AWK programming language called git diffn, which shows line numbers. It's in my eRCaGuy_dotfiles repo here. Here is a quick way to install and use it.

  1. Install it

    mkdir -p ~/bin
    cd ~/bin
    curl -LO https://raw.githubusercontent.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/master/useful_scripts/git-diffn.sh
    chmod +x git-diffn.sh
    mv git-diffn.sh git-diffn
    echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
    . ~/.bashrc
    
  2. Use it exactly like git diff, with all options git diff supports

    git diffn
    

    Sample output:

    $ git diffn
    diff --git a/hello_world.c b/hello_world.c
    index e01704a..e971b73 100644
    --- a/hello_world.c
    +++ b/hello_world.c
    @@ -1,8 +1,12 @@
    +        1:+// Basic hello world example
    +        2:+
        1,   3: #include <stdio.h>
        2,   4: 
    -   3     :-int main()
    +        5:+int main(int argc, char *argv[])
        4,   6: {
    -   5     :-    printf("Hello World\n");
    -   6     :-
    +        7:+    printf("Hello Gabriel\n");
    +        8:+    
    +        9:+    int i = 700;
    +       10:+    printf("i = %i\n", i);
        7,  11:     return 0;
    -   8     :-}
    \ No newline at end of file
    +       12:+}
    

    A screenshot of this output is just below.

Details

As of 24 May 2020, you can now use the third-party tool git diffn (full disclosure: I wrote it, and you must run a couple commands, as described below, to install it) for this purpose. It's a light-weight wrapper around git diff, written in the awk pattern/action-based programming language. Here's a sample output from running git diffn. The colons (:) all staying white is intentional, to act as a visual queue that they are a separator from left to right. (If you don't like that, it's easy to change in the code).

enter image description here

1/3: What is it?

From the top of git-diffn.sh:

DESCRIPTION:

git-diffn.sh

  1. a drop-in replacement for git diff which also shows line 'n'umbers! Use it exactly like git diff, except you'll see these beautiful line numbers as well to help you make sense of your changes.
  2. since it's just a light-weight awk-language-based wrapper around git diff, it accepts ALL options and parameters that git diff accepts. Examples:
    1. git diffn HEAD~
    2. git diffn HEAD~3..HEAD~2
  3. works with any of your git diff color settings, even if you are using custom colors
    1. See my answer here for how to set custom diff colors, as well as to see a screenshot of custom-color output from git diffn: How do you customize the color of the diff header in git diff?
    2. Here are some sample git config commands from my answer above to set custom git diff colors and attributes (text formatting):
      git config --global color.diff.meta "blue"
      git config --global color.diff.old "black red strike"
      git config --global color.diff.new "black green italic"
      git config --global color.diff.context "yellow bold"
      
  4. in git diffn, color output is ON by default; if you want to disable the output color, you must use --no-color or --color=never. See man git diff for details. Examples:
    git diffn --color=never HEAD~
    git diffn --no-color HEAD~3..HEAD~2
    

2/3: Installation

  1. Windows (untested): this may work inside the bash terminal that comes with Git for Windows, but is untested. Install Git for Windows. Open the bash terminal it comes with, and try to follow the instructions below. I need some testers who will test this in Git for Windows. Please see and answer here: https://github.com/git-for-windows/git/issues/2635.
  2. Mac (untested): use the terminal and follow the instructions below. You may need to install gawk. If so, try this: brew install gawk.
  3. Linux (tested on Ubuntu 18.04 and works perfectly): follow the terminal instructions below.

Option 1 (my recommendation): download the whole repo and then create a symlink to the program so that you can easily receive updates by doing a git pull from the repo whenever you want.

First, cd to wherever you want to install this. Then run:

git clone https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles.git
cd eRCaGuy_dotfiles/useful_scripts
mkdir -p ~/bin
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
ln -si "${PWD}/git-diffn.sh" ~/bin/git-diffn
. ~/.bashrc

Done! Now just do the final step below!

Option 2 (for those who just want the 1 file): download just the one file one time.

mkdir -p ~/bin
cd ~/bin
curl -LO https://raw.githubusercontent.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/master/useful_scripts/git-diffn.sh
chmod +x git-diffn.sh
mv git-diffn.sh git-diffn
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
. ~/.bashrc

Done! Now just do the final step below!

Final step:

Now close and re-open your terminal, or re-source it with . ~/.bashrc, and you are done!

git diffn will now work as an exact drop-in replacement for git diff!

Here's a demo:

3/3: Demo of git diffn:

Create this file:

hello_world.c:

#include <stdio.h>

int main()
{
    printf("Hello World\n");

    return 0;
}

Commit it:

git add hello_world.c
git commit -m "add hello_world.c"

Change it to this and save the file:

hello_world.c:

// Basic hello world example

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("Hello Gabriel\n");
    
    int i = 700;
    printf("i = %i\n", i);
    return 0;
}

Now run:

git diff

Here's the output of git diff first for comparison purposes:

$ git diff
diff --git a/hello_world.c b/hello_world.c
index e01704a..e971b73 100644
--- a/hello_world.c
+++ b/hello_world.c
@@ -1,8 +1,12 @@
+// Basic hello world example
+
 #include <stdio.h>
 
-int main()
+int main(int argc, char *argv[])
 {
-    printf("Hello World\n");
-
+    printf("Hello Gabriel\n");
+    
+    int i = 700;
+    printf("i = %i\n", i);
     return 0;
-}
\ No newline at end of file
+}

And a screenshot to show the color. Note that the red highlighted section is simply showing empty whitespace (spaces in this case) that could be deleted:

enter image description here

Now here's the output of git diffn. Notice it shows all line numbers perfectly!

  • Line numbers for deleted lines are on the left, and show a - sign on both the far left AND to the right of the : to help you see better--whether your eyes like to scan down to the right of the colon or down on the far left of the screen.
  • Line numbers for added lines are farther to the right, and show a + sign on both the far left AND to the right of the :.
  • Line numbers for unchanged lines shown for context are shown for both the left (old file) AND the right (new file), separated by a ,.

Output of git diffn:

$ git diffn
diff --git a/hello_world.c b/hello_world.c
index e01704a..e971b73 100644
--- a/hello_world.c
+++ b/hello_world.c
@@ -1,8 +1,12 @@
+        1:+// Basic hello world example
+        2:+
    1,   3: #include <stdio.h>
    2,   4: 
-   3     :-int main()
+        5:+int main(int argc, char *argv[])
    4,   6: {
-   5     :-    printf("Hello World\n");
-   6     :-
+        7:+    printf("Hello Gabriel\n");
+        8:+    
+        9:+    int i = 700;
+       10:+    printf("i = %i\n", i);
    7,  11:     return 0;
-   8     :-}
\ No newline at end of file
+       12:+}

And a screenshot to show the color. Notice that the colons are NOT colored or stylized to match the surrounding text on the left and right. This is intentional and designed-in behavior to act as a visual separator between the line numbers added on the left and the original git diff output on the right.

enter image description here

See also

  1. See also my other answer here, where I just recommend you use meld as your git difftool instead. Meld is awesome and I use it waaaay more than git diffn, though I do use both. Having git diffn available now though, I pretty much never use git diff unless I'm on another computer.
5
  • 1
    first try on linux: copied from browser, pasted into vim :new git-diffn.sh, "+gP, :wq, chmod +x git-diffn.sh ...worked.
    – John
    Commented Mar 11, 2021 at 20:08
  • 2
    Great documentation, detailed as it should be.
    – Timo
    Commented Jun 16, 2021 at 7:16
  • I downvoted because the answer is über-long and 99% of it has nothing to do with OP’s question. IMHO this should be made succinct, just presenting the tool and a quick usage.
    – bfontaine
    Commented Dec 12, 2022 at 10:52
  • 1
    @bfontaine, thanks for the feedback. I just added a very short section in a "Quick Summary" at the top to get the answer and use the tool as quickly as possible. Commented Dec 12, 2022 at 18:23
  • 1
    Just amazing. It's just amazingly amazing. Thanks for this tool. Commented Jul 19, 2023 at 12:21
10

A quick way is to use git diff -U0. That will set the lines of context to 0, which will make the @@ values match the actual changed lines. By default, the @@ values include 3 lines of before/after context, which is not convenient for humans.

Example:

git diff # default
@@ -10,8 +10,8 @@

This is hard to calculate the line numbers of the changed lines because line 10 refers to the first line of the before context. The actual line number of the first changed line is 10+3=13. To calculate the number of changed lines, then you have to also subtract the before and after context: 8-3-3=2.

git diff -U0
@@ -13,2 +13,2 @@

As you can see, setting context = 0 makes the @@ values easier for humans to read. You can see that the changed lines start at line 13, and there are 2 changed lines.

This isn't perfect, since it only shows the line number for each block. If you want to see line numbers for every line, then use difftool for an external editor. See https://stackoverflow.com/a/50049752

9

Here is a script that attempts to fix this - not tested it in anger but it seems ok. It relies on the records git diff produces and uses awk to maintain line counts.

# Massage the @@ counts so they are usable
function prep1() {
   cat | awk -F',' 'BEGIN { convert = 0; }
       /^@@ / { convert=1; }
       /^/  { if ( convert == 1 ) { print $1,$2,$3;
              } else { print $0;
              }
              convert=0;
             }'
}

# Extract all new changes added with the line count
function prep2() {
  cat | awk 'BEGIN { display=0; line=0; left=0; out=1;}
     /^@@ / { out=0; inc=0; line=$4; line--; display=line; left=line;        }
     /^[-]/   { left++; display=left; inc=0; }
     /^[+]/   { line++; display=line; inc=0; }
     /^[-+][-+][-+] / { out=0; inc=0; }
     /^/    { 
               line += inc;
               left += inc;
               display += inc;
               if ( out == 1 ) {
                   print display,$0;
               } else {
                   print $0;
               }
               out = 1;
               inc = 1;
               display = line;
            }'
} 

git diff $1 | prep1 | prep2 
7

You can use git difftool to do the diff with an external editor that will display line numbers. Here's how to do it with vim / vimdiff:

  1. Set vimdiff as git's difftool:

    git config --global diff.tool vimdiff
    
  2. Configure ~/.vimrc to automatically show line numbers when using vimdiff:

    if &diff
        set number
    endif
    
  3. Run git difftool, which will use vimdiff with line numbers:

    git difftool
    
1
  • By only doing git difftool it openned for me the tkdiff tool, which it self have the line number function. Thanks wisbucky
    – Richardd
    Commented Jan 7, 2020 at 18:32
5

I like to use git difftool with meld as my difftool. It's easier to look at than git diff, has a nice side-by-side gui comparison, and shows line numbers on each side.

Setup:

  1. My instructions on how to set up meld as your difftool for Windows or Linux

Sample Screenshot:

enter image description here

Update 24 May 2020:

I just wrote git diffn over the last few days to be a drop-in replacement for git diff on the command-line. Give it a shot. See my other answer here.

2

Try https://github.com/so-fancy/diff-so-fancy

brew install diff-so-fancy or npm install -g diff-so-fancy

then

git config --global core.pager "diff-so-fancy | less --tabs=4 -RFX"
git config --global interactive.diffFilter "diff-so-fancy --patch"
1

PFudd's answer is pretty much perfect, but you can get the line indentation to be less jumpy with a small modification:

git diff "$@" | gawk '
  match($0,"^@@ -([0-9]+),([0-9]+) [+]([0-9]+),([0-9]+) @@",a){
    left=a[1]
    ll=length(a[2])
    right=a[3]
    rl=length(a[4])
  }
  /^(---|\+\+\+|[^-+ ])/{ print;next }
  { line=substr($0,2) }
  /^[-]/{ padding = right;
          gsub(/./, " ", padding);
          printf "-%"ll"s %"rl"s:%s\n",left++,padding,line; next }
  /^[+]/{ padding = left;
          gsub(/./, " ", padding);
          printf "+%"ll"s %"rl"s:%s\n",padding,right++,line; next }
        { printf " %"ll"s %"rl"s:%s\n",left++,right++,line }
'

Also piping it into bat looks great.

enter image description here

0

You can try

git blame

on the file. It shows you the committer, commit id, and line number for each line in the file.

4
  • 5
    The problem with this is that it doesn't show you a diff, which is what the original poster asked for. git blame will just show the current state of the file with line numbers.
    – user456814
    Commented Jun 28, 2014 at 2:44
  • 5
    I knew about git blame, but someone googling might not have. It might help someone. Thanks for answering. Commented Jun 30, 2014 at 18:35
  • 2
    git blame in no way answers the question; I'm pretty baffled at the upvotes here Commented Aug 8, 2018 at 16:12
  • 1
    Please don't spam the stackoverflow with answers that are not related to the questions.
    – Ahmed
    Commented Nov 19, 2019 at 16:52
0

First, config your git diff tool, e.g. Meld

git config --global diff.tool meld

Then, yank your difftool on some file:

git difftool -y config.rb

Remember setting line number in your diff tool's preference.

0

If you are OK with using a separate application then you can try https://github.com/dandavison/delta it has syntax highlight, can also use it as a native Linux's diff alternative and tons of other features also

Not the answer you're looking for? Browse other questions tagged or ask your own question.