Skip to content

Commit 0b0a56f

Browse files
committed
Merge branch 'bh/diff-highlight-graph'
"diff-highlight" script (in contrib/) learned to work better with "git log -p --graph" output. * bh/diff-highlight-graph: diff-highlight: avoid highlighting combined diffs diff-highlight: add multi-byte tests diff-highlight: ignore test cruft diff-highlight: add support for --graph output diff-highlight: add failing test for handling --graph output diff-highlight: add some tests
2 parents d012326 + 3dbfe2b commit 0b0a56f

File tree

5 files changed

+338
-6
lines changed

5 files changed

+338
-6
lines changed

contrib/diff-highlight/Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# nothing to build
2+
all:
3+
4+
test:
5+
$(MAKE) -C t

contrib/diff-highlight/diff-highlight

+13-6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ my $RESET = "\x1b[m";
2121
my $COLOR = qr/\x1b\[[0-9;]*m/;
2222
my $BORING = qr/$COLOR|\s/;
2323

24+
# The patch portion of git log -p --graph should only ever have preceding | and
25+
# not / or \ as merge history only shows up on the commit line.
26+
my $GRAPH = qr/$COLOR?\|$COLOR?\s+/;
27+
2428
my @removed;
2529
my @added;
2630
my $in_hunk;
@@ -32,12 +36,12 @@ $SIG{PIPE} = 'DEFAULT';
3236
while (<>) {
3337
if (!$in_hunk) {
3438
print;
35-
$in_hunk = /^$COLOR*\@/;
39+
$in_hunk = /^$GRAPH*$COLOR*\@\@ /;
3640
}
37-
elsif (/^$COLOR*-/) {
41+
elsif (/^$GRAPH*$COLOR*-/) {
3842
push @removed, $_;
3943
}
40-
elsif (/^$COLOR*\+/) {
44+
elsif (/^$GRAPH*$COLOR*\+/) {
4145
push @added, $_;
4246
}
4347
else {
@@ -46,7 +50,7 @@ while (<>) {
4650
@added = ();
4751

4852
print;
49-
$in_hunk = /^$COLOR*[\@ ]/;
53+
$in_hunk = /^$GRAPH*$COLOR*[\@ ]/;
5054
}
5155

5256
# Most of the time there is enough output to keep things streaming,
@@ -163,6 +167,9 @@ sub highlight_pair {
163167
}
164168
}
165169

170+
# we split either by $COLOR or by character. This has the side effect of
171+
# leaving in graph cruft. It works because the graph cruft does not contain "-"
172+
# or "+"
166173
sub split_line {
167174
local $_ = shift;
168175
return utf8::decode($_) ?
@@ -211,8 +218,8 @@ sub is_pair_interesting {
211218
my $suffix_a = join('', @$a[($sa+1)..$#$a]);
212219
my $suffix_b = join('', @$b[($sb+1)..$#$b]);
213220

214-
return $prefix_a !~ /^$COLOR*-$BORING*$/ ||
215-
$prefix_b !~ /^$COLOR*\+$BORING*$/ ||
221+
return $prefix_a !~ /^$GRAPH*$COLOR*-$BORING*$/ ||
222+
$prefix_b !~ /^$GRAPH*$COLOR*\+$BORING*$/ ||
216223
$suffix_a !~ /^$BORING*$/ ||
217224
$suffix_b !~ /^$BORING*$/;
218225
}

contrib/diff-highlight/t/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/trash directory*
2+
/test-results

contrib/diff-highlight/t/Makefile

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-include ../../../config.mak.autogen
2+
-include ../../../config.mak
3+
4+
# copied from ../../t/Makefile
5+
SHELL_PATH ?= $(SHELL)
6+
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
7+
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
8+
9+
all: test
10+
test: $(T)
11+
12+
.PHONY: help clean all test $(T)
13+
14+
help:
15+
@echo 'Run "$(MAKE) test" to launch test scripts'
16+
@echo 'Run "$(MAKE) clean" to remove trash folders'
17+
18+
$(T):
19+
@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
20+
21+
clean:
22+
$(RM) -r 'trash directory'.*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
#!/bin/sh
2+
3+
test_description='Test diff-highlight'
4+
5+
CURR_DIR=$(pwd)
6+
TEST_OUTPUT_DIRECTORY=$(pwd)
7+
TEST_DIRECTORY="$CURR_DIR"/../../../t
8+
DIFF_HIGHLIGHT="$CURR_DIR"/../diff-highlight
9+
10+
CW="$(printf "\033[7m")" # white
11+
CR="$(printf "\033[27m")" # reset
12+
13+
. "$TEST_DIRECTORY"/test-lib.sh
14+
15+
if ! test_have_prereq PERL
16+
then
17+
skip_all='skipping diff-highlight tests; perl not available'
18+
test_done
19+
fi
20+
21+
# dh_test is a test helper function which takes 3 file names as parameters. The
22+
# first 2 files are used to generate diff and commit output, which is then
23+
# piped through diff-highlight. The 3rd file should contain the expected output
24+
# of diff-highlight (minus the diff/commit header, ie. everything after and
25+
# including the first @@ line).
26+
dh_test () {
27+
a="$1" b="$2" &&
28+
29+
cat >patch.exp &&
30+
31+
{
32+
cat "$a" >file &&
33+
git add file &&
34+
git commit -m "Add a file" &&
35+
36+
cat "$b" >file &&
37+
git diff file >diff.raw &&
38+
git commit -a -m "Update a file" &&
39+
git show >commit.raw
40+
} >/dev/null &&
41+
42+
"$DIFF_HIGHLIGHT" <diff.raw | test_strip_patch_header >diff.act &&
43+
"$DIFF_HIGHLIGHT" <commit.raw | test_strip_patch_header >commit.act &&
44+
test_cmp patch.exp diff.act &&
45+
test_cmp patch.exp commit.act
46+
}
47+
48+
test_strip_patch_header () {
49+
sed -n '/^@@/,$p' $*
50+
}
51+
52+
# dh_test_setup_history generates a contrived graph such that we have at least
53+
# 1 nesting (E) and 2 nestings (F).
54+
#
55+
# A branch
56+
# /
57+
# D---E---F master
58+
#
59+
# git log --all --graph
60+
# * commit
61+
# | A
62+
# | * commit
63+
# | | F
64+
# | * commit
65+
# |/
66+
# | E
67+
# * commit
68+
# D
69+
#
70+
dh_test_setup_history () {
71+
echo "file1" >file1 &&
72+
echo "file2" >file2 &&
73+
echo "file3" >file3 &&
74+
75+
cat file1 >file &&
76+
git add file &&
77+
git commit -m "D" &&
78+
79+
git checkout -b branch &&
80+
cat file2 >file &&
81+
git commit -a -m "A" &&
82+
83+
git checkout master &&
84+
cat file2 >file &&
85+
git commit -a -m "E" &&
86+
87+
cat file3 >file &&
88+
git commit -a -m "F"
89+
}
90+
91+
left_trim () {
92+
"$PERL_PATH" -pe 's/^\s+//'
93+
}
94+
95+
trim_graph () {
96+
# graphs start with * or |
97+
# followed by a space or / or \
98+
"$PERL_PATH" -pe 's@^((\*|\|)( |/|\\))+@@'
99+
}
100+
101+
test_expect_success 'diff-highlight highlights the beginning of a line' '
102+
cat >a <<-\EOF &&
103+
aaa
104+
bbb
105+
ccc
106+
EOF
107+
108+
cat >b <<-\EOF &&
109+
aaa
110+
0bb
111+
ccc
112+
EOF
113+
114+
dh_test a b <<-EOF
115+
@@ -1,3 +1,3 @@
116+
aaa
117+
-${CW}b${CR}bb
118+
+${CW}0${CR}bb
119+
ccc
120+
EOF
121+
'
122+
123+
test_expect_success 'diff-highlight highlights the end of a line' '
124+
cat >a <<-\EOF &&
125+
aaa
126+
bbb
127+
ccc
128+
EOF
129+
130+
cat >b <<-\EOF &&
131+
aaa
132+
bb0
133+
ccc
134+
EOF
135+
136+
dh_test a b <<-EOF
137+
@@ -1,3 +1,3 @@
138+
aaa
139+
-bb${CW}b${CR}
140+
+bb${CW}0${CR}
141+
ccc
142+
EOF
143+
'
144+
145+
test_expect_success 'diff-highlight highlights the middle of a line' '
146+
cat >a <<-\EOF &&
147+
aaa
148+
bbb
149+
ccc
150+
EOF
151+
152+
cat >b <<-\EOF &&
153+
aaa
154+
b0b
155+
ccc
156+
EOF
157+
158+
dh_test a b <<-EOF
159+
@@ -1,3 +1,3 @@
160+
aaa
161+
-b${CW}b${CR}b
162+
+b${CW}0${CR}b
163+
ccc
164+
EOF
165+
'
166+
167+
test_expect_success 'diff-highlight does not highlight whole line' '
168+
cat >a <<-\EOF &&
169+
aaa
170+
bbb
171+
ccc
172+
EOF
173+
174+
cat >b <<-\EOF &&
175+
aaa
176+
000
177+
ccc
178+
EOF
179+
180+
dh_test a b <<-EOF
181+
@@ -1,3 +1,3 @@
182+
aaa
183+
-bbb
184+
+000
185+
ccc
186+
EOF
187+
'
188+
189+
test_expect_failure 'diff-highlight highlights mismatched hunk size' '
190+
cat >a <<-\EOF &&
191+
aaa
192+
bbb
193+
EOF
194+
195+
cat >b <<-\EOF &&
196+
aaa
197+
b0b
198+
ccc
199+
EOF
200+
201+
dh_test a b <<-EOF
202+
@@ -1,3 +1,3 @@
203+
aaa
204+
-b${CW}b${CR}b
205+
+b${CW}0${CR}b
206+
+ccc
207+
EOF
208+
'
209+
210+
# These two code points share the same leading byte in UTF-8 representation;
211+
# a naive byte-wise diff would highlight only the second byte.
212+
#
213+
# - U+00f3 ("o" with acute)
214+
o_accent=$(printf '\303\263')
215+
# - U+00f8 ("o" with stroke)
216+
o_stroke=$(printf '\303\270')
217+
218+
test_expect_success 'diff-highlight treats multibyte utf-8 as a unit' '
219+
echo "unic${o_accent}de" >a &&
220+
echo "unic${o_stroke}de" >b &&
221+
dh_test a b <<-EOF
222+
@@ -1 +1 @@
223+
-unic${CW}${o_accent}${CR}de
224+
+unic${CW}${o_stroke}${CR}de
225+
EOF
226+
'
227+
228+
# Unlike the UTF-8 above, these are combining code points which are meant
229+
# to modify the character preceding them:
230+
#
231+
# - U+0301 (combining acute accent)
232+
combine_accent=$(printf '\314\201')
233+
# - U+0302 (combining circumflex)
234+
combine_circum=$(printf '\314\202')
235+
236+
test_expect_failure 'diff-highlight treats combining code points as a unit' '
237+
echo "unico${combine_accent}de" >a &&
238+
echo "unico${combine_circum}de" >b &&
239+
dh_test a b <<-EOF
240+
@@ -1 +1 @@
241+
-unic${CW}o${combine_accent}${CR}de
242+
+unic${CW}o${combine_circum}${CR}de
243+
EOF
244+
'
245+
246+
test_expect_success 'diff-highlight works with the --graph option' '
247+
dh_test_setup_history &&
248+
249+
# topo-order so that the order of the commits is the same as with --graph
250+
# trim graph elements so we can do a diff
251+
# trim leading space because our trim_graph is not perfect
252+
git log --branches -p --topo-order |
253+
"$DIFF_HIGHLIGHT" | left_trim >graph.exp &&
254+
git log --branches -p --graph |
255+
"$DIFF_HIGHLIGHT" | trim_graph | left_trim >graph.act &&
256+
test_cmp graph.exp graph.act
257+
'
258+
259+
# Most combined diffs won't meet diff-highlight's line-number filter. So we
260+
# create one here where one side drops a line and the other modifies it. That
261+
# should result in a diff like:
262+
#
263+
# - modified content
264+
# ++resolved content
265+
#
266+
# which naively looks like one side added "+resolved".
267+
test_expect_success 'diff-highlight ignores combined diffs' '
268+
echo "content" >file &&
269+
git add file &&
270+
git commit -m base &&
271+
272+
>file &&
273+
git commit -am master &&
274+
275+
git checkout -b other HEAD^ &&
276+
echo "modified content" >file &&
277+
git commit -am other &&
278+
279+
test_must_fail git merge master &&
280+
echo "resolved content" >file &&
281+
git commit -am resolved &&
282+
283+
cat >expect <<-\EOF &&
284+
--- a/file
285+
+++ b/file
286+
@@@ -1,1 -1,0 +1,1 @@@
287+
- modified content
288+
++resolved content
289+
EOF
290+
291+
git show -c | "$DIFF_HIGHLIGHT" >actual.raw &&
292+
sed -n "/^---/,\$p" <actual.raw >actual &&
293+
test_cmp expect actual
294+
'
295+
296+
test_done

0 commit comments

Comments
 (0)