Nice! Should we nominate this one for backport to the 1.4.x line as
well?
On Mon, 30 Jul 2007, cmpilato@tigris.org wrote:
> Author: cmpilato
> Date: Mon Jul 30 13:37:14 2007
> New Revision: 25891
>
> Log:
> Finish issue #2853 (do_single_file_merge() lacks comprehensive
> "replace-style merge" capability).
>
> Prior to this change, merges performed on a single file failed to
> honor the --ignore-ancestry flag, opting instead to always ignore
> ancestry. This caused unexpected behavior when merging single-file
> replacements.
>
> * subversion/libsvn_client/merge.c
> (merge_file_added): Be a little more forgiving when calculation
> copyfrom URLs for merge-added files.
> (do_single_file_merge): Add 'ignore_ancestry' parameter, and use it to
> determine whether not a merge across unrelated file nodes should
> be treated as a file modification or a delete+add. While here,
> actually grow support for the delete+add case.
> (discover_and_merge_children, svn_client_merge3, svn_client_merge_peg3):
> Pass 'ignore_ancestry' to do_single_file_merge().
>
> * subversion/tests/cmdline/merge_tests.py
> (test_list): No longer expect single_file_replace_style_merge_capability()
> to fail.
>
> Patch by: Kamesh Jayachandran,
> me
>
> Modified:
> trunk/subversion/libsvn_client/merge.c
> trunk/subversion/tests/cmdline/merge_tests.py
>
> Modified: trunk/subversion/libsvn_client/merge.c
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/merge.c?pathrev=25891&r1=25890&r2=25891
> ==============================================================================
> --- trunk/subversion/libsvn_client/merge.c (original)
> +++ trunk/subversion/libsvn_client/merge.c Mon Jul 30 13:37:14 2007
> @@ -458,9 +458,11 @@
> if (! merge_b->dry_run)
> {
> child = svn_path_is_child(merge_b->target, mine, subpool);
> - assert(child != NULL);
> - copyfrom_url = svn_path_url_add_component(merge_b->url, child,
> - subpool);
> + if (child != NULL)
> + copyfrom_url = svn_path_url_add_component(merge_b->url, child,
> + subpool);
> + else
> + copyfrom_url = merge_b->url;
> SVN_ERR(check_scheme_match(adm_access, copyfrom_url));
>
> /* Since 'mine' doesn't exist, and this is
> @@ -2098,7 +2100,6 @@
> revision1 = &assumed_initial_revision1;
> revision2 = &assumed_initial_revision2;
> }
> -
>
> notify_b.same_urls = (strcmp(URL1, URL2) == 0);
> if (!notify_b.same_urls && merge_b->record_only)
> @@ -2390,6 +2391,7 @@
> const char *target_wcpath,
> svn_wc_adm_access_t *adm_access,
> struct merge_cmd_baton *merge_b,
> + svn_boolean_t ignore_ancestry,
> apr_pool_t *pool)
> {
> apr_hash_t *props1, *props2;
> @@ -2412,7 +2414,7 @@
> apr_hash_t *target_mergeinfo;
> const svn_wc_entry_t *entry;
> int i;
> - svn_boolean_t indirect = FALSE;
> + svn_boolean_t indirect = FALSE, is_replace = FALSE;
> apr_size_t target_count, merge_target_count;
> svn_opt_revision_t assumed_initial_revision1, assumed_initial_revision2;
> apr_pool_t *subpool;
> @@ -2421,8 +2423,8 @@
> pool));
>
> /* Establish first RA session to URL1. */
> - SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, initial_URL1, NULL,
> - NULL, NULL, FALSE, TRUE,
> + SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, initial_URL1,
> + NULL, NULL, NULL, FALSE, TRUE,
> ctx, pool));
> SVN_ERR(assume_default_rev_range(initial_revision1,
> &assumed_initial_revision1,
> @@ -2447,7 +2449,7 @@
> &URL2, &revision2,
> NULL,
> initial_path2 ? initial_path2
> - : initial_URL2,
> + : initial_URL2,
> peg_revision,
> &assumed_initial_revision1,
> &assumed_initial_revision2,
> @@ -2467,6 +2469,51 @@
> path1 = NULL;
> path2 = NULL;
> }
> + /* If we are not performing a pegged merge, but we also are not
> + ignoring ancestry, then we need to check the relationship between
> + the two sides of our merge. */
> + else if (! ignore_ancestry)
> + {
> + const char *location_url;
> + svn_opt_revision_t unspecified_revision, *location_rev;
> + unspecified_revision.kind = svn_opt_revision_unspecified;
> +
> + /* Try to locate the left side of the merge location by tracing the
> + history of right side. We do this only do verify that one of
> + these locations is an ancestor of the other. */
> + err = svn_client__repos_locations(&location_url, &location_rev,
> + NULL, NULL,
> + NULL,
> + initial_path2 ? initial_path2
> + : initial_URL2,
> + &assumed_initial_revision2,
> + &assumed_initial_revision1,
> + &unspecified_revision,
> + ctx, pool);
> +
> + /* If the two sides don't have an ancestral relationship, that's
> + okay. But because we are preserving ancestry, we have to
> + treat a merge across those locations as a deletion of the one
> + and addition of the other. */
> + if (err && err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
> + {
> + is_replace = TRUE;
> + svn_error_clear(err);
> + err = SVN_NO_ERROR;
> + }
> + SVN_ERR(err);
> +
> + /* ### FIXME: We need to actual do some relationship checks. */
> + URL1 = initial_URL1;
> + URL2 = initial_URL2;
> + path1 = initial_path1;
> + path2 = initial_path2;
> + revision1 = &assumed_initial_revision1;
> + revision2 = &assumed_initial_revision2;
> + }
> + /* And if we aren't performing a pegged merge but we are ignoring
> + ancestry, then we don't care about the relationships between the
> + sides of our merge source, and happily accept them as they are. */
> else
> {
> URL1 = initial_URL1;
> @@ -2476,7 +2523,7 @@
> revision1 = &assumed_initial_revision1;
> revision2 = &assumed_initial_revision2;
> }
> -
> +
> /* reparent RA session to URL1. */
> SVN_ERR(svn_ra_reparent(ra_session1, URL1, pool));
> /* Establish RA session to URL2. */
> @@ -2581,6 +2628,9 @@
> r->start,
> URL1, target_wcpath, subpool));
>
> + /* ### CMP ### */
> + if (is_replace)
> + SVN_ERR(svn_ra_reparent(ra_session2, URL1, pool));
> SVN_ERR(single_file_merge_get_file(&tmpfile2, ra_session2, &props2,
> r->end,
> URL2, target_wcpath, subpool));
> @@ -2597,16 +2647,76 @@
> /* Deduce property diffs. */
> SVN_ERR(svn_prop_diffs(&propchanges, props2, props1, subpool));
>
> - SVN_ERR(merge_file_changed(adm_access,
> - &text_state, &prop_state,
> - target_wcpath,
> - tmpfile1,
> - tmpfile2,
> - r->start,
> - r->end,
> - mimetype1, mimetype2,
> - propchanges, props1,
> - merge_b));
> + if (is_replace)
> + {
> + SVN_ERR(merge_file_deleted(adm_access,
> + &text_state,
> + target_wcpath,
> + NULL,
> + NULL,
> + mimetype1, mimetype2,
> + props1,
> + merge_b));
> + {
> + svn_wc_notify_t *notify
> + = svn_wc_create_notify(target_wcpath, svn_wc_notify_update_delete,
> + subpool);
> + notify->kind = svn_node_file;
> + notify->content_state = text_state;
> +
> + if (notify->content_state == svn_wc_notify_state_missing)
> + notify->action = svn_wc_notify_skip;
> + notification_receiver(¬ify_b, notify, subpool);
> + }
> +
> + SVN_ERR(merge_file_added(adm_access,
> + &text_state, &prop_state,
> + target_wcpath,
> + tmpfile1,
> + tmpfile2,
> + r->start,
> + r->end,
> + mimetype1, mimetype2,
> + propchanges, props1,
> + merge_b));
> + {
> + svn_wc_notify_t *notify
> + = svn_wc_create_notify(target_wcpath, svn_wc_notify_update_add,
> + subpool);
> + notify->kind = svn_node_file;
> + notify->content_state = text_state;
> + notify->prop_state = prop_state;
> +
> + if (notify->content_state == svn_wc_notify_state_missing)
> + notify->action = svn_wc_notify_skip;
> + notification_receiver(¬ify_b, notify, subpool);
> + }
> + }
> + else
> + {
> + SVN_ERR(merge_file_changed(adm_access,
> + &text_state, &prop_state,
> + target_wcpath,
> + tmpfile1,
> + tmpfile2,
> + r->start,
> + r->end,
> + mimetype1, mimetype2,
> + propchanges, props1,
> + merge_b));
> + {
> + svn_wc_notify_t *notify
> + = svn_wc_create_notify(target_wcpath, svn_wc_notify_update_update,
> + subpool);
> + notify->kind = svn_node_file;
> + notify->content_state = text_state;
> + notify->prop_state = prop_state;
> +
> + if (notify->content_state == svn_wc_notify_state_missing)
> + notify->action = svn_wc_notify_skip;
> + notification_receiver(¬ify_b, notify, subpool);
> + }
> + }
>
> /* Ignore if temporary file not found. It may have been renamed. */
> err = svn_io_remove_file(tmpfile1, subpool);
> @@ -2618,19 +2728,6 @@
> return err;
> svn_error_clear(err);
>
> - {
> - svn_wc_notify_t *notify
> - = svn_wc_create_notify(target_wcpath, svn_wc_notify_update_update,
> - subpool);
> - notify->kind = svn_node_file;
> - notify->content_state = text_state;
> - notify->prop_state = prop_state;
> -
> - if (notify->content_state == svn_wc_notify_state_missing)
> - notify->action = svn_wc_notify_skip;
> - notification_receiver(¬ify_b, notify, subpool);
> - }
> -
> /* ### LATER: Give the caller a shot at resolving any conflicts
> ### we've detected. If the conflicts are not resolved, abort
> ### application of any remaining revision ranges for this WC
> @@ -2861,6 +2958,7 @@
> child_wcpath,
> adm_access,
> merge_cmd_baton,
> + ignore_ancestry,
> pool));
> }
> else if (child_entry->kind == svn_node_dir)
> @@ -3020,6 +3118,7 @@
> target_wcpath,
> adm_access,
> &merge_cmd_baton,
> + ignore_ancestry,
> pool));
> }
>
> @@ -3252,6 +3351,7 @@
> target_wcpath,
> adm_access,
> &merge_cmd_baton,
> + ignore_ancestry,
> pool));
> }
>
>
> Modified: trunk/subversion/tests/cmdline/merge_tests.py
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/tests/cmdline/merge_tests.py?pathrev=25891&r1=25890&r2=25891
> ==============================================================================
> --- trunk/subversion/tests/cmdline/merge_tests.py (original)
> +++ trunk/subversion/tests/cmdline/merge_tests.py Mon Jul 30 13:37:14 2007
> @@ -7059,7 +7059,7 @@
> Skip(mergeinfo_and_skipped_paths, svntest.main.is_ra_type_file),
> update_loses_mergeinfo,
> merge_loses_mergeinfo,
> - XFail(single_file_replace_style_merge_capability),
> + single_file_replace_style_merge_capability,
> ]
>
> if __name__ == '__main__':
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: svn-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: svn-help@subversion.tigris.org
- application/pgp-signature attachment: stored
Received on Tue Jul 31 00:32:16 2007