Używając git diff, w jaki sposób mogę uzyskać dodane i zmodyfikowane numery linii?

80

Zakładając, że mam plik tekstowy

alex
bob
matrix
will be removed
git repo

i zaktualizowałem go, aby był

alex
new line here
another new line
bob
matrix
git

Tutaj dodałem numer linii (2,3) i zaktualizowałem numer linii (6)

Jak mogę uzyskać informacje o tych numerach linii za pomocą git diff lub innego polecenia git?

Mahmoud Khaled
źródło

Odpowiedzi:

78

git diff --stat pokaże ci wynik, jaki uzyskasz podczas zatwierdzania rzeczy, o których myślę, że się odnosisz.

git diff --stat

Aby pokazać dokładnie numery linii, które zostały zmienione, możesz użyć

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

A zmieniona linia będzie ostatnią liczbą przed końcowym nawiasem w wyniku. Nie jest to jednak czyste rozwiązanie :(

Sedrik
źródło
3
stat wyświetla tylko, ile wierszy zostało wstawionych / usuniętych / zaktualizowanych. Ale muszę wiedzieć, które numery linii
Mahmoud Khaled,
Wydawało się, że jest to trudniejszy problem niż powinien, ale udało mi się go rozwiązać za pomocą git blame i grep. Zobacz moją zaktualizowaną odpowiedź
Sedrik
1
Zwykle należy wywołać „git blame -p”, jeśli dane wyjściowe mają być przetwarzane przez inne programy, takie jak „awk” lub „grep”.
Mikko Rantalainen
8
git blame nie złapie usuniętych linii
Vitali
2
Dlaczego jest to oznaczone jako poprawne, skoro nie robi tego, o co prosił OP?
Shardj,
27

Oto funkcja bash do obliczania wynikowych numerów wierszy z różnicy:

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
}

Może generować dane wyjściowe, takie jak:

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

z różnicy, takiej jak ta:

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

Jeśli chcesz pokazać tylko dodane / usunięte / zmodyfikowane linie, a nie otaczający kontekst, możesz przekazać -U0do 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);

Jest odporny na kody kolorów ANSI, więc możesz przejść --color=alwaysdo git diff, aby uzyskać zwykłe kodowanie kolorów dla dodanych / usuniętych linii.

Wynik można łatwo grepować:

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

W twoim przypadku git diff -U0dałoby:

$ 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

Jeśli chcesz tylko numery linii, zmień na echo "$path:$line:$REPLY"just echo "$line"i potokuj wyjście uniq.

