git blame (не определяет перемещение и копирование строк между файлами)

IDE, VCS и прочее

Модератор: Модераторы разделов

Аватара пользователя
sgfault
Сообщения: 586
Статус: -

git blame

Сообщение sgfault »

Вот репозиторий:

Код: Выделить всё

$ git log --oneline  --name-status --graph
* 0ad6eb6 Change g.
| M     g
* 51a479e Changes from other files to f.
| M     f
| M     g
| A     k
| M     l
* aac7be6 Add line to g, h, l.
| M     g
| M     h
| M     l
* e70bb4b Initail commit. Add f, g, h, l.
  A     f
  A     g
  A     h
  A     l


Код: Выделить всё

commit 0ad6eb69e111278caf1debcbf5e04ce5fbb4c7c5
Author: sgf <sgf.dma@gmail.com>
Date:   Fri Nov 23 21:53:06 2012 +0400

    Change g.

diff --git a/g b/g
index 538e316..5fd00e5 100644
--- a/g
+++ b/g
@@ -1 +1,2 @@
 blue blue blue blue blue blue blue blue blue blue blue blue blue blue blue
+pink pink pink pink pink pink pink pink pink pink pink pink pink pink pink

commit 51a479eb3aecc6bb8e3dfde441a8ed149632f9ff
Author: sgf <sgf.dma@gmail.com>
Date:   Fri Nov 23 21:52:25 2012 +0400

    Changes from other files to f.

diff --git a/f b/f
index 3beb53d..bc7d86b 100644
--- a/f
+++ b/f
@@ -1 +1,5 @@
 green green green green green green green green green green green green green
+yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
+cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
+black black black black black black black black black black black black black
+purple purple purple purple purple purple purple purple purple purple purple
diff --git a/g b/g
index 6228ae4..538e316 100644
--- a/g
+++ b/g
@@ -1,2 +1 @@
 blue blue blue blue blue blue blue blue blue blue blue blue blue blue blue
-yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
diff --git a/k b/k
new file mode 100644
index 0000000..c851261
--- /dev/null
+++ b/k
@@ -0,0 +1,2 @@
+red red red red red red red red red red red red red red red red red red red
+purple purple purple purple purple purple purple purple purple purple purple
diff --git a/l b/l
index 85edc30..eb6cf5a 100644
--- a/l
+++ b/l
@@ -1,2 +1,3 @@
 black black black black black black black black black black black black black
 grey grey grey grey grey grey grey grey grey grey grey grey grey grey grey
+silver silver silver silver silver silver silver silver silver silver silver

commit aac7be6c49312c06fd1e7798fd3137507776fd20
Author: sgf <sgf.dma@gmail.com>
Date:   Fri Nov 23 21:36:17 2012 +0400

    Add line to g, h, l.

diff --git a/g b/g
index 538e316..6228ae4 100644
--- a/g
+++ b/g
@@ -1 +1,2 @@
 blue blue blue blue blue blue blue blue blue blue blue blue blue blue blue
+yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
diff --git a/h b/h
index 4d478d6..fe5f36f 100644
--- a/h
+++ b/h
@@ -1 +1,2 @@
 cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
+magenta magenta magenta magenta magenta magenta magenta magenta magenta
diff --git a/l b/l
index ad61d2d..85edc30 100644
--- a/l
+++ b/l
@@ -1 +1,2 @@
 black black black black black black black black black black black black black
+grey grey grey grey grey grey grey grey grey grey grey grey grey grey grey

commit e70bb4b5507cc3618cbb5bd1c2e48a9fd8212f1e
Author: sgf <sgf.dma@gmail.com>
Date:   Fri Nov 23 21:32:27 2012 +0400

    Initail commit. Add f, g, h, l.

