为什么移动的代码在 git diff 中没有着色?
why moved code is not colored in git diff?
我安装最新的 git 并将其配置为突出显示移动的代码:
$ git config diff.colormoved default
这是移动代码后的样子(参见 1->2)
但是 3-4 没有突出显示为移动代码。
这是独立的更改:
请参阅 git-diff(1) 中 --color-moved
/colormoved
的文档:
--color-moved[=<mode>]
Moved lines of code are colored differently. It can be changed by the diff.colorMoved
configuration setting. The <mode>
defaults to no
if the option is not given and to zebra
if the option with no mode is given. The mode must be one of:
no
Moved lines are not highlighted.
default
Is a synonym for zebra. This may change to a more sensible mode in the future.
plain
Any line that is added in one location and was removed in another location will be colored with color.diff.newMoved. Similarly
color.diff.oldMoved will be used for removed lines that are added
somewhere else in the diff. This mode picks up any moved line, but it
is not very useful in a review to
determine if a block of code was moved without permutation.
zebra
Blocks of moved text of at least 20 alphanumeric characters are detected greedily. The detected blocks are painted using either the
color.diff.{old,new}Moved color or
color.diff.{old,new}MovedAlternative. The change between the two
colors indicates that a new block was detected.
dimmed_zebra
Similar to zebra, but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent blocks
are considered interesting, the rest is uninteresting.
具体来说,默认值是 zebra
并且它检测到
Blocks of moved text of at least 20 alphanumeric characters
。 my $ctx = shift;
不包含至少 20 个字母数字字符。如果您使用 git diff --color-moved=plain
,或将 # ten more ANs
添加到行尾,您的示例将突出显示为已移动。
请注意,还有另一种情况,彩色移动代码(当代码量足够时)会产生不当的副作用:
“git diff --color-moved --cc --stat -p
”由于 color-moved 中的错误与其他错误之间的有趣交互而无法正常工作。
此问题已在 Git 2.21(2019 年 2 月)
中修复
参见 commit dac03b5, commit 04b19fc, commit 8290faa, commit 8817f0c, commit 48edf3a, commit 426fd36 (24 Jan 2019) by Jeff King (peff
)。
(由 Junio C Hamano -- gitster
-- in commit 5d2710b 合并,2019 年 2 月 5 日)
diff: clear emitted_symbols flag after use
There's an odd bug when "log --color-moved
" is used with the combination
of "--cc --stat -p
": the stat for merge commits is erroneously shown
with the diff of the next commit.
The included test demonstrates the issue.
Our history looks something like this:
A-B-M--D
\ /
C
When we run "git log --cc --stat -p --color-moved
" starting at D
, we get
this sequence of events:
The diff for D
is using -p
, so diff_flush()
calls into
diff_flush_patch_all_file_pairs()
. There we see that o->color_moved
is in effect, so we point o->emitted_symbols
to a static local
struct, causing diff_flush_patch()
to queue the symbols instead of
actually writing them out.
We then do our move detection, emit the symbols, and clear the
struct. But we leave o->emitted_symbols
pointing to our struct.
Next we compute the diff for M
. This is a merge, so we use the
combined diff code.
In find_paths_generic()
, we compute the
pairwise diff between each commit and its parent. Normally this is
done with DIFF_FORMAT_NO_OUTPUT
, since we're just looking for
intersecting paths. But since "--stat --cc
" shows the first-parent
stat, and since we're computing that diff anyway, we enable
DIFF_FORMAT_DIFFSTAT
for the first parent. This outputs the stat
information immediately, saving us from running a separate
first-parent diff later.
But where does that output go? Normally it goes directly to stdout,
but because o->emitted_symbols
is set, we queue it. As a result, we
don't actually print the diffstat for the merge commit (yet), which
is wrong.
- Next we compute the diff for
C
. We're actually showing a patch
again, so we end up in diff_flush_patch_all_file_pairs()
, but this
time we have the queued stat from step 2 waiting in our struct.
We add new elements to it for C
's diff, and then flush the whole
thing. And we see the diffstat
from M
as part of C
's diff, which is
wrong.
So triggering the bug really does require the combination of all of
those options.
我安装最新的 git 并将其配置为突出显示移动的代码:
$ git config diff.colormoved default
这是移动代码后的样子(参见 1->2)
但是 3-4 没有突出显示为移动代码。
这是独立的更改:
请参阅 git-diff(1) 中 --color-moved
/colormoved
的文档:
--color-moved[=<mode>]
Moved lines of code are colored differently. It can be changed by the
diff.colorMoved
configuration setting. The<mode>
defaults tono
if the option is not given and tozebra
if the option with no mode is given. The mode must be one of:
no
Moved lines are not highlighted.default
Is a synonym for zebra. This may change to a more sensible mode in the future.plain
Any line that is added in one location and was removed in another location will be colored with color.diff.newMoved. Similarly color.diff.oldMoved will be used for removed lines that are added somewhere else in the diff. This mode picks up any moved line, but it is not very useful in a review to determine if a block of code was moved without permutation.zebra
Blocks of moved text of at least 20 alphanumeric characters are detected greedily. The detected blocks are painted using either the color.diff.{old,new}Moved color or color.diff.{old,new}MovedAlternative. The change between the two colors indicates that a new block was detected.dimmed_zebra
Similar to zebra, but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent blocks are considered interesting, the rest is uninteresting.
具体来说,默认值是 zebra
并且它检测到
Blocks of moved text of at least 20 alphanumeric characters
。 my $ctx = shift;
不包含至少 20 个字母数字字符。如果您使用 git diff --color-moved=plain
,或将 # ten more ANs
添加到行尾,您的示例将突出显示为已移动。
请注意,还有另一种情况,彩色移动代码(当代码量足够时)会产生不当的副作用:
“git diff --color-moved --cc --stat -p
”由于 color-moved 中的错误与其他错误之间的有趣交互而无法正常工作。
此问题已在 Git 2.21(2019 年 2 月)
中修复参见 commit dac03b5, commit 04b19fc, commit 8290faa, commit 8817f0c, commit 48edf3a, commit 426fd36 (24 Jan 2019) by Jeff King (peff
)。
(由 Junio C Hamano -- gitster
-- in commit 5d2710b 合并,2019 年 2 月 5 日)
diff: clear emitted_symbols flag after use
There's an odd bug when "
log --color-moved
" is used with the combination of "--cc --stat -p
": the stat for merge commits is erroneously shown with the diff of the next commit.The included test demonstrates the issue.
Our history looks something like this:A-B-M--D \ / C
When we run "
git log --cc --stat -p --color-moved
" starting atD
, we get this sequence of events:
The diff for
D
is using-p
, sodiff_flush()
calls intodiff_flush_patch_all_file_pairs()
. There we see thato->color_moved
is in effect, so we pointo->emitted_symbols
to a static local struct, causingdiff_flush_patch()
to queue the symbols instead of actually writing them out.
We then do our move detection, emit the symbols, and clear the struct. But we leaveo->emitted_symbols
pointing to our struct.Next we compute the diff for
M
. This is a merge, so we use the combined diff code.
Infind_paths_generic()
, we compute the pairwise diff between each commit and its parent. Normally this is done withDIFF_FORMAT_NO_OUTPUT
, since we're just looking for intersecting paths. But since "--stat --cc
" shows the first-parent stat, and since we're computing that diff anyway, we enableDIFF_FORMAT_DIFFSTAT
for the first parent. This outputs the stat information immediately, saving us from running a separate first-parent diff later.
But where does that output go? Normally it goes directly to stdout, but becauseo->emitted_symbols
is set, we queue it. As a result, we don't actually print the diffstat for the merge commit (yet), which is wrong.
- Next we compute the diff for
C
. We're actually showing a patch again, so we end up indiff_flush_patch_all_file_pairs()
, but this time we have the queued stat from step 2 waiting in our struct.
We add new elements to it forC
's diff, and then flush the whole thing. And we see thediffstat
fromM
as part ofC
's diff, which is wrong.So triggering the bug really does require the combination of all of those options.