Allow combined diff to ignore white-spaces

The combined diff --cc output does not honor options to ignore whitespace changes (-b, -w, and --ignore-space-at-eol). Correct this by passing diff flags to diff engine, so that combined diff behaves as normal diff does with spaces, and by coalescing lines that are removed from both (or more) parents, honoring the same rule to ignore whitespace changes. With this change, a conflict-less merge done using a ignore-* strategy option will not show any conflict if shown in combined-diff using the same option. Signed-off-by: Antoine Pelisse <apelisse@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Antoine Pelisse committed Mar 14, 2013 at 22:03 UTC fa04ae0be8cca1291a60bc8a393f9c52d2582089
2 files changed +161 -7
combine-diff.c
+50 -7
@@ -5,6 +5,7 @@
5 #include "diffcore.h"
6 #include "quote.h"
7 #include "xdiff-interface.h"
8 +#include "xdiff/xmacros.h"
9 #include "log-tree.h"
10 #include "refs.h"
11 #include "userdiff.h"
@@ -122,7 +123,47 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode,
123 return blob;
124 }
125
125 -static void append_lost(struct sline *sline, int n, const char *line, int len)
126 +static int match_string_spaces(const char *line1, int len1,
127 + const char *line2, int len2,
128 + long flags)
129 +{
130 + if (flags & XDF_WHITESPACE_FLAGS) {
131 + for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
132 + for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
133 + }
134 +
135 + if (!(flags & (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE)))
136 + return (len1 == len2 && !memcmp(line1, line2, len1));
137 +
138 + while (len1 > 0 && len2 > 0) {
139 + len1--;
140 + len2--;
141 + if (XDL_ISSPACE(line1[len1]) || XDL_ISSPACE(line2[len2])) {
142 + if ((flags & XDF_IGNORE_WHITESPACE_CHANGE) &&
143 + (!XDL_ISSPACE(line1[len1]) || !XDL_ISSPACE(line2[len2])))
144 + return 0;
145 +
146 + for (; len1 > 0 && XDL_ISSPACE(line1[len1]); len1--);
147 + for (; len2 > 0 && XDL_ISSPACE(line2[len2]); len2--);
148 + }
149 + if (line1[len1] != line2[len2])
150 + return 0;
151 + }
152 +
153 + if (flags & XDF_IGNORE_WHITESPACE) {
154 + /* Consume remaining spaces */
155 + for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
156 + for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
157 + }
158 +
159 + /* We matched full line1 and line2 */
160 + if (!len1 && !len2)
161 + return 1;
162 +
163 + return 0;
164 +}
165 +
166 +static void append_lost(struct sline *sline, int n, const char *line, int len, long flags)
167 {
168 struct lline *lline;
169 unsigned long this_mask = (1UL<<n);
@@ -133,8 +174,8 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
174 if (sline->lost_head) {
175 lline = sline->next_lost;
176 while (lline) {
136 - if (lline->len == len &&
137 - !memcmp(lline->line, line, len)) {
177 + if (match_string_spaces(lline->line, lline->len,
178 + line, len, flags)) {
179 lline->parent_map |= this_mask;
180 sline->next_lost = lline->next;
181 return;
@@ -162,6 +203,7 @@ struct combine_diff_state {
203 int n;
204 struct sline *sline;
205 struct sline *lost_bucket;
206 + long flags;
207 };
208
209 static void consume_line(void *state_, char *line, unsigned long len)
@@ -201,7 +243,7 @@ static void consume_line(void *state_, char *line, unsigned long len)
243 return; /* not in any hunk yet */
244 switch (line[0]) {
245 case '-':
204 - append_lost(state->lost_bucket, state->n, line+1, len-1);
246 + append_lost(state->lost_bucket, state->n, line+1, len-1, state->flags);
247 break;
248 case '+':
249 state->sline[state->lno-1].flag |= state->nmask;
@@ -215,7 +257,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
257 struct sline *sline, unsigned int cnt, int n,
258 int num_parent, int result_deleted,
259 struct userdiff_driver *textconv,
218 - const char *path)
260 + const char *path, long flags)
261 {
262 unsigned int p_lno, lno;
263 unsigned long nmask = (1UL << n);
@@ -231,9 +273,10 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
273 parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
274 parent_file.size = sz;
275 memset(&xpp, 0, sizeof(xpp));
234 - xpp.flags = 0;
276 + xpp.flags = flags;
277 memset(&xecfg, 0, sizeof(xecfg));
278 memset(&state, 0, sizeof(state));
279 + state.flags = flags;
280 state.nmask = nmask;
281 state.sline = sline;
282 state.lno = 1;
@@ -962,7 +1005,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
1005 elem->parent[i].mode,
1006 &result_file, sline,
1007 cnt, i, num_parent, result_deleted,
965 - textconv, elem->path);
1008 + textconv, elem->path, opt->xdl_opts);
1009 }
1010
1011 show_hunks = make_hunks(sline, cnt, num_parent, dense);
t/t4038-diff-combined.sh
+111
@@ -3,6 +3,7 @@
3 test_description='combined diff'
4
5 . ./test-lib.sh
6 +. "$TEST_DIRECTORY"/diff-lib.sh
7
8 setup_helper () {
9 one=$1 branch=$2 side=$3 &&
@@ -113,4 +114,114 @@ test_expect_success 'check --cc --raw with forty trees' '
114 grep "^::::::::::::::::::::::::::::::::::::::::[^:]" out
115 '
116
117 +test_expect_success 'setup combined ignore spaces' '
118 + git checkout master &&
119 + >test &&
120 + git add test &&
121 + git commit -m initial &&
122 +
123 + tr -d Q <<-\EOF >test &&
124 + always coalesce
125 + eol space coalesce Q
126 + space change coalesce
127 + all spa ces coalesce
128 + eol spaces Q
129 + space change
130 + all spa ces
131 + EOF
132 + git commit -m "test space change" -a &&
133 +
134 + git checkout -b side HEAD^ &&
135 + tr -d Q <<-\EOF >test &&
136 + always coalesce
137 + eol space coalesce
138 + space change coalesce
139 + all spaces coalesce
140 + eol spaces
141 + space change
142 + all spaces
143 + EOF
144 + git commit -m "test other space changes" -a &&
145 +
146 + test_must_fail git merge master &&
147 + tr -d Q <<-\EOF >test &&
148 + eol spaces Q
149 + space change
150 + all spa ces
151 + EOF
152 + git commit -m merged -a
153 +'
154 +
155 +test_expect_success 'check combined output (no ignore space)' '
156 + git show >actual.tmp &&
157 + sed -e "1,/^@@@/d" < actual.tmp >actual &&
158 + tr -d Q <<-\EOF >expected &&
159 + --always coalesce
160 + - eol space coalesce
161 + - space change coalesce
162 + - all spaces coalesce
163 + - eol spaces
164 + - space change
165 + - all spaces
166 + -eol space coalesce Q
167 + -space change coalesce
168 + -all spa ces coalesce
169 + + eol spaces Q
170 + + space change
171 + + all spa ces
172 + EOF
173 + compare_diff_patch expected actual
174 +'
175 +
176 +test_expect_success 'check combined output (ignore space at eol)' '
177 + git show --ignore-space-at-eol >actual.tmp &&
178 + sed -e "1,/^@@@/d" < actual.tmp >actual &&
179 + tr -d Q <<-\EOF >expected &&
180 + --always coalesce
181 + --eol space coalesce
182 + - space change coalesce
183 + - all spaces coalesce
184 + -space change coalesce
185 + -all spa ces coalesce
186 + eol spaces Q
187 + - space change
188 + - all spaces
189 + + space change
190 + + all spa ces
191 + EOF
192 + compare_diff_patch expected actual
193 +'
194 +
195 +test_expect_success 'check combined output (ignore space change)' '
196 + git show -b >actual.tmp &&
197 + sed -e "1,/^@@@/d" < actual.tmp >actual &&
198 + tr -d Q <<-\EOF >expected &&
199 + --always coalesce
200 + --eol space coalesce
201 + --space change coalesce
202 + - all spaces coalesce
203 + -all spa ces coalesce
204 + eol spaces Q
205 + space change
206 + - all spaces
207 + + all spa ces
208 + EOF
209 + compare_diff_patch expected actual
210 +'
211 +
212 +test_expect_success 'check combined output (ignore all spaces)' '
213 + git show -w >actual.tmp &&
214 + sed -e "1,/^@@@/d" < actual.tmp >actual &&
215 + tr -d Q <<-\EOF >expected &&
216 + --always coalesce
217 + --eol space coalesce
218 + --space change coalesce
219 + --all spaces coalesce
220 + eol spaces Q
221 + space change
222 + all spa ces
223 + EOF
224 + compare_diff_patch expected actual
225 +'
226 +
227 test_done