使用 git diff,如何获得添加和修改的行号?

Posted

技术标签:

【中文标题】使用 git diff,如何获得添加和修改的行号?【英文标题】:Using git diff, how can I get added and modified lines numbers? 【发布时间】:2012-01-05 18:54:08 【问题描述】:

假设我有一个文本文件

alex
bob
matrix
will be removed
git repo

我已将其更新为

alex
new line here
another new line
bob
matrix
git

在这里,我添加了行号 (2,3) 并更新了行号 (6)

如何使用 git diff 或任何其他 git 命令获取这些行号信息?

【问题讨论】:

【参考方案1】:

这是一个 bash 函数,用于从 diff 计算结果行号:

diff-lines() 
    local path=
    local line=
    while read; do
        esc=$'\033'
        if [[ $REPLY =~ ---\ (a/)?.* ]]; then
            continue
        elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
            path=$BASH_REMATCH[2]
        elif [[ $REPLY =~ @@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then
            line=$BASH_REMATCH[2]
        elif [[ $REPLY =~ ^($esc\[[0-9;]*m)*([\ +-]) ]]; then
            echo "$path:$line:$REPLY"
            if [[ $BASH_REMATCH[2] != - ]]; then
                ((line++))
            fi
        fi
    done

它可以产生如下输出:

$ git diff | diff-lines
http-fetch.c:1: #include "cache.h"
http-fetch.c:2: #include "walker.h"
http-fetch.c:3: 
http-fetch.c:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
http-fetch.c:4:+int main(int argc, const char **argv)
http-fetch.c:5: 
http-fetch.c:6:+       const char *prefix;
http-fetch.c:7:        struct walker *walker;
http-fetch.c:8:        int commits_on_stdin = 0;
http-fetch.c:9:        int commits;
http-fetch.c:19:        int get_verbosely = 0;
http-fetch.c:20:        int get_recover = 0;
http-fetch.c:21: 
http-fetch.c:22:+       prefix = setup_git_directory();
http-fetch.c:23:+
http-fetch.c:24:        git_config(git_default_config, NULL);
http-fetch.c:25: 
http-fetch.c:26:        while (arg < argc && argv[arg][0] == '-') 
fetch.h:1: #include "config.h"
fetch.h:2: #include "http.h"
fetch.h:3: 
fetch.h:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix);
fetch.h:4:+int main(int argc, const char **argv);
fetch.h:5: 
fetch.h:6: void start_fetch(const char* uri);
fetch.h:7: bool fetch_succeeded(int status_code);

来自这样的差异:

$ git diff
diff --git a/builtin-http-fetch.c b/http-fetch.c
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7..e8f44ba 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "walker.h"
 
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
 
+       const char *prefix;
        struct walker *walker;
        int commits_on_stdin = 0;
        int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        int get_verbosely = 0;
        int get_recover = 0;
 
+       prefix = setup_git_directory();
+
        git_config(git_default_config, NULL);
 
        while (arg < argc && argv[arg][0] == '-') 
diff --git a/fetch.h b/fetch.h
index 5fd3e65..d43e0ca 100644
--- a/fetch.h
+++ b/fetch.h
@@ -1,7 +1,7 @@
 #include "config.h"
 #include "http.h"
 
-int cmd_http_fetch(int argc, const char **argv, const char *prefix);
+int main(int argc, const char **argv);
 
 void start_fetch(const char* uri);
 bool fetch_succeeded(int status_code);

如果您只想显示添加/删除/修改的行,而不是周围的上下文,您可以将 -U0 传递给 git diff:

$ git diff -U0 | diff-lines
http-fetch.c:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
http-fetch.c:4:+int main(int argc, const char **argv)
http-fetch.c:6:+       const char *prefix;
http-fetch.c:22:+       prefix = setup_git_directory();
http-fetch.c:23:+
fetch.h:4:-int cmd_http_fetch(int argc, const char **argv, const char *prefix);
fetch.h:4:+int main(int argc, const char **argv);

它对 ANSI 颜色代码非常强大,因此您可以将 --color=always 传递给 git diff 以获得添加/删除行的常用颜色代码。

输出很容易被grep:

$ git diff -U0 | diff-lines | grep 'main'
http-fetch.c:4:+int main(int argc, const char **argv)
fetch.h:4:+int main(int argc, const char **argv);

在你的情况下,git diff -U0 会给出:

$ git diff -U0 | diff-lines
test.txt:2:+new line here
test.txt:3:+another new line
test.txt:6:-will be removed
test.txt:6:-git repo
test.txt:6:+git

如果您只需要行号,请将echo "$path:$line:$REPLY" 更改为仅echo "$line" 并通过uniq 管道输出。

【讨论】:

如何传递 bash 颜色转义码?这很好,但是来自git diff --color 的颜色代码无法通过。还是您认为将颜色转义添加到此函数的返回中会更好? 我更新了函数,使各种正则表达式对 ANSI 颜色代码具有鲁棒性。 git diff --color | diff-lines 现在按预期工作:) 这个解决方案非常棒!它应该被标记为答案,因为它确实符合 OP 的要求。如果它对你有用,请投票,这样我们就可以让它成为流行的答案:) 我使用 zsh 时不断收到此错误:zsh: parse error near `]+m' 有什么想法吗?错误来自这一行:elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then @HoshSadiq 简单地引用正则表达式似乎有效。【参考方案2】:

也许这要归功于 Jakub Bochenski - Git diff with line numbers (Git log with line numbers)

git diff --unified=0 | grep -Po '^\+\+\+ ./\K.*|^@@ -[0-9]+(,[0-9]+)? \+\K[0-9]+(,[0-9]+)?(?= @@)'

【讨论】:

【参考方案3】:

这里有一些 Python copypasta 用于获取修改/删除行的行号,以防您在寻找该问题时遇到此问题。

应该很容易将其修改为同时获得修改和添加的行号的东西。

我只在 Windows 上测试过,但应该也是跨平台的。

import re
import subprocess

def main(file1: str, file2: str):
    diff = get_git_diff(file1, file2)
    print(edited_lines(diff))

def edited_lines(git_diff: str):
    ans = []
    diff_lines = git_diff.split("\n")
    found_first = False
    # adjust for added lines
    adjust = 0
    # how many lines since the start
    count = 0
    for line in diff_lines:
        if found_first:
            count += 1
            if line.startswith('-'):
                # minus one because count is 1 when we're looking at the start line
                ans.append(start + count - adjust - 1)
                continue

            if line.startswith('+'):
                adjust += 1
                continue

        # get the start line
        match = re.fullmatch(r'@@ \-(\d+),\d+ \+\d+,\d+ @@', line)
        if match:
            start = int(match.group(1))
            count = 0
            adjust = 0
            found_first = True

    return ans


def get_git_diff(file1: str, file2: str):
    try:
        diff_process: subprocess.CompletedProcess = subprocess.run(['git', 'diff', '--no-index', '-u', file1, file2], shell=True, check=True, stdout=subprocess.PIPE)
        ans = diff_process.stdout
    # git may exit with 1 even though it worked
    except subprocess.CalledProcessError as e:
        if e.stdout and e.stderr is None:
            ans = e.stdout
        else:
            raise

    # remove carriage at the end of lines from Windows
    ans = ans.decode()
    ans.replace('\r', '')
    return ans


if __name__ == "__main__":
    main("file1.txt", "file2.txt")

【讨论】:

【参考方案4】:

我正在寻找一种使用 git diff 仅输出每个文件更改的行的方法。 我的想法是将此输出提供给 linter 以进行类型检查。 This is what helped me

【讨论】:

【参考方案5】:

所有未提交行的行号(添加/修改):

git blame <file> | grep -n '^0\8\ ' | cut -f1 -d:

示例输出:

1
2
8
12
13
14

【讨论】:

那些被改变的行的内容呢?【参考方案6】:

我使用git diff--unified=0 选项。

例如,git diff --unified=0 commit1 commit2 输出差异:

由于--unified=0 选项,差异输出显示0 个上下文行;换句话说,它准确地显示了更改的行

现在,您可以识别以“@@”开头的行,并根据模式对其进行解析:

@@ -startline1,count1 +startline2,count2 @@

回到上面的例子,对于文件 WildcardBinding.java,从第 910 行开始,删除了 0 行。从第 911 行开始,增加了 4 行。

【讨论】:

如果@@ -910,10,+911,15@@ 什么的,那我们如何准确地说添加、删除或修改了多少行 你有没有像 OP 要求的那样在列表中输出行号的好方法?【参考方案7】:

git diff --stat 将向您显示提交内容时获得的输出,我猜这就是您所指的内容。

git diff --stat

为了准确显示已更改的行号,您可以使用

git blame -p <file> | grep "Not Committed Yet"

并且更改的行将是结果中结束括号之前的最后一个数字。虽然不是一个干净的解决方案:(

【讨论】:

stat 只显示插入/删除/更新了多少行。但我需要知道哪些行号 这似乎是一个比它应该的更难的问题,但我设法通过使用 git blame 和 grep 来解决它。查看我的更新答案 如果输出要由 'awk' 或 'grep' 等其他程序处理,通常应该调用 'git blame -p'。 git blame 不会捕获删除的行 当它没有按照 OP 的要求执行时,为什么它被标记为正确?【参考方案8】:

这可能是更改行的相当准确的计数:

git diff --word-diff <commit> |egrep '(?:\[-)|(?:\\+)' |wc -l

另外,这里是您的差异中行号的解决方案:https://github.com/jay/showlinenum

【讨论】:

【参考方案9】:

我遇到了同样的问题,所以我编写了一个 gawk 脚本来更改 git diff 的输出,以便为每一行添加行号。当我需要区分工作树时,我发现它有时很有用,尽管它不限于此。也许它对这里的某人有用?

$ git diff HEAD~1 |showlinenum.awk
diff --git a/doc.txt b/doc.txt
index fae6176..6ca8c26 100644
--- a/doc.txt
+++ b/doc.txt
@@ -1,3 +1,3 @@
1: red
2: blue
 :-green
3:+yellow

你可以从这里下载:https://github.com/jay/showlinenum

【讨论】:

看起来很方便。请记住,此代码具有获得 GPL 许可的优势(或劣势)。 I wrote git diffn 也这样做,它完全保留了终端颜色并在左侧显示旧文件的行号和在右侧显示新文件的行号。【参考方案10】:

您可以使用git diff 加上shortstat 参数来仅显示no of 行已更改。

自上次提交以来更改的行数(在存储库中已经存在的文件中)

git diff HEAD --shortstat

它会输出类似于

的东西
1 file changed, 4 insertions(+)

【讨论】:

该问题询问已更改的每一行的行号,而不是总共更改了多少行。【参考方案11】:

这是我拼凑的一个 bash 函数:

echo $f:
for n in $(git --no-pager blame --line-porcelain $1 |
        awk '/author Not Committed Yet/if (a && a !~ /author Not Committed Yet/) print a a=$0' |
        awk 'print $3') ; do
    if (( prev_line > -1 )) ; then
        if (( "$n" > (prev_line + 1) )) ; then
            if (( (prev_line - range_start) > 1 )) ; then
                echo -n "$range_start-$prev_line,"
            else
                echo -n "$range_start,$prev_line,"
            fi
            range_start=$n
        fi
    else
        range_start=$n
    fi
    prev_line=$n
done
if (( "$range_start" != "$prev_line" )) ; then
    echo "$range_start-$prev_line"
else
    echo "$range_start"
fi

它最终看起来像这样:

views.py:
403,404,533-538,546-548,550-552,554-559,565-567,580-582

【讨论】:

【参考方案12】:

不完全符合您的要求,但git blame TEXTFILE 可能会有所帮助。

【讨论】:

【参考方案13】:

配置一个显示行号的外部差异工具。例如,这是我的 git 全局配置中的内容:

diff.guitool=kdiff3
difftool.kdiff3.path=c:/Program Files (x86)/KDiff3/kdiff3.exe
difftool.kdiff3.cmd="c:/Program Files (x86)/KDiff3/kdiff3.exe" "$LOCAL" "$REMOTE"

更多详情请查看此答案:https://***.com/q/949242/526535

【讨论】:

不使用 diff 工具就没有其他方法可以获取这些信息。只使用 git 命令?

以上是关于使用 git diff,如何获得添加和修改的行号?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 git diff 读取输出?

如何从 git diff 读取输出?

执行“git diff”时如何获得并排差异?

如何获得完整上下文的 git diff?

方法级别的 Git Diff

GitGit 基础命令 ( 添加暂存文件 git add | 提交文件至版本库 git commit | 查看版本库状态 git status | 查询文件修改 git diff )