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_error.h"
25 #include "svn_version.h"
28 #include "svn_pools.h"
29 #include "svn_error.h"
31 #include "svn_types.h"
37 svn_diff__resolve_conflict(svn_diff_t
*hunk
,
38 svn_diff__position_t
**position_list1
,
39 svn_diff__position_t
**position_list2
,
42 apr_off_t modified_start
= hunk
->modified_start
+ 1;
43 apr_off_t latest_start
= hunk
->latest_start
+ 1;
44 apr_off_t common_length
;
45 apr_off_t modified_length
= hunk
->modified_length
;
46 apr_off_t latest_length
= hunk
->latest_length
;
47 svn_diff__position_t
*start_position
[2];
48 svn_diff__position_t
*position
[2];
49 svn_diff__lcs_t
*lcs
= NULL
;
50 svn_diff__lcs_t
**lcs_ref
= &lcs
;
51 svn_diff_t
**diff_ref
= &hunk
->resolved_diff
;
54 /* First find the starting positions for the
58 start_position
[0] = *position_list1
;
59 start_position
[1] = *position_list2
;
61 while (start_position
[0]->offset
< modified_start
)
62 start_position
[0] = start_position
[0]->next
;
64 while (start_position
[1]->offset
< latest_start
)
65 start_position
[1] = start_position
[1]->next
;
67 position
[0] = start_position
[0];
68 position
[1] = start_position
[1];
70 common_length
= modified_length
< latest_length
71 ? modified_length
: latest_length
;
73 while (common_length
> 0
74 && position
[0]->node
== position
[1]->node
)
76 position
[0] = position
[0]->next
;
77 position
[1] = position
[1]->next
;
82 if (common_length
== 0
83 && modified_length
== latest_length
)
85 hunk
->type
= svn_diff__type_diff_common
;
86 hunk
->resolved_diff
= NULL
;
88 *position_list1
= position
[0];
89 *position_list2
= position
[1];
94 hunk
->type
= svn_diff__type_conflict
;
96 /* ### If we have a conflict we can try to find the
97 * ### common parts in it by getting an lcs between
98 * ### modified (start to start + length) and
99 * ### latest (start to start + length).
100 * ### We use this lcs to create a simple diff. Only
101 * ### where there is a diff between the two, we have
103 * ### This raises a problem; several common diffs and
104 * ### conflicts can occur within the same original
105 * ### block. This needs some thought.
107 * ### NB: We can use the node _pointers_ to identify
108 * ### different tokens
111 subpool
= svn_pool_create(pool
);
113 /* Calculate how much of the two sequences was
116 common_length
= (modified_length
< latest_length
117 ? modified_length
: latest_length
)
120 /* If there were matching symbols at the start of
121 * both sequences, record that fact.
123 if (common_length
> 0)
125 lcs
= apr_palloc(subpool
, sizeof(*lcs
));
127 lcs
->position
[0] = start_position
[0];
128 lcs
->position
[1] = start_position
[1];
129 lcs
->length
= common_length
;
131 lcs_ref
= &lcs
->next
;
134 modified_length
-= common_length
;
135 latest_length
-= common_length
;
137 modified_start
= start_position
[0]->offset
;
138 latest_start
= start_position
[1]->offset
;
140 start_position
[0] = position
[0];
141 start_position
[1] = position
[1];
143 /* Create a new ring for svn_diff__lcs to grok.
144 * We can safely do this given we don't need the
145 * positions we processed anymore.
147 if (modified_length
== 0)
149 *position_list1
= position
[0];
154 while (--modified_length
)
155 position
[0] = position
[0]->next
;
157 *position_list1
= position
[0]->next
;
158 position
[0]->next
= start_position
[0];
161 if (latest_length
== 0)
163 *position_list2
= position
[1];
168 while (--latest_length
)
169 position
[1] = position
[1]->next
;
171 *position_list2
= position
[1]->next
;
172 position
[1]->next
= start_position
[1];
175 *lcs_ref
= svn_diff__lcs(position
[0], position
[1],
178 /* Fix up the EOF lcs element in case one of
179 * the two sequences was NULL.
181 if ((*lcs_ref
)->position
[0]->offset
== 1)
182 (*lcs_ref
)->position
[0] = *position_list1
;
184 if ((*lcs_ref
)->position
[1]->offset
== 1)
185 (*lcs_ref
)->position
[1] = *position_list2
;
187 /* Restore modified_length and latest_length */
188 modified_length
= hunk
->modified_length
;
189 latest_length
= hunk
->latest_length
;
191 /* Produce the resolved diff */
194 if (modified_start
< lcs
->position
[0]->offset
195 || latest_start
< lcs
->position
[1]->offset
)
197 (*diff_ref
) = apr_palloc(pool
, sizeof(**diff_ref
));
199 (*diff_ref
)->type
= svn_diff__type_conflict
;
200 (*diff_ref
)->original_start
= hunk
->original_start
;
201 (*diff_ref
)->original_length
= hunk
->original_length
;
202 (*diff_ref
)->modified_start
= modified_start
- 1;
203 (*diff_ref
)->modified_length
= lcs
->position
[0]->offset
205 (*diff_ref
)->latest_start
= latest_start
- 1;
206 (*diff_ref
)->latest_length
= lcs
->position
[1]->offset
208 (*diff_ref
)->resolved_diff
= NULL
;
210 diff_ref
= &(*diff_ref
)->next
;
214 if (lcs
->length
== 0)
217 modified_start
= lcs
->position
[0]->offset
;
218 latest_start
= lcs
->position
[1]->offset
;
220 (*diff_ref
) = apr_palloc(pool
, sizeof(**diff_ref
));
222 (*diff_ref
)->type
= svn_diff__type_diff_common
;
223 (*diff_ref
)->original_start
= hunk
->original_start
;
224 (*diff_ref
)->original_length
= hunk
->original_length
;
225 (*diff_ref
)->modified_start
= modified_start
- 1;
226 (*diff_ref
)->modified_length
= lcs
->length
;
227 (*diff_ref
)->latest_start
= latest_start
- 1;
228 (*diff_ref
)->latest_length
= lcs
->length
;
229 (*diff_ref
)->resolved_diff
= NULL
;
231 diff_ref
= &(*diff_ref
)->next
;
233 modified_start
+= lcs
->length
;
234 latest_start
+= lcs
->length
;
241 svn_pool_destroy(subpool
);
246 svn_diff_diff3(svn_diff_t
**diff
,
248 const svn_diff_fns_t
*vtable
,
251 svn_diff__tree_t
*tree
;
252 svn_diff__position_t
*position_list
[3];
253 svn_diff__lcs_t
*lcs_om
;
254 svn_diff__lcs_t
*lcs_ol
;
256 apr_pool_t
*treepool
;
260 subpool
= svn_pool_create(pool
);
261 treepool
= svn_pool_create(pool
);
263 svn_diff__tree_create(&tree
, treepool
);
265 SVN_ERR(svn_diff__get_tokens(&position_list
[0],
268 svn_diff_datasource_original
,
271 SVN_ERR(svn_diff__get_tokens(&position_list
[1],
274 svn_diff_datasource_modified
,
277 SVN_ERR(svn_diff__get_tokens(&position_list
[2],
280 svn_diff_datasource_latest
,
283 /* Get rid of the tokens, we don't need them to calc the diff */
284 if (vtable
->token_discard_all
!= NULL
)
285 vtable
->token_discard_all(diff_baton
);
287 /* We don't need the nodes in the tree either anymore, nor the tree itself */
288 svn_pool_destroy(treepool
);
290 /* Get the lcs for original-modified and original-latest */
291 lcs_om
= svn_diff__lcs(position_list
[0], position_list
[1],
293 lcs_ol
= svn_diff__lcs(position_list
[0], position_list
[2],
296 /* Produce a merged diff */
298 svn_diff_t
**diff_ref
= diff
;
300 apr_off_t original_start
= 1;
301 apr_off_t modified_start
= 1;
302 apr_off_t latest_start
= 1;
303 apr_off_t original_sync
;
304 apr_off_t modified_sync
;
305 apr_off_t latest_sync
;
306 apr_off_t common_length
;
307 apr_off_t original_length
;
308 apr_off_t modified_length
;
309 apr_off_t latest_length
;
310 svn_boolean_t is_modified
;
311 svn_boolean_t is_latest
;
312 svn_diff__position_t sentinel_position
[2];
314 /* Point the position lists to the start of the list
315 * so that common_diff/conflict detection actually is
318 if (position_list
[1])
320 sentinel_position
[0].next
= position_list
[1]->next
;
321 sentinel_position
[0].offset
= position_list
[1]->offset
+ 1;
322 position_list
[1]->next
= &sentinel_position
[0];
323 position_list
[1] = sentinel_position
[0].next
;
327 sentinel_position
[0].offset
= 1;
328 sentinel_position
[0].next
= NULL
;
329 position_list
[1] = &sentinel_position
[0];
332 if (position_list
[2])
334 sentinel_position
[1].next
= position_list
[2]->next
;
335 sentinel_position
[1].offset
= position_list
[2]->offset
+ 1;
336 position_list
[2]->next
= &sentinel_position
[1];
337 position_list
[2] = sentinel_position
[1].next
;
341 sentinel_position
[1].offset
= 1;
342 sentinel_position
[1].next
= NULL
;
343 position_list
[2] = &sentinel_position
[1];
348 /* Find the sync points */
351 if (lcs_om
->position
[0]->offset
> lcs_ol
->position
[0]->offset
)
353 original_sync
= lcs_om
->position
[0]->offset
;
355 while (lcs_ol
->position
[0]->offset
+ lcs_ol
->length
357 lcs_ol
= lcs_ol
->next
;
359 /* If the sync point is the EOF, and our current lcs segment
360 * doesn't reach as far as EOF, we need to skip this segment.
362 if (lcs_om
->length
== 0 && lcs_ol
->length
> 0
363 && lcs_ol
->position
[0]->offset
+ lcs_ol
->length
365 && lcs_ol
->position
[1]->offset
+ lcs_ol
->length
366 != lcs_ol
->next
->position
[1]->offset
)
367 lcs_ol
= lcs_ol
->next
;
369 if (lcs_ol
->position
[0]->offset
<= original_sync
)
374 original_sync
= lcs_ol
->position
[0]->offset
;
376 while (lcs_om
->position
[0]->offset
+ lcs_om
->length
378 lcs_om
= lcs_om
->next
;
380 /* If the sync point is the EOF, and our current lcs segment
381 * doesn't reach as far as EOF, we need to skip this segment.
383 if (lcs_ol
->length
== 0 && lcs_om
->length
> 0
384 && lcs_om
->position
[0]->offset
+ lcs_om
->length
386 && lcs_om
->position
[1]->offset
+ lcs_om
->length
387 != lcs_om
->next
->position
[1]->offset
)
388 lcs_om
= lcs_om
->next
;
390 if (lcs_om
->position
[0]->offset
<= original_sync
)
395 modified_sync
= lcs_om
->position
[1]->offset
396 + (original_sync
- lcs_om
->position
[0]->offset
);
397 latest_sync
= lcs_ol
->position
[1]->offset
398 + (original_sync
- lcs_ol
->position
[0]->offset
);
400 /* Determine what is modified, if anything */
401 is_modified
= lcs_om
->position
[0]->offset
- original_start
> 0
402 || lcs_om
->position
[1]->offset
- modified_start
> 0;
404 is_latest
= lcs_ol
->position
[0]->offset
- original_start
> 0
405 || lcs_ol
->position
[1]->offset
- latest_start
> 0;
407 if (is_modified
|| is_latest
)
409 original_length
= original_sync
- original_start
;
410 modified_length
= modified_sync
- modified_start
;
411 latest_length
= latest_sync
- latest_start
;
413 (*diff_ref
) = apr_palloc(pool
, sizeof(**diff_ref
));
415 (*diff_ref
)->original_start
= original_start
- 1;
416 (*diff_ref
)->original_length
= original_sync
- original_start
;
417 (*diff_ref
)->modified_start
= modified_start
- 1;
418 (*diff_ref
)->modified_length
= modified_length
;
419 (*diff_ref
)->latest_start
= latest_start
- 1;
420 (*diff_ref
)->latest_length
= latest_length
;
421 (*diff_ref
)->resolved_diff
= NULL
;
423 if (is_modified
&& is_latest
)
425 svn_diff__resolve_conflict(*diff_ref
,
430 else if (is_modified
)
432 (*diff_ref
)->type
= svn_diff__type_diff_modified
;
436 (*diff_ref
)->type
= svn_diff__type_diff_latest
;
439 diff_ref
= &(*diff_ref
)->next
;
443 if (lcs_om
->length
== 0 || lcs_ol
->length
== 0)
446 modified_length
= lcs_om
->length
447 - (original_sync
- lcs_om
->position
[0]->offset
);
448 latest_length
= lcs_ol
->length
449 - (original_sync
- lcs_ol
->position
[0]->offset
);
450 common_length
= modified_length
< latest_length
451 ? modified_length
: latest_length
;
453 (*diff_ref
) = apr_palloc(pool
, sizeof(**diff_ref
));
455 (*diff_ref
)->type
= svn_diff__type_common
;
456 (*diff_ref
)->original_start
= original_sync
- 1;
457 (*diff_ref
)->original_length
= common_length
;
458 (*diff_ref
)->modified_start
= modified_sync
- 1;
459 (*diff_ref
)->modified_length
= common_length
;
460 (*diff_ref
)->latest_start
= latest_sync
- 1;
461 (*diff_ref
)->latest_length
= common_length
;
462 (*diff_ref
)->resolved_diff
= NULL
;
464 diff_ref
= &(*diff_ref
)->next
;
466 /* Set the new offsets */
467 original_start
= original_sync
+ common_length
;
468 modified_start
= modified_sync
+ common_length
;
469 latest_start
= latest_sync
+ common_length
;
471 /* Make it easier for diff_common/conflict detection
472 by recording last lcs start positions
474 if (position_list
[1]->offset
< lcs_om
->position
[1]->offset
)
475 position_list
[1] = lcs_om
->position
[1];
477 if (position_list
[2]->offset
< lcs_ol
->position
[1]->offset
)
478 position_list
[2] = lcs_ol
->position
[1];
480 /* Make sure we are pointing to lcs entries beyond
481 * the range we just processed
483 while (original_start
>= lcs_om
->position
[0]->offset
+ lcs_om
->length
484 && lcs_om
->length
> 0)
486 lcs_om
= lcs_om
->next
;
489 while (original_start
>= lcs_ol
->position
[0]->offset
+ lcs_ol
->length
490 && lcs_ol
->length
> 0)
492 lcs_ol
= lcs_ol
->next
;
499 svn_pool_destroy(subpool
);