summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_diff/diff3.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
committerpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
commitd25dac7fcc6acc838b71bbda8916fd9665c709ab (patch)
tree135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_diff/diff3.c
downloadFreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip
FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_diff/diff3.c')
-rw-r--r--subversion/libsvn_diff/diff3.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/subversion/libsvn_diff/diff3.c b/subversion/libsvn_diff/diff3.c
new file mode 100644
index 0000000..8b7c9b3
--- /dev/null
+++ b/subversion/libsvn_diff/diff3.c
@@ -0,0 +1,529 @@
+/*
+ * diff3.c : routines for doing diffs
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+#include <apr.h>
+#include <apr_pools.h>
+#include <apr_general.h>
+
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_diff.h"
+#include "svn_types.h"
+
+#include "diff.h"
+
+
+void
+svn_diff__resolve_conflict(svn_diff_t *hunk,
+ svn_diff__position_t **position_list1,
+ svn_diff__position_t **position_list2,
+ svn_diff__token_index_t num_tokens,
+ apr_pool_t *pool)
+{
+ apr_off_t modified_start = hunk->modified_start + 1;
+ apr_off_t latest_start = hunk->latest_start + 1;
+ apr_off_t common_length;
+ apr_off_t modified_length = hunk->modified_length;
+ apr_off_t latest_length = hunk->latest_length;
+ svn_diff__position_t *start_position[2];
+ svn_diff__position_t *position[2];
+ svn_diff__token_index_t *token_counts[2];
+ svn_diff__lcs_t *lcs = NULL;
+ svn_diff__lcs_t **lcs_ref = &lcs;
+ svn_diff_t **diff_ref = &hunk->resolved_diff;
+ apr_pool_t *subpool;
+
+ /* First find the starting positions for the
+ * comparison
+ */
+
+ start_position[0] = *position_list1;
+ start_position[1] = *position_list2;
+
+ while (start_position[0]->offset < modified_start)
+ start_position[0] = start_position[0]->next;
+
+ while (start_position[1]->offset < latest_start)
+ start_position[1] = start_position[1]->next;
+
+ position[0] = start_position[0];
+ position[1] = start_position[1];
+
+ common_length = modified_length < latest_length
+ ? modified_length : latest_length;
+
+ while (common_length > 0
+ && position[0]->token_index == position[1]->token_index)
+ {
+ position[0] = position[0]->next;
+ position[1] = position[1]->next;
+
+ common_length--;
+ }
+
+ if (common_length == 0
+ && modified_length == latest_length)
+ {
+ hunk->type = svn_diff__type_diff_common;
+ hunk->resolved_diff = NULL;
+
+ *position_list1 = position[0];
+ *position_list2 = position[1];
+
+ return;
+ }
+
+ hunk->type = svn_diff__type_conflict;
+
+ /* ### If we have a conflict we can try to find the
+ * ### common parts in it by getting an lcs between
+ * ### modified (start to start + length) and
+ * ### latest (start to start + length).
+ * ### We use this lcs to create a simple diff. Only
+ * ### where there is a diff between the two, we have
+ * ### a conflict.
+ * ### This raises a problem; several common diffs and
+ * ### conflicts can occur within the same original
+ * ### block. This needs some thought.
+ * ###
+ * ### NB: We can use the node _pointers_ to identify
+ * ### different tokens
+ */
+
+ subpool = svn_pool_create(pool);
+
+ /* Calculate how much of the two sequences was
+ * actually the same.
+ */
+ common_length = (modified_length < latest_length
+ ? modified_length : latest_length)
+ - common_length;
+
+ /* If there were matching symbols at the start of
+ * both sequences, record that fact.
+ */
+ if (common_length > 0)
+ {
+ lcs = apr_palloc(subpool, sizeof(*lcs));
+ lcs->next = NULL;
+ lcs->position[0] = start_position[0];
+ lcs->position[1] = start_position[1];
+ lcs->length = common_length;
+
+ lcs_ref = &lcs->next;
+ }
+
+ modified_length -= common_length;
+ latest_length -= common_length;
+
+ modified_start = start_position[0]->offset;
+ latest_start = start_position[1]->offset;
+
+ start_position[0] = position[0];
+ start_position[1] = position[1];
+
+ /* Create a new ring for svn_diff__lcs to grok.
+ * We can safely do this given we don't need the
+ * positions we processed anymore.
+ */
+ if (modified_length == 0)
+ {
+ *position_list1 = position[0];
+ position[0] = NULL;
+ }
+ else
+ {
+ while (--modified_length)
+ position[0] = position[0]->next;
+
+ *position_list1 = position[0]->next;
+ position[0]->next = start_position[0];
+ }
+
+ if (latest_length == 0)
+ {
+ *position_list2 = position[1];
+ position[1] = NULL;
+ }
+ else
+ {
+ while (--latest_length)
+ position[1] = position[1]->next;
+
+ *position_list2 = position[1]->next;
+ position[1]->next = start_position[1];
+ }
+
+ token_counts[0] = svn_diff__get_token_counts(position[0], num_tokens,
+ subpool);
+ token_counts[1] = svn_diff__get_token_counts(position[1], num_tokens,
+ subpool);
+
+ *lcs_ref = svn_diff__lcs(position[0], position[1], token_counts[0],
+ token_counts[1], num_tokens, 0, 0, subpool);
+
+ /* Fix up the EOF lcs element in case one of
+ * the two sequences was NULL.
+ */
+ if ((*lcs_ref)->position[0]->offset == 1)
+ (*lcs_ref)->position[0] = *position_list1;
+
+ if ((*lcs_ref)->position[1]->offset == 1)
+ (*lcs_ref)->position[1] = *position_list2;
+
+ /* Produce the resolved diff */
+ while (1)
+ {
+ if (modified_start < lcs->position[0]->offset
+ || latest_start < lcs->position[1]->offset)
+ {
+ (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
+
+ (*diff_ref)->type = svn_diff__type_conflict;
+ (*diff_ref)->original_start = hunk->original_start;
+ (*diff_ref)->original_length = hunk->original_length;
+ (*diff_ref)->modified_start = modified_start - 1;
+ (*diff_ref)->modified_length = lcs->position[0]->offset
+ - modified_start;
+ (*diff_ref)->latest_start = latest_start - 1;
+ (*diff_ref)->latest_length = lcs->position[1]->offset
+ - latest_start;
+ (*diff_ref)->resolved_diff = NULL;
+
+ diff_ref = &(*diff_ref)->next;
+ }
+
+ /* Detect the EOF */
+ if (lcs->length == 0)
+ break;
+
+ modified_start = lcs->position[0]->offset;
+ latest_start = lcs->position[1]->offset;
+
+ (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
+
+ (*diff_ref)->type = svn_diff__type_diff_common;
+ (*diff_ref)->original_start = hunk->original_start;
+ (*diff_ref)->original_length = hunk->original_length;
+ (*diff_ref)->modified_start = modified_start - 1;
+ (*diff_ref)->modified_length = lcs->length;
+ (*diff_ref)->latest_start = latest_start - 1;
+ (*diff_ref)->latest_length = lcs->length;
+ (*diff_ref)->resolved_diff = NULL;
+
+ diff_ref = &(*diff_ref)->next;
+
+ modified_start += lcs->length;
+ latest_start += lcs->length;
+
+ lcs = lcs->next;
+ }
+
+ *diff_ref = NULL;
+
+ svn_pool_destroy(subpool);
+}
+
+
+svn_error_t *
+svn_diff_diff3_2(svn_diff_t **diff,
+ void *diff_baton,
+ const svn_diff_fns2_t *vtable,
+ apr_pool_t *pool)
+{
+ svn_diff__tree_t *tree;
+ svn_diff__position_t *position_list[3];
+ svn_diff__token_index_t num_tokens;
+ svn_diff__token_index_t *token_counts[3];
+ svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
+ svn_diff_datasource_modified,
+ svn_diff_datasource_latest};
+ svn_diff__lcs_t *lcs_om;
+ svn_diff__lcs_t *lcs_ol;
+ apr_pool_t *subpool;
+ apr_pool_t *treepool;
+ apr_off_t prefix_lines = 0;
+ apr_off_t suffix_lines = 0;
+
+ *diff = NULL;
+
+ subpool = svn_pool_create(pool);
+ treepool = svn_pool_create(pool);
+
+ svn_diff__tree_create(&tree, treepool);
+
+ SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
+ datasource, 3));
+
+ SVN_ERR(svn_diff__get_tokens(&position_list[0],
+ tree,
+ diff_baton, vtable,
+ svn_diff_datasource_original,
+ prefix_lines,
+ subpool));
+
+ SVN_ERR(svn_diff__get_tokens(&position_list[1],
+ tree,
+ diff_baton, vtable,
+ svn_diff_datasource_modified,
+ prefix_lines,
+ subpool));
+
+ SVN_ERR(svn_diff__get_tokens(&position_list[2],
+ tree,
+ diff_baton, vtable,
+ svn_diff_datasource_latest,
+ prefix_lines,
+ subpool));
+
+ num_tokens = svn_diff__get_node_count(tree);
+
+ /* Get rid of the tokens, we don't need them to calc the diff */
+ if (vtable->token_discard_all != NULL)
+ vtable->token_discard_all(diff_baton);
+
+ /* We don't need the nodes in the tree either anymore, nor the tree itself */
+ svn_pool_destroy(treepool);
+
+ token_counts[0] = svn_diff__get_token_counts(position_list[0], num_tokens,
+ subpool);
+ token_counts[1] = svn_diff__get_token_counts(position_list[1], num_tokens,
+ subpool);
+ token_counts[2] = svn_diff__get_token_counts(position_list[2], num_tokens,
+ subpool);
+
+ /* Get the lcs for original-modified and original-latest */
+ lcs_om = svn_diff__lcs(position_list[0], position_list[1], token_counts[0],
+ token_counts[1], num_tokens, prefix_lines,
+ suffix_lines, subpool);
+ lcs_ol = svn_diff__lcs(position_list[0], position_list[2], token_counts[0],
+ token_counts[2], num_tokens, prefix_lines,
+ suffix_lines, subpool);
+
+ /* Produce a merged diff */
+ {
+ svn_diff_t **diff_ref = diff;
+
+ apr_off_t original_start = 1;
+ apr_off_t modified_start = 1;
+ apr_off_t latest_start = 1;
+ apr_off_t original_sync;
+ apr_off_t modified_sync;
+ apr_off_t latest_sync;
+ apr_off_t common_length;
+ apr_off_t modified_length;
+ apr_off_t latest_length;
+ svn_boolean_t is_modified;
+ svn_boolean_t is_latest;
+ svn_diff__position_t sentinel_position[2];
+
+ /* Point the position lists to the start of the list
+ * so that common_diff/conflict detection actually is
+ * able to work.
+ */
+ if (position_list[1])
+ {
+ sentinel_position[0].next = position_list[1]->next;
+ sentinel_position[0].offset = position_list[1]->offset + 1;
+ position_list[1]->next = &sentinel_position[0];
+ position_list[1] = sentinel_position[0].next;
+ }
+ else
+ {
+ sentinel_position[0].offset = prefix_lines + 1;
+ sentinel_position[0].next = NULL;
+ position_list[1] = &sentinel_position[0];
+ }
+
+ if (position_list[2])
+ {
+ sentinel_position[1].next = position_list[2]->next;
+ sentinel_position[1].offset = position_list[2]->offset + 1;
+ position_list[2]->next = &sentinel_position[1];
+ position_list[2] = sentinel_position[1].next;
+ }
+ else
+ {
+ sentinel_position[1].offset = prefix_lines + 1;
+ sentinel_position[1].next = NULL;
+ position_list[2] = &sentinel_position[1];
+ }
+
+ while (1)
+ {
+ /* Find the sync points */
+ while (1)
+ {
+ if (lcs_om->position[0]->offset > lcs_ol->position[0]->offset)
+ {
+ original_sync = lcs_om->position[0]->offset;
+
+ while (lcs_ol->position[0]->offset + lcs_ol->length
+ < original_sync)
+ lcs_ol = lcs_ol->next;
+
+ /* If the sync point is the EOF, and our current lcs segment
+ * doesn't reach as far as EOF, we need to skip this segment.
+ */
+ if (lcs_om->length == 0 && lcs_ol->length > 0
+ && lcs_ol->position[0]->offset + lcs_ol->length
+ == original_sync
+ && lcs_ol->position[1]->offset + lcs_ol->length
+ != lcs_ol->next->position[1]->offset)
+ lcs_ol = lcs_ol->next;
+
+ if (lcs_ol->position[0]->offset <= original_sync)
+ break;
+ }
+ else
+ {
+ original_sync = lcs_ol->position[0]->offset;
+
+ while (lcs_om->position[0]->offset + lcs_om->length
+ < original_sync)
+ lcs_om = lcs_om->next;
+
+ /* If the sync point is the EOF, and our current lcs segment
+ * doesn't reach as far as EOF, we need to skip this segment.
+ */
+ if (lcs_ol->length == 0 && lcs_om->length > 0
+ && lcs_om->position[0]->offset + lcs_om->length
+ == original_sync
+ && lcs_om->position[1]->offset + lcs_om->length
+ != lcs_om->next->position[1]->offset)
+ lcs_om = lcs_om->next;
+
+ if (lcs_om->position[0]->offset <= original_sync)
+ break;
+ }
+ }
+
+ modified_sync = lcs_om->position[1]->offset
+ + (original_sync - lcs_om->position[0]->offset);
+ latest_sync = lcs_ol->position[1]->offset
+ + (original_sync - lcs_ol->position[0]->offset);
+
+ /* Determine what is modified, if anything */
+ is_modified = lcs_om->position[0]->offset - original_start > 0
+ || lcs_om->position[1]->offset - modified_start > 0;
+
+ is_latest = lcs_ol->position[0]->offset - original_start > 0
+ || lcs_ol->position[1]->offset - latest_start > 0;
+
+ if (is_modified || is_latest)
+ {
+ modified_length = modified_sync - modified_start;
+ latest_length = latest_sync - latest_start;
+
+ (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
+
+ (*diff_ref)->original_start = original_start - 1;
+ (*diff_ref)->original_length = original_sync - original_start;
+ (*diff_ref)->modified_start = modified_start - 1;
+ (*diff_ref)->modified_length = modified_length;
+ (*diff_ref)->latest_start = latest_start - 1;
+ (*diff_ref)->latest_length = latest_length;
+ (*diff_ref)->resolved_diff = NULL;
+
+ if (is_modified && is_latest)
+ {
+ svn_diff__resolve_conflict(*diff_ref,
+ &position_list[1],
+ &position_list[2],
+ num_tokens,
+ pool);
+ }
+ else if (is_modified)
+ {
+ (*diff_ref)->type = svn_diff__type_diff_modified;
+ }
+ else
+ {
+ (*diff_ref)->type = svn_diff__type_diff_latest;
+ }
+
+ diff_ref = &(*diff_ref)->next;
+ }
+
+ /* Detect EOF */
+ if (lcs_om->length == 0 || lcs_ol->length == 0)
+ break;
+
+ modified_length = lcs_om->length
+ - (original_sync - lcs_om->position[0]->offset);
+ latest_length = lcs_ol->length
+ - (original_sync - lcs_ol->position[0]->offset);
+ common_length = modified_length < latest_length
+ ? modified_length : latest_length;
+
+ (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
+
+ (*diff_ref)->type = svn_diff__type_common;
+ (*diff_ref)->original_start = original_sync - 1;
+ (*diff_ref)->original_length = common_length;
+ (*diff_ref)->modified_start = modified_sync - 1;
+ (*diff_ref)->modified_length = common_length;
+ (*diff_ref)->latest_start = latest_sync - 1;
+ (*diff_ref)->latest_length = common_length;
+ (*diff_ref)->resolved_diff = NULL;
+
+ diff_ref = &(*diff_ref)->next;
+
+ /* Set the new offsets */
+ original_start = original_sync + common_length;
+ modified_start = modified_sync + common_length;
+ latest_start = latest_sync + common_length;
+
+ /* Make it easier for diff_common/conflict detection
+ by recording last lcs start positions
+ */
+ if (position_list[1]->offset < lcs_om->position[1]->offset)
+ position_list[1] = lcs_om->position[1];
+
+ if (position_list[2]->offset < lcs_ol->position[1]->offset)
+ position_list[2] = lcs_ol->position[1];
+
+ /* Make sure we are pointing to lcs entries beyond
+ * the range we just processed
+ */
+ while (original_start >= lcs_om->position[0]->offset + lcs_om->length
+ && lcs_om->length > 0)
+ {
+ lcs_om = lcs_om->next;
+ }
+
+ while (original_start >= lcs_ol->position[0]->offset + lcs_ol->length
+ && lcs_ol->length > 0)
+ {
+ lcs_ol = lcs_ol->next;
+ }
+ }
+
+ *diff_ref = NULL;
+ }
+
+ svn_pool_destroy(subpool);
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud