Wie kann ich mit git diff Zeilennummern hinzufügen und ändern?

80

Angenommen, ich habe eine Textdatei

alex
bob
matrix
will be removed
git repo

und ich habe es aktualisiert, um zu sein

alex
new line here
another new line
bob
matrix
git

Hier habe ich die Zeilennummer (2,3) hinzugefügt und die Zeilennummer (6) aktualisiert.

Wie kann ich diese Zeilennummerninformationen mit git diff oder einem anderen git-Befehl abrufen?

Mahmoud Khaled
quelle

Antworten:

78

git diff --stat zeigt Ihnen die Ausgabe, die Sie erhalten, wenn Sie Dinge begehen, auf die Sie sich beziehen, denke ich.

git diff --stat

Um genau die geänderten Zeilennummern anzuzeigen, können Sie verwenden

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

Die geänderte Zeile ist die letzte Zahl vor der Endklammer im Ergebnis. Keine saubere Lösung :(

Sedrik
quelle
3
stat zeigt nur an, wie viele Zeilen eingefügt / gelöscht / aktualisiert wurden. Aber ich muss wissen, welche Zeilennummern
Mahmoud Khaled
Dies schien ein schwierigeres Problem zu sein, als es sein sollte, aber ich schaffte es, es mit Git-Schuld und Grep zu bekommen. Siehe meine aktualisierte Antwort
Sedrik
1
Normalerweise sollte man "git tad -p" nennen, wenn die Ausgabe von anderen Programmen wie "awk" oder "grep" verarbeitet werden soll.
Mikko Rantalainen
8
Git Schuld wird nicht entfernte Zeilen fangen
Vitali
2
Warum wird dies als korrekt markiert, wenn es nicht das tut, was OP verlangt?
Shardj
27

Hier ist eine Bash-Funktion zum Berechnen der resultierenden Zeilennummern aus einem 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
}

Es kann Ausgabe erzeugen wie:

$ 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);

von einem Diff wie diesem:

$ 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);

Wenn Sie nur hinzugefügte / entfernte / geänderte Zeilen und nicht den umgebenden Kontext -U0anzeigen möchten, können Sie an git diff übergeben:

$ 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);

Es ist robust gegenüber ANSI-Farbcodes, sodass Sie --color=alwaysan git diff übergeben können, um die übliche Farbcodierung für hinzugefügte / entfernte Zeilen zu erhalten.

Die Ausgabe kann leicht erfasst werden:

$ 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);

In Ihrem Fall git diff -U0würde geben:

$ 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

Wenn Sie nur die Zeilennummern möchten, ändern Sie die echo "$path:$line:$REPLY"in just echo "$line"und leiten Sie die Ausgabe durch uniq.

