2
* diff3.c : routines for doing diffs
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
21
#include <apr_pools.h>
22
#include <apr_general.h>
24
#include "svn_pools.h"
25
#include "svn_error.h"
27
#include "svn_types.h"
33
svn_diff__resolve_conflict(svn_diff_t *hunk,
34
svn_diff__position_t **position_list1,
35
svn_diff__position_t **position_list2,
38
apr_off_t modified_start = hunk->modified_start + 1;
39
apr_off_t latest_start = hunk->latest_start + 1;
40
apr_off_t common_length;
41
apr_off_t modified_length = hunk->modified_length;
42
apr_off_t latest_length = hunk->latest_length;
43
svn_diff__position_t *start_position[2];
44
svn_diff__position_t *position[2];
45
svn_diff__lcs_t *lcs = NULL;
46
svn_diff__lcs_t **lcs_ref = &lcs;
47
svn_diff_t **diff_ref = &hunk->resolved_diff;
50
/* First find the starting positions for the
54
start_position[0] = *position_list1;
55
start_position[1] = *position_list2;
57
while (start_position[0]->offset < modified_start)
58
start_position[0] = start_position[0]->next;
60
while (start_position[1]->offset < latest_start)
61
start_position[1] = start_position[1]->next;
63
position[0] = start_position[0];
64
position[1] = start_position[1];
66
common_length = modified_length < latest_length
67
? modified_length : latest_length;
69
while (common_length > 0
70
&& position[0]->node == position[1]->node)
72
position[0] = position[0]->next;
73
position[1] = position[1]->next;
78
if (common_length == 0
79
&& modified_length == latest_length)
81
hunk->type = svn_diff__type_diff_common;
82
hunk->resolved_diff = NULL;
84
*position_list1 = position[0];
85
*position_list2 = position[1];
90
hunk->type = svn_diff__type_conflict;
92
/* ### If we have a conflict we can try to find the
93
* ### common parts in it by getting an lcs between
94
* ### modified (start to start + length) and
95
* ### latest (start to start + length).
96
* ### We use this lcs to create a simple diff. Only
97
* ### where there is a diff between the two, we have
99
* ### This raises a problem; several common diffs and
100
* ### conflicts can occur within the same original
101
* ### block. This needs some thought.
103
* ### NB: We can use the node _pointers_ to identify
104
* ### different tokens
107
subpool = svn_pool_create(pool);
109
/* Calculate how much of the two sequences was
112
common_length = (modified_length < latest_length
113
? modified_length : latest_length)
116
/* If there were matching symbols at the start of
117
* both sequences, record that fact.
119
if (common_length > 0)
121
lcs = apr_palloc(subpool, sizeof(*lcs));
123
lcs->position[0] = start_position[0];
124
lcs->position[1] = start_position[1];
125
lcs->length = common_length;
127
lcs_ref = &lcs->next;
130
modified_length -= common_length;
131
latest_length -= common_length;
133
modified_start = start_position[0]->offset;
134
latest_start = start_position[1]->offset;
136
start_position[0] = position[0];
137
start_position[1] = position[1];
139
/* Create a new ring for svn_diff__lcs to grok.
140
* We can safely do this given we don't need the
141
* positions we processed anymore.
143
if (modified_length == 0)
145
*position_list1 = position[0];
150
while (--modified_length)
151
position[0] = position[0]->next;
153
*position_list1 = position[0]->next;
154
position[0]->next = start_position[0];
157
if (latest_length == 0)
159
*position_list2 = position[1];
164
while (--latest_length)
165
position[1] = position[1]->next;
167
*position_list2 = position[1]->next;
168
position[1]->next = start_position[1];
171
*lcs_ref = svn_diff__lcs(position[0], position[1],
174
/* Fix up the EOF lcs element in case one of
175
* the two sequences was NULL.
177
if ((*lcs_ref)->position[0]->offset == 1)
178
(*lcs_ref)->position[0] = *position_list1;
180
if ((*lcs_ref)->position[1]->offset == 1)
181
(*lcs_ref)->position[1] = *position_list2;
183
/* Restore modified_length and latest_length */
184
modified_length = hunk->modified_length;
185
latest_length = hunk->latest_length;
187
/* Produce the resolved diff */
190
if (modified_start < lcs->position[0]->offset
191
|| latest_start < lcs->position[1]->offset)
193
(*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
195
(*diff_ref)->type = svn_diff__type_conflict;
196
(*diff_ref)->original_start = hunk->original_start;
197
(*diff_ref)->original_length = hunk->original_length;
198
(*diff_ref)->modified_start = modified_start - 1;
199
(*diff_ref)->modified_length = lcs->position[0]->offset
201
(*diff_ref)->latest_start = latest_start - 1;
202
(*diff_ref)->latest_length = lcs->position[1]->offset
204
(*diff_ref)->resolved_diff = NULL;
206
diff_ref = &(*diff_ref)->next;
210
if (lcs->length == 0)
213
modified_start = lcs->position[0]->offset;
214
latest_start = lcs->position[1]->offset;
216
(*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
218
(*diff_ref)->type = svn_diff__type_diff_common;
219
(*diff_ref)->original_start = hunk->original_start;
220
(*diff_ref)->original_length = hunk->original_length;
221
(*diff_ref)->modified_start = modified_start - 1;
222
(*diff_ref)->modified_length = lcs->length;
223
(*diff_ref)->latest_start = latest_start - 1;
224
(*diff_ref)->latest_length = lcs->length;
225
(*diff_ref)->resolved_diff = NULL;
227
diff_ref = &(*diff_ref)->next;
229
modified_start += lcs->length;
230
latest_start += lcs->length;
237
svn_pool_destroy(subpool);
242
svn_diff_diff3(svn_diff_t **diff,
244
const svn_diff_fns_t *vtable,
247
svn_diff__tree_t *tree;
248
svn_diff__position_t *position_list[3];
249
svn_diff__lcs_t *lcs_om;
250
svn_diff__lcs_t *lcs_ol;
252
apr_pool_t *treepool;
256
subpool = svn_pool_create(pool);
257
treepool = svn_pool_create(pool);
259
svn_diff__tree_create(&tree, treepool);
261
SVN_ERR(svn_diff__get_tokens(&position_list[0],
264
svn_diff_datasource_original,
267
SVN_ERR(svn_diff__get_tokens(&position_list[1],
270
svn_diff_datasource_modified,
273
SVN_ERR(svn_diff__get_tokens(&position_list[2],
276
svn_diff_datasource_latest,
279
/* Get rid of the tokens, we don't need them to calc the diff */
280
if (vtable->token_discard_all != NULL)
281
vtable->token_discard_all(diff_baton);
283
/* We don't need the nodes in the tree either anymore, nor the tree itself */
284
svn_pool_destroy(treepool);
286
/* Get the lcs for original-modified and original-latest */
287
lcs_om = svn_diff__lcs(position_list[0], position_list[1],
289
lcs_ol = svn_diff__lcs(position_list[0], position_list[2],
292
/* Produce a merged diff */
294
svn_diff_t **diff_ref = diff;
296
apr_off_t original_start = 1;
297
apr_off_t modified_start = 1;
298
apr_off_t latest_start = 1;
299
apr_off_t original_sync;
300
apr_off_t modified_sync;
301
apr_off_t latest_sync;
302
apr_off_t common_length;
303
apr_off_t original_length;
304
apr_off_t modified_length;
305
apr_off_t latest_length;
306
svn_boolean_t is_modified;
307
svn_boolean_t is_latest;
308
svn_diff__position_t sentinel_position[2];
310
/* Point the position lists to the start of the list
311
* so that common_diff/conflict detection actually is
314
if (position_list[1])
316
sentinel_position[0].next = position_list[1]->next;
317
sentinel_position[0].offset = position_list[1]->offset + 1;
318
position_list[1]->next = &sentinel_position[0];
319
position_list[1] = sentinel_position[0].next;
323
sentinel_position[0].offset = 1;
324
sentinel_position[0].next = NULL;
325
position_list[1] = &sentinel_position[0];
328
if (position_list[2])
330
sentinel_position[1].next = position_list[2]->next;
331
sentinel_position[1].offset = position_list[2]->offset + 1;
332
position_list[2]->next = &sentinel_position[1];
333
position_list[2] = sentinel_position[1].next;
337
sentinel_position[1].offset = 1;
338
sentinel_position[1].next = NULL;
339
position_list[2] = &sentinel_position[1];
344
/* Find the sync points */
347
if (lcs_om->position[0]->offset > lcs_ol->position[0]->offset)
349
original_sync = lcs_om->position[0]->offset;
351
while (lcs_ol->position[0]->offset + lcs_ol->length
353
lcs_ol = lcs_ol->next;
355
/* If the sync point is the EOF, and our current lcs segment
356
* doesn't reach as far as EOF, we need to skip this segment.
358
if (lcs_om->length == 0 && lcs_ol->length > 0
359
&& lcs_ol->position[0]->offset + lcs_ol->length
361
&& lcs_ol->position[1]->offset + lcs_ol->length
362
!= lcs_ol->next->position[1]->offset)
363
lcs_ol = lcs_ol->next;
365
if (lcs_ol->position[0]->offset <= original_sync)
370
original_sync = lcs_ol->position[0]->offset;
372
while (lcs_om->position[0]->offset + lcs_om->length
374
lcs_om = lcs_om->next;
376
/* If the sync point is the EOF, and our current lcs segment
377
* doesn't reach as far as EOF, we need to skip this segment.
379
if (lcs_ol->length == 0 && lcs_om->length > 0
380
&& lcs_om->position[0]->offset + lcs_om->length
382
&& lcs_om->position[1]->offset + lcs_om->length
383
!= lcs_om->next->position[1]->offset)
384
lcs_om = lcs_om->next;
386
if (lcs_om->position[0]->offset <= original_sync)
391
modified_sync = lcs_om->position[1]->offset
392
+ (original_sync - lcs_om->position[0]->offset);
393
latest_sync = lcs_ol->position[1]->offset
394
+ (original_sync - lcs_ol->position[0]->offset);
396
/* Determine what is modified, if anything */
397
is_modified = lcs_om->position[0]->offset - original_start > 0
398
|| lcs_om->position[1]->offset - modified_start > 0;
400
is_latest = lcs_ol->position[0]->offset - original_start > 0
401
|| lcs_ol->position[1]->offset - latest_start > 0;
403
if (is_modified || is_latest)
405
original_length = original_sync - original_start;
406
modified_length = modified_sync - modified_start;
407
latest_length = latest_sync - latest_start;
409
(*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
411
(*diff_ref)->original_start = original_start - 1;
412
(*diff_ref)->original_length = original_sync - original_start;
413
(*diff_ref)->modified_start = modified_start - 1;
414
(*diff_ref)->modified_length = modified_length;
415
(*diff_ref)->latest_start = latest_start - 1;
416
(*diff_ref)->latest_length = latest_length;
417
(*diff_ref)->resolved_diff = NULL;
419
if (is_modified && is_latest)
421
svn_diff__resolve_conflict(*diff_ref,
426
else if (is_modified)
428
(*diff_ref)->type = svn_diff__type_diff_modified;
432
(*diff_ref)->type = svn_diff__type_diff_latest;
435
diff_ref = &(*diff_ref)->next;
439
if (lcs_om->length == 0 || lcs_ol->length == 0)
442
modified_length = lcs_om->length
443
- (original_sync - lcs_om->position[0]->offset);
444
latest_length = lcs_ol->length
445
- (original_sync - lcs_ol->position[0]->offset);
446
common_length = modified_length < latest_length
447
? modified_length : latest_length;
449
(*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
451
(*diff_ref)->type = svn_diff__type_common;
452
(*diff_ref)->original_start = original_sync - 1;
453
(*diff_ref)->original_length = common_length;
454
(*diff_ref)->modified_start = modified_sync - 1;
455
(*diff_ref)->modified_length = common_length;
456
(*diff_ref)->latest_start = latest_sync - 1;
457
(*diff_ref)->latest_length = common_length;
458
(*diff_ref)->resolved_diff = NULL;
460
diff_ref = &(*diff_ref)->next;
462
/* Set the new offsets */
463
original_start = original_sync + common_length;
464
modified_start = modified_sync + common_length;
465
latest_start = latest_sync + common_length;
467
/* Make it easier for diff_common/conflict detection
468
by recording last lcs start positions
470
if (position_list[1]->offset < lcs_om->position[1]->offset)
471
position_list[1] = lcs_om->position[1];
473
if (position_list[2]->offset < lcs_ol->position[1]->offset)
474
position_list[2] = lcs_ol->position[1];
476
/* Make sure we are pointing to lcs entries beyond
477
* the range we just processed
479
while (original_start >= lcs_om->position[0]->offset + lcs_om->length
480
&& lcs_om->length > 0)
482
lcs_om = lcs_om->next;
485
while (original_start >= lcs_ol->position[0]->offset + lcs_ol->length
486
&& lcs_ol->length > 0)
488
lcs_ol = lcs_ol->next;
495
svn_pool_destroy(subpool);