diff --git a/f b/f
new file mode 100644
index 0000000..3beb53d
--- /dev/null
+++ b/f
@@ -0,0 +1 @@
+green green green green green green green green green green green green green
diff --git a/g b/g
new file mode 100644
index 0000000..538e316
--- /dev/null
+++ b/g
@@ -0,0 +1 @@
+blue blue blue blue blue blue blue blue blue blue blue blue blue blue blue
diff --git a/h b/h
new file mode 100644
index 0000000..4d478d6
--- /dev/null
+++ b/h
@@ -0,0 +1 @@
+cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
diff --git a/l b/l
new file mode 100644
index 0000000..ad61d2d
--- /dev/null
+++ b/l
@@ -0,0 +1 @@
+black black black black black black black black black black black black black


Те, как вы можете видеть, в комите 51a479e в файл f было скопировано по одной строчке из файлов h, k, l и перемещена одна строчка из файла g. Те, насколько я понимаю, git blame должен определить, что хотя бы какие-то строки были взяты из других файлов, но он не находит вообще ничего:

Код: Выделить всё

$ git blame -p -C -C -C10 '51a479e^!' -- f
aac7be6c49312c06fd1e7798fd3137507776fd20 1 1 1
author sgf
author-mail <sgf.dma@gmail.com>
author-time 1353692177
author-tz +0400
committer sgf
committer-mail <sgf.dma@gmail.com>
committer-time 1353692177
committer-tz +0400
summary Add line to g, h, l.
boundary
filename f
        green green green green green green green green green green green green green
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 2 2 4
author sgf
author-mail <sgf.dma@gmail.com>
author-time 1353693145
author-tz +0400
committer sgf
committer-mail <sgf.dma@gmail.com>
committer-time 1353693145
committer-tz +0400
summary Changes from other files to f.
previous aac7be6c49312c06fd1e7798fd3137507776fd20 f
filename f
        yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 3 3
        cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 4 4
        black black black black black black black black black black black black black
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 5 5
        purple purple purple purple purple purple purple purple purple purple purple

и

Код: Выделить всё

$ git blame -p -C -C -C10 51a479e -- f
e70bb4b5507cc3618cbb5bd1c2e48a9fd8212f1e 1 1 1
author sgf
author-mail <sgf.dma@gmail.com>
author-time 1353691947
author-tz +0400
committer sgf
committer-mail <sgf.dma@gmail.com>
committer-time 1353691947
committer-tz +0400
summary Initail commit. Add f, g, h, l.
boundary
filename f
        green green green green green green green green green green green green green
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 2 2 4
author sgf
author-mail <sgf.dma@gmail.com>
author-time 1353693145
author-tz +0400
committer sgf
committer-mail <sgf.dma@gmail.com>
committer-time 1353693145
committer-tz +0400
summary Changes from other files to f.
previous aac7be6c49312c06fd1e7798fd3137507776fd20 f
filename f
        yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 3 3
        cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 4 4
        black black black black black black black black black black black black black
51a479eb3aecc6bb8e3dfde441a8ed149632f9ff 5 5
        purple purple purple purple purple purple purple purple purple purple purple


Почему?
Спасибо сказали:
Аватара пользователя
sgfault
Сообщения: 586
Статус: -

Re: git blame

Сообщение sgfault »

В последний момент перед пуском я решил проверить основную деталь своего "двигателя" - git blame, - и результат оказался для меня совершенно неожиданным. Я до сих пор не могу ответить на свой вопрос (первый пост), но, честно говоря, меня гораздо больше интересовал другой: что конкретно делают две опции '-C' ? В мане явно не хватало подробностей. И на этот вопрос я, думаю, наконец-то ответить могу. Но начну я все равно с первого.

Вместо вступления добавлю, что в исходники я не смотрел и не буду. Так что все объяснения ниже могут быть неверными.

Судя по всему, для опции '-C' существует минимальное количество последовательных строк из файла F, которое должно (последовательно) совпасть где-то в файла G, чтобы blame признал файл G "источником" этих строк. Видимо, это минимальное количество - 3-и строки (>= 3). И хотя в репозитории из первого поста можно сделать, добавив ограничение на линии ('-L'), чтобы blame нашел одну строку, скопированную из другого файла,