John Mellor
quelle
Wie kann ich Bash-Farb-Escape-Codes durchgehen? Das ist großartig, aber die Farbcodes, die von git diff --colorkommen, kommen nicht durch. Oder denkst du, es wäre besser, nur die Farbfluchten in die Rückkehr von dieser Funktion einzufügen?
New Alexandria
2
Ich habe die Funktion aktualisiert, damit die verschiedenen regulären Ausdrücke gegenüber ANSI-Farbcodes robust sind. git diff --color | diff-linesfunktioniert jetzt wie erwartet :)
John Mellor
1
Diese Lösung funktioniert super! Es sollte als Antwort markiert werden, da es wirklich das tut, was das OP verlangt hat. Wenn es für Sie funktioniert hat, stimmen Sie es bitte ab, damit wir es zur beliebten Antwort machen können :)
markdrake
Ich erhalte diesen Fehler immer wieder mit zsh: zsh: parse error near `]+m'Irgendwelche Ideen? Der Fehler kommt von dieser Zeile:elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
Hosh Sadiq
@HoshSadiq Das einfache Zitieren des regulären Ausdrucks scheint funktioniert zu haben.
Koobz
20

Ich benutze die --unified=0Option von git diff.

Gibt beispielsweise git diff --unified=0 commit1 commit2das Diff aus:

* Bildbeschreibung hier eingeben *

Aufgrund der --unified=0Option zeigt die Diff-Ausgabe 0 Kontextzeilen an. Mit anderen Worten, es werden genau die geänderten Zeilen angezeigt .

Jetzt können Sie die Zeilen identifizieren, die mit '@@' beginnen, und sie anhand des Musters analysieren:

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

Zurück zum obigen Beispiel: Beginnen Sie für die Datei WildcardBinding.java ab Zeile 910, 0 Zeilen werden gelöscht. Ab Zeile 911 werden 4 Zeilen hinzugefügt.

Ida
quelle
1
Was wäre wenn @@ -910,10,+911,15@@ oder so, wie sagen wir dann genau, wie viele Zeilen hinzugefügt, gelöscht oder geändert wurden
Kasun Siyambalapitiya
1
Haben Sie eine gute Möglichkeit, die Zeilennummern in einer Liste wie der von OP angeforderten auszugeben?
Shardj
7

Ich hatte das gleiche Problem, also schrieb ich ein Gawk-Skript, das die Ausgabe von Git Diff ändert, um die Zeilennummer für jede Zeile voranzustellen. Ich finde es manchmal nützlich, wenn ich einen Arbeitsbaum unterscheiden muss, obwohl es nicht darauf beschränkt ist. Vielleicht ist es für jemanden hier nützlich?

$ 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

Sie können es hier herunterladen:
https://github.com/jay/showlinenum

Jay
quelle
Sieht sehr praktisch aus. Beachten Sie, dass dieser Code den Vorteil (oder Nachteil) hat, eine GPL-Lizenz zu haben.
BlackVegetable
Ich habe auch geschriebengit diffn , um dies zu tun, und es behält die Terminalfarben vollständig bei und zeigt die Zeilennummern sowohl der alten Datei links als auch der neuen Datei rechts.
Gabriel Staples
3

Zeilennummern aller nicht festgeschriebenen Zeilen (hinzugefügt / geändert):

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

Beispielausgabe:

1
2
8
12
13
14
Reich
quelle
Was ist mit dem Inhalt der Zeilen, die ebenfalls geändert wurden?
anon58192932
2

Konfigurieren Sie ein externes Diff-Tool, das Ihnen die Zeilennummern anzeigt. Zum Beispiel ist dies das, was ich in meiner globalen Git-Konfiguration habe:

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"

Weitere Informationen finden Sie in dieser Antwort: https://stackoverflow.com/q/949242/526535

Manojlds
quelle
Gibt es keine andere Möglichkeit, diese Informationen ohne Verwendung des Diff-Tools abzurufen? Nur Git-Befehle verwenden?
Mahmoud Khaled
1

Hier ist eine Bash-Funktion, die ich zusammengeschustert habe:

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

Und am Ende sieht es so aus:

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

Dies ist wahrscheinlich eine ziemlich genaue Anzahl geänderter Zeilen:

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

Hier ist auch eine Lösung für Zeilennummern in Ihrem Diff: https://github.com/jay/showlinenum

Jay Carroll
quelle
0

Nicht genau das, wonach Sie gefragt haben, git blame TEXTFILEkann aber helfen.

u-punkt
quelle
0

Sie können git diffgekoppelt mit shortstatParameter verwenden, um nur die Anzahl der geänderten Zeilen anzuzeigen.

Für die Anzahl der Zeilen, die seit Ihrem letzten Commit geändert wurden (in einer Datei, die sich bereits im Repo befindet)

git diff HEAD --shortstat

Es wird etwas Ähnliches ausgeben wie

1 file changed, 4 insertions(+)
shahalpk
quelle
In der Frage werden die Zeilennummern für jede geänderte Zeile abgefragt, nicht die Gesamtzahl der geänderten Zeilen.
Pro Q
0

Ich suchte nach einer Möglichkeit, nur die für jede Datei geänderten Zeilen mit git diff auszugeben. Meine Idee war es, diese Ausgabe einem Linter zur Typprüfung zuzuführen. Das hat mir geholfen

maximus
quelle
0

Hier sind einige Python-Copypasta, um die Zeilennummern für geänderte / entfernte Zeilen abzurufen, falls Sie auf diese Frage gestoßen sind.

Es sollte ziemlich einfach sein, es in etwas zu ändern, das auch die geänderten und hinzugefügten Zeilennummern erhält.

Ich habe nur unter Windows getestet, aber es sollte auch plattformübergreifend sein.

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")
Pro Q.
quelle