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


79

假设我有一个文本文件

alex
bob
matrix
will be removed
git repo

我已经将其更新为

alex
new line here
another new line
bob
matrix
git

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

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

Answers:


76

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

git diff --stat

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

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

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


3
stat仅显示插入/删除/更新了多少行。但是我需要知道哪个行号
Mahmoud Khaled

这似乎比应该解决的要难,但是我设法通过使用git blame和grep来解决这个问题。查看我的最新答案
Sedrik

1
如果输出要由其他程序(例如“ awk”或“ grep”)处理,则通常应调用“ git blame -p”。
Mikko Rantalainen 2012年

8
git blame不会抓住删除的行
Vitali

2
为什么它没有按照OP的要求被标记为正确?
Shardj

27

这是一个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"为just,echo "$line"然后将输出通过管道传输uniq


如何传递bash颜色转义码?很好,但是颜色代码git diff --color不会通过。还是您认为将颜色转义添加到此函数的返回值中会更好吗?
2013年

2
我更新了该函数,以便各种正则表达式对ANSI颜色代码均具有鲁棒性。git diff --color | diff-lines现在可以正常运行了:)
John Mellor 2013年

1
这个解决方案很棒!应该将其标记为答案,因为它确实符合OP的要求。如果它对您
有用,

我使用zsh不断收到此错误:有zsh: parse error near `]+m'什么想法吗?错误来自此行:elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
Hosh Sadiq 2014年

@HoshSadiq简单地引用正则表达式似乎有效。
Koobz 2015年

20

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

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

*在此处输入图片说明*

由于该--unified=0选项,diff输出显示0条上下文行;换句话说,它精确地显示了变化的线

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

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

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


1
如果有@@ -910,10,+911,15@@ 什么事情,那么我们怎么确切地说增加,删除或修改了多少行
Kasun Siyambalapitiya

1
您是否有一种很好的方法来在要求的OP之类的列表中输出行号?
Shardj

7

我遇到了同样的问题,所以我写了一个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许可的优点(或缺点)。
BlackVegetable 2014年

git diffn也是这样做的,它完全保留了终端颜色,并在左侧显示了旧文件,在右侧显示了新文件的行号。
加布里埃尔·斯台普斯

3

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

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

输出示例:

1
2
8
12
13
14

更改的行内容又如何呢?
anon58192932

2

配置一个外部差异工具,它将显示行号。例如,这就是我在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 : //stackoverflow.com/q/949242/526535


没有其他任何方法无需使用差异工具就可以获取这些信息。仅使用git命令?
Mahmoud Khaled

1

这是我拼凑的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


0

并非完全是您的要求,但git blame TEXTFILE可能会有所帮助。


0

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

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

git diff HEAD --shortstat

它会输出类似于

1 file changed, 4 insertions(+)

该问题询问已更改的每行的行号,而不是已更改多少行的总数。
Pro Q


0

这是一些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")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.