diff options
| author | Peter Wemm <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 | 
|---|---|---|
| committer | Peter Wemm <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 | 
| commit | 32547653cc5376642e1231fb644db99933ac8db4 (patch) | |
| tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_diff/diff3.c | |
Notes
Diffstat (limited to 'subversion/libsvn_diff/diff3.c')
| -rw-r--r-- | subversion/libsvn_diff/diff3.c | 529 | 
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 000000000000..8b7c9b332817 --- /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; +} | 
