Получить журнал фиксации для определенной строки в файле? - PullRequest
434 голосов
/ 08 декабря 2011

Есть ли способ заставить git выдать вам журнал коммитов только для коммитов, которые коснулись определенной строки в файле?

Как git blame, но git blame будетпоказать последний коммит, который коснулся конкретной строки.

Мне бы очень хотелось получить похожий журнал, а не список коммитов в любом месте файла, а только коммиты, которые коснулись определенной строки.

Ответы [ 10 ]

543 голосов
/ 04 ноября 2013

См. Также Git: узнайте, какие коммиты когда-либо касались диапазона строк .


Начиная с Git 1.8.4 , git log имеет -L для просмотра эволюции ряда линий.

Например, предположим, вы смотрите на вывод git blame. Здесь -L 150,+11 означает «смотреть только на линии от 150 до 150 + 11»:

$ git blame -L 150,+11 -- git-web--browse.sh
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &

И вы хотите узнать историю того, что сейчас является линией 155.

Затем используйте git log. Здесь -L 155,155:git-web--browse.sh означает «проследить эволюцию строк с 155 по 155 в файле с именем git-web--browse.sh».

$ git log --pretty=short -u -L 155,155:git-web--browse.sh
commit 81f42f11496b9117273939c98d270af273c8a463
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: support opera, seamonkey and elinks

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -143,1 +143,1 @@
-firefox|iceweasel)
+firefox|iceweasel|seamonkey|iceape)

commit a180055a47c6793eaaba6289f623cff32644215b
Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>

    web--browse: coding style

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -142,1 +142,1 @@
-    firefox|iceweasel)
+firefox|iceweasel)

commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
Author: Christian Couder <chriscool@tuxfamily.org>

    Rename 'git-help--browse.sh' to 'git-web--browse.sh'.

diff --git a/git-web--browse.sh b/git-web--browse.sh
--- /dev/null
+++ b/git-web--browse.sh
@@ -0,0 +127,1 @@
+    firefox|iceweasel)
61 голосов
/ 08 декабря 2011

Вы можете получить набор коммитов, используя кирку.

git log -S'the line from your file' -- path/to/your/file.txt

Это даст вам все коммиты, которые повлияли на этот текст в этом файле.Если файл был переименован в какой-то момент, вы можете добавить --follow-parent.

Если вы хотите проверять коммиты при каждом из этих правок, вы можете передать результат в git show:

git log ... | xargs -n 1 git show
38 голосов
/ 13 августа 2015

Попробуйте использовать приведенную ниже команду, реализованную в Git 1.8.4.

git log -u -L <upperLimit>,<lowerLimit>:<path_to_filename>

Итак, в вашем случае upperLimit & lowerLimit - это прикосновение line_number

Подробнее -https://www.techpurohit.com/list-some-useful-git-commands

13 голосов
/ 25 марта 2015

Очень простой способ сделать это - использовать vim-fugitive . Просто откройте файл в vim, выберите нужные строки, используя V, затем введите

:Glog

Теперь вы можете использовать :cnext и :cprev, чтобы увидеть все ревизии файла, в котором изменена эта строка. В любой момент введите :Gblame, чтобы увидеть информацию о ша, авторе и дате.

11 голосов
/ 09 ноября 2017

Упрощение ответа @ Мэтта -

git blame -L14,15 -- <file_path>

Здесь вы получите вину за строки 14 to 15.

