带有行号的Git差异(带有行号的Git日志)

135

当我执行 git diff 或者 git log -p 命令时,如何让源文件的行号与输出一起显示?

我尝试查看 man git-diff | grep "line numbers" 和谷歌搜索,但没能快速找到答案。

12个回答

127

使用git diff无法获得可读的行号。

目前没有选项可以在侧边垂直显示带有git diff的行号。

统一差异格式

尽管如此,每个更改的(c)hunk头中都有该信息,它只是以统一差异格式的形式存在:

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

文件的原始状态用-表示,新状态用+表示(它们不是hunk头中的添加和删除)。start表示文件每个版本的起始行号,count表示从起始点开始包含多少行。

示例

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

代码块头部

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

表示文件的上一个版本从第11行开始,包括7行:

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

虽然文件的下一个版本也从第11行开始,但包括7行。

统一差异格式并不适合人类阅读

正如您可能已经注意到的那样,统一差异格式并不容易确定行号(至少对于非机器而言)。如果您真的想要可读的行号,您需要使用一个差异工具来为您显示它们。

额外阅读


7
这是一个用于提取修改文件中行号的grep命令,例如可用于过滤checkstyle报告:git diff --unified=0 | grep -Po '^\+\+\+ ./\K.*|^@@ -[0-9]+(,[0-9]+)? \+\K[0-9]+(,[0-9]+)?(?= @@)' - Jakub Bochenski
1
@JakubBochenski的评论非常好地解决了我的问题。 - 0xbe5077ed
2
这只有在指定 --unified=0-U0 时才真正有用。 - caw
我刚刚完成了 git diffn,它是 git diff 的一个即插即用替代品(包装器),可以显示行号,并且完全兼容 git diff 的所有用法和选项:https://dev59.com/vGAf5IYBdhLWcg3wnDwI#61997003 - Gabriel Staples
@JakubBochenski grep 的 -Po 标志是什么? - Peiti Li
1
@PeitiLi 你可能已经在谷歌上搜索过了,但是为了记录一下:-P指定正则表达式模式采用Perl格式,而-o告诉grep只打印精确匹配项,而不是找到匹配项的整行。 - Shayna

28

以下是基于Andy Talkowski的代码的另一种解决方案。

纯文本:

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 }
'

这是示例输出:

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"


我注意到它会破坏代码的对齐(缩进),而本地的 git diff 会仔细地保持对齐。目前这段代码超出了我的能力范围,所以你能修复它吗?换句话说,当一行代码说 +240:+ 而下一行说 (241,257): 时,你需要在顶部的那一行添加一些额外的空格,以使其代码与下面一行的代码保持正确的对齐和缩进。也许可以用 print 轻松完成这个任务? - Gabriel Staples
https://stackoverflow.com/questions/61932427/git-diff-with-line-numbers-and-proper-code-alignment-indentation - Gabriel Staples
1
没事了,我搞定了!我刚刚完成了 git diffn。请看这里:https://dev59.com/vGAf5IYBdhLWcg3wnDwI#61997003。感谢 @PFudd 的回答。我学习了它并用它来学习,然后从头开始编写了 git diffn。一旦 格式化以便我能够阅读您的代码(感谢 @EdMorton),我就能够从中学到很多有用的东西。 - Gabriel Staples
抱歉,距离我写那篇文章已经超过5年了,如果现在我会做得更好。 :-) - PFudd
我进行了更改,并删除了有颜色的版本,只是因为‘git diff’在通过管道传输到另一个程序时不发出颜色代码。 - PFudd
显示剩余4条评论

25

在Ubuntu 18.04、20.04、22.04以及Windows 10和11的Git Bash终端中经过测试并可正常工作。它应该在任何操作系统中都能正常工作。

快速摘要

虽然它还不是git的一部分,但我用AWK编程语言编写了一个包装工具,名为git diffn,它显示行号。它在我的eRCaGuy_dotfiles存储库这里。下面是安装和使用它的快速方法。

  1. 安装它

    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. 使用它就像使用git diff一样,支持git diff的所有选项

    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:+}
    

    此输出的屏幕截图如下所示。

