@@ -74,16 +74,24 @@ static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr,
74
74
75
75
/* Lines lost from parent */
76
76
struct lline {
77
- struct lline * next ;
77
+ struct lline * next , * prev ;
78
78
int len ;
79
79
unsigned long parent_map ;
80
80
char line [FLEX_ARRAY ];
81
81
};
82
82
83
+ /* Lines lost from current parent (before coalescing) */
84
+ struct plost {
85
+ struct lline * lost_head , * lost_tail ;
86
+ int len ;
87
+ };
88
+
83
89
/* Lines surviving in the merge result */
84
90
struct sline {
85
- struct lline * lost_head , * * lost_tail ;
86
- struct lline * next_lost ;
91
+ /* Accumulated and coalesced lost lines */
92
+ struct lline * lost ;
93
+ int lenlost ;
94
+ struct plost plost ;
87
95
char * bol ;
88
96
int len ;
89
97
/* bit 0 up to (N-1) are on if the parent has this line (i.e.
@@ -95,34 +103,6 @@ struct sline {
95
103
unsigned long * p_lno ;
96
104
};
97
105
98
- static char * grab_blob (const unsigned char * sha1 , unsigned int mode ,
99
- unsigned long * size , struct userdiff_driver * textconv ,
100
- const char * path )
101
- {
102
- char * blob ;
103
- enum object_type type ;
104
-
105
- if (S_ISGITLINK (mode )) {
106
- blob = xmalloc (100 );
107
- * size = snprintf (blob , 100 ,
108
- "Subproject commit %s\n" , sha1_to_hex (sha1 ));
109
- } else if (is_null_sha1 (sha1 )) {
110
- /* deleted blob */
111
- * size = 0 ;
112
- return xcalloc (1 , 1 );
113
- } else if (textconv ) {
114
- struct diff_filespec * df = alloc_filespec (path );
115
- fill_filespec (df , sha1 , 1 , mode );
116
- * size = fill_textconv (textconv , df , & blob );
117
- free_filespec (df );
118
- } else {
119
- blob = read_sha1_file (sha1 , & type , size );
120
- if (type != OBJ_BLOB )
121
- die ("object '%s' is not a blob!" , sha1_to_hex (sha1 ));
122
- }
123
- return blob ;
124
- }
125
-
126
106
static int match_string_spaces (const char * line1 , int len1 ,
127
107
const char * line2 , int len2 ,
128
108
long flags )
@@ -163,36 +143,180 @@ static int match_string_spaces(const char *line1, int len1,
163
143
return 0 ;
164
144
}
165
145
166
- static void append_lost (struct sline * sline , int n , const char * line , int len , long flags )
146
+ enum coalesce_direction { MATCH , BASE , NEW };
147
+
148
+ /* Coalesce new lines into base by finding LCS */
149
+ static struct lline * coalesce_lines (struct lline * base , int * lenbase ,
150
+ struct lline * new , int lennew ,
151
+ unsigned long parent , long flags )
167
152
{
168
- struct lline * lline ;
169
- unsigned long this_mask = ( 1UL << n ) ;
170
- if ( line [ len - 1 ] == '\n' )
171
- len -- ;
153
+ int * * lcs ;
154
+ enum coalesce_direction * * directions ;
155
+ struct lline * baseend , * newend = NULL ;
156
+ int i , j , origbaselen = * lenbase ;
172
157
173
- /* Check to see if we can squash things */
174
- if (sline -> lost_head ) {
175
- lline = sline -> next_lost ;
176
- while (lline ) {
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 ;
158
+ if (new == NULL )
159
+ return base ;
160
+
161
+ if (base == NULL ) {
162
+ * lenbase = lennew ;
163
+ return new ;
164
+ }
165
+
166
+ /*
167
+ * Coalesce new lines into base by finding the LCS
168
+ * - Create the table to run dynamic programing
169
+ * - Compute the LCS
170
+ * - Then reverse read the direction structure:
171
+ * - If we have MATCH, assign parent to base flag, and consume
172
+ * both baseend and newend
173
+ * - Else if we have BASE, consume baseend
174
+ * - Else if we have NEW, insert newend lline into base and
175
+ * consume newend
176
+ */
177
+ lcs = xcalloc (origbaselen + 1 , sizeof (int * ));
178
+ directions = xcalloc (origbaselen + 1 , sizeof (enum coalesce_direction * ));
179
+ for (i = 0 ; i < origbaselen + 1 ; i ++ ) {
180
+ lcs [i ] = xcalloc (lennew + 1 , sizeof (int ));
181
+ directions [i ] = xcalloc (lennew + 1 , sizeof (enum coalesce_direction ));
182
+ directions [i ][0 ] = BASE ;
183
+ }
184
+ for (j = 1 ; j < lennew + 1 ; j ++ )
185
+ directions [0 ][j ] = NEW ;
186
+
187
+ for (i = 1 , baseend = base ; i < origbaselen + 1 ; i ++ ) {
188
+ for (j = 1 , newend = new ; j < lennew + 1 ; j ++ ) {
189
+ if (match_string_spaces (baseend -> line , baseend -> len ,
190
+ newend -> line , newend -> len , flags )) {
191
+ lcs [i ][j ] = lcs [i - 1 ][j - 1 ] + 1 ;
192
+ directions [i ][j ] = MATCH ;
193
+ } else if (lcs [i ][j - 1 ] >= lcs [i - 1 ][j ]) {
194
+ lcs [i ][j ] = lcs [i ][j - 1 ];
195
+ directions [i ][j ] = NEW ;
196
+ } else {
197
+ lcs [i ][j ] = lcs [i - 1 ][j ];
198
+ directions [i ][j ] = BASE ;
199
+ }
200
+ if (newend -> next )
201
+ newend = newend -> next ;
202
+ }
203
+ if (baseend -> next )
204
+ baseend = baseend -> next ;
205
+ }
206
+
207
+ for (i = 0 ; i < origbaselen + 1 ; i ++ )
208
+ free (lcs [i ]);
209
+ free (lcs );
210
+
211
+ /* At this point, baseend and newend point to the end of each lists */
212
+ i -- ;
213
+ j -- ;
214
+ while (i != 0 || j != 0 ) {
215
+ if (directions [i ][j ] == MATCH ) {
216
+ baseend -> parent_map |= 1 <<parent ;
217
+ baseend = baseend -> prev ;
218
+ newend = newend -> prev ;
219
+ i -- ;
220
+ j -- ;
221
+ } else if (directions [i ][j ] == NEW ) {
222
+ struct lline * lline ;
223
+
224
+ lline = newend ;
225
+ /* Remove lline from new list and update newend */
226
+ if (lline -> prev )
227
+ lline -> prev -> next = lline -> next ;
228
+ else
229
+ new = lline -> next ;
230
+ if (lline -> next )
231
+ lline -> next -> prev = lline -> prev ;
232
+
233
+ newend = lline -> prev ;
234
+ j -- ;
235
+
236
+ /* Add lline to base list */
237
+ if (baseend ) {
238
+ lline -> next = baseend -> next ;
239
+ lline -> prev = baseend ;
240
+ if (lline -> prev )
241
+ lline -> prev -> next = lline ;
182
242
}
183
- lline = lline -> next ;
243
+ else {
244
+ lline -> next = base ;
245
+ base = lline ;
246
+ }
247
+ (* lenbase )++ ;
248
+
249
+ if (lline -> next )
250
+ lline -> next -> prev = lline ;
251
+
252
+ } else {
253
+ baseend = baseend -> prev ;
254
+ i -- ;
184
255
}
185
256
}
186
257
258
+ newend = new ;
259
+ while (newend ) {
260
+ struct lline * lline = newend ;
261
+ newend = newend -> next ;
262
+ free (lline );
263
+ }
264
+
265
+ for (i = 0 ; i < origbaselen + 1 ; i ++ )
266
+ free (directions [i ]);
267
+ free (directions );
268
+
269
+ return base ;
270
+ }
271
+
272
+ static char * grab_blob (const unsigned char * sha1 , unsigned int mode ,
273
+ unsigned long * size , struct userdiff_driver * textconv ,
274
+ const char * path )
275
+ {
276
+ char * blob ;
277
+ enum object_type type ;
278
+
279
+ if (S_ISGITLINK (mode )) {
280
+ blob = xmalloc (100 );
281
+ * size = snprintf (blob , 100 ,
282
+ "Subproject commit %s\n" , sha1_to_hex (sha1 ));
283
+ } else if (is_null_sha1 (sha1 )) {
284
+ /* deleted blob */
285
+ * size = 0 ;
286
+ return xcalloc (1 , 1 );
287
+ } else if (textconv ) {
288
+ struct diff_filespec * df = alloc_filespec (path );
289
+ fill_filespec (df , sha1 , 1 , mode );
290
+ * size = fill_textconv (textconv , df , & blob );
291
+ free_filespec (df );
292
+ } else {
293
+ blob = read_sha1_file (sha1 , & type , size );
294
+ if (type != OBJ_BLOB )
295
+ die ("object '%s' is not a blob!" , sha1_to_hex (sha1 ));
296
+ }
297
+ return blob ;
298
+ }
299
+
300
+ static void append_lost (struct sline * sline , int n , const char * line , int len )
301
+ {
302
+ struct lline * lline ;
303
+ unsigned long this_mask = (1UL <<n );
304
+ if (line [len - 1 ] == '\n' )
305
+ len -- ;
306
+
187
307
lline = xmalloc (sizeof (* lline ) + len + 1 );
188
308
lline -> len = len ;
189
309
lline -> next = NULL ;
310
+ lline -> prev = sline -> plost .lost_tail ;
311
+ if (lline -> prev )
312
+ lline -> prev -> next = lline ;
313
+ else
314
+ sline -> plost .lost_head = lline ;
315
+ sline -> plost .lost_tail = lline ;
316
+ sline -> plost .len ++ ;
190
317
lline -> parent_map = this_mask ;
191
318
memcpy (lline -> line , line , len );
192
319
lline -> line [len ] = 0 ;
193
- * sline -> lost_tail = lline ;
194
- sline -> lost_tail = & lline -> next ;
195
- sline -> next_lost = NULL ;
196
320
}
197
321
198
322
struct combine_diff_state {
@@ -203,7 +327,6 @@ struct combine_diff_state {
203
327
int n ;
204
328
struct sline * sline ;
205
329
struct sline * lost_bucket ;
206
- long flags ;
207
330
};
208
331
209
332
static void consume_line (void * state_ , char * line , unsigned long len )
@@ -236,14 +359,13 @@ static void consume_line(void *state_, char *line, unsigned long len)
236
359
xcalloc (state -> num_parent ,
237
360
sizeof (unsigned long ));
238
361
state -> sline [state -> nb - 1 ].p_lno [state -> n ] = state -> ob ;
239
- state -> lost_bucket -> next_lost = state -> lost_bucket -> lost_head ;
240
362
return ;
241
363
}
242
364
if (!state -> lost_bucket )
243
365
return ; /* not in any hunk yet */
244
366
switch (line [0 ]) {
245
367
case '-' :
246
- append_lost (state -> lost_bucket , state -> n , line + 1 , len - 1 , state -> flags );
368
+ append_lost (state -> lost_bucket , state -> n , line + 1 , len - 1 );
247
369
break ;
248
370
case '+' :
249
371
state -> sline [state -> lno - 1 ].flag |= state -> nmask ;
@@ -276,7 +398,6 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
276
398
xpp .flags = flags ;
277
399
memset (& xecfg , 0 , sizeof (xecfg ));
278
400
memset (& state , 0 , sizeof (state ));
279
- state .flags = flags ;
280
401
state .nmask = nmask ;
281
402
state .sline = sline ;
282
403
state .lno = 1 ;
@@ -298,8 +419,18 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
298
419
struct lline * ll ;
299
420
sline [lno ].p_lno [n ] = p_lno ;
300
421
422
+ /* Coalesce new lines */
423
+ if (sline [lno ].plost .lost_head ) {
424
+ struct sline * sl = & sline [lno ];
425
+ sl -> lost = coalesce_lines (sl -> lost , & sl -> lenlost ,
426
+ sl -> plost .lost_head ,
427
+ sl -> plost .len , n , flags );
428
+ sl -> plost .lost_head = sl -> plost .lost_tail = NULL ;
429
+ sl -> plost .len = 0 ;
430
+ }
431
+
301
432
/* How many lines would this sline advance the p_lno? */
302
- ll = sline [lno ].lost_head ;
433
+ ll = sline [lno ].lost ;
303
434
while (ll ) {
304
435
if (ll -> parent_map & nmask )
305
436
p_lno ++ ; /* '-' means parent had it */
@@ -319,7 +450,7 @@ static int interesting(struct sline *sline, unsigned long all_mask)
319
450
/* If some parents lost lines here, or if we have added to
320
451
* some parent, it is interesting.
321
452
*/
322
- return ((sline -> flag & all_mask ) || sline -> lost_head );
453
+ return ((sline -> flag & all_mask ) || sline -> lost );
323
454
}
324
455
325
456
static unsigned long adjust_hunk_tail (struct sline * sline ,
@@ -502,7 +633,7 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
502
633
has_interesting = 0 ;
503
634
for (j = i ; j < hunk_end && !has_interesting ; j ++ ) {
504
635
unsigned long this_diff = sline [j ].flag & all_mask ;
505
- struct lline * ll = sline [j ].lost_head ;
636
+ struct lline * ll = sline [j ].lost ;
506
637
if (this_diff ) {
507
638
/* This has some changes. Is it the
508
639
* same as others?
@@ -656,7 +787,7 @@ static void dump_sline(struct sline *sline, const char *line_prefix,
656
787
int j ;
657
788
unsigned long p_mask ;
658
789
struct sline * sl = & sline [lno ++ ];
659
- ll = (sl -> flag & no_pre_delete ) ? NULL : sl -> lost_head ;
790
+ ll = (sl -> flag & no_pre_delete ) ? NULL : sl -> lost ;
660
791
while (ll ) {
661
792
printf ("%s%s" , line_prefix , c_old );
662
793
for (j = 0 ; j < num_parent ; j ++ ) {
@@ -707,7 +838,7 @@ static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
707
838
jmask = (1UL <<j );
708
839
709
840
for (lno = 0 ; lno <= cnt ; lno ++ ) {
710
- struct lline * ll = sline -> lost_head ;
841
+ struct lline * ll = sline -> lost ;
711
842
sline -> p_lno [i ] = sline -> p_lno [j ];
712
843
while (ll ) {
713
844
if (ll -> parent_map & jmask )
@@ -966,10 +1097,6 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
966
1097
967
1098
sline = xcalloc (cnt + 2 , sizeof (* sline ));
968
1099
sline [0 ].bol = result ;
969
- for (lno = 0 ; lno <= cnt + 1 ; lno ++ ) {
970
- sline [lno ].lost_tail = & sline [lno ].lost_head ;
971
- sline [lno ].flag = 0 ;
972
- }
973
1100
for (lno = 0 , cp = result ; cp < result + result_size ; cp ++ ) {
974
1101
if (* cp == '\n' ) {
975
1102
sline [lno ].len = cp - sline [lno ].bol ;
@@ -1019,8 +1146,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
1019
1146
free (result );
1020
1147
1021
1148
for (lno = 0 ; lno < cnt ; lno ++ ) {
1022
- if (sline [lno ].lost_head ) {
1023
- struct lline * ll = sline [lno ].lost_head ;
1149
+ if (sline [lno ].lost ) {
1150
+ struct lline * ll = sline [lno ].lost ;
1024
1151
while (ll ) {
1025
1152
struct lline * tmp = ll ;
1026
1153
ll = ll -> next ;
0 commit comments