Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/subversion/libsvn_client/merge.c  view on Meta::CPAN

            } /* Iteration over each merge source in younger_mergeinfo. */
        } /* if (younger_mergeinfo) */

      /* Filter self-referential mergeinfo from "older" mergeinfo. */
      if (mergeinfo)
        {
          svn_mergeinfo_t implicit_mergeinfo;

          SVN_ERR(svn_client__get_history_as_mergeinfo(
            &implicit_mergeinfo, NULL,
            &target_base, target_base.rev, SVN_INVALID_REVNUM,
            ra_session, ctx, iterpool));

          /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
          SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
                                        implicit_mergeinfo,
                                        mergeinfo, TRUE, iterpool, iterpool));
        }

      /* Combine whatever older and younger filtered mergeinfo exists
         into filtered_mergeinfo. */
      if (filtered_mergeinfo && filtered_younger_mergeinfo)
        SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
                                     filtered_younger_mergeinfo, iterpool,
                                     iterpool));
      else if (filtered_younger_mergeinfo)
        filtered_mergeinfo = filtered_younger_mergeinfo;

      /* If there is any incoming mergeinfo remaining after filtering
         then put it in adjusted_props. */
      if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
        {
          /* Convert filtered_mergeinfo to a svn_prop_t and put it
             back in the array. */
          svn_string_t *filtered_mergeinfo_str;
          svn_prop_t *adjusted_prop = apr_pcalloc(pool,
                                                  sizeof(*adjusted_prop));
          SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
                                          filtered_mergeinfo,
                                          pool));
          adjusted_prop->name = SVN_PROP_MERGEINFO;
          adjusted_prop->value = filtered_mergeinfo_str;
          APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
        }
    }
  svn_pool_destroy(iterpool);

  *props = adjusted_props;
  return SVN_NO_ERROR;
}

/* Prepare a set of property changes PROPCHANGES to be used for a merge
   operation on LOCAL_ABSPATH.

   Remove all non-regular prop-changes (entry-props and WC-props).
   Remove all non-mergeinfo prop-changes if it's a record-only merge.
   Remove self-referential mergeinfo (### in some cases...)
   Remove foreign-repository mergeinfo (### in some cases...)

   Store the resulting property changes in *PROP_UPDATES.
   Store information on where mergeinfo is updated in MERGE_B.

   Used for both file and directory property merges. */
static svn_error_t *
prepare_merge_props_changed(const apr_array_header_t **prop_updates,
                            const char *local_abspath,
                            const apr_array_header_t *propchanges,
                            merge_cmd_baton_t *merge_b,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
{
  apr_array_header_t *props;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));

  /* We only want to merge "regular" version properties:  by
     definition, 'svn merge' shouldn't touch any data within .svn/  */
  SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
                               result_pool));

  /* If we are only applying mergeinfo changes then we need to do
     additional filtering of PROPS so it contains only mergeinfo changes. */
  if (merge_b->record_only && props->nelts)
    {
      apr_array_header_t *mergeinfo_props =
        apr_array_make(result_pool, 1, sizeof(svn_prop_t));
      int i;

      for (i = 0; i < props->nelts; i++)
        {
          svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);

          if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
            {
              APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
              break;
            }
        }
      props = mergeinfo_props;
    }

  if (props->nelts)
    {
      /* Issue #3383: We don't want mergeinfo from a foreign repos.

         If this is a merge from a foreign repository we must strip all
         incoming mergeinfo (including mergeinfo deletions). */
      if (! merge_b->same_repos)
        SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));

      /* If this is a forward merge then don't add new mergeinfo to
         PATH that is already part of PATH's own history, see
         http://svn.haxx.se/dev/archive-2008-09/0006.shtml.  If the
         merge sources are not ancestral then there is no concept of a
         'forward' or 'reverse' merge and we filter unconditionally. */
      if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
          || !merge_b->merge_source.ancestral)
        {
          if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
            SVN_ERR(filter_self_referential_mergeinfo(&props,
                                                      local_abspath,

src/subversion/subversion/libsvn_client/merge.c  view on Meta::CPAN

            }

          if (merge_b->same_repos)
            {
              const char *original_url;

              original_url = svn_path_url_add_component2(
                                        merge_b->merge_source.loc2->url,
                                        relpath, scratch_pool);

              /* Limitation (aka HACK):
                 We create a newly added directory with an original URL and
                 revision as that in the repository, but without its properties
                 and children.

                 When the merge is cancelled before the final dir_added(), the
                 copy won't really represent the in-repository state of the node.
               */
              SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
                                  svn_depth_infinity,
                                  original_url,
                                  right_source->revision,
                                  merge_b->ctx->cancel_func,
                                  merge_b->ctx->cancel_baton,
                                  NULL, NULL /* no notify! */,
                                  scratch_pool));
            }
          else
            {
              SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
                                            apr_hash_make(scratch_pool),
                                            NULL, NULL /* no notify! */,
                                            scratch_pool));
            }

          if (old_tc != NULL)
            {
              /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
              SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
                                           svn_node_dir,
                                           db->tree_conflict_action,
                                           db->tree_conflict_reason,
                                           old_tc, FALSE,
                                           scratch_pool));
            }
        }

      if (! db->shadowed && !merge_b->record_only)
        SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
                                  db->add_is_replace, scratch_pool));
    }
  return SVN_NO_ERROR;
}