细节

截至2020年5月24日,您现在可以使用第三方工具git diffn(充分披露:我编写了它,并且您必须运行几个命令来安装它)来实现此目的。它是一个轻量级的git diff包装器,使用awk模式/动作编程语言编写。下面是运行git diffn的示例输出。冒号(:)都保持为白色是有意的,作为一个视觉提示,它们是从左到右的分隔符。(如果您不喜欢这样,可以在代码中轻松更改)。

enter image description here

1/3: 这是什么?
git-diffn.sh的顶部描述:

git-diffn.sh

  • 一个可以替代git diff的工具,还可以显示行号!使用方法和git diff一样,只不过你还可以看到这些漂亮的行号,帮助你理解你的修改。
  • 由于它只是一个基于轻量级awk语言的git diff包装器,它接受git diff接受的所有选项和参数。例如:
    1. git diffn HEAD~
    2. git diffn HEAD~3..HEAD~2
  • 它可以与任何git diff的颜色设置一起使用,即使你使用自定义颜色。
    1. 在这里查看我的答案,了解如何设置自定义的diff颜色,以及查看git diffn的自定义颜色输出的截图:How do you customize the color of the diff header in git diff?
    2. 以下是我上面答案中设置自定义git diff颜色和属性(文本格式)的一些示例git config命令:
      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"
      
  • git diffn中,默认情况下输出是彩色的;如果你想禁用输出颜色,你必须使用--no-color--color=never选项。详细信息请参阅man git diff。例如:
    git diffn --color=never HEAD~
    git diffn --no-color HEAD~3..HEAD~2
    
  • 2/3:安装
    1. Windows(未经测试):这可能在Git for Windows附带的bash终端中运行,但未经测试。请安装Git for Windows。打开它附带的bash终端,并尝试按照下面的说明进行操作。我需要一些愿意在Git for Windows中测试的测试人员。请参阅并回答这里:https://github.com/git-for-windows/git/issues/2635
    2. Mac(未经测试):使用终端并按照下面的说明进行操作。您可能需要安装gawk。如果需要,请尝试thisbrew install gawk
    3. Linux(在Ubuntu 18.04上经过测试并且完美运行):按照下面的终端说明进行操作。

    选项1(我推荐的):下载整个存储库,然后创建一个符号链接到程序,这样您可以通过从存储库中执行git pull随时轻松获取更新。

    首先,使用cd命令切换到您想要安装的位置。然后运行以下命令:
    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
    

    完成了!现在只需完成最后一步!
    选项2(适用于只想要一个文件的人):只需下载一次该文件。
    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
    

    完成了!现在只需完成下面的最后一步!
    最后一步:
    现在关闭并重新打开您的终端,或使用. ~/.bashrc重新加载,然后您就完成了! git diffn现在可以作为git diff的完全替代品使用!
    这是一个演示:
    3/3: git diffn的演示:
    创建这个文件:

    hello_world.c:

    #include <stdio.h>
    
    int main()
    {
        printf("Hello World\n");
    
        return 0;
    }
    

    提交它:
    git add hello_world.c
    git commit -m "add hello_world.c"
    

    将其更改为以下内容并保存文件:

    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;
    }
    

    现在运行:
    git diff
    

    这是首先为了比较目的而输出的git diff的结果:
    $ 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
    +}
    

    并附上一张截图以展示颜色。请注意,红色高亮部分仅显示了可以删除的空白空间(在这种情况下是空格)。

    enter image description here

    现在这是git diffn的输出结果。请注意,它完美地显示了所有的行号!
    • 被删除行的行号在左边显示,并在冒号的最左边和最右边都显示了一个减号,以帮助您更好地查看 - 无论您的眼睛是喜欢扫描冒号的右边还是屏幕的最左边。
    • 被添加行的行号在右边显示,并在冒号的最左边和最右边都显示了一个加号。
    • 为了上下文而显示的未更改行的行号,分别在左侧(旧文件)和右侧(新文件)显示,用逗号分隔。
    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:+}
    

    并附上一个截图来展示颜色。请注意,冒号的颜色或样式与左右的周围文本不匹配。这是有意为之的设计行为,旨在作为左侧添加的行号和右侧原始git diff输出之间的视觉分隔符。

    enter image description here

    另请参阅

    1. 请参阅我在这里的其他答案,我在那里推荐您使用meld作为您的git difftool。Meld非常棒,我比git diffn使用得更多,尽管我两者都使用。现在有了git diffn,除非我在另一台电脑上,否则我几乎不再使用git diff

    1
    第一次在Linux上尝试:从浏览器复制,粘贴到vim中 :new git-diffn.sh"+gP:wqchmod +x git-diffn.sh ... 成功了。 - John
    2
    文档非常好,详细到位。 - Timo
    我点了踩,因为答案太长了,99%的内容与OP的问题无关。在我看来,这应该被简化,只呈现工具和快速使用方法。 - bfontaine
    1
    @bfontaine,感谢您的反馈。我刚在顶部添加了一个非常简短的“快速摘要”部分,以便尽快获取答案并使用该工具。 - Gabriel Staples
    1
    太棒了。真是太令人惊叹了。非常感谢这个工具。 - Mohit Tomar

    9
    一个快速的方法是使用git diff -U0。这将把上下文行数设置为0,使@@值匹配实际更改的行。默认情况下,@@值包括3行之前/之后的上下文,这对人类不太方便。
    示例:
    git diff # default
    @@ -10,8 +10,8 @@
    

    这是一个关于计算已更改的代码行数的问题,由于第10行涉及到先前上下文的第一行导致难以计算。实际更改的第一行代码的行号是10+3=13。要计算更改行数的数量,还需要减去先前和后续上下文的行数: 8-3-3=2。

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

    如你所见,将上下文设置为0可以使@@值更容易被人类阅读。你可以看到修改的行从第13行开始,有2行已经被更改。
    这并不完美,因为它只显示每个块的行号。如果您想查看每行的行号,请使用difftool进行外部编辑。请参见https://dev59.com/vGAf5IYBdhLWcg3wnDwI#50049752

    9
    这里有一个脚本,旨在修复此问题-没有对其进行严格测试,但似乎可以。它依赖于记录Git diff产生的结果,并使用awk来维护行计数。
    # 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
    你可以使用git difftool来使用外部编辑器进行差异比较,该编辑器将显示行号。以下是如何在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
      

    仅通过执行git difftool命令,它为我打开了tkdiff工具,该工具本身具有行号功能。感谢wisbucky。 - Richardd

    5

    2
    尝试使用 https://github.com/so-fancy/diff-so-fancy
    通过 brew install diff-so-fancynpm install -g diff-so-fancy 进行安装。
    然后。
    git config --global core.pager "diff-so-fancy | less --tabs=4 -RFX"
    git config --global interactive.diffFilter "diff-so-fancy --patch"
    

    1

    你可以尝试

    git blame
    

    在文件上运行此命令。它会显示文件中每一行的提交者、提交ID和行号。


    5
    问题在于它没有显示出所需的差异(diff),这正是原帖发布者所要求的。git blame 只会显示当前带有行号的文件状态。 - user456814
    5
    我知道git blame,但是可能会有人不知道。这可能会对某些人有所帮助。谢谢你的回答。 - Drew LeSueur
    2
    “git blame” 绝对不能回答这个问题;我对这里的点赞感到非常困惑。 - Michael Mrozek
    1
    请不要在stackoverflow上发布与问题无关的答案,避免垃圾信息。 - Ahmed

    1

    PFudd的回答几乎完美,但是你可以通过一个小修改使得行缩进变得更加平滑:

    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 }
    '
    

    也可以将其导入bat中,效果很棒。

    enter image description here


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接