Код: Выделить всё

$ git blame -f -n -C -C -C -- f | cat -
^e70bb4b f 1 (sgf 2012-11-23 21:32:27 +0400 1) green green green green green green green green green green green green green
51a479eb f 2 (sgf 2012-11-23 21:52:25 +0400 2) yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
51a479eb f 3 (sgf 2012-11-23 21:52:25 +0400 3) cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
51a479eb f 4 (sgf 2012-11-23 21:52:25 +0400 4) black black black black black black black black black black black black black
51a479eb f 5 (sgf 2012-11-23 21:52:25 +0400 5) purple purple purple purple purple purple purple purple purple purple purple
$ git blame -f -n -C -L2,2 -- f | cat -
aac7be6c g 2 (sgf 2012-11-23 21:36:17 +0400 2) yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow yellow
$ git blame -f -n -C -C -C -L3,3 -- f | cat -
^e70bb4b h 1 (sgf 2012-11-23 21:32:27 +0400 3) cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan cyan
$ git blame -f -n -C -L4,4 -- f | cat -
^e70bb4b l 1 (sgf 2012-11-23 21:32:27 +0400 4) black black black black black black black black black black black black black

в других такой фокус повторить не удается. Как раз наоборот, добавление ограничение (1 или 2 строки) приводит к тому, что blame не определяет перемещение совсем. Также, обычно, blame может отнести одну строку к другому файлу, но только если есть еще совпадения с этим файлом и там было >= 3-ех строк. Что касается аргумента опции '-C', то больше похоже на то, что он изменяет минимальную длину строки, а не минимальное количество строк.

Поэтому все изменения в тестах были > 3 строк. Во всех случаях я запускал blame для всего файла.

Обратите внимание, что вызов

Код: Выделить всё

$ git blame -CC

неправильный! Те это сработает, но как с _одной_ опцией '-C' (вне зависимости от того, сколько вы укажете).

1. Без опции '-C'. Строки приписываются тому же файлу на коммите, который последним изменил соответствующую строку.
2. Одна опция '-C'. Строки, которые были последний раз изменены на коммите C, blame будет искать в версиях из коммита C^ всех файлов, которые были изменены на коммите С. Те если один и тот же текст был добавлен в файлы F и G на коммите C, то файл G не будет "источником" для этих строчек (ну, так все и должно быть). Также это означает, что изменения в файле G на коммите C не имеют значения при поиске "источника" строчек. Для строчек, источником которых является файл G, будет указан коммит, который последним изменяет эти строчки в файле G. В частности, из этого следует, что для строчек, источником которых является другой файл, коммит C не будет указан никогда.

Пример.

Код: Выделить всё

    $ git log --oneline --name-status  | cat -
    1e84185 Move from SWO to SW. Copy from SWT to SW. Change (partially) copied lines in SWT. Add the same to SWC and SW.
    M       ShowWords.hs
    M       ShowWordsConfig.hs
    M       ShowWordsOutput.hs
    M       ShowWordsText.hs
    6a36c44 Add SW.
    A       ShowWords.hs
    579b036 Add SWO.
    A       ShowWordsOutput.hs
    12d5b07 Initial commit. Add SWC, SWT.
    A       ShowWordsConfig.hs
    A       ShowWordsText.hs


Описание изменений на коммите 1e84185:
- Несколько строчек перемещены из ShowWordsOutput в ShowWords (те они были удалены из ShowWordsOutput).
- Несколько строчек скопированы из ShowWordsText в ShowWords. В ShowWordsText изменены некоторые из скопированных строчек.
- Одни и те же строчки добавлены в ShowWordsConfig и ShowWords.

Как было написано выше, ни одна из строчек ShowWords не будет приписана файлу ShowWordsConfig, даже с тремя '-C':

