Як я можу отримати додані та змінені номери рядків за допомогою git diff?


80

Якщо припустити, що у мене є текстовий файл

alex
bob
matrix
will be removed
git repo

і я його оновив

alex
new line here
another new line
bob
matrix
git

Тут я додав номер рядка (2,3) та оновлений номер рядка (6)

Як я можу отримати інформацію про ці номери рядків за допомогою git diff або будь-якої іншої команди git?

Відповіді:


78

git diff --stat покаже вам вихідні дані, які ви отримуєте при здійсненні речей, які я маю на увазі.

git diff --stat

Для відображення точно змінених номерів рядків ви можете використовувати

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

І змінений рядок буде останнім числом перед кінцевою дужкою в результаті. Не чисте рішення, хоча :(


3
stat відображає лише кількість вставлених / видалених / оновлених рядків. Але мені потрібно знати, які номери рядків
Махмуд Халед

Здавалося, це є більш складною проблемою, ніж це мало б бути, але мені вдалося її отримати, використовуючи git вина і grep. Дивіться мою оновлену відповідь
Седрік

1
Зазвичай слід зателефонувати "git винувати -p", якщо результат буде оброблятися іншими програмами, такими як "awk" або "grep".
Мікко Ранталайнен

8
git вина не буде ловити видалені рядки
Vitali

2
Чому це позначено як правильне, коли воно не виконує те, що просив ОП?
Шардж

27

Ось функція bash для обчислення отриманих номерів рядків з різниці:

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, щоб отримати звичайне кольорове кодування для доданих / видалених рядків.

Вихід можна легко отримати:

$ 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не надходять. Або ви вважаєте, що було б краще просто додати кольорові екрани у повернення від цієї функції?
Нова Олександрія

2
Я оновив функцію, щоб різні регулярні вирази були надійними до колірних кодів ANSI. git diff --color | diff-linesзараз працює, як очікувалось :)
Джон Меллор

1
Це рішення працює неймовірно! його слід позначити як відповідь, оскільки він насправді робить те, що запитував ОП. Якщо вам це
вдалося,

Я постійно отримую цю помилку за допомогою zsh: zsh: parse error near `]+m'Будь-які ідеї? Помилка випливає з цього рядка:elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
Hosh Sadiq

@HoshSadiq Просто цитування регулярного виразу, здається, спрацювало.
Koobz

20

Я використовую --unified=0опцію git diff.

Наприклад, git diff --unified=0 commit1 commit2виводить різницю:

* введіть тут опис зображення *

Через цю --unified=0опцію вивід diff показує 0 контекстних рядків; іншими словами, він показує точно змінені рядки .

Тепер ви можете визначити рядки, які починаються на '@@', і проаналізувати його на основі шаблону:

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

Повертаючись до наведеного вище прикладу, для файлу WildcardBinding.java, починаючи з рядка 910, 0 рядків видаляються. Почніть з рядка 911, додано 4 рядки.


1
що якщо @@ -910,10,+911,15@@ чи щось таке, як тоді сказати, скільки саме рядків додано, видалено або змінено
Касун Сіямбалапітія

1
Чи є у вас хороший спосіб вивести номери рядків у такому списку, як просив OP?
Шардж

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

Я теж писав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


чи немає іншого способу отримати цю інформацію без використання інструменту diff. Тільки за допомогою команд git?
Махмуд Халед

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

Я шукав спосіб вивести лише рядки, змінені для кожного файлу, використовуючи git diff. Моя ідея полягала в тому, щоб подати цей результат до лінтера для перевірки типу. Це мені допомогло


0

Ось деякі копії Python, щоб отримати номери рядків для змінених / видалених рядків, на випадок, якщо ви зіткнулися з цим питанням, шукаючи це.

Це повинно бути досить легко змінити його на те, що також отримує змінені та додані номери рядків.

Я тестував лише на 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")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.