/* An svn_diff_tree_processor_t function.
 *
 * Called after merge_dir_opened() when a node exists in both the left and
 * right source, but has its properties changed inbetween.
 *
 * After the merge_dir_opened() but before the call to this merge_dir_changed()
 * function all descendants will have been updated.
 */
static svn_error_t *
merge_dir_changed(const char *relpath,
                  const svn_diff_source_t *left_source,
                  const svn_diff_source_t *right_source,
                  /*const*/ apr_hash_t *left_props,
                  /*const*/ apr_hash_t *right_props,
                  const apr_array_header_t *prop_changes,
                  void *dir_baton,
                  const struct svn_diff_tree_processor_t *processor,
                  apr_pool_t *scratch_pool)
{
  merge_cmd_baton_t *merge_b = processor->baton;
  struct merge_dir_baton_t *db = dir_baton;
  const apr_array_header_t *props;
  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                              relpath, scratch_pool);

  SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));

  SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));

  if (db->shadowed)
    {
      if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
        {
          /* We haven't notified for this node yet: report a skip */
          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
                              svn_wc_notify_update_shadowed_update,
                              db->skip_reason, scratch_pool));
        }

      return SVN_NO_ERROR;
    }

  SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
                                      merge_b, scratch_pool, scratch_pool));

  if (props->nelts)
    {
      const svn_wc_conflict_version_t *left;
      const svn_wc_conflict_version_t *right;
      svn_client_ctx_t *ctx = merge_b->ctx;
      svn_wc_notify_state_t prop_state;

      SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
                                     svn_node_dir, &merge_b->merge_source,
                                     merge_b->target,
                                     scratch_pool, scratch_pool));

      SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
                                  left, right,
                                  left_props, props,
                                  merge_b->dry_run,
                                  NULL, NULL,
                                  ctx->cancel_func, ctx->cancel_baton,
                                  scratch_pool));

      if (prop_state == svn_wc_notify_state_conflicted)
        {

src/subversion/subversion/libsvn_client/merge.c  view on Meta::CPAN

          else
            {
              void *file_baton = NULL;
              svn_boolean_t skip = FALSE;
              apr_array_header_t *propchanges;


              /* Deduce property diffs. */
              SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
                                     iterpool));

              SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
                                             left_source,
                                             right_source,
                                             NULL /* copyfrom_source */,
                                             NULL /* dir_baton */,
                                             processor,
                                             iterpool, iterpool));
              if (! skip)
                SVN_ERR(processor->file_changed(target_relpath,
                                              left_source,
                                              right_source,
                                              left_file,
                                              right_file,
                                              left_props,
                                              right_props,
                                              TRUE /* file changed */,
                                              propchanges,
                                              file_baton,
                                              processor,
                                              iterpool));
            }

          if (is_path_conflicted_by_merge(merge_b))
            {
              merge_source_t *remaining_range = NULL;

              if (real_source->loc2->rev != source->loc2->rev)
                remaining_range = subrange_source(source,
                                                  real_source->loc2->rev,
                                                  source->loc2->rev,
                                                  scratch_pool);
              *conflict_report = single_range_conflict_report_create(
                                   real_source, remaining_range, result_pool);

              /* Only record partial mergeinfo if only a partial merge was
                 performed before a conflict was encountered. */
              range.end = r->end;
              break;
            }

          /* Now delete the just merged range from the hash
             (This list is used from notify_merge_begin)

            Directory merges use remove_first_range_from_remaining_ranges() */
          svn_sort__array_delete(ranges_to_merge, 0, 1);
        }
      merge_b->notify_begin.last_abspath = NULL;
    } /* !merge_b->record_only */

  /* Record updated WC mergeinfo to account for our new merges, minus
     any unresolved conflicts and skips.  We use the original
     REMAINING_RANGES here because we want to record all the requested
     merge ranges, include the noop ones.  */
  if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
    {
      const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
                                                              scratch_pool);
      svn_rangelist_t *filtered_rangelist;

      /* Filter any ranges from TARGET_WCPATH's own history, there is no
         need to record this explicitly in mergeinfo, it is already part
         of TARGET_WCPATH's natural history (implicit mergeinfo). */
      SVN_ERR(filter_natural_history_from_mergeinfo(
        &filtered_rangelist,
        mergeinfo_path,
        merge_target->implicit_mergeinfo,
        &range,
        iterpool));

      /* Only record mergeinfo if there is something other than
         self-referential mergeinfo, but don't record mergeinfo if
         TARGET_WCPATH was skipped. */
      if (filtered_rangelist->nelts
          && (apr_hash_count(merge_b->skipped_abspaths) == 0))
        {
          apr_hash_t *merges = apr_hash_make(iterpool);

          /* If merge target has inherited mergeinfo set it before
             recording the first merge range. */
          if (inherited)
            SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
                                                    target_mergeinfo,
                                                    FALSE, ctx,
                                                    iterpool));

          svn_hash_sets(merges, target_abspath, filtered_rangelist);

          if (!squelch_mergeinfo_notifications)
            {
              /* Notify that we are recording mergeinfo describing a merge. */
              svn_merge_range_t n_range;

              SVN_ERR(svn_mergeinfo__get_range_endpoints(
                        &n_range.end, &n_range.start, merges, iterpool));
              n_range.inheritable = TRUE;
              notify_mergeinfo_recording(target_abspath, &n_range,
                                         merge_b->ctx, iterpool);
            }

          SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
                                      mergeinfo_path, merges, is_rollback,
                                      ctx, iterpool));
        }
    }

  merge_b->notify_begin.nodes_with_mergeinfo = NULL;

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;

src/subversion/subversion/libsvn_client/merge.c  view on Meta::CPAN

              svn_error_t *err;
              svn_mergeinfo_t subtree_history_as_mergeinfo;
              svn_rangelist_t *child_merge_src_rangelist;
              svn_client__pathrev_t *subtree_mergeinfo_pathrev
                = svn_client__pathrev_create_with_relpath(
                    merge_b->target->loc.repos_root_url,
                    merge_b->target->loc.repos_uuid,
                    merged_range->end, child_merge_src_fspath + 1,
                    iterpool);

              /* Confirm that the naive mergeinfo we want to set on
                 CHILD->ABSPATH both exists and is part of
                 (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
                 history. */
              /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
                 because we only do this for forward merges. */
              err = svn_client__get_history_as_mergeinfo(
                &subtree_history_as_mergeinfo, NULL,
                subtree_mergeinfo_pathrev,
                merged_range->end, merged_range->start,
                merge_b->ra_session2, merge_b->ctx, iterpool);

              /* If CHILD is a subtree it may have been deleted prior to
                 MERGED_RANGE->END so the above call to get its history
                 will fail. */
              if (err)
                {
                  if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
                      return svn_error_trace(err);
                  svn_error_clear(err);
                }
              else
                {
                  child_merge_src_rangelist = svn_hash_gets(
                                                subtree_history_as_mergeinfo,
                                                child_merge_src_fspath);
                  SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
                                                  child_merge_rangelist,
                                                  child_merge_src_rangelist,
                                                  FALSE, iterpool));
                  if (child->record_noninheritable)
                    svn_rangelist__set_inheritance(child_merge_rangelist,
                                                   FALSE);
                }
            }

          svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
          SVN_ERR(update_wc_mergeinfo(result_catalog,
                                      child->abspath,
                                      child_merge_src_fspath,
                                      child_merges, is_rollback,
                                      merge_b->ctx, iterpool));

          /* Once is enough: We don't need to record mergeinfo describing
             the merge a second.  If CHILD->ABSPATH is in
             MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
             former from the latter. */
          svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
        }

      /* Elide explicit subtree mergeinfo whether or not we updated it. */
      if (i > 0)
        {
          svn_boolean_t in_switched_subtree = FALSE;

          if (child->switched)
            in_switched_subtree = TRUE;
          else if (i > 1)
            {
              /* Check if CHILD is part of a switched subtree */
              svn_client__merge_path_t *parent;
              int j = i - 1;
              for (; j > 0; j--)
                {
                  parent = APR_ARRAY_IDX(children_with_mergeinfo,
                                         j, svn_client__merge_path_t *);
                  if (parent
                      && parent->switched
                      && svn_dirent_is_ancestor(parent->abspath,
                                                child->abspath))
                    {
                      in_switched_subtree = TRUE;
                      break;
                    }
                }
            }

          /* Allow mergeinfo on switched subtrees to elide to the
             repository. Otherwise limit elision to the merge target
             for now.  do_directory_merge() will eventually try to
             elide that when the merge is complete. */
          SVN_ERR(svn_client__elide_mergeinfo(
            child->abspath,
            in_switched_subtree ? NULL : merge_b->target->abspath,
            merge_b->ctx, iterpool));
        }
    } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */

  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}

