You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/12/11 01:16:08 UTC
svn commit: r1044548 [6/39] - in /subversion/branches/ignore-mergeinfo: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/ contrib/hook-scripts/
contrib/server-side/ notes/api-errata/ notes/api-erra...
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c?rev=1044548&r1=1044547&r2=1044548&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/merge.c Sat Dec 11 00:15:55 2010
@@ -1121,8 +1121,6 @@ merge_props_changed(const char *local_di
definition, 'svn merge' shouldn't touch any data within .svn/ */
if (props->nelts)
{
- int i;
-
/* 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
@@ -1150,6 +1148,8 @@ merge_props_changed(const char *local_di
is having its existing mergeinfo deleted. */
if (!merge_b->dry_run)
{
+ int i;
+
for (i = 0; i < props->nelts; ++i)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
@@ -1723,13 +1723,14 @@ merge_file_added(const char *local_dir_a
copying 'yours' to 'mine', isn't enough; we need to get
the whole text-base and props installed too, just as if
we had called 'svn cp wc wc'. */
- /* ### would be nice to have cancel/notify funcs to pass */
SVN_ERR(svn_wc_add_repos_file4(
merge_b->ctx->wc_ctx, mine_abspath,
new_base_contents, new_contents,
new_base_props, new_props,
copyfrom_url, copyfrom_rev,
- NULL, NULL, NULL, NULL, subpool));
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ subpool));
/* ### delete 'yours' ? */
}
@@ -3289,6 +3290,110 @@ fix_deleted_subtree_ranges(const char *u
/*** Determining What Remains To Be Merged ***/
+
+/* Attempt to determine if a working copy path inherits any invalid
+ mergeinfo.
+
+ Query the repository for the mergeinfo TARGET_ABSPATH inherits at its
+ base revision and set *VALIDATED to indicate to the caller if we can
+ determine what portions of that inherited mergeinfo are invalid.
+
+ If no mergeinfo is inherited set *INVALID_INHERITED_MERGEINFO to NULL and
+ *VALIDATED to true.
+
+ If only empty mergeinfo is inherited set *INVALID_INHERITED_MERGEINFO to
+ and empty hash and *VALIDATED to true.
+
+ If non-empty inherited mergeinfo is inherited then, if the server (1.7+)
+ supports inherited mergeinfo validation, remove all valid path-revisions
+ from the raw inherited mergeinfo, set *INVALID_INHERITED_MERGEINFO to the
+ remainder, and set *VALIDATED to true. If the server doesn't support
+ inherited mergeinfo validation then set *INVALID_INHERITED_MERGEINFO to
+ NULL and set *VALIDATED to false.
+
+ Note that if validation occurs, but all inherited mergeinfo describes
+ non-existent paths, then *INVALID_INHERITED_MERGEINFO is set to an empty
+ hash.
+
+ RA_SESSION is an open session that points to TARGET_ABSPATH's repository
+ location or to the location of one of TARGET_ABSPATH's parents. It may
+ be temporarily reparented.
+
+ RESULT_POOL is used to allocate *INVALID_INHERITED_MERGEINFO, SCRATCH_POOL
+ is used for any temporary allocations. */
+static svn_error_t *
+get_invalid_inherited_mergeinfo(svn_mergeinfo_t *invalid_inherited_mergeinfo,
+ svn_boolean_t *validated,
+ svn_ra_session_t *ra_session,
+ const char *target_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_mergeinfo_t repos_raw_inherited;
+ svn_mergeinfo_t repos_validated_inherited;
+ svn_revnum_t base_revision;
+ svn_boolean_t validate_inherited_mergeinfo;
+
+ /* Our starting assumptions. */
+ *invalid_inherited_mergeinfo = NULL;
+ *validated = TRUE;
+
+ SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
+ target_abspath, scratch_pool));
+
+ /* If there is no base revision then TARGET_ABSPATH doesn't exist
+ in the repository yet, so we're done. */
+ if (SVN_IS_VALID_REVNUM(base_revision))
+ {
+ const char *target_url;
+ const char *session_url;
+
+ /* Reparent RA_SESSION if necessary. */
+ SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, target_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
+ target_url, scratch_pool));
+
+ /* Contact the repository to derive the portion of
+ TARGET_ABSPATH's inherited mergeinfo which is non-existent
+ and remove it from */
+ validate_inherited_mergeinfo = FALSE;
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ ra_session, &repos_raw_inherited, "", base_revision,
+ svn_mergeinfo_inherited, TRUE,
+ &validate_inherited_mergeinfo,
+ scratch_pool));
+
+ if (repos_raw_inherited == NULL)
+ {
+ *invalid_inherited_mergeinfo = NULL;
+ }
+ else if (apr_hash_count(repos_raw_inherited) == 0)
+ {
+ *invalid_inherited_mergeinfo = apr_hash_make(result_pool);
+ }
+ else
+ {
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ ra_session, &repos_validated_inherited, "", base_revision,
+ svn_mergeinfo_inherited, TRUE,
+ validated,
+ scratch_pool));
+ if (*validated)
+ SVN_ERR(svn_mergeinfo_remove2(invalid_inherited_mergeinfo,
+ repos_validated_inherited,
+ repos_raw_inherited, FALSE,
+ result_pool, scratch_pool));
+ else
+ *invalid_inherited_mergeinfo = NULL;
+ }
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
+ session_url, scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
/* Get explicit and/or implicit mergeinfo for the working copy path
TARGET_ABSPATH.
@@ -3299,6 +3404,10 @@ fix_deleted_subtree_ranges(const char *u
If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
+ If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
+ *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
+ removed from *RECORDED_MERGEINFO.
+
If INDIRECT is not NULL set *INDIRECT to TRUE if *RECORDED_MERGEINFO
is inherited and not explicit. If RECORDED_MERGEINFO is NULL then
INDIRECT is ignored.
@@ -3309,6 +3418,9 @@ fix_deleted_subtree_ranges(const char *u
is older than START, then the base revision is used as the younger
bound in place of START.
+ RA_SESSION is an open session that may be temporarily reparented as
+ needed by this function.
+
Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
Use SCRATCH_POOL for any temporary allocations. */
static svn_error_t *
@@ -3324,11 +3436,14 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- /* First, we get the real mergeinfo. */
+ svn_boolean_t inherited = FALSE;
+ svn_boolean_t inherited_validated = FALSE;
+
+ /* First, we get the real mergeinfo. We use SCRATCH_POOL throughout this
+ block because we'll make a final copy of *RECORDED_MERGEINFO only after
+ removing any self-referential mergeinfo. */
if (recorded_mergeinfo)
{
- svn_boolean_t inherited;
-
/* ### FIXME: There's probably an RA session we could/should be
### using here instead of having this function possibly spawn
### yet another one. */
@@ -3336,9 +3451,29 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
&inherited, FALSE,
inherit, NULL,
target_abspath,
- ctx, result_pool));
+ ctx, scratch_pool));
if (indirect)
*indirect = inherited;
+
+ /* Issue #3669: Remove any non-existent mergeinfo sources
+ from TARGET_ABSPATH's inherited mergeinfo. */
+ if (inherited)
+ {
+ svn_mergeinfo_t invalid_inherited_mergeinfo;
+
+ SVN_ERR(get_invalid_inherited_mergeinfo(
+ &invalid_inherited_mergeinfo, &inherited_validated,
+ ra_session, target_abspath, ctx,
+ scratch_pool, scratch_pool));
+
+ if (inherited_validated
+ && invalid_inherited_mergeinfo
+ && apr_hash_count(invalid_inherited_mergeinfo))
+ SVN_ERR(svn_mergeinfo_remove2(recorded_mergeinfo,
+ invalid_inherited_mergeinfo,
+ *recorded_mergeinfo, FALSE,
+ scratch_pool, scratch_pool));
+ }
}
if (implicit_mergeinfo)
@@ -3346,7 +3481,6 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
const char *session_url = NULL, *url;
svn_revnum_t target_rev;
svn_opt_revision_t peg_revision;
- apr_pool_t *sesspool = NULL;
svn_error_t *err;
/* Assert that we have sane input. */
@@ -3354,10 +3488,12 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
&& SVN_IS_VALID_REVNUM(end)
&& (start > end));
+ *implicit_mergeinfo = NULL;
+
peg_revision.kind = svn_opt_revision_working;
err = svn_client__derive_location(&url, &target_rev, target_abspath,
&peg_revision, ra_session,
- ctx, result_pool, scratch_pool);
+ ctx, scratch_pool, scratch_pool);
if (err)
{
@@ -3370,7 +3506,6 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
* mergeinfo is empty. */
svn_error_clear(err);
*implicit_mergeinfo = apr_hash_make(result_pool);
- return SVN_NO_ERROR;
}
else
return svn_error_return(err);
@@ -3381,52 +3516,60 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
/* We're asking about a range outside our natural history
altogether. That means our implicit mergeinfo is empty. */
*implicit_mergeinfo = apr_hash_make(result_pool);
- return SVN_NO_ERROR;
}
- /* Temporarily point our RA_SESSION at our target URL so we can
- fetch so-called "implicit mergeinfo" (that is, natural history). */
- if (ra_session)
+ if (*implicit_mergeinfo == NULL)
{
- SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
- url, scratch_pool));
- }
- else
- {
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url,
- NULL, NULL,
- FALSE, TRUE,
- ctx, scratch_pool));
- }
-
- /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
- TARGET_ABSPATH might not even exist, and even if it does the
- working copy is *at* TARGET_REV so its implicit history ends
- at TARGET_REV! */
- if (target_rev < start)
- start = target_rev;
-
- /* Fetch the implicit mergeinfo. */
- peg_revision.kind = svn_opt_revision_number;
- peg_revision.value.number = target_rev;
- SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, url,
- &peg_revision, start, end,
- ra_session, ctx,
- result_pool));
+ /* Temporarily point our RA_SESSION at our target URL so we can
+ fetch so-called "implicit mergeinfo" (that is, natural
+ history). */
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
+ ra_session, url,
+ scratch_pool));
- /* If we created an RA_SESSION above, destroy it. Otherwise, if
- reparented an existing session, point it back where it was when
- we were called. */
- if (sesspool)
- {
- svn_pool_destroy(sesspool);
- }
- else if (session_url)
- {
- SVN_ERR(svn_ra_reparent(ra_session, session_url, scratch_pool));
+ /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
+ TARGET_ABSPATH might not even exist, and even if it does the
+ working copy is *at* TARGET_REV so its implicit history ends
+ at TARGET_REV! */
+ if (target_rev < start)
+ start = target_rev;
+
+ /* Fetch the implicit mergeinfo. */
+ peg_revision.kind = svn_opt_revision_number;
+ peg_revision.value.number = target_rev;
+ SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
+ url, &peg_revision,
+ start, end,
+ ra_session, ctx,
+ result_pool));
+
+ /* Return RA_SESSION back to where it was when we were called. */
+ SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
+ ra_session, session_url,
+ scratch_pool));
}
} /*if (implicit_mergeinfo) */
+
+ if (recorded_mergeinfo && *recorded_mergeinfo)
+ {
+ /* Issue #3668: Remove any self-referential mergeinfo from that
+ which TARGET_ABSPATH inherited; but only do this if we were able to
+ validate inherited mergeinfo (issue #3669) or otherwise we end
+ up with fragmented mergeinfo, see
+ http://subversion.tigris.org/issues/show_bug.cgi?id=3668#desc5 */
+ if (implicit_mergeinfo
+ && inherited
+ && inherited_validated)
+ SVN_ERR(svn_mergeinfo_remove2(recorded_mergeinfo,
+ *implicit_mergeinfo,
+ *recorded_mergeinfo, FALSE,
+ result_pool, scratch_pool));
+ else
+ *recorded_mergeinfo = svn_mergeinfo_dup(*recorded_mergeinfo,
+ result_pool);
+ }
+
return SVN_NO_ERROR;
}
@@ -3778,8 +3921,8 @@ filter_merged_revisions(svn_client__merg
child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
pool);
}
- }
#endif
+ }
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
@@ -4257,7 +4400,7 @@ populate_remaining_ranges(apr_array_head
svn_mergeinfo_inherited, ra_session,
child->abspath,
MAX(revision1, revision2),
- MIN(revision1, revision2),
+ 0, /* Get all implicit mergeinfo */
merge_b->ctx, pool, pool));
/* If CHILD isn't the merge target find its parent. */
@@ -4313,7 +4456,7 @@ populate_remaining_ranges(apr_array_head
/* If this is a reverse merge reorder CHILD->REMAINING_RANGES
so it will work with the svn_rangelist_* APIs below. */
if (revision1 > revision2)
- svn_rangelist_reverse(child->remaining_ranges, iterpool);
+ SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
for (j = 0; j < child->remaining_ranges->nelts; j++)
{
@@ -4355,7 +4498,7 @@ populate_remaining_ranges(apr_array_head
}
if (revision1 > revision2) /* Reverse merge */
- svn_rangelist_reverse(child->remaining_ranges, iterpool);
+ SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
}
}
@@ -5449,18 +5592,16 @@ get_mergeinfo_walk_cb(const char *local_
/* TODO(#2843) How to deal with a excluded item on merge? */
+ SVN_ERR(svn_wc__get_mergeinfo_walk_info(&is_present, &deleted, &absent,
+ &depth,
+ wb->ctx->wc_ctx, local_abspath,
+ scratch_pool));
+
/* Ignore LOCAL_ABSPATH if its parent thinks it exists, but it is not
actually present. */
- SVN_ERR(svn_wc__node_is_status_present(&is_present, wb->ctx->wc_ctx,
- local_abspath, scratch_pool));
if (!is_present)
return SVN_NO_ERROR;
- SVN_ERR(svn_wc__node_is_status_deleted(&deleted, wb->ctx->wc_ctx,
- local_abspath, scratch_pool));
- SVN_ERR(svn_wc__node_is_status_absent(&absent, wb->ctx->wc_ctx,
- local_abspath, scratch_pool));
-
if (deleted || absent)
{
propval = NULL;
@@ -5484,8 +5625,6 @@ get_mergeinfo_walk_cb(const char *local_
SVN_ERR(svn_wc_read_kind(&kind, wb->ctx->wc_ctx, local_abspath, TRUE,
scratch_pool));
- SVN_ERR(svn_wc__node_get_depth(&depth, wb->ctx->wc_ctx, local_abspath,
- scratch_pool));
immediate_child_dir = ((wb->depth == svn_depth_immediates)
&&(kind == svn_node_dir)
@@ -6596,7 +6735,6 @@ do_file_merge(svn_mergeinfo_catalog_t re
svn_merge_range_t range;
svn_mergeinfo_t target_mergeinfo;
svn_merge_range_t *conflicted_range = NULL;
- int i;
svn_boolean_t indirect = FALSE;
apr_pool_t *subpool;
svn_boolean_t is_rollback = (revision1 > revision2);
@@ -6643,7 +6781,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
&indirect, svn_mergeinfo_inherited,
merge_b->ra_session1, target_abspath,
MAX(revision1, revision2),
- MIN(revision1, revision2),
+ 0, /* Get all implicit mergeinfo */
ctx, scratch_pool, scratch_pool));
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, scratch_pool));
@@ -6676,6 +6814,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
if (!merge_b->record_only)
{
apr_array_header_t *ranges_to_merge = remaining_ranges;
+ int i;
/* If we have ancestrally related sources and more than one
range to merge, eliminate no-op ranges before going through
@@ -7244,11 +7383,11 @@ get_inoperative_immediate_children(apr_h
apr_hash_set(*immediate_children,
apr_pstrdup(result_pool, child->abspath),
APR_HASH_KEY_STRING,
- svn_uri_join(merge_source_repos_abspath,
- svn_dirent_is_child(merge_target_abspath,
- child->abspath,
- iterpool),
- result_pool));
+ svn_fspath__join(merge_source_repos_abspath,
+ svn_dirent_is_child(merge_target_abspath,
+ child->abspath,
+ iterpool),
+ result_pool));
}
svn_pool_destroy(iterpool);
@@ -7424,9 +7563,9 @@ record_mergeinfo_for_dir_merge(svn_merge
if (!child_repos_path)
child_repos_path = "";
- child_merge_src_canon_path = svn_uri_join(mergeinfo_path,
- child_repos_path,
- iterpool);
+ child_merge_src_canon_path = svn_fspath__join(mergeinfo_path,
+ child_repos_path,
+ iterpool);
/* Filter any ranges from each child's natural history before
setting mergeinfo describing the merge. */
SVN_ERR(filter_natural_history_from_mergeinfo(
@@ -7734,7 +7873,7 @@ log_noop_revs(void *baton,
/* Adjust REL_PATH so it is relative to the merge source then use it to
calculate what path in the merge target would be affected by this
revision. */
- rel_path = svn_uri_skip_ancestor(log_gap_baton->source_repos_abs, path);
+ rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_repos_abs, path);
cwmi_path = svn_dirent_join(log_gap_baton->merge_b->target_abspath,
rel_path, pool);
@@ -8028,7 +8167,18 @@ do_directory_merge(svn_mergeinfo_catalog
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
+
+ /* The range defining the mergeinfo we will record to describe the merge
+ (assuming we are recording mergeinfo
+
+ Note: This may be a subset of REVISION1:REVISION2 if
+ populate_remaining_ranges() determines that some part of
+ REVISION1:REVISION2 has already been wholly merged to TARGET_ABSPATH.
+ Also, the actual editor drive(s) may be a subset of RANGE, if
+ remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
+ further tweak things. */
svn_merge_range_t range;
+
svn_ra_session_t *ra_session;
svn_client__merge_path_t *target_merge_path;
svn_boolean_t is_rollback = (revision1 > revision2);
@@ -8101,17 +8251,22 @@ do_directory_merge(svn_mergeinfo_catalog
if (honor_mergeinfo && !merge_b->reintegrate_merge)
{
- svn_revnum_t start_rev, end_rev;
+ svn_revnum_t new_range_start, start_rev, end_rev;
apr_pool_t *iterpool = svn_pool_create(pool);
- /* The merge target target_wcpath and/or its subtrees may not need all
+ /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
of REVISION1:REVISION2 applied. So examine
NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting
revision that actually needs to be merged (for reverse merges this is
- the youngest starting revision). */
- start_rev =
- get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
- is_rollback);
+ the youngest starting revision).
+
+ We'll do this twice, right now for the start of the mergeinfo we will
+ ultimately record to describe this merge and then later for the
+ start of the actual editor drive. */
+ new_range_start = get_most_inclusive_start_rev(
+ notify_b->children_with_mergeinfo, is_rollback);
+ if (SVN_IS_VALID_REVNUM(new_range_start))
+ range.start = new_range_start;
/* Remove inoperative ranges from any subtrees' remaining_ranges
to spare the expense of noop editor drives. */
@@ -8121,16 +8276,20 @@ do_directory_merge(svn_mergeinfo_catalog
notify_b, merge_b,
pool, iterpool));
+ /* Adjust subtrees' remaining_ranges to deal with issue #3067 */
SVN_ERR(fix_deleted_subtree_ranges(url1, revision1, url2, revision2,
ra_session, notify_b, merge_b, pool));
+ /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
+ may have further refined the starting revision for our editor
+ drive. */
+ start_rev =
+ get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
+ is_rollback);
+
/* Is there anything to merge? */
if (SVN_IS_VALID_REVNUM(start_rev))
{
- /* Adjust range to describe the start of our most
- inclusive merge. */
- range.start = start_rev;
-
/* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the youngest
ending revision that actually needs to be merged (for reverse
merges this is the oldest starting revision). */
@@ -8170,6 +8329,33 @@ do_directory_merge(svn_mergeinfo_catalog
svn_revnum_t next_end_rev;
const char *real_url1 = url1, *real_url2 = url2;
const char *old_sess1_url = NULL, *old_sess2_url = NULL;
+ svn_merge_range_t *first_target_range = APR_ARRAY_IDX(
+ target_merge_path->remaining_ranges, 0, svn_merge_range_t *);
+
+ /* Issue #3324: Stop editor abuse! Don't call
+ drive_merge_report_editor() in such a way that we request an
+ editor with svn_client__get_diff_editor() for some rev X,
+ then call svn_ra_do_diff3() for some revision Y, and then
+ call reporter->set_path(PATH=="") to set the root revision
+ for the editor drive to revision Z where
+ (X != Z && X < Z < Y). This is bogus because the server will
+ send us the diff between X:Y but the client is expecting the
+ diff between Y:Z. See issue #3324 for full details on the
+ problems this can cause. */
+ if (first_target_range
+ && start_rev != first_target_range->start)
+ {
+ if (is_rollback)
+ {
+ if (end_rev < first_target_range->start)
+ end_rev = first_target_range->start;
+ }
+ else
+ {
+ if (end_rev > first_target_range->start)
+ end_rev = first_target_range->start;
+ }
+ }
svn_pool_clear(iterpool);
@@ -8468,11 +8654,11 @@ do_merge(apr_hash_t **modified_subtrees,
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
pool));
+
if (target_kind != svn_node_dir && target_kind != svn_node_file)
- return svn_error_return(svn_error_createf(
- SVN_ERR_ILLEGAL_TARGET, NULL,
- _("Merge target '%s' does not exist in the "
- "working copy"), target_abspath));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Merge target '%s' does not exist in the "
+ "working copy"), target_abspath);
/* Ensure a known depth. */
if (depth == svn_depth_unknown)
@@ -8815,6 +9001,73 @@ merge_cousins_and_supplement_mergeinfo(c
return SVN_NO_ERROR;
}
+/* Perform checks to determine whether of the working copy at TARGET_ABSPATH
+ * can safely be used as a merge target. Checks are performed according to
+ * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
+ * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
+ *
+ * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
+ * be a single-revision, pristine, unswitched working copy.
+ * In other words, it must reflect a subtree of the repostiory as found
+ * at single revision -- although sparse checkouts are permitted. */
+static svn_error_t *
+ensure_wc_is_suitable_merge_target(const char *target_abspath,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t allow_mixed_rev,
+ svn_boolean_t allow_local_mods,
+ svn_boolean_t allow_switched_subtrees,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_revision_status_t *wc_stat;
+
+ /* Avoid the following status crawl if we don't need it. */
+ if (allow_mixed_rev && allow_local_mods && allow_switched_subtrees)
+ return SVN_NO_ERROR;
+
+ /* Get a WC summary with min/max revisions set to the BASE revision. */
+ SVN_ERR(svn_wc_revision_status2(&wc_stat, ctx->wc_ctx, target_abspath, NULL,
+ FALSE, ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool, scratch_pool));
+
+ if (! allow_switched_subtrees && wc_stat->switched)
+ return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
+ _("Cannot merge into a working copy "
+ "with a switched subtree"));
+
+ if (! allow_local_mods && wc_stat->modified)
+ return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
+ _("Cannot merge into a working copy "
+ "that has local modifications"));
+
+ if (! allow_mixed_rev)
+ {
+ if (! (SVN_IS_VALID_REVNUM(wc_stat->min_rev)
+ && SVN_IS_VALID_REVNUM(wc_stat->max_rev)))
+ {
+ svn_boolean_t is_added;
+
+ /* Allow merge into added nodes. */
+ SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
+ scratch_pool));
+ if (is_added)
+ return SVN_NO_ERROR;
+ else
+ return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
+ _("Cannot determine revision of working "
+ "copy"));
+ }
+
+ if (wc_stat->min_rev != wc_stat->max_rev)
+ return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
+ _("Cannot merge into mixed-revision working "
+ "copy [%lu:%lu]; try updating first"),
+ wc_stat->min_rev, wc_stat->max_rev);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
/*-----------------------------------------------------------------------*/
/*** Public APIs ***/
@@ -8830,6 +9083,7 @@ merge_locked(const char *source1,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
+ svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
@@ -8866,10 +9120,11 @@ merge_locked(const char *source1,
|| (revision2->kind == svn_opt_revision_unspecified))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
+
if (svn_path_is_url(source1) != svn_path_is_url(source2))
- return svn_error_return(svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("Merge sources must both be "
- "either paths or URLs")));
+ return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Merge sources must both be "
+ "either paths or URLs"));
/* ### FIXME: This function really ought to do a history check on
the left and right sides of the merge source, and -- if one is an
@@ -8899,11 +9154,16 @@ merge_locked(const char *source1,
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
scratch_pool));
+
if (target_kind != svn_node_dir && target_kind != svn_node_file)
- return svn_error_return(svn_error_createf(
- SVN_ERR_ILLEGAL_TARGET, NULL,
- _("Merge target '%s' does not exist in the "
- "working copy"), target_abspath));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Merge target '%s' does not exist in the "
+ "working copy"), target_abspath);
+
+ /* Do not allow merges into mixed-revision working copies. */
+ SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
+ allow_mixed_rev, TRUE, TRUE,
+ scratch_pool));
/* Determine the working copy target's repository root URL. */
working_rev.kind = svn_opt_revision_working;
@@ -9103,6 +9363,7 @@ struct merge_baton {
svn_boolean_t force;
svn_boolean_t record_only;
svn_boolean_t dry_run;
+ svn_boolean_t allow_mixed_rev;
const apr_array_header_t *merge_options;
svn_client_ctx_t *ctx;
};
@@ -9116,7 +9377,8 @@ merge_cb(void *baton, apr_pool_t *result
SVN_ERR(merge_locked(b->source1, b->revision1, b->source2, b->revision2,
b->target_abspath, b->depth, b->ignore_ancestry,
b->force, b->record_only, b->dry_run,
- b->merge_options, b->ctx, scratch_pool));
+ b->allow_mixed_rev, b->merge_options, b->ctx,
+ scratch_pool));
return SVN_NO_ERROR;
}
@@ -9144,7 +9406,7 @@ get_target_and_lock_abspath(const char *
}
svn_error_t *
-svn_client_merge3(const char *source1,
+svn_client_merge4(const char *source1,
const svn_opt_revision_t *revision1,
const char *source2,
const svn_opt_revision_t *revision2,
@@ -9154,6 +9416,7 @@ svn_client_merge3(const char *source1,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
+ svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
@@ -9174,6 +9437,7 @@ svn_client_merge3(const char *source1,
baton.force = force;
baton.record_only = record_only;
baton.dry_run = dry_run;
+ baton.allow_mixed_rev = allow_mixed_rev;
baton.merge_options = merge_options;
baton.ctx = ctx;
@@ -9188,45 +9452,6 @@ svn_client_merge3(const char *source1,
}
-/* If TARGET_WCPATH does not reflect a single-revision, pristine,
- unswitched working copy -- in other words, a subtree found in a
- single revision (although sparse checkouts are permitted) -- raise
- SVN_ERR_CLIENT_NOT_READY_TO_MERGE. */
-static svn_error_t *
-ensure_wc_reflects_repository_subtree(const char *target_abspath,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- svn_wc_revision_status_t *wc_stat;
-
- /* Get a WC summary with min/max revisions set to the BASE revision. */
- SVN_ERR(svn_wc_revision_status2(&wc_stat, ctx->wc_ctx, target_abspath, NULL,
- FALSE, ctx->cancel_func, ctx->cancel_baton,
- scratch_pool, scratch_pool));
-
- if (wc_stat->switched)
- return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
- _("Cannot reintegrate into a working copy "
- "with a switched subtree"));
-
- if (wc_stat->modified)
- return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
- _("Cannot reintegrate into a working copy "
- "that has local modifications"));
-
- if (! (SVN_IS_VALID_REVNUM(wc_stat->min_rev)
- && SVN_IS_VALID_REVNUM(wc_stat->max_rev)))
- return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
- _("Cannot determine revision of working copy"));
-
- if (wc_stat->min_rev != wc_stat->max_rev)
- return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
- _("Cannot reintegrate into mixed-revision "
- "working copy; try updating first"));
-
- return SVN_NO_ERROR;
-}
-
/* Check if mergeinfo for a given path is described explicitly or via
inheritance in a mergeinfo catalog.
@@ -9351,10 +9576,10 @@ log_find_operative_revs(void *baton,
svn_mergeinfo_t log_entry_as_mergeinfo;
/* Easy out: The path is not within the tree of interest. */
- if (!svn_uri_is_ancestor(log_baton->target_abspath, path))
+ if (!svn_fspath__is_ancestor(log_baton->target_abspath, path))
continue;
- rel_path = svn_uri_skip_ancestor(log_baton->target_abspath, path);
+ rel_path = svn_fspath__skip_ancestor(log_baton->target_abspath, path);
source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
rel_path, pool);
@@ -9383,8 +9608,8 @@ log_find_operative_revs(void *baton,
&& strcmp(subtree_missing_this_rev, source_rel_path))
{
const char *suffix =
- svn_uri_skip_ancestor(subtree_missing_this_rev,
- source_rel_path);
+ svn_relpath_skip_ancestor(subtree_missing_this_rev,
+ source_rel_path);
missing_path = apr_pstrmemdup(pool, path,
strlen(path) - strlen(suffix) - 1);
}
@@ -9436,9 +9661,9 @@ log_find_operative_revs(void *baton,
of the unmerged revisions in UNMERGED_CATALOG's mergeinfos are "phantoms",
that is, one of the following conditions holds:
- 1) The revision effects no corresponding paths in SOURCE_REPOS_REL_PATH.
+ 1) The revision affects no corresponding paths in SOURCE_REPOS_REL_PATH.
- 2) The revision effects corresponding paths in SOURCE_REPOS_REL_PATH,
+ 2) The revision affects corresponding paths in SOURCE_REPOS_REL_PATH,
but based on the mergeinfo in MERGED_CATALOG, the change was
previously merged.
@@ -9489,7 +9714,7 @@ find_unsynced_ranges(const char *source_
}
}
- /* Find any unmerged revisions which both effect the source and
+ /* Find any unmerged revisions which both affect the source and
are not yet merged to it. */
if (potentially_unmerged_ranges)
{
@@ -9615,10 +9840,10 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
svn_pool_clear(iterpool);
source_path = path + strlen(target_repos_rel_path);
- if (source_path[0] == '/') /* Remove leading '/' for svn_uri_join. */
+ if (source_path[0] == '/') /* Remove leading '/'. */
source_path++;
- source_path = svn_uri_join(source_repos_rel_path, source_path,
- iterpool);
+ source_path = svn_relpath_join(source_repos_rel_path, source_path,
+ iterpool);
source_path_rel_to_session =
svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
@@ -9769,7 +9994,7 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
svn_pool_clear(iterpool);
target_path = source_path + strlen(source_repos_rel_path);
- if (target_path[0] == '/') /* Remove leading '/' for svn_uri_join. */
+ if (target_path[0] == '/') /* Remove leading '/'. */
target_path++;
err = svn_client__repos_location_segments(&segments,
target_ra_session,
@@ -10174,8 +10399,11 @@ merge_reintegrate_locked(const char *sou
svn_dirent_local_style(target_abspath,
scratch_pool));
- SVN_ERR(ensure_wc_reflects_repository_subtree(target_abspath, ctx,
- scratch_pool));
+ /* A reintegrate merge requires the merge target to reflect a subtree
+ * of the repository as found at a single revision. */
+ SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
+ FALSE, FALSE, FALSE,
+ scratch_pool));
SVN_ERR(svn_wc__node_get_base_rev(&target_base_rev, ctx->wc_ctx,
target_abspath, scratch_pool));
@@ -10391,6 +10619,7 @@ merge_peg_locked(const char *source,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
+ svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
@@ -10432,6 +10661,10 @@ merge_peg_locked(const char *source,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath));
+ SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
+ allow_mixed_rev, TRUE, TRUE,
+ scratch_pool));
+
/* Determine the working copy target's repository root URL. */
working_rev.kind = svn_opt_revision_working;
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
@@ -10494,6 +10727,7 @@ struct merge_peg_baton {
svn_boolean_t force;
svn_boolean_t record_only;
svn_boolean_t dry_run;
+ svn_boolean_t allow_mixed_rev;
const apr_array_header_t *merge_options;
svn_client_ctx_t *ctx;
};
@@ -10507,13 +10741,14 @@ merge_peg_cb(void *baton, apr_pool_t *re
SVN_ERR(merge_peg_locked(b->source, b->ranges_to_merge, b->peg_revision,
b->target_abspath, b->depth, b->ignore_ancestry,
b->force, b->record_only, b->dry_run,
- b->merge_options, b->ctx, scratch_pool));
+ b->allow_mixed_rev, b->merge_options, b->ctx,
+ scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client_merge_peg3(const char *source,
+svn_client_merge_peg4(const char *source,
const apr_array_header_t *ranges_to_merge,
const svn_opt_revision_t *peg_revision,
const char *target_wcpath,
@@ -10522,6 +10757,7 @@ svn_client_merge_peg3(const char *source
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
+ svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
@@ -10545,6 +10781,7 @@ svn_client_merge_peg3(const char *source
baton.force = force;
baton.record_only = record_only;
baton.dry_run = dry_run;
+ baton.allow_mixed_rev = allow_mixed_rev;
baton.merge_options = merge_options;
baton.ctx = ctx;
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.c?rev=1044548&r1=1044547&r2=1044548&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.c Sat Dec 11 00:15:55 2010
@@ -156,7 +156,7 @@ svn_client__adjust_mergeinfo_source_path
/* Copy inherited mergeinfo into our output hash, adjusting the
merge source as appropriate. */
- path = svn_uri_join(merge_source, rel_path, pool);
+ path = svn_fspath__join(merge_source, rel_path, pool);
copied_rangelist = svn_rangelist_dup(rangelist, pool);
apr_hash_set(adjusted_mergeinfo, path, APR_HASH_KEY_STRING,
copied_rangelist);
@@ -442,17 +442,16 @@ svn_client__get_repos_mergeinfo(svn_ra_s
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
+ svn_boolean_t *validate_inherited_mergeinfo,
apr_pool_t *pool)
{
svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
*target_mergeinfo = NULL;
- SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
- ra_session,
- rel_path, rev, inherit,
- squelch_incapable, FALSE,
- pool, pool));
+ SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
+ &tgt_mergeinfo_cat, ra_session, rel_path, rev, inherit,
+ squelch_incapable, FALSE, validate_inherited_mergeinfo, pool, pool));
if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
{
@@ -468,15 +467,17 @@ svn_client__get_repos_mergeinfo(svn_ra_s
}
svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
- svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t rev,
- svn_mergeinfo_inheritance_t inherit,
- svn_boolean_t squelch_incapable,
- svn_boolean_t include_descendants,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_client__get_repos_mergeinfo_catalog(
+ svn_mergeinfo_catalog_t *mergeinfo_cat,
+ svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t rev,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t squelch_incapable,
+ svn_boolean_t include_descendants,
+ svn_boolean_t *validate_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_mergeinfo_t repos_mergeinfo;
@@ -486,8 +487,9 @@ svn_client__get_repos_mergeinfo_catalog(
APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
/* Fetch the mergeinfo. */
- err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo, rel_paths, rev,
- inherit, include_descendants, result_pool);
+ err = svn_ra_get_mergeinfo2(ra_session, &repos_mergeinfo, rel_paths, rev,
+ inherit, validate_inherited_mergeinfo,
+ include_descendants, result_pool);
if (err)
{
if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
@@ -606,6 +608,7 @@ svn_client__get_wc_or_repos_mergeinfo_ca
{
const char *session_url = NULL;
apr_pool_t *sesspool = NULL;
+ svn_boolean_t validate_inherited_mergeinfo = FALSE;
if (ra_session)
{
@@ -624,7 +627,8 @@ svn_client__get_wc_or_repos_mergeinfo_ca
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
target_mergeinfo_catalog, ra_session,
"", target_rev, inherit,
- TRUE, FALSE, result_pool, scratch_pool));
+ TRUE, FALSE, &validate_inherited_mergeinfo,
+ result_pool, scratch_pool));
if (*target_mergeinfo_catalog
&& apr_hash_get(*target_mergeinfo_catalog, "",
@@ -672,7 +676,7 @@ svn_client__mergeinfo_from_segments(svn_
continue;
/* Prepend a leading slash to our path. */
- source_path = apr_pstrcat(pool, "/", segment->path, NULL);
+ source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
/* See if we already stored ranges for this path. If not, make
a new list. */
@@ -1027,6 +1031,7 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
if (is_url)
{
svn_mergeinfo_catalog_t tmp_catalog;
+ svn_boolean_t validate_inherited_mergeinfo = FALSE;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", scratch_pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
@@ -1037,14 +1042,10 @@ get_mergeinfo(svn_mergeinfo_catalog_t *m
local_abspath, ra_session,
&peg_rev, scratch_pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, scratch_pool));
- SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tmp_catalog,
- ra_session,
- "", rev,
- svn_mergeinfo_inherited,
- FALSE,
- include_descendants,
- result_pool,
- scratch_pool));
+ SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
+ &tmp_catalog, ra_session, "", rev, svn_mergeinfo_inherited,
+ FALSE, include_descendants, &validate_inherited_mergeinfo,
+ result_pool, scratch_pool));
/* If we're not querying the root of the repository, the catalog
we fetched will be keyed on paths relative to the session
@@ -1258,11 +1259,12 @@ static svn_mergeinfo_t
find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
const char *abs_repos_path)
{
- int i;
int ancestor_index = -1;
if (depth_first_catalog_index)
{
+ int i;
+
for (i = 0; i < depth_first_catalog_index->nelts; i++)
{
svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
@@ -1393,7 +1395,7 @@ filter_log_entry_with_rangelist(void *ba
{
merge_source_path =
APR_ARRAY_IDX(fleb->merge_source_paths, i, const char *);
- if (svn_uri_is_ancestor(merge_source_path, path))
+ if (svn_fspath__is_ancestor(merge_source_path, path))
{
/* If MERGE_SOURCE was itself deleted, replaced, or added
in LOG_ENTRY->REVISION then ignore this PATH since you
@@ -1410,11 +1412,11 @@ filter_log_entry_with_rangelist(void *ba
continue;
/* Calculate the target path which PATH would affect if merged. */
- merge_source_rel_target = svn_uri_skip_ancestor(merge_source_path,
- path);
- target_path_affected = svn_uri_join(fleb->abs_repos_target_path,
- merge_source_rel_target,
- iterpool);
+ merge_source_rel_target = svn_fspath__skip_ancestor(merge_source_path,
+ path);
+ target_path_affected = svn_fspath__join(fleb->abs_repos_target_path,
+ merge_source_rel_target,
+ iterpool);
nearest_ancestor_mergeinfo =
find_nearest_ancestor(fleb->depth_first_catalog_index,
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.h?rev=1044548&r1=1044547&r2=1044548&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/mergeinfo.h Sat Dec 11 00:15:55 2010
@@ -155,7 +155,20 @@ svn_client__get_wc_mergeinfo_catalog(svn
If there is no mergeinfo available for REL_PATH, or if the server
doesn't support a mergeinfo capability and SQUELCH_INCAPABLE is
- TRUE, set *TARGET_MERGEINFO to NULL. */
+ TRUE, set *TARGET_MERGEINFO to NULL.
+
+ If the *TARGET_MERGEINFO for REL_PATH path is inherited and
+ *VALIDATE_INHERITED_MERGEINFO is TRUE, then *TARGET_MERGEINFO
+ will only contain merge source path-revisions that actually
+ exist in repository.
+
+ If the *TARGET_MERGEINFO for REL_PATH path is inherited and
+ *VALIDATE_INHERITED_MERGEINFO is TRUE, then request that the server
+ validate the mergeinfo in *TARGET_MERGEINFO, so it contains only merge
+ source path-revisions that actually exist in repository. If validation
+ is requested and the server supports it, then set
+ *VALIDATE_INHERITED_MERGEINFO to TRUE on return. Set it to FALSE in
+ all other cases. */
svn_error_t *
svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
svn_mergeinfo_t *target_mergeinfo,
@@ -163,6 +176,7 @@ svn_client__get_repos_mergeinfo(svn_ra_s
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
+ svn_boolean_t *validate_inherited_mergeinfo,
apr_pool_t *pool);
/* If INCLUDE_DESCENDANTS is FALSE, behave exactly like
@@ -176,15 +190,17 @@ svn_client__get_repos_mergeinfo(svn_ra_s
paths of the subtrees. If no mergeinfo is found, then
*TARGET_MERGEINFO_CAT is set to NULL. */
svn_error_t *
-svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
- svn_ra_session_t *ra_session,
- const char *rel_path,
- svn_revnum_t rev,
- svn_mergeinfo_inheritance_t inherit,
- svn_boolean_t squelch_incapable,
- svn_boolean_t include_descendants,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+svn_client__get_repos_mergeinfo_catalog(
+ svn_mergeinfo_catalog_t *mergeinfo_cat,
+ svn_ra_session_t *ra_session,
+ const char *rel_path,
+ svn_revnum_t rev,
+ svn_mergeinfo_inheritance_t inherit,
+ svn_boolean_t squelch_incapable,
+ svn_boolean_t include_descendants,
+ svn_boolean_t *validate_inherited_mergeinfo,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* Retrieve the direct mergeinfo for the TARGET_WCPATH from the WC's
mergeinfo prop, or that inherited from its nearest ancestor if the
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_client/patch.c?rev=1044548&r1=1044547&r2=1044548&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_client/patch.c Sat Dec 11 00:15:55 2010
@@ -203,6 +203,9 @@ typedef struct patch_target_t {
/* True if the patch changed any of the properties of the target. */
svn_boolean_t has_prop_changes;
+ /* True if the patch contained a svn:special property. */
+ svn_boolean_t is_special;
+
/* All the information that is specific to the content of the target. */
target_content_info_t *content_info;
@@ -235,7 +238,7 @@ strip_path(const char **result, const ch
apr_array_header_t *stripped;
components = svn_path_decompose(path, scratch_pool);
- if (strip_count >= components->nelts)
+ if (strip_count > components->nelts)
return svn_error_createf(SVN_ERR_CLIENT_PATCH_BAD_STRIP_COUNT, NULL,
_("Cannot strip %u components from '%s'"),
strip_count,
@@ -346,9 +349,7 @@ resolve_target_path(patch_target_t *targ
target->canon_path_from_patchfile = svn_dirent_internal_style(
path_from_patchfile, result_pool);
- /* We allow properties to be set on the wc root dir.
- * ### Do we need to check for empty paths here, shouldn't the parser
- * ### guarentee that the paths returned are non-empty? */
+ /* We allow properties to be set on the wc root dir. */
if (! prop_changes_only && target->canon_path_from_patchfile[0] == '\0')
{
/* An empty patch target path? What gives? Skip this. */
@@ -498,7 +499,7 @@ init_prop_target(prop_patch_target_t **p
new_prop_target->content_info = content_info;
err = svn_wc_prop_get2(&value, wc_ctx, local_abspath, prop_name,
- result_pool, scratch_pool);
+ result_pool, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
@@ -528,12 +529,40 @@ init_prop_target(prop_patch_target_t **p
return SVN_NO_ERROR;
}
+/* Return a suitable filename for the target of PATCH.
+ * Examine the ``old'' and ``new'' file names, and choose the file name
+ * with the fewest path components, the shortest basename, and the shortest
+ * total file name length (in that order). In case of a tie, return the new
+ * filename. This heuristic is also used by Larry Wall's UNIX patch (except
+ * that it prompts for a filename in case of a tie). */
+static const char *
+choose_target_filename(const svn_patch_t *patch)
+{
+ apr_size_t old;
+ apr_size_t new;
+
+ old = svn_path_component_count(patch->old_filename);
+ new = svn_path_component_count(patch->new_filename);
+
+ if (old == new)
+ {
+ old = strlen(svn_dirent_basename(patch->old_filename, NULL));
+ new = strlen(svn_dirent_basename(patch->new_filename, NULL));
+
+ if (old == new)
+ {
+ old = strlen(patch->old_filename);
+ new = strlen(patch->new_filename);
+ }
+ }
+
+ return (old < new) ? patch->old_filename : patch->new_filename;
+}
+
/* Attempt to initialize a *PATCH_TARGET structure for a target file
* described by PATCH. Use working copy context WC_CTX.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
- * OLD_PATCH_TARGET_NAMES indicates whether the old target's name parsed
- * from the patch file should be preferred over the new target's name.
* The patch target structure is allocated in RESULT_POOL, but if the target
* should be skipped, PATCH_TARGET->SKIPPED is set and the target should be
* treated as not fully initialized, e.g. the caller should not not do any
@@ -545,9 +574,7 @@ static svn_error_t *
init_patch_target(patch_target_t **patch_target,
const svn_patch_t *patch,
const char *base_dir,
- svn_wc_context_t *wc_ctx,
- int strip_count,
- svn_boolean_t old_patch_target_names,
+ svn_wc_context_t *wc_ctx, int strip_count,
svn_boolean_t remove_tempfiles,
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
@@ -593,8 +620,7 @@ init_patch_target(patch_target_t **patch
target->prop_targets = apr_hash_make(result_pool);
target->pool = result_pool;
- SVN_ERR(resolve_target_path(target, old_patch_target_names ?
- patch->old_filename : patch->new_filename,
+ SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
base_dir, strip_count, prop_changes_only,
wc_ctx, result_pool, scratch_pool));
if (! target->skipped)
@@ -631,13 +657,16 @@ init_patch_target(patch_target_t **patch
scratch_pool));
}
- /* ### Is it ok to set target->added here? Isn't the target supposed to
- * ### be marked as added after it's been proven that it can be added?
- * ### One alternative is to include a git_added flag. Or maybe we
- * ### should have kept the patch field in patch_target_t? Then we
- * ### could have checked for target->patch->operation == added */
+ /* ### Is it ok to set the operation of the target already here? Isn't
+ * ### the target supposed to be marked with an operation after we have
+ * ### determined that the changes will apply cleanly to the WC? Maybe
+ * ### we should have kept the patch field in patch_target_t to be
+ * ### able to distinguish between 'what the patch says we should do'
+ * ### and 'what we can do with the given state of our WC'. */
if (patch->operation == svn_diff_op_added)
target->added = TRUE;
+ else if (patch->operation == svn_diff_op_deleted)
+ target->deleted = TRUE;
SVN_ERR(svn_stream_open_unique(&patched_raw,
&target->patched_path, NULL,
@@ -869,7 +898,7 @@ match_hunk(svn_boolean_t *matched, targe
/* If the last line doesn't have a newline, we get EOF but still
* have a non-empty line to compare. */
if ((hunk_eof && hunk_line->len == 0) ||
- (content_info->eof && strlen(target_line) == 0))
+ (content_info->eof && *target_line == 0))
break;
/* Leading/trailing fuzzy lines always match. */
@@ -910,9 +939,9 @@ match_hunk(svn_boolean_t *matched, targe
* Return the line at which HUNK was matched in *MATCHED_LINE.
* If the hunk did not match at all, set *MATCHED_LINE to zero.
* If the hunk matched multiple times, and MATCH_FIRST is TRUE,
- * return the line number at which the first match occured in *MATCHED_LINE.
+ * return the line number at which the first match occurred in *MATCHED_LINE.
* If the hunk matched multiple times, and MATCH_FIRST is FALSE,
- * return the line number at which the last match occured in *MATCHED_LINE.
+ * return the line number at which the last match occurred in *MATCHED_LINE.
* If IGNORE_WHITESPACE is set, ignore whitespace during the matching.
* If MATCH_MODIFIED is TRUE, match the modified hunk text,
* rather than the original hunk text.
@@ -936,7 +965,6 @@ scan_for_match(svn_linenum_t *matched_li
! content_info->eof)
{
svn_boolean_t matched;
- int i;
svn_pool_clear(iterpool);
@@ -948,6 +976,7 @@ scan_for_match(svn_linenum_t *matched_li
if (matched)
{
svn_boolean_t taken = FALSE;
+ int i;
/* Don't allow hunks to match at overlapping locations. */
for (i = 0; i < content_info->hunks->nelts; i++)
@@ -1235,24 +1264,6 @@ get_hunk_info(hunk_info_t **hi, patch_ta
return SVN_NO_ERROR;
}
-/* Attempt to write LEN bytes of DATA to STREAM, the underlying file
- * of which is at ABSPATH. Fail if not all bytes could be written to
- * the stream. Do temporary allocations in POOL. */
-static svn_error_t *
-try_stream_write(svn_stream_t *stream, const char *abspath,
- const char *data, apr_size_t len, apr_pool_t *pool)
-{
- apr_size_t written;
-
- written = len;
- SVN_ERR(svn_stream_write(stream, data, &written));
- if (written != len)
- return svn_error_createf(SVN_ERR_IO_WRITE_ERROR, NULL,
- _("Error writing to '%s'"),
- svn_dirent_local_style(abspath, pool));
- return SVN_NO_ERROR;
-}
-
/* Copy lines to the patched stream until the specified LINE has been
* reached. Indicate in *EOF whether end-of-file was encountered while
* reading from the target.
@@ -1269,28 +1280,29 @@ copy_lines_to_target(target_content_info
&& ! content_info->eof)
{
const char *target_line;
+ apr_size_t len;
svn_pool_clear(iterpool);
SVN_ERR(read_line(content_info, &target_line, iterpool, iterpool));
if (! content_info->eof)
target_line = apr_pstrcat(iterpool, target_line, content_info->eol_str,
- NULL);
-
- SVN_ERR(try_stream_write(content_info->patched, patched_path,
- target_line, strlen(target_line), iterpool));
+ (char *)NULL);
+ len = strlen(target_line);
+ SVN_ERR(svn_stream_write(content_info->patched, target_line, &len));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
-/* Write the diff text of the hunk described by HI to the
- * reject stream of CONTENT_INFO, and mark TARGET as having had rejects.
+/* Write the diff text of HUNK to the reject stream of CONTENT_INFO,
+ * and mark TARGET as having had rejects.
* Do temporary allocations in POOL. */
static svn_error_t *
reject_hunk(patch_target_t *target, target_content_info_t *content_info,
- hunk_info_t *hi, const char *prop_name, apr_pool_t *pool)
+ const svn_diff_hunk_t *hunk, const char *prop_name,
+ apr_pool_t *pool)
{
const char *hunk_header;
apr_size_t len;
@@ -1301,7 +1313,7 @@ reject_hunk(patch_target_t *target, targ
{
const char *prop_header;
- /* ### Print 'Added', 'Deleted' or 'Modified' instead of 'Propperty'.
+ /* ### Print 'Added', 'Deleted' or 'Modified' instead of 'Property'.
*/
prop_header = apr_psprintf(pool, "Property: %s\n", prop_name);
len = strlen(prop_header);
@@ -1311,18 +1323,18 @@ reject_hunk(patch_target_t *target, targ
/* ### What about just setting a variable to either "@@" or "##",
* ### and merging with the else clause below? */
hunk_header = apr_psprintf(pool, "## -%lu,%lu +%lu,%lu ##%s",
- svn_diff_hunk_get_original_start(hi->hunk),
- svn_diff_hunk_get_original_length(hi->hunk),
- svn_diff_hunk_get_modified_start(hi->hunk),
- svn_diff_hunk_get_modified_length(hi->hunk),
+ svn_diff_hunk_get_original_start(hunk),
+ svn_diff_hunk_get_original_length(hunk),
+ svn_diff_hunk_get_modified_start(hunk),
+ svn_diff_hunk_get_modified_length(hunk),
APR_EOL_STR);
}
else
hunk_header = apr_psprintf(pool, "@@ -%lu,%lu +%lu,%lu @@%s",
- svn_diff_hunk_get_original_start(hi->hunk),
- svn_diff_hunk_get_original_length(hi->hunk),
- svn_diff_hunk_get_modified_start(hi->hunk),
- svn_diff_hunk_get_modified_length(hi->hunk),
+ svn_diff_hunk_get_original_start(hunk),
+ svn_diff_hunk_get_original_length(hunk),
+ svn_diff_hunk_get_modified_start(hunk),
+ svn_diff_hunk_get_modified_length(hunk),
APR_EOL_STR);
len = strlen(hunk_header);
SVN_ERR(svn_stream_write(content_info->reject, hunk_header, &len));
@@ -1335,17 +1347,22 @@ reject_hunk(patch_target_t *target, targ
svn_pool_clear(iterpool);
- SVN_ERR(svn_diff_hunk_readline_diff_text(hi->hunk, &hunk_line, &eol_str,
+ SVN_ERR(svn_diff_hunk_readline_diff_text(hunk, &hunk_line, &eol_str,
&eof, iterpool, iterpool));
if (! eof)
{
if (hunk_line->len >= 1)
- SVN_ERR(try_stream_write(content_info->reject, target->reject_path,
- hunk_line->data, hunk_line->len,
- iterpool));
+ {
+ len = hunk_line->len;
+ SVN_ERR(svn_stream_write(content_info->reject, hunk_line->data,
+ &len));
+ }
+
if (eol_str)
- SVN_ERR(try_stream_write(content_info->reject, target->reject_path,
- eol_str, strlen(eol_str), iterpool));
+ {
+ len = strlen(eol_str);
+ SVN_ERR(svn_stream_write(content_info->reject, eol_str, &len));
+ }
}
}
while (! eof);
@@ -1394,7 +1411,7 @@ apply_hunk(patch_target_t *target, targe
{
/* Seek failed, reject this hunk. */
hi->rejected = TRUE;
- SVN_ERR(reject_hunk(target, content_info, hi, prop_name, pool));
+ SVN_ERR(reject_hunk(target, content_info, hi->hunk, prop_name, pool));
return SVN_NO_ERROR;
}
}
@@ -1418,11 +1435,15 @@ apply_hunk(patch_target_t *target, targe
if (! eof && lines_read > hi->fuzz &&
lines_read <= svn_diff_hunk_get_modified_length(hi->hunk) - hi->fuzz)
{
+ apr_size_t len;
+
if (hunk_line->len >= 1)
- SVN_ERR(try_stream_write(content_info->patched,
- target->patched_path,
- hunk_line->data, hunk_line->len,
- iterpool));
+ {
+ len = hunk_line->len;
+ SVN_ERR(svn_stream_write(content_info->patched, hunk_line->data,
+ &len));
+ }
+
if (eol_str)
{
/* Use the EOL as it was read from the patch file,
@@ -1430,9 +1451,8 @@ apply_hunk(patch_target_t *target, targe
if (content_info->eol_style != svn_subst_eol_style_none)
eol_str = content_info->eol_str;
- SVN_ERR(try_stream_write(content_info->patched,
- target->patched_path, eol_str,
- strlen(eol_str), iterpool));
+ len = strlen(eol_str);
+ SVN_ERR(svn_stream_write(content_info->patched, eol_str, &len));
}
}
}
@@ -1592,53 +1612,12 @@ send_patch_notification(const patch_targ
return SVN_NO_ERROR;
}
-/* Close the streams of the TARGET so that their content is flushed
- * to disk. This will also close underlying streams and files. Use POOL for
- * temporary allocations. */
-static svn_error_t *
-close_target_streams(const patch_target_t *target,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- target_content_info_t *prop_content_info;
-
- /* First the streams belonging to properties .. */
- for (hi = apr_hash_first(pool, target->prop_targets);
- hi;
- hi = apr_hash_next(hi))
- {
- prop_patch_target_t *prop_target;
- prop_target = svn__apr_hash_index_val(hi);
- prop_content_info = prop_target->content_info;
-
- /* ### If the prop did not exist pre-patching we'll not have a
- * ### stream to read from. Find a better way to store info on
- * ### the existence of the target prop. */
- if (prop_content_info->stream)
- SVN_ERR(svn_stream_close(prop_content_info->stream));
-
- SVN_ERR(svn_stream_close(prop_content_info->patched));
- }
-
-
- /* .. And then streams associted with the file. The reject stream is
- * shared between all target_content_info structures. */
- if (target->kind_on_disk == svn_node_file)
- SVN_ERR(svn_stream_close(target->content_info->stream));
- SVN_ERR(svn_stream_close(target->content_info->patched));
- SVN_ERR(svn_stream_close(target->content_info->reject));
-
- return SVN_NO_ERROR;
-}
-
/* Apply a PATCH to a working copy at ABS_WC_PATH and put the result
* into temporary files, to be installed in the working copy later.
* Return information about the patch target in *PATCH_TARGET, allocated
* in RESULT_POOL. Use WC_CTX as the working copy context.
* STRIP_COUNT specifies the number of leading path components
* which should be stripped from target paths in the patch.
- * OLD_PATCH_TARGET_NAMES indicates whether the old filename parsed
- * from the patch file should be preferred over the new filename.
* REMOVE_TEMPFILES, PATCH_FUNC, and PATCH_BATON as in svn_client_patch().
* IGNORE_WHITESPACE tells whether whitespace should be considered when
* doing the matching.
@@ -1648,7 +1627,6 @@ static svn_error_t *
apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
const char *abs_wc_path, svn_wc_context_t *wc_ctx,
int strip_count,
- svn_boolean_t old_patch_target_names,
svn_boolean_t ignore_whitespace,
svn_boolean_t remove_tempfiles,
svn_client_patch_func_t patch_func,
@@ -1662,9 +1640,9 @@ apply_one_patch(patch_target_t **patch_t
int i;
static const int MAX_FUZZ = 2;
apr_hash_index_t *hash_index;
+ target_content_info_t *prop_content_info;
- SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx,
- strip_count, old_patch_target_names,
+ SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
remove_tempfiles, result_pool, scratch_pool));
if (target->skipped)
{
@@ -1727,7 +1705,7 @@ apply_one_patch(patch_target_t **patch_t
if (hi->already_applied)
continue;
else if (hi->rejected)
- SVN_ERR(reject_hunk(target, target->content_info, hi,
+ SVN_ERR(reject_hunk(target, target->content_info, hi->hunk,
NULL /* prop_name */,
iterpool));
else
@@ -1761,6 +1739,9 @@ apply_one_patch(patch_target_t **patch_t
prop_name = svn__apr_hash_index_key(hash_index);
prop_patch = svn__apr_hash_index_val(hash_index);
+ if (! strcmp(prop_name, SVN_PROP_SPECIAL))
+ target->is_special = TRUE;
+
/* We'll store matched hunks in prop_content_info. */
prop_target = apr_hash_get(target->prop_targets, prop_name,
APR_HASH_KEY_STRING);
@@ -1816,7 +1797,7 @@ apply_one_patch(patch_target_t **patch_t
if (hi->already_applied)
continue;
else if (hi->rejected)
- SVN_ERR(reject_hunk(target, prop_target->content_info, hi,
+ SVN_ERR(reject_hunk(target, prop_target->content_info, hi->hunk,
prop_target->name,
iterpool));
else
@@ -1843,7 +1824,31 @@ apply_one_patch(patch_target_t **patch_t
svn_pool_destroy(iterpool);
- SVN_ERR(close_target_streams(target, scratch_pool));
+ /* Now close some streams that we don't need any longer to get
+ * file buffers flushed to disk. First, close the props streams... */
+ for (hash_index = apr_hash_first(scratch_pool, target->prop_targets);
+ hash_index;
+ hash_index = apr_hash_next(hash_index))
+ {
+ prop_patch_target_t *prop_target;
+ prop_target = svn__apr_hash_index_val(hash_index);
+ prop_content_info = prop_target->content_info;
+
+ /* ### If the prop did not exist pre-patching we'll not have a
+ * ### stream to read from. Find a better way to store info on
+ * ### the existence of the target prop. */
+ if (prop_content_info->stream)
+ SVN_ERR(svn_stream_close(prop_content_info->stream));
+
+ SVN_ERR(svn_stream_close(prop_content_info->patched));
+ }
+
+ /* .. and then streams associated with the file.
+ * But we're not closing the reject stream -- it still needed and
+ * will be closed later in write_out_rejected_hunks(). */
+ if (target->kind_on_disk == svn_node_file)
+ SVN_ERR(svn_stream_close(target->content_info->stream));
+ SVN_ERR(svn_stream_close(target->content_info->patched));
if (! target->skipped)
{
@@ -2030,14 +2035,13 @@ create_missing_parents(patch_target_t *t
* to version control. Allow cancellation since we
* have not modified the working copy yet for this
* target. */
- SVN_ERR(svn_wc_add4(ctx->wc_ctx, local_abspath,
- svn_depth_infinity,
- NULL, SVN_INVALID_REVNUM,
- ctx->cancel_func,
- ctx->cancel_baton,
- ctx->notify_func2,
- ctx->notify_baton2,
- iterpool));
+
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
+ SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, local_abspath,
+ ctx->notify_func2, ctx->notify_baton2,
+ iterpool));
}
}
}
@@ -2107,20 +2111,42 @@ install_patched_target(patch_target_t *t
if (! dry_run && ! target->skipped)
{
- /* Copy the patched file on top of the target file. */
- SVN_ERR(svn_io_copy_file(target->patched_path,
- target->local_abspath, FALSE, pool));
+ if (target->is_special)
+ {
+ svn_stream_t *stream;
+ svn_stream_t *patched_stream;
+ apr_file_t *file;
+
+ SVN_ERR(svn_io_file_open(&file, target->patched_path,
+ APR_READ | APR_BINARY, APR_OS_DEFAULT,
+ pool));
+
+ patched_stream = svn_stream_from_aprfile2(file, FALSE /* disown */,
+ pool);
+ SVN_ERR(svn_subst_create_specialfile(&stream,
+ target->local_abspath,
+ pool, pool));
+ SVN_ERR(svn_stream_copy3(patched_stream, stream,
+ NULL, /* cancel_func */
+ NULL, /* cancel_baton */
+ pool));
+ }
+ else
+ {
+ /* Copy the patched file on top of the target file. */
+ SVN_ERR(svn_io_copy_file(target->patched_path,
+ target->local_abspath, FALSE, pool));
+ }
+
if (target->added || target->replaced)
{
/* The target file didn't exist previously,
* so add it to version control.
* Suppress notification, we'll do that later (and also
- * during dry-run). Also suppress cancellation because
+ * during dry-run). Don't allow cancellation because
* we'd rather notify about what we did before aborting. */
- SVN_ERR(svn_wc_add4(ctx->wc_ctx, target->local_abspath,
- svn_depth_infinity,
- NULL, SVN_INVALID_REVNUM,
- NULL, NULL, NULL, NULL, pool));
+ SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
+ NULL, NULL, pool));
}
/* Restore the target's executable bit if necessary. */
@@ -2142,6 +2168,8 @@ write_out_rejected_hunks(patch_target_t
svn_boolean_t dry_run,
apr_pool_t *pool)
{
+ SVN_ERR(svn_stream_close(target->content_info->reject));
+
if (! dry_run && (target->had_rejects || target->had_prop_rejects))
{
/* Write out rejected hunks, if any. */
@@ -2165,14 +2193,6 @@ install_patched_prop_targets(patch_targe
apr_hash_index_t *hi;
apr_pool_t *iterpool;
- if (dry_run)
- {
- if (! target->has_text_changes && target->kind_on_disk == svn_node_none)
- target->added = TRUE;
-
- return SVN_NO_ERROR;
- }
-
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, target->prop_targets);
@@ -2184,19 +2204,22 @@ install_patched_prop_targets(patch_targe
svn_stream_t *patched_stream;
svn_stringbuf_t *line;
svn_stringbuf_t *prop_content;
+ const svn_string_t *prop_val;
const char *eol_str;
svn_boolean_t eof;
+ svn_error_t *err;
svn_pool_clear(iterpool);
/* For a deleted prop we only set the value to NULL. */
if (prop_target->operation == svn_diff_op_deleted)
{
- SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
- prop_target->name, NULL,
- TRUE /* skip_checks */,
- NULL, NULL, /* suppress notification */
- iterpool));
+ if (! dry_run)
+ SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
+ prop_target->name, NULL,
+ TRUE /* skip_checks */,
+ NULL, NULL, /* suppress notification */
+ iterpool));
continue;
}
@@ -2240,31 +2263,73 @@ install_patched_prop_targets(patch_targe
&& target->kind_on_disk == svn_node_none
&& ! target->added)
{
- SVN_ERR(svn_io_file_create(target->local_abspath, "", scratch_pool));
- SVN_ERR(svn_wc_add4(ctx->wc_ctx, target->local_abspath,
- svn_depth_infinity,
- NULL, SVN_INVALID_REVNUM,
- ctx->cancel_func,
- ctx->cancel_baton,
- NULL, NULL, /* suppress notification */
- iterpool));
+ if (! dry_run)
+ {
+ SVN_ERR(svn_io_file_create(target->local_abspath, "",
+ scratch_pool));
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
+ /* suppress notification */
+ NULL, NULL,
+ iterpool));
+ }
target->added = TRUE;
}
- /* ### How should we handle SVN_ERR_ILLEGAL_TARGET and
- * ### SVN_ERR_BAD_MIME_TYPE?
- *
- * ### stsp: I'd say reject the property hunk.
- * ### We should verify all modified prop hunk texts using
- * ### svn_wc_canonicalize_svn_prop() before starting the
- * ### patching process, to reject them as early as possible. */
- SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
- prop_target->name,
- svn_string_create_from_buf(prop_content,
- iterpool),
- TRUE /* skip_checks */,
- NULL, NULL,
- iterpool));
+ /* Attempt to set the property, and reject all hunks if this fails. */
+ prop_val = svn_string_create_from_buf(prop_content, iterpool);
+ if (dry_run)
+ {
+ const svn_string_t *canon_propval;
+
+ err = svn_wc_canonicalize_svn_prop(&canon_propval,
+ prop_target->name,
+ prop_val, target->local_abspath,
+ target->db_kind,
+ TRUE, /* ### Skipping checks */
+ NULL, NULL,
+ iterpool);
+ }
+ else
+ {
+ err = (svn_wc_prop_set4(ctx->wc_ctx, target->local_abspath,
+ prop_target->name, prop_val,
+ TRUE, /* ### Skipping checks */
+ NULL, NULL,
+ iterpool));
+ }
+
+ if (err)
+ {
+ /* ### The errors which svn_wc_canonicalize_svn_prop() will
+ * ### return aren't documented. */
+ if (err->apr_err == SVN_ERR_ILLEGAL_TARGET ||
+ err->apr_err == SVN_ERR_NODE_UNEXPECTED_KIND ||
+ err->apr_err == SVN_ERR_IO_UNKNOWN_EOL ||
+ err->apr_err == SVN_ERR_BAD_MIME_TYPE ||
+ err->apr_err == SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION)
+ {
+ int i;
+
+ svn_error_clear(err);
+
+ for (i = 0; i < prop_target->content_info->hunks->nelts; i++)
+ {
+ hunk_info_t *hunk_info;
+
+ hunk_info = APR_ARRAY_IDX(prop_target->content_info->hunks,
+ i, hunk_info_t *);
+ hunk_info->rejected = TRUE;
+ SVN_ERR(reject_hunk(target, prop_target->content_info,
+ hunk_info->hunk, prop_target->name,
+ iterpool));
+ }
+ }
+ else
+ return svn_error_return(err);
+ }
+
}
svn_pool_destroy(iterpool);
@@ -2551,9 +2616,6 @@ typedef struct {
/* Number of leading components to strip from patch target paths. */
int strip_count;
- /* Whether to use the old path from the patch file instead of the new one. */
- svn_boolean_t old_patch_target_names;
-
/* Whether to apply the patch in reverse. */
svn_boolean_t reverse;
@@ -2619,7 +2681,6 @@ apply_patches(void *baton,
SVN_ERR(apply_one_patch(&target, patch, btn->abs_wc_path,
btn->ctx->wc_ctx, btn->strip_count,
- btn->old_patch_target_names,
btn->ignore_whitespace,
btn->remove_tempfiles,
btn->patch_func, btn->patch_baton,
@@ -2639,7 +2700,9 @@ apply_patches(void *baton,
if (! target->skipped)
{
- if (target->has_text_changes || target->added)
+ if (target->has_text_changes
+ || target->added
+ || target->deleted)
SVN_ERR(install_patched_target(target, btn->abs_wc_path,
btn->ctx, btn->dry_run,
iterpool));
@@ -2675,7 +2738,6 @@ svn_client_patch(const char *patch_abspa
const char *local_abspath,
svn_boolean_t dry_run,
int strip_count,
- svn_boolean_t old_patch_target_names,
svn_boolean_t reverse,
svn_boolean_t ignore_whitespace,
svn_boolean_t remove_tempfiles,
@@ -2691,12 +2753,15 @@ svn_client_patch(const char *patch_abspa
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("strip count must be positive"));
+ if (svn_path_is_url(local_abspath))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), local_abspath);
+
baton.patch_abspath = patch_abspath;
baton.abs_wc_path = local_abspath;
baton.dry_run = dry_run;
baton.ctx = ctx;
baton.strip_count = strip_count;
- baton.old_patch_target_names = old_patch_target_names;
baton.reverse = reverse;
baton.ignore_whitespace = ignore_whitespace;
baton.remove_tempfiles = remove_tempfiles;