Код: Выделить всё

    $ git blame --incremental -C -C -C -- ShowWords.hs | grep filename
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWordsOutput.hs
    filename ShowWordsText.hs


строчки из файлов ShowWordsOutput и ShowWordsText будут найдены при одном '-C':

Код: Выделить всё

    $ git blame --incremental -C  -- ShowWords.hs | grep filename
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWordsOutput.hs
    filename ShowWordsText.hs


3. Две опции '-C'. Строчки, которые _не_ были изменены после коммита C, на котором файл F был добавлен в репозиторий, blame будет искать в версиях из коммита C^ _всех_ файлов на коммите C. Строчки, измененные последний раз на других коммитах, blame будет искать только в файлах, измененных на соответствующих коммитах (также, как с одной '-C'). Другими словами, две опции '-C' могут изменить результат только для строчек, которые не изменялись с момента добавления файла F в репозиторий.

Пример.

Код: Выделить всё

    $ git log --oneline --name-status
    3421478 Move from SWO to SW. Copy from SWC and SOL to SW.
    M       ShowWords.hs
    M       ShowWordsOutput.hs
    4f0d505 Add SOL.
    A       SgfOrderedLine.hs
    ebfa18d Add to SWT and SW the same func. Copy from SWC and SL to SW.
    A       ShowWords.hs
    M       ShowWordsText.hs
    5c84fe3 Add SL.
    A       SgfList.hs
    f5b0553 Initial commit. Add SWT, SWO, SWC.
    A       ShowWordsConfig.hs
    A       ShowWordsOutput.hs
    A       ShowWordsText.hs


Описание изменений на коммите ebfa18d:
- Одни и те же строчки добавлены в файлы ShowWords и ShowWordsText.
- Скопированы несколько строчек из ShowWordsConfig в ShowWords (ShowWordsConfig не изменялся).
- Скопированы несколько строчек из SgfList в ShowWords (SgfList не изменялся).

Описание изменений на коммите 3421478:
- Несколько строчек перемещены из ShowWordsOutput в ShowWords (соответственно, эти строчки удалены из ShowWordsOutput).
- Другие несколько строчек (_не_ те же, что на коммите ebfa18d) скопированы из ShowWordsConfig в ShowWords (ShowWordsConfig не изменялся).
- Несколько строчек скопированы из SgfOrderedLine в ShowWords (SgfOrderedLine остался без изменений).

Одна опция '-C' заметит только перемещение из ShowWordsOutput:

Код: Выделить всё

    $ git blame --incremental -C -- ShowWords.hs | grep filename
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWordsOutput.hs


Две опции '-C', кроме этого, найдут копирование из SgfList и _первое_ копирование (на коммите ebfa18d) из ShowWordsConfig:

Код: Выделить всё

    $ git blame --incremental -C -C -- ShowWords.hs | grep filename
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWordsConfig.hs
    filename SgfList.hs
    filename ShowWordsOutput.hs


Три опции '-C', кроме всего предыдущего, найдут копирование из SgfOrderedLine и _второе_ копирование (на коммите 3421478) из ShowWordsConfig:

Код: Выделить всё

    $ git blame --incremental -C -C -C -- ShowWords.hs | grep filename
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWords.hs
    filename ShowWordsConfig.hs
    filename SgfList.hs
    filename ShowWordsOutput.hs
    filename ShowWordsConfig.hs
    filename SgfOrderedLine.hs


4. Три опции '-C'. Строчки, которые были изменены последний раз на коммите C, blame будет искать в версиях из коммита C^ _всех_ файлов существующих на коммите C.


Это был сокращенный и вольный (авторский) перевод Strange behavior of git blame '-C' option. Можно считать, что основная деталь моего "двигателя" снова заработала, вот только не слишком ли поздно.. :huh:

Upd1.
Обновлен пункт 2.
Спасибо сказали: