Git diff with line numbers (Git log with line numbers)

当执行 git diffgit log -p时,如何获得与输出内联的源文件的行号?

我试过查 man git-diff | grep "line numbers",也试过谷歌,但很快就一无所获。

71917 次浏览

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

目前还没有任何选项来获得与 git diff垂直显示在一边的行号。

统一差异格式

That information is available in the (c)hunk headers for each change in the diff though, it's just in 统一差分格式统一差分格式:

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

文件的原始状态用 -表示,新的状态用 +表示(它们并不意味着在块头中添加和删除)。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行。

统一差异格式实际上并不适合人类使用

正如您可能看到的,统一-差异格式不容易计算出行号(至少如果您不是一台机器)。如果您真的想要能够阅读的行号,那么您需要使用一个不同的工具来为您显示它们。

其他阅读资料

You can try

git blame

它显示文件中每一行的提交者、提交 ID 和行号。

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

这是另一个解决方案,扩展 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 difftool来使用一个显示行号的外部编辑器进行差异化。以下是使用 vim/vimdiff 的方法:

  1. 将 vimdiff 设置为 git 的 afftool:

    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 diff -U0。这将把上下文的行设置为0,从而使@@ 值与实际更改的行相匹配。默认情况下,@@ 值包括3行 before/after 上下文,这对人类不方便。

例如:

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

这很难计算已更改行的行号,因为第10行指的是 before 上下文的第一行。第一个更改行的实际行号是10 + 3 = 13。要计算更改的行数,还必须减去上下文前后的行数: 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.

这并不完美,因为它只显示每个块的行号。如果您希望看到每一行的行号,那么可以对外部编辑器使用 ddtool。参见 https://stackoverflow.com/a/50049752

我喜欢使用 git difftool融合作为我的模具。它比 git diff更容易查看,具有很好的并行 GUI 比较,并且在每一边显示行号。

设置:

  1. 我的说明如何设置融合作为您的 Windows或者 Linux的扩散工具

样本截图:

enter image description here

2020年5月24日更新:

在过去的几天里,我只是编写了 git diffn,作为在命令行上代替 git diff的插件。试试看。看看我的另一个答案.

首先,配置您的 git diff 工具,例如 Meld

git config --global diff.tool meld

然后,在一些文件上猛拉你的工具:

git difftool -y config.rb

记住在 diff 工具的首选项中设置行号。

截至2020年5月24日,您现在可以为此目的使用第三方工具 git diffn(完全公开: 它是我编写的,您必须运行几个命令,如下所述,才能安装它)。它是围绕 git diff的一个轻量级包装器,使用基于 awk模式/操作的编程语言编写。下面是运行 git diffn的一个输出示例。所有的冒号(:)都保持白色是有意的,作为一个可视的队列,它们是从左到右的分隔符。(如果您不喜欢这样,很容易在代码中进行更改)。

enter image description here

1/3: What is it?

来自 git-diffn.sh的顶部:

描述:

git-diffn.sh

  1. 代替 git diff的一个插件,它也显示了行 n’umbers!像使用 git diff一样使用 没错,除了你会看到这些漂亮的行号,以帮助你理解 你的改变。

  2. 因为它只是 git diff周围的一个轻量级的基于 awk 语言的包装器,所以它接受 git diff接受的所有选项和参数:

  3. git diffn HEAD~

  4. git diffn HEAD~3..HEAD~2

  5. 可以使用任何 git diff颜色设置,即使您使用的是自定义颜色

  6. 点击这里查看如何设置自定义 diff 颜色,以及从 git diffn看到自定义颜色输出的截图: 如何在 git diff 中自定义 diff 头的颜色?

  7. 下面是一些来自我上面答案的样例 git config命令,用于设置自定义 git diff颜色和属性(文本格式) :

       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"
    
  8. git diffn中,颜色输出默认为 ON; 如果要禁用输出颜色,则必须使用 --no-color--color=never。详情请参阅 man git diff。例子:

     git diffn --color=never HEAD~
    git diffn --no-color HEAD~3..HEAD~2
    

2/3: 安装

  1. Windows (未测试) : 这可以在 适用于 Windows 的 Git附带的 bash 终端中工作,但是未测试。为 Windows 安装 Git。打开它附带的 bash 终端,并尝试按照下面的说明操作。我需要一些测试人员在 Git for Windows 中测试这一点。请在这里查看并回答: https://github.com/git-for-windows/git/issues/2635
  2. Mac (未测试) : 使用终端并遵循下面的说明。您可能需要安装 gawk。如果是,请尝试 这个: brew 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
ln -si "${PWD}/git-diffn.sh" ~/bin/git-diffn

完成! 现在只要做下面的最后一步!

选项2(对于那些只想要1个文件的人) : 一次只下载一个文件。

mkdir -p ~/bin
cd ~/bin
wget https://raw.githubusercontent.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/master/useful_scripts/git-diffn.sh
chmod +x git-diffn.sh
mv git-diffn.sh git-diffn

完成! 现在只要做下面的最后一步!

最后一步:

现在关闭并重新打开您的终端,或者使用 . ~/.bashrc重新资源化终端,然后您就完成了!

git diffn现在将工作作为一个准确的下拉替换 git diff

下面是一个演示:

3/3: Demo of git diffn:

创建这个文件:

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"

将其更改为此并保存文件:

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的输出,注意它完美地显示了所有行号!

  • 删除的行的行号在左边,在 :的最左边和最右边都显示一个 -符号,这样可以帮助你看得更清楚——无论你的眼睛喜欢向下扫描结肠的右边还是向下扫描屏幕的最左边。
  • Line numbers for 加了线 are farther to the right, and show a + sign on both the far left AND to the right of the :.
  • 为上下文显示的 不变的台词的行号显示为左(旧文件)和右(新文件) ,用 ,分隔。

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 是 awesome,我使用它远远超过 git diffn,尽管我两者都用。现在有了 git diffn,我几乎从来不用 git diff,除非我在另一台电脑上。

Try 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"

If you are OK with using a separate application then you can try https://github.com/dandavison/delta 它具有语法高亮,也可以作为本地 Linux 的 diff替代品使用,还有很多其他特性