John Mellor
źródło
Jak mogę przekazać kody ucieczki kolorów bash? To jest świetne, ale kody kolorów pochodzące z git diff --colornie przechodzą. A może myślisz, że byłoby lepiej po prostu dodać ucieczki koloru do powrotu z tej funkcji?
Nowa Aleksandria
2
Zaktualizowałem funkcję, aby różne wyrażenia regularne były odporne na kody kolorów ANSI. git diff --color | diff-linesteraz działa zgodnie z oczekiwaniami :)
John Mellor
1
To rozwiązanie działa świetnie! powinien zostać oznaczony jako odpowiedź, ponieważ naprawdę robi to, o co prosił PO. Jeśli pracował dla was zadowolić się głos tak możemy to zrobić popularnej odpowiedzi :)
markdrake
Ciągle otrzymuję ten błąd, używając zsh: zsh: parse error near `]+m'jakieś pomysły? Błąd pochodzi z tej linii:elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
Hosh Sadiq
@HoshSadiq Wydaje się, że zadziałało zwykłe cytowanie wyrażenia regularnego.
Koobz
20

Korzystam z --unified=0opcji git diff.

Na przykład git diff --unified=0 commit1 commit2wyświetla różnicę:

* wprowadź opis zdjęcia tutaj *

Ze względu na tę --unified=0opcję, wyjście diff pokazuje 0 linii kontekstu; innymi słowy, pokazuje dokładnie zmienione linie .

Teraz możesz zidentyfikować wiersze zaczynające się od „@@” i przeanalizować je na podstawie wzorca:

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

Wracając do powyższego przykładu, dla pliku WildcardBinding.java, zacznij od wiersza 910, usuwanych jest 0 wierszy. Zacznij od linii 911, dodawane są 4 linie.

Ida
źródło
1
co jeśli @@ -910,10,+911,15@@ lub coś, to jak dokładnie określimy, ile wierszy jest dodawanych,
usuwanych
1
Czy masz dobry sposób na wyświetlenie numerów linii na liście, o którą prosił OP?
Shardj,
7

Miałem ten sam problem, więc napisałem skrypt gawk, który zmienia wynik działania git diff tak, aby poprzedzał numer linii dla każdej linii. Uważam to za przydatne czasami, gdy potrzebuję porównać drzewo robocze, chociaż nie jest to ograniczone do tego. Może komuś się tutaj przyda?

$ 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

Możesz go pobrać stąd:
https://github.com/jay/showlinenum

Sójka
źródło
Wygląda bardzo poręcznie. Należy pamiętać, że ten kod ma tę zaletę (lub wadę), że jest licencjonowany na GPL.
BlackVegetable
Napisałem,git diffn aby to zrobić, i w pełni zachowuje kolory terminala i pokazuje numery wierszy zarówno starego pliku po lewej, jak i nowego pliku po prawej.
Gabriel Staples
4

Numery wierszy wszystkich niezatwierdzonych wierszy (dodane / zmodyfikowane):

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

Przykładowe dane wyjściowe:

1
2
8
12
13
14
Bogaty
źródło
co z zawartością wierszy, które również zostały zmienione?
anon58192932
2

Skonfiguruj zewnętrzne narzędzie porównujące, które pokaże numery linii. Na przykład to jest to, co mam w mojej globalnej konfiguracji 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"

Zobacz tę odpowiedź, aby uzyskać więcej informacji: https://stackoverflow.com/q/949242/526535

manojlds
źródło
czy nie ma innego sposobu na uzyskanie tych informacji bez użycia narzędzia porównywania. Używasz tylko poleceń git?
Mahmoud Khaled
1

Oto funkcja bash, którą zebrałem razem:

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

I wygląda to tak:

views.py:
403,404,533-538,546-548,550-552,554-559,565-567,580-582
forivall
źródło
1

Jest to prawdopodobnie dość dokładna liczba zmienionych linii:

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

Również tutaj jest rozwiązanie dla numerów linii w twoim diff: https://github.com/jay/showlinenum

Jay Carroll
źródło
0

Nie jest to dokładnie to, o co prosiłeś, ale git blame TEXTFILEmoże pomóc.

u-punkt
źródło
0

Możesz użyć w git diffpołączeniu z shortstatparametrem, aby po prostu pokazać liczbę zmienionych linii.

Dla liczby linii zmienionych (w pliku, który jest już w repozytorium) od ostatniego zatwierdzenia

git diff HEAD --shortstat

Wyświetli coś podobnego do

1 file changed, 4 insertions(+)
shahalpk
źródło
Pytanie dotyczy numerów linii dla każdej linii, która została zmieniona, a nie łącznej liczby zmienionych linii.
Pro Q
0

Szukałem sposobu, aby wyświetlić tylko te wiersze zmienione dla każdego pliku za pomocą git diff. Moim pomysłem było przekazanie tego wyjścia do lintera w celu sprawdzenia typu. To mi pomogło

maximus
źródło
0

Oto trochę Python copypasta, aby uzyskać numery linii dla zmodyfikowanych / usuniętych linii, na wypadek gdybyś natknął się na to pytanie, szukając tego.

Powinno być dość łatwo zmodyfikować go w coś, co pobiera również zmodyfikowane i dodane numery linii.

Testowałem tylko w systemie Windows, ale powinien również działać na wielu platformach.

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
źródło