С -L опцияожидает, что Range в качестве параметра, мы не можем получить Blame для одной строки, используя -L option` .

Reference

10 голосов
/ 08 декабря 2011

Я не верю, что для этого есть что-то встроенное. Это усложняется тем фактом, что редко бывает, что одна строка меняется несколько раз без существенного изменения остальной части файла, поэтому вы, как правило, в конечном итоге будете сильно менять номера строк.

Если вам повезло, что в строке всегда есть некоторая идентифицирующая характеристика, например, присваивание переменной, имя которой никогда не менялось, вы можете использовать регулярное выражение для git blame -L. Например:

git blame -L '/variable_name *= */',+1

Но это только находит совпадение first для этого регулярного выражения, поэтому, если у вас нет хорошего способа сопоставления строки, это не слишком полезно.

Вы могли бы что-то взломать, я полагаю. У меня нет времени писать код только сейчас, но ... что-то в этом роде. Запустите git blame -n -L $n,$n $file. Первое поле - это предыдущий коммит, а второе - номер строки в , который фиксирует, так как он мог измениться. Возьмите их и запустите git blame -n $n,$n $commit^ $file, то есть то же самое, начиная с коммита до последнего изменения файла.

(Обратите внимание, что это не даст вам результата, если последний коммит, который изменил строку, был коммитом слияния. Основной способ это могло произойти, если строка была изменена как часть разрешения конфликта слияния.)

Редактировать: Я встречался в этом посте списка рассылки с марта 2011 года сегодня, в котором упоминается, что tig и git gui имеют функцию, которая поможет вам сделать это. Похоже, что функция была рассмотрена, но не завершена, для самого git.

6 голосов
/ 19 августа 2013

Вот решение, которое определяет псевдоним git, поэтому вы сможете использовать его следующим образом:

git rblame -M -n -L '/REGEX/,+1' FILE

Пример вывода:

00000000 18 (Not Committed Yet 2013-08-19 13:04:52 +0000 728) fooREGEXbar
15227b97 18 (User1 2013-07-11 18:51:26 +0000 728) fooREGEX
1748695d 23 (User2 2013-03-19 21:09:09 +0000 741) REGEXbar

Вы можете определить псевдоним вваш .gitconfig или просто выполните следующую команду

git config alias.rblame !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); echo $line; done' dumb_param

Это уродливая однострочная строка, поэтому здесь приведена де-запутанная эквивалентная функция bash:

git-rblame () {
    local commit line
    while line=$(git blame "$@" $commit 2>/dev/null); do
        commit="${line:0:8}^"
        if [ "00000000^" == "$commit" ]; then
            commit=$(git rev-parse HEAD)
        fi
        echo $line
    done
}

Решение кирки ( git log --pickaxe-regex -S'REGEX ') даст вам только добавления / удаления строк, а не другие изменения строки, содержащей обычныеexpression.

Ограничением этого решения является то, что git blame возвращает только 1-е совпадение с REGEX, поэтому, если существует несколько совпадений, рекурсия может «перепрыгнуть», чтобы следовать за другой строкой.Обязательно проверьте полный вывод истории, чтобы определить эти «скачки», а затем исправьте свой REGEX, чтобы игнорировать линии паразитов.

Наконец, вот альтернативная версия, которая запускает git show на каждомсовершить, чтобы получить полный diff:

git config alias.rblameshow !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); git show $commit; done' dumb_param
6 голосов
/ 23 июля 2013

Это вызовет git blame для каждой значимой ревизии, чтобы показать строку $LINE файла $FILE:

git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE

Как обычно, обвинение показывает номер ревизии вначало каждой строки.Вы можете добавить

| sort | uniq -c

, чтобы получить агрегированные результаты, что-то вроде списка коммитов, которые изменили эту строку.(Не совсем, если бы только код был перемещен, это может показывать один и тот же идентификатор коммита дважды для различного содержимого строки. Для более детального анализа вам нужно будет выполнить запаздывающее сравнение результатов git blame для смежных коммитов. Кто-нибудь?)

2 голосов
/ 13 января 2017

В моем случае номер строки сильно изменился с течением времени. Я также был на git 1.8.3, которая не поддерживает регулярные выражения в "git blame -L". (RHEL7 все еще имеет 1.8.3)

myfile=haproxy.cfg
git rev-list HEAD -- $myfile | while read i
do
    git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"
done | grep "<sometext>"

Oneliner:

myfile=<myfile> ; git rev-list HEAD -- $myfile | while read i; do     git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"; done | grep "<sometext>"

Это, конечно, можно превратить в скрипт или функцию.

1 голос
/ 05 марта 2016

Вы можете смешать команды git blame и git log, чтобы получить сводную информацию о каждом коммите в команде git blame и добавить их. Что-то вроде следующего скрипта bash + awk. Он добавляет сводку коммита как встроенный комментарий к коду.

git blame FILE_NAME | awk -F" " \
'{
   commit = substr($0, 0, 8);
   if (!a[commit]) {
     query = "git log --oneline -n 1 " commit " --";
     (query | getline a[commit]);
   }
   print $0 "  // " substr(a[commit], 9);
 }'

В одну строку:

git blame FILE_NAME | awk -F" " '{ commit = substr($0, 0, 8); if (!a[commit]) { query = "git log --oneline -n 1 " commit " --"; (query | getline a[commit]); } print $0 "  // " substr(a[commit], 9); }'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...