/* Helper for do_directory_merge().

   Record mergeinfo describing a merge of
   MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
   MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
   mergeinfo or is the immediate child of a parent with explicit
   non-inheritable mergeinfo.

   DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
   cascaded from do_directory_merge's arguments of the same names.

   Note: This is intended to support forward merges only, i.e.
   MERGED_RANGE->START must be older than MERGED_RANGE->END.
*/
static svn_error_t *
record_mergeinfo_for_added_subtrees(
  svn_merge_range_t *merged_range,
  const char *mergeinfo_fspath,
  svn_depth_t depth,

src/subversion/subversion/libsvn_client/merge.c  view on Meta::CPAN

  /* Simplify unmerged_catalog through elision then make a copy in POOL. */
  SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
                                              iterpool));
  *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
                                                          result_pool);

  if (youngest_merged_rev == SVN_INVALID_REVNUM)
    {
      /* We never merged to the source.  Just return the branch point. */
      *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
    }
  else
    {
      /* We've previously merged some or all of the target, up to
         youngest_merged_rev, to the source.  Set
         *LEFT_P to cover the youngest part of this range. */
      SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
                                         &target->loc, youngest_merged_rev,
                                         ctx, result_pool, iterpool));
    }

  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}

/* Determine the URLs and revisions needed to perform a reintegrate merge
 * from SOURCE_LOC into the working copy at TARGET.
 *
 * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
 * URLs of SOURCE_LOC and TARGET->loc respectively.
 *
 * Set *SOURCE_P to
 * the source-left and source-right locations of the required merge.  Set
 * *YC_ANCESTOR_P to the location of the youngest ancestor.
 * Any of these output pointers may be NULL if not wanted.
 *
 * See svn_client_find_reintegrate_merge() for other details.
 */
static svn_error_t *
find_reintegrate_merge(merge_source_t **source_p,
                       svn_client__pathrev_t **yc_ancestor_p,
                       svn_ra_session_t *source_ra_session,
                       const svn_client__pathrev_t *source_loc,
                       svn_ra_session_t *target_ra_session,
                       const merge_target_t *target,
                       svn_client_ctx_t *ctx,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
{
  svn_client__pathrev_t *yc_ancestor;
  svn_client__pathrev_t *loc1;
  merge_source_t source;
  svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
  svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
  svn_error_t *err;
  apr_hash_t *subtrees_with_mergeinfo;

  assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
  assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));

  /* As the WC tree is "pure", use its last-updated-to revision as
     the default revision for the left side of our merge, since that's
     what the repository sub-tree is required to be up to date with
     (with regard to the WC). */
  /* ### Bogus/obsolete comment? */

  /* Can't reintegrate to or from the root of the repository. */
  if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
      || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
                             _("Neither the reintegrate source nor target "
                               "can be the root of the repository"));

  /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
  err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
                                          target->abspath, svn_depth_infinity,
                                          ctx, scratch_pool, scratch_pool);
  /* Issue #3896: If invalid mergeinfo in the reintegrate target
     prevents us from proceeding, then raise the best error possible. */
  if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
    err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
  SVN_ERR(err);

  SVN_ERR(calculate_left_hand_side(&loc1,
                                   &merged_to_source_mergeinfo_catalog,
                                   &unmerged_to_source_mergeinfo_catalog,
                                   target,
                                   subtrees_with_mergeinfo,
                                   source_loc,
                                   source_ra_session,
                                   target_ra_session,
                                   ctx,
                                   scratch_pool, scratch_pool));

  /* Did calculate_left_hand_side() decide that there was no merge to
     be performed here?  */
  if (! loc1)
    {
      if (source_p)
        *source_p = NULL;
      if (yc_ancestor_p)
        *yc_ancestor_p = NULL;
      return SVN_NO_ERROR;
    }

  source.loc1 = loc1;
  source.loc2 = source_loc;

  /* If the target was moved after the source was branched from it,
     it is possible that the left URL differs from the target's current
     URL.  If so, then adjust TARGET_RA_SESSION to point to the old URL. */
  if (strcmp(source.loc1->url, target->loc.url))
    SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));

  SVN_ERR(svn_client__get_youngest_common_ancestor(
            &yc_ancestor, source.loc2, source.loc1, target_ra_session,
            ctx, scratch_pool, scratch_pool));

  if (! yc_ancestor)
    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
                             _("'%s@%ld' must be ancestrally related to "



( run in 0.440 second using v1.01-cache-2.11-cpan-5623c5533a1 )