Navigate selection history via log jump up/down button
[TortoiseGit.git] / src / TortoiseMerge / libsvn_diff / diff3.c
blobf65eb9c766292f8e4c5a04887f1e4cd98a067b90
1 /*
2 * diff3.c : routines for doing diffs
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
25 #include <apr.h>
26 #include <apr_pools.h>
27 #include <apr_general.h>
29 #include "svn_pools.h"
30 #include "svn_error.h"
31 #include "svn_diff.h"
32 #include "svn_types.h"
34 #include "diff.h"
37 void
38 svn_diff__resolve_conflict(svn_diff_t *hunk,
39 svn_diff__position_t **position_list1,
40 svn_diff__position_t **position_list2,
41 svn_diff__token_index_t num_tokens,
42 apr_pool_t *pool)
44 apr_off_t modified_start = hunk->modified_start + 1;
45 apr_off_t latest_start = hunk->latest_start + 1;
46 apr_off_t common_length;
47 apr_off_t modified_length = hunk->modified_length;
48 apr_off_t latest_length = hunk->latest_length;
49 svn_diff__position_t *start_position[2];
50 svn_diff__position_t *position[2];
51 svn_diff__token_index_t *token_counts[2];
52 svn_diff__lcs_t *lcs = NULL;
53 svn_diff__lcs_t **lcs_ref = &lcs;
54 svn_diff_t **diff_ref = &hunk->resolved_diff;
55 apr_pool_t *subpool;
57 /* First find the starting positions for the
58 * comparison
61 start_position[0] = *position_list1;
62 start_position[1] = *position_list2;
64 while (start_position[0]->offset < modified_start)
65 start_position[0] = start_position[0]->next;
67 while (start_position[1]->offset < latest_start)
68 start_position[1] = start_position[1]->next;
70 position[0] = start_position[0];
71 position[1] = start_position[1];
73 common_length = modified_length < latest_length
74 ? modified_length : latest_length;
76 while (common_length > 0
77 && position[0]->token_index == position[1]->token_index)
79 position[0] = position[0]->next;
80 position[1] = position[1]->next;
82 common_length--;
85 if (common_length == 0
86 && modified_length == latest_length)
88 hunk->type = svn_diff__type_diff_common;
89 hunk->resolved_diff = NULL;
91 *position_list1 = position[0];
92 *position_list2 = position[1];
94 return;
97 hunk->type = svn_diff__type_conflict;
99 /* ### If we have a conflict we can try to find the
100 * ### common parts in it by getting an lcs between
101 * ### modified (start to start + length) and
102 * ### latest (start to start + length).
103 * ### We use this lcs to create a simple diff. Only
104 * ### where there is a diff between the two, we have
105 * ### a conflict.
106 * ### This raises a problem; several common diffs and
107 * ### conflicts can occur within the same original
108 * ### block. This needs some thought.
109 * ###
110 * ### NB: We can use the node _pointers_ to identify
111 * ### different tokens
114 subpool = svn_pool_create(pool);
116 /* Calculate how much of the two sequences was
117 * actually the same.
119 common_length = (modified_length < latest_length
120 ? modified_length : latest_length)
121 - common_length;
123 /* If there were matching symbols at the start of
124 * both sequences, record that fact.
126 if (common_length > 0)
128 lcs = apr_palloc(subpool, sizeof(*lcs));
129 lcs->next = NULL;
130 lcs->position[0] = start_position[0];
131 lcs->position[1] = start_position[1];
132 lcs->length = common_length;
134 lcs_ref = &lcs->next;
137 modified_length -= common_length;
138 latest_length -= common_length;
140 modified_start = start_position[0]->offset;
141 latest_start = start_position[1]->offset;
143 start_position[0] = position[0];
144 start_position[1] = position[1];
146 /* Create a new ring for svn_diff__lcs to grok.
147 * We can safely do this given we don't need the
148 * positions we processed anymore.
150 if (modified_length == 0)
152 *position_list1 = position[0];
153 position[0] = NULL;
155 else
157 while (--modified_length)
158 position[0] = position[0]->next;
160 *position_list1 = position[0]->next;
161 position[0]->next = start_position[0];
164 if (latest_length == 0)
166 *position_list2 = position[1];
167 position[1] = NULL;
169 else
171 while (--latest_length)
172 position[1] = position[1]->next;
174 *position_list2 = position[1]->next;
175 position[1]->next = start_position[1];
178 token_counts[0] = svn_diff__get_token_counts(position[0], num_tokens,
179 subpool);
180 token_counts[1] = svn_diff__get_token_counts(position[1], num_tokens,
181 subpool);
183 *lcs_ref = svn_diff__lcs(position[0], position[1], token_counts[0],
184 token_counts[1], num_tokens, 0, 0, subpool);
186 /* Fix up the EOF lcs element in case one of
187 * the two sequences was NULL.
189 if ((*lcs_ref)->position[0]->offset == 1)
190 (*lcs_ref)->position[0] = *position_list1;
192 if ((*lcs_ref)->position[1]->offset == 1)
193 (*lcs_ref)->position[1] = *position_list2;
195 /* Produce the resolved diff */
196 while (1)
198 if (modified_start < lcs->position[0]->offset
199 || latest_start < lcs->position[1]->offset)
201 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
203 (*diff_ref)->type = svn_diff__type_conflict;
204 (*diff_ref)->original_start = hunk->original_start;
205 (*diff_ref)->original_length = hunk->original_length;
206 (*diff_ref)->modified_start = modified_start - 1;
207 (*diff_ref)->modified_length = lcs->position[0]->offset
208 - modified_start;
209 (*diff_ref)->latest_start = latest_start - 1;
210 (*diff_ref)->latest_length = lcs->position[1]->offset
211 - latest_start;
212 (*diff_ref)->resolved_diff = NULL;
214 diff_ref = &(*diff_ref)->next;
217 /* Detect the EOF */
218 if (lcs->length == 0)
219 break;
221 modified_start = lcs->position[0]->offset;
222 latest_start = lcs->position[1]->offset;
224 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
226 (*diff_ref)->type = svn_diff__type_diff_common;
227 (*diff_ref)->original_start = hunk->original_start;
228 (*diff_ref)->original_length = hunk->original_length;
229 (*diff_ref)->modified_start = modified_start - 1;
230 (*diff_ref)->modified_length = lcs->length;
231 (*diff_ref)->latest_start = latest_start - 1;
232 (*diff_ref)->latest_length = lcs->length;
233 (*diff_ref)->resolved_diff = NULL;
235 diff_ref = &(*diff_ref)->next;
237 modified_start += lcs->length;
238 latest_start += lcs->length;
240 lcs = lcs->next;
243 *diff_ref = NULL;
245 svn_pool_destroy(subpool);
249 svn_error_t *
250 svn_diff_diff3_2(svn_diff_t **diff,
251 void *diff_baton,
252 const svn_diff_fns2_t *vtable,
253 apr_pool_t *pool)
255 svn_diff__tree_t *tree;
256 svn_diff__position_t *position_list[3];
257 svn_diff__token_index_t num_tokens;
258 svn_diff__token_index_t *token_counts[3];
259 svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
260 svn_diff_datasource_modified,
261 svn_diff_datasource_latest};
262 svn_diff__lcs_t *lcs_om;
263 svn_diff__lcs_t *lcs_ol;
264 apr_pool_t *subpool;
265 apr_pool_t *treepool;
266 apr_off_t prefix_lines = 0;
267 apr_off_t suffix_lines = 0;
269 *diff = NULL;
271 subpool = svn_pool_create(pool);
272 treepool = svn_pool_create(pool);
274 svn_diff__tree_create(&tree, treepool);
276 SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
277 datasource, 3));
279 SVN_ERR(svn_diff__get_tokens(&position_list[0],
280 tree,
281 diff_baton, vtable,
282 svn_diff_datasource_original,
283 prefix_lines,
284 subpool));
286 SVN_ERR(svn_diff__get_tokens(&position_list[1],
287 tree,
288 diff_baton, vtable,
289 svn_diff_datasource_modified,
290 prefix_lines,
291 subpool));
293 SVN_ERR(svn_diff__get_tokens(&position_list[2],
294 tree,
295 diff_baton, vtable,
296 svn_diff_datasource_latest,
297 prefix_lines,
298 subpool));
300 num_tokens = svn_diff__get_node_count(tree);
302 /* Get rid of the tokens, we don't need them to calc the diff */
303 if (vtable->token_discard_all != NULL)
304 vtable->token_discard_all(diff_baton);
306 /* We don't need the nodes in the tree either anymore, nor the tree itself */
307 svn_pool_destroy(treepool);
309 token_counts[0] = svn_diff__get_token_counts(position_list[0], num_tokens,
310 subpool);
311 token_counts[1] = svn_diff__get_token_counts(position_list[1], num_tokens,
312 subpool);
313 token_counts[2] = svn_diff__get_token_counts(position_list[2], num_tokens,
314 subpool);
316 /* Get the lcs for original-modified and original-latest */
317 lcs_om = svn_diff__lcs(position_list[0], position_list[1], token_counts[0],
318 token_counts[1], num_tokens, prefix_lines,
319 suffix_lines, subpool);
320 lcs_ol = svn_diff__lcs(position_list[0], position_list[2], token_counts[0],
321 token_counts[2], num_tokens, prefix_lines,
322 suffix_lines, subpool);
324 /* Produce a merged diff */
326 svn_diff_t **diff_ref = diff;
328 apr_off_t original_start = 1;
329 apr_off_t modified_start = 1;
330 apr_off_t latest_start = 1;
331 apr_off_t original_sync;
332 apr_off_t modified_sync;
333 apr_off_t latest_sync;
334 apr_off_t common_length;
335 apr_off_t modified_length;
336 apr_off_t latest_length;
337 svn_boolean_t is_modified;
338 svn_boolean_t is_latest;
339 svn_diff__position_t sentinel_position[2];
341 /* Point the position lists to the start of the list
342 * so that common_diff/conflict detection actually is
343 * able to work.
345 if (position_list[1])
347 sentinel_position[0].next = position_list[1]->next;
348 sentinel_position[0].offset = position_list[1]->offset + 1;
349 position_list[1]->next = &sentinel_position[0];
350 position_list[1] = sentinel_position[0].next;
352 else
354 sentinel_position[0].offset = prefix_lines + 1;
355 sentinel_position[0].next = NULL;
356 position_list[1] = &sentinel_position[0];
359 if (position_list[2])
361 sentinel_position[1].next = position_list[2]->next;
362 sentinel_position[1].offset = position_list[2]->offset + 1;
363 position_list[2]->next = &sentinel_position[1];
364 position_list[2] = sentinel_position[1].next;
366 else
368 sentinel_position[1].offset = prefix_lines + 1;
369 sentinel_position[1].next = NULL;
370 position_list[2] = &sentinel_position[1];
373 while (1)
375 /* Find the sync points */
376 while (1)
378 if (lcs_om->position[0]->offset > lcs_ol->position[0]->offset)
380 original_sync = lcs_om->position[0]->offset;
382 while (lcs_ol->position[0]->offset + lcs_ol->length
383 < original_sync)
384 lcs_ol = lcs_ol->next;
386 /* If the sync point is the EOF, and our current lcs segment
387 * doesn't reach as far as EOF, we need to skip this segment.
389 if (lcs_om->length == 0 && lcs_ol->length > 0
390 && lcs_ol->position[0]->offset + lcs_ol->length
391 == original_sync
392 && lcs_ol->position[1]->offset + lcs_ol->length
393 != lcs_ol->next->position[1]->offset)
394 lcs_ol = lcs_ol->next;
396 if (lcs_ol->position[0]->offset <= original_sync)
397 break;
399 else
401 original_sync = lcs_ol->position[0]->offset;
403 while (lcs_om->position[0]->offset + lcs_om->length
404 < original_sync)
405 lcs_om = lcs_om->next;
407 /* If the sync point is the EOF, and our current lcs segment
408 * doesn't reach as far as EOF, we need to skip this segment.
410 if (lcs_ol->length == 0 && lcs_om->length > 0
411 && lcs_om->position[0]->offset + lcs_om->length
412 == original_sync
413 && lcs_om->position[1]->offset + lcs_om->length
414 != lcs_om->next->position[1]->offset)
415 lcs_om = lcs_om->next;
417 if (lcs_om->position[0]->offset <= original_sync)
418 break;
422 modified_sync = lcs_om->position[1]->offset
423 + (original_sync - lcs_om->position[0]->offset);
424 latest_sync = lcs_ol->position[1]->offset
425 + (original_sync - lcs_ol->position[0]->offset);
427 /* Determine what is modified, if anything */
428 is_modified = lcs_om->position[0]->offset - original_start > 0
429 || lcs_om->position[1]->offset - modified_start > 0;
431 is_latest = lcs_ol->position[0]->offset - original_start > 0
432 || lcs_ol->position[1]->offset - latest_start > 0;
434 if (is_modified || is_latest)
436 modified_length = modified_sync - modified_start;
437 latest_length = latest_sync - latest_start;
439 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
441 (*diff_ref)->original_start = original_start - 1;
442 (*diff_ref)->original_length = original_sync - original_start;
443 (*diff_ref)->modified_start = modified_start - 1;
444 (*diff_ref)->modified_length = modified_length;
445 (*diff_ref)->latest_start = latest_start - 1;
446 (*diff_ref)->latest_length = latest_length;
447 (*diff_ref)->resolved_diff = NULL;
449 if (is_modified && is_latest)
451 svn_diff__resolve_conflict(*diff_ref,
452 &position_list[1],
453 &position_list[2],
454 num_tokens,
455 pool);
457 else if (is_modified)
459 (*diff_ref)->type = svn_diff__type_diff_modified;
461 else
463 (*diff_ref)->type = svn_diff__type_diff_latest;
466 diff_ref = &(*diff_ref)->next;
469 /* Detect EOF */
470 if (lcs_om->length == 0 || lcs_ol->length == 0)
471 break;
473 modified_length = lcs_om->length
474 - (original_sync - lcs_om->position[0]->offset);
475 latest_length = lcs_ol->length
476 - (original_sync - lcs_ol->position[0]->offset);
477 common_length = modified_length < latest_length
478 ? modified_length : latest_length;
480 (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
482 (*diff_ref)->type = svn_diff__type_common;
483 (*diff_ref)->original_start = original_sync - 1;
484 (*diff_ref)->original_length = common_length;
485 (*diff_ref)->modified_start = modified_sync - 1;
486 (*diff_ref)->modified_length = common_length;
487 (*diff_ref)->latest_start = latest_sync - 1;
488 (*diff_ref)->latest_length = common_length;
489 (*diff_ref)->resolved_diff = NULL;
491 diff_ref = &(*diff_ref)->next;
493 /* Set the new offsets */
494 original_start = original_sync + common_length;
495 modified_start = modified_sync + common_length;
496 latest_start = latest_sync + common_length;
498 /* Make it easier for diff_common/conflict detection
499 by recording last lcs start positions
501 if (position_list[1]->offset < lcs_om->position[1]->offset)
502 position_list[1] = lcs_om->position[1];
504 if (position_list[2]->offset < lcs_ol->position[1]->offset)
505 position_list[2] = lcs_ol->position[1];
507 /* Make sure we are pointing to lcs entries beyond
508 * the range we just processed
510 while (original_start >= lcs_om->position[0]->offset + lcs_om->length
511 && lcs_om->length > 0)
513 lcs_om = lcs_om->next;
516 while (original_start >= lcs_ol->position[0]->offset + lcs_ol->length
517 && lcs_ol->length > 0)
519 lcs_ol = lcs_ol->next;
523 *diff_ref = NULL;
526 svn_pool_destroy(subpool);
528 return SVN_NO_ERROR;