Success build TortoiseMerge.
[TortoiseGit.git] / src / TortoiseMerge / libsvn_diff / diff3.c
blob269cb4d5c939d32997d0baa709e371c9d27e5c8e
1 /*
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 * ====================================================================
20 #include <apr.h>
21 //#include <apr_pools.h>
22 #include <apr_general.h>
23 #include "svn_error.h"
24 #include "svn_version.h"
25 #include "svn_io.h"
27 #include "svn_pools.h"
28 #include "svn_error.h"
29 #include "svn_diff.h"
30 #include "svn_types.h"
32 #include "diff.h"
35 void
36 svn_diff__resolve_conflict(svn_diff_t *hunk,
37 svn_diff__position_t **position_list1,
38 svn_diff__position_t **position_list2,
39 apr_pool_t *pool)
41 apr_off_t modified_start = hunk->modified_start + 1;
42 apr_off_t latest_start = hunk->latest_start + 1;
43 apr_off_t common_length;
44 apr_off_t modified_length = hunk->modified_length;
45 apr_off_t latest_length = hunk->latest_length;
46 svn_diff__position_t *start_position[2];
47 svn_diff__position_t *position[2];
48 svn_diff__lcs_t *lcs = NULL;
49 svn_diff__lcs_t **lcs_ref = &lcs;
50 svn_diff_t **diff_ref = &hunk->resolved_diff;
51 apr_pool_t *subpool;
53 /* First find the starting positions for the
54 * comparison
57 start_position[0] = *position_list1;
58 start_position[1] = *position_list2;
60 while (start_position[0]->offset < modified_start)
61 start_position[0] = start_position[0]->next;
63 while (start_position[1]->offset < latest_start)
64 start_position[1] = start_position[1]->next;
66 position[0] = start_position[0];
67 position[1] = start_position[1];
69 common_length = modified_length < latest_length
70 ? modified_length : latest_length;
72 while (common_length > 0
73 && position[0]->node == position[1]->node)
75 position[0] = position[0]->next;
76 position[1] = position[1]->next;
78 common_length--;
81 if (common_length == 0
82 && modified_length == latest_length)
84 hunk->type = svn_diff__type_diff_common;
85 hunk->resolved_diff = NULL;
87 *position_list1 = position[0];
88 *position_list2 = position[1];
90 return;
93 hunk->type = svn_diff__type_conflict;
95 /* ### If we have a conflict we can try to find the
96 * ### common parts in it by getting an lcs between
97 * ### modified (start to start + length) and
98 * ### latest (start to start + length).
99 * ### We use this lcs to create a simple diff. Only
100 * ### where there is a diff between the two, we have
101 * ### a conflict.
102 * ### This raises a problem; several common diffs and
103 * ### conflicts can occur within the same original
104 * ### block. This needs some thought.
105 * ###
106 * ### NB: We can use the node _pointers_ to identify
107 * ### different tokens
110 subpool = svn_pool_create(pool);
112 /* Calculate how much of the two sequences was
113 * actually the same.
115 common_length = (modified_length < latest_length
116 ? modified_length : latest_length)
117 - common_length;
119 /* If there were matching symbols at the start of
120 * both sequences, record that fact.
122 if (common_length > 0)
124 lcs = apr_palloc(subpool, sizeof(*lcs));
125 lcs->next = NULL;
126 lcs->position[0] = start_position[0];
127 lcs->position[1] = start_position[1];
128 lcs->length = common_length;
130 lcs_ref = &lcs->next;
133 modified_length -= common_length;
134 latest_length -= common_length;
136 modified_start = start_position[0]->offset;
137 latest_start = start_position[1]->offset;
139 start_position[0] = position[0];
140 start_position[1] = position[1];
142 /* Create a new ring for svn_diff__lcs to grok.
143 * We can safely do this given we don't need the
144 * positions we processed anymore.
146 if (modified_length == 0)
148 *position_list1 = position[0];
149 position[0] = NULL;
151 else
153 while (--modified_length)
154 position[0] = position[0]->next;
156 *position_list1 = position[0]->next;
157 position[0]->next = start_position[0];
160 if (latest_length == 0)
162 *position_list2 = position[1];
163 position[1] = NULL;
165 else
167 while (--latest_length)
168 position[1] = position[1]->next;
170 *position_list2 = position[1]->next;
171 position[1]->next = start_position[1];
174 *lcs_ref = svn_diff__lcs(position[0], position[1],
175 subpool);
177 /* Fix up the EOF lcs element in case one of
178 * the two sequences was NULL.
180 if ((*lcs_ref)->position[0]->offset == 1)
181 (*lcs_ref)->position[0] = *position_list1;
183 if ((*lcs_ref)->position[1]->offset == 1)
184 (*lcs_ref)->position[1] = *position_list2;
186 /* Restore modified_length and latest_length */
187 modified_length = hunk->modified_length;
188 latest_length = hunk->latest_length;
190 /* Produce the resolved diff */
191 while (1)
193 if (modified_start < lcs->position[0]->offset
194 || latest_start < lcs->position[1]->offset)
196 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
198 (*diff_ref)->type = svn_diff__type_conflict;
199 (*diff_ref)->original_start = hunk->original_start;
200 (*diff_ref)->original_length = hunk->original_length;
201 (*diff_ref)->modified_start = modified_start - 1;
202 (*diff_ref)->modified_length = lcs->position[0]->offset
203 - modified_start;
204 (*diff_ref)->latest_start = latest_start - 1;
205 (*diff_ref)->latest_length = lcs->position[1]->offset
206 - latest_start;
207 (*diff_ref)->resolved_diff = NULL;
209 diff_ref = &(*diff_ref)->next;
212 /* Detect the EOF */
213 if (lcs->length == 0)
214 break;
216 modified_start = lcs->position[0]->offset;
217 latest_start = lcs->position[1]->offset;
219 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
221 (*diff_ref)->type = svn_diff__type_diff_common;
222 (*diff_ref)->original_start = hunk->original_start;
223 (*diff_ref)->original_length = hunk->original_length;
224 (*diff_ref)->modified_start = modified_start - 1;
225 (*diff_ref)->modified_length = lcs->length;
226 (*diff_ref)->latest_start = latest_start - 1;
227 (*diff_ref)->latest_length = lcs->length;
228 (*diff_ref)->resolved_diff = NULL;
230 diff_ref = &(*diff_ref)->next;
232 modified_start += lcs->length;
233 latest_start += lcs->length;
235 lcs = lcs->next;
238 *diff_ref = NULL;
240 svn_pool_destroy(subpool);
244 svn_error_t *
245 svn_diff_diff3(svn_diff_t **diff,
246 void *diff_baton,
247 const svn_diff_fns_t *vtable,
248 apr_pool_t *pool)
250 svn_diff__tree_t *tree;
251 svn_diff__position_t *position_list[3];
252 svn_diff__lcs_t *lcs_om;
253 svn_diff__lcs_t *lcs_ol;
254 apr_pool_t *subpool;
255 apr_pool_t *treepool;
257 *diff = NULL;
259 subpool = svn_pool_create(pool);
260 treepool = svn_pool_create(pool);
262 svn_diff__tree_create(&tree, treepool);
264 SVN_ERR(svn_diff__get_tokens(&position_list[0],
265 tree,
266 diff_baton, vtable,
267 svn_diff_datasource_original,
268 subpool));
270 SVN_ERR(svn_diff__get_tokens(&position_list[1],
271 tree,
272 diff_baton, vtable,
273 svn_diff_datasource_modified,
274 subpool));
276 SVN_ERR(svn_diff__get_tokens(&position_list[2],
277 tree,
278 diff_baton, vtable,
279 svn_diff_datasource_latest,
280 subpool));
282 /* Get rid of the tokens, we don't need them to calc the diff */
283 if (vtable->token_discard_all != NULL)
284 vtable->token_discard_all(diff_baton);
286 /* We don't need the nodes in the tree either anymore, nor the tree itself */
287 svn_pool_destroy(treepool);
289 /* Get the lcs for original-modified and original-latest */
290 lcs_om = svn_diff__lcs(position_list[0], position_list[1],
291 subpool);
292 lcs_ol = svn_diff__lcs(position_list[0], position_list[2],
293 subpool);
295 /* Produce a merged diff */
297 svn_diff_t **diff_ref = diff;
299 apr_off_t original_start = 1;
300 apr_off_t modified_start = 1;
301 apr_off_t latest_start = 1;
302 apr_off_t original_sync;
303 apr_off_t modified_sync;
304 apr_off_t latest_sync;
305 apr_off_t common_length;
306 apr_off_t original_length;
307 apr_off_t modified_length;
308 apr_off_t latest_length;
309 svn_boolean_t is_modified;
310 svn_boolean_t is_latest;
311 svn_diff__position_t sentinel_position[2];
313 /* Point the position lists to the start of the list
314 * so that common_diff/conflict detection actually is
315 * able to work.
317 if (position_list[1])
319 sentinel_position[0].next = position_list[1]->next;
320 sentinel_position[0].offset = position_list[1]->offset + 1;
321 position_list[1]->next = &sentinel_position[0];
322 position_list[1] = sentinel_position[0].next;
324 else
326 sentinel_position[0].offset = 1;
327 sentinel_position[0].next = NULL;
328 position_list[1] = &sentinel_position[0];
331 if (position_list[2])
333 sentinel_position[1].next = position_list[2]->next;
334 sentinel_position[1].offset = position_list[2]->offset + 1;
335 position_list[2]->next = &sentinel_position[1];
336 position_list[2] = sentinel_position[1].next;
338 else
340 sentinel_position[1].offset = 1;
341 sentinel_position[1].next = NULL;
342 position_list[2] = &sentinel_position[1];
345 while (1)
347 /* Find the sync points */
348 while (1)
350 if (lcs_om->position[0]->offset > lcs_ol->position[0]->offset)
352 original_sync = lcs_om->position[0]->offset;
354 while (lcs_ol->position[0]->offset + lcs_ol->length
355 < original_sync)
356 lcs_ol = lcs_ol->next;
358 /* If the sync point is the EOF, and our current lcs segment
359 * doesn't reach as far as EOF, we need to skip this segment.
361 if (lcs_om->length == 0 && lcs_ol->length > 0
362 && lcs_ol->position[0]->offset + lcs_ol->length
363 == original_sync
364 && lcs_ol->position[1]->offset + lcs_ol->length
365 != lcs_ol->next->position[1]->offset)
366 lcs_ol = lcs_ol->next;
368 if (lcs_ol->position[0]->offset <= original_sync)
369 break;
371 else
373 original_sync = lcs_ol->position[0]->offset;
375 while (lcs_om->position[0]->offset + lcs_om->length
376 < original_sync)
377 lcs_om = lcs_om->next;
379 /* If the sync point is the EOF, and our current lcs segment
380 * doesn't reach as far as EOF, we need to skip this segment.
382 if (lcs_ol->length == 0 && lcs_om->length > 0
383 && lcs_om->position[0]->offset + lcs_om->length
384 == original_sync
385 && lcs_om->position[1]->offset + lcs_om->length
386 != lcs_om->next->position[1]->offset)
387 lcs_om = lcs_om->next;
389 if (lcs_om->position[0]->offset <= original_sync)
390 break;
394 modified_sync = lcs_om->position[1]->offset
395 + (original_sync - lcs_om->position[0]->offset);
396 latest_sync = lcs_ol->position[1]->offset
397 + (original_sync - lcs_ol->position[0]->offset);
399 /* Determine what is modified, if anything */
400 is_modified = lcs_om->position[0]->offset - original_start > 0
401 || lcs_om->position[1]->offset - modified_start > 0;
403 is_latest = lcs_ol->position[0]->offset - original_start > 0
404 || lcs_ol->position[1]->offset - latest_start > 0;
406 if (is_modified || is_latest)
408 original_length = original_sync - original_start;
409 modified_length = modified_sync - modified_start;
410 latest_length = latest_sync - latest_start;
412 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
414 (*diff_ref)->original_start = original_start - 1;
415 (*diff_ref)->original_length = original_sync - original_start;
416 (*diff_ref)->modified_start = modified_start - 1;
417 (*diff_ref)->modified_length = modified_length;
418 (*diff_ref)->latest_start = latest_start - 1;
419 (*diff_ref)->latest_length = latest_length;
420 (*diff_ref)->resolved_diff = NULL;
422 if (is_modified && is_latest)
424 svn_diff__resolve_conflict(*diff_ref,
425 &position_list[1],
426 &position_list[2],
427 pool);
429 else if (is_modified)
431 (*diff_ref)->type = svn_diff__type_diff_modified;
433 else
435 (*diff_ref)->type = svn_diff__type_diff_latest;
438 diff_ref = &(*diff_ref)->next;
441 /* Detect EOF */
442 if (lcs_om->length == 0 || lcs_ol->length == 0)
443 break;
445 modified_length = lcs_om->length
446 - (original_sync - lcs_om->position[0]->offset);
447 latest_length = lcs_ol->length
448 - (original_sync - lcs_ol->position[0]->offset);
449 common_length = modified_length < latest_length
450 ? modified_length : latest_length;
452 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
454 (*diff_ref)->type = svn_diff__type_common;
455 (*diff_ref)->original_start = original_sync - 1;
456 (*diff_ref)->original_length = common_length;
457 (*diff_ref)->modified_start = modified_sync - 1;
458 (*diff_ref)->modified_length = common_length;
459 (*diff_ref)->latest_start = latest_sync - 1;
460 (*diff_ref)->latest_length = common_length;
461 (*diff_ref)->resolved_diff = NULL;
463 diff_ref = &(*diff_ref)->next;
465 /* Set the new offsets */
466 original_start = original_sync + common_length;
467 modified_start = modified_sync + common_length;
468 latest_start = latest_sync + common_length;
470 /* Make it easier for diff_common/conflict detection
471 by recording last lcs start positions
473 if (position_list[1]->offset < lcs_om->position[1]->offset)
474 position_list[1] = lcs_om->position[1];
476 if (position_list[2]->offset < lcs_ol->position[1]->offset)
477 position_list[2] = lcs_ol->position[1];
479 /* Make sure we are pointing to lcs entries beyond
480 * the range we just processed
482 while (original_start >= lcs_om->position[0]->offset + lcs_om->length
483 && lcs_om->length > 0)
485 lcs_om = lcs_om->next;
488 while (original_start >= lcs_ol->position[0]->offset + lcs_ol->length
489 && lcs_ol->length > 0)
491 lcs_ol = lcs_ol->next;
495 *diff_ref = NULL;
498 svn_pool_destroy(subpool);
500 return SVN_NO_ERROR;