You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2015/10/26 12:06:51 UTC
svn commit: r1710565 [2/3] - in
/subversion/branches/move-tracking-2/subversion/svnmover: merge3.c
svnmover.c svnmover.h
Copied: subversion/branches/move-tracking-2/subversion/svnmover/merge3.c (from r1710534, subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c)
URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/svnmover/merge3.c?p2=subversion/branches/move-tracking-2/subversion/svnmover/merge3.c&p1=subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c&r1=1710534&r2=1710565&rev=1710565&view=diff
==============================================================================
--- subversion/branches/move-tracking-2/subversion/svnmover/svnmover.c (original)
+++ subversion/branches/move-tracking-2/subversion/svnmover/merge3.c Mon Oct 26 11:06:50 2015
@@ -1,5 +1,5 @@
/*
- * svnmover.c: Concept Demo for Move Tracking and Branching
+ * merge3.c: 3-way merging
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
@@ -27,91 +27,25 @@
#include <apr_lib.h>
-#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_iter.h"
#include "svn_client.h"
-#include "svn_cmdline.h"
-#include "svn_config.h"
#include "svn_error.h"
-#include "svn_path.h"
#include "svn_pools.h"
#include "svn_props.h"
#include "svn_string.h"
-#include "svn_subst.h"
-#include "svn_utf.h"
-#include "svn_version.h"
-#include "svnmover.h"
-#include "private/svn_cmdline_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_branch_repos.h"
#include "private/svn_branch_nested.h"
#include "private/svn_branch_compat.h"
-#include "private/svn_ra_private.h"
-#include "private/svn_string_private.h"
#include "private/svn_sorts_private.h"
-#include "private/svn_token.h"
#include "private/svn_client_private.h"
-#include "../libsvn_delta/debug_editor.h"
-
-#define HAVE_LINENOISE
-#ifdef HAVE_LINENOISE
-#include "../libsvn_subr/linenoise/linenoise.h"
-#endif
-
-/* Version compatibility check */
-static svn_error_t *
-check_lib_versions(void)
-{
- static const svn_version_checklist_t checklist[] =
- {
- { "svn_client", svn_client_version },
- { "svn_subr", svn_subr_version },
- { "svn_ra", svn_ra_version },
- { NULL, NULL }
- };
- SVN_VERSION_DEFINE(my_version);
-
- return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
-}
-
-static svn_boolean_t quiet = FALSE;
-/* UI mode: whether to display output in terms of paths or elements */
-enum { UI_MODE_EIDS, UI_MODE_PATHS, UI_MODE_SERIAL };
-static int the_ui_mode = UI_MODE_EIDS;
-static const svn_token_map_t ui_mode_map[]
- = { {"eids", UI_MODE_EIDS},
- {"e", UI_MODE_EIDS},
- {"paths", UI_MODE_PATHS},
- {"p", UI_MODE_PATHS},
- {"serial", UI_MODE_SERIAL},
- {"s", UI_MODE_SERIAL},
- {NULL, SVN_TOKEN_UNKNOWN} };
-
-#define is_branch_root_element(branch, eid) \
- (svn_branch_root_eid(branch) == (eid))
-
-/* Is BRANCH1 the same branch as BRANCH2? Compare by full branch-ids; don't
- require identical branch objects. */
-#define BRANCH_IS_SAME_BRANCH(branch1, branch2, scratch_pool) \
- (strcmp(svn_branch_get_id(branch1, scratch_pool), \
- svn_branch_get_id(branch2, scratch_pool)) == 0)
+#include "svnmover.h"
-/* Print a notification. */
-__attribute__((format(printf, 1, 2)))
-static void
-notify(const char *fmt,
- ...)
-{
- va_list ap;
+#include "svn_private_config.h"
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
- printf("\n");
-}
/* Print a verbose notification: in 'quiet' mode, don't print it. */
__attribute__((format(printf, 1, 2)))
@@ -121,7 +55,7 @@ notify_v(const char *fmt,
{
va_list ap;
- if (! quiet)
+ /*if (! quiet)*/
{
va_start(ap, fmt);
vprintf(fmt, ap);
@@ -130,136 +64,19 @@ notify_v(const char *fmt,
}
}
-#define SVN_CL__LOG_SEP_STRING \
- "------------------------------------------------------------------------\n"
-
-/* ====================================================================== */
-
-/* Update the WC to revision BASE_REVISION (SVN_INVALID_REVNUM means HEAD).
- *
- * Requires these fields in WC:
- * head_revision
- * repos_root_url
- * ra_session
- * pool
- *
- * Initializes these fields in WC:
- * base_revision
- * base_branch_id
- * base_branch
- * working_branch_id
- * working_branch
- * editor
- *
- * Assumes there are no changes in the WC: throws away the existing txn
- * and starts a new one.
- */
-static svn_error_t *
-wc_checkout(svnmover_wc_t *wc,
- svn_revnum_t base_revision,
- const char *base_branch_id,
- apr_pool_t *scratch_pool)
+/* */
+static int
+sort_compare_items_by_eid(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
{
- const char *branch_info_dir = NULL;
- svn_branch_compat__shim_fetch_func_t fetch_func;
- void *fetch_baton;
- svn_branch_txn_t *base_txn;
-
- /* Validate and store the new base revision number */
- if (! SVN_IS_VALID_REVNUM(base_revision))
- base_revision = wc->head_revision;
- else if (base_revision > wc->head_revision)
- return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
- _("No such revision %ld (HEAD is %ld)"),
- base_revision, wc->head_revision);
-
- /* Choose whether to store branching info in a local dir or in revprops.
- (For now, just to exercise the options, we choose local files for
- RA-local and revprops for a remote repo.) */
- if (strncmp(wc->repos_root_url, "file://", 7) == 0)
- {
- const char *repos_dir;
-
- SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dir, wc->repos_root_url,
- scratch_pool));
- branch_info_dir = svn_dirent_join(repos_dir, "branch-info", scratch_pool);
- }
-
- /* Get a mutable transaction based on that rev. (This implementation
- re-reads all the move-tracking data from the repository.) */
- SVN_ERR(svn_ra_load_branching_state(&wc->edit_txn,
- &fetch_func, &fetch_baton,
- wc->ra_session, branch_info_dir,
- base_revision,
- wc->pool, scratch_pool));
-
- wc->edit_txn = svn_nested_branch_txn_create(wc->edit_txn, wc->pool);
-
- /* Store the WC base state */
- base_txn = svn_branch_repos_get_base_revision_root(wc->edit_txn);
- wc->base = apr_pcalloc(wc->pool, sizeof(*wc->base));
- wc->base->revision = base_revision;
- wc->base->branch_id = apr_pstrdup(wc->pool, base_branch_id);
- wc->base->branch
- = svn_branch_txn_get_branch_by_id(base_txn, wc->base->branch_id,
- scratch_pool);
- if (! wc->base->branch)
- return svn_error_createf(SVN_ERR_BRANCHING, NULL,
- "Cannot check out WC: branch %s not found in r%ld",
- base_branch_id, base_revision);
-
- wc->working = apr_pcalloc(wc->pool, sizeof(*wc->working));
- wc->working->revision = SVN_INVALID_REVNUM;
- wc->working->branch_id = wc->base->branch_id;
- wc->working->branch
- = svn_branch_txn_get_branch_by_id(wc->edit_txn, wc->working->branch_id,
- scratch_pool);
- SVN_ERR_ASSERT(wc->working->branch);
+ int eid_a = *(const int *)a->key;
+ int eid_b = *(const int *)b->key;
- return SVN_NO_ERROR;
+ return eid_a - eid_b;
}
-/* Create a simulated WC, in memory.
- *
- * Initializes these fields in WC:
- * head_revision
- * repos_root_url
- * ra_session
- * made_changes
- * ctx
- * pool
- *
- * BASE_REVISION is the revision to work on, or SVN_INVALID_REVNUM for HEAD.
- */
-static svn_error_t *
-wc_create(svnmover_wc_t **wc_p,
- const char *anchor_url,
- svn_revnum_t base_revision,
- const char *base_branch_id,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *wc_pool = svn_pool_create(result_pool);
- svnmover_wc_t *wc = apr_pcalloc(wc_pool, sizeof(*wc));
-
- wc->pool = wc_pool;
- wc->ctx = ctx;
-
- SVN_ERR(svn_client_open_ra_session2(&wc->ra_session, anchor_url,
- NULL /* wri_abspath */, ctx,
- wc_pool, scratch_pool));
-
- SVN_ERR(svn_ra_get_repos_root2(wc->ra_session, &wc->repos_root_url,
- result_pool));
- SVN_ERR(svn_ra_get_latest_revnum(wc->ra_session, &wc->head_revision,
- scratch_pool));
- SVN_ERR(svn_ra_reparent(wc->ra_session, wc->repos_root_url, scratch_pool));
- SVN_ERR(wc_checkout(wc, base_revision, base_branch_id, scratch_pool));
- *wc_p = wc;
- return SVN_NO_ERROR;
-}
+/* ====================================================================== */
/* Return (left, right) pairs of element content that differ between
* subtrees LEFT and RIGHT.
@@ -307,1185 +124,288 @@ element_differences(apr_hash_t **diff_p,
return SVN_NO_ERROR;
}
-/* Set *IS_CHANGED to true if EDIT_TXN differs from its base txn, else to
- * false.
+/* Return a string suitable for appending to a displayed element name or
+ * element id to indicate that it is a subbranch root element for SUBBRANCH.
+ * Return "" if SUBBRANCH is null.
*/
-static svn_error_t *
-txn_is_changed(svn_branch_txn_t *edit_txn,
- svn_boolean_t *is_changed,
- apr_pool_t *scratch_pool)
+static const char *
+branch_str(svn_branch_state_t *subbranch,
+ apr_pool_t *result_pool)
{
- SVN_ITER_T(svn_branch_state_t) *bi;
- svn_branch_txn_t *base_txn
- = svn_branch_repos_get_base_revision_root(edit_txn);
- apr_array_header_t *edit_branches
- = svn_branch_txn_get_branches(edit_txn, scratch_pool);
- apr_array_header_t *base_branches
- = svn_branch_txn_get_branches(base_txn, scratch_pool);
-
- *is_changed = FALSE;
-
- /* If any previous branch is now missing, that's a change. */
- for (SVN_ARRAY_ITER(bi, base_branches, scratch_pool))
- {
- svn_branch_state_t *base_branch = bi->val;
- svn_branch_state_t *edit_branch
- = svn_branch_txn_get_branch_by_id(edit_txn, base_branch->bid,
- scratch_pool);
-
- if (! edit_branch)
- {
- *is_changed = TRUE;
- return SVN_NO_ERROR;
- }
- }
-
- /* If any current branch is new or changed, that's a change. */
- for (SVN_ARRAY_ITER(bi, edit_branches, scratch_pool))
- {
- svn_branch_state_t *edit_branch = bi->val;
- svn_branch_state_t *base_branch
- = svn_branch_txn_get_branch_by_id(base_txn, edit_branch->bid,
- scratch_pool);
- apr_hash_t *diff;
-
- if (! base_branch)
- {
- *is_changed = TRUE;
- return SVN_NO_ERROR;
- }
+ if (subbranch)
+ return apr_psprintf(result_pool,
+ " (branch %s)",
+ svn_branch_get_id(subbranch, result_pool));
+ return "";
+}
- SVN_ERR(element_differences(&diff,
- svn_branch_get_element_tree(edit_branch),
- svn_branch_get_element_tree(base_branch),
- scratch_pool, scratch_pool));
- if (apr_hash_count(diff))
- {
- *is_changed = TRUE;
- return SVN_NO_ERROR;
- }
- }
+/* Return a string suitable for appending to a displayed element name or
+ * element id to indicate that BRANCH:EID is a subbranch root element.
+ * Return "" if the element is not a subbranch root element.
+ */
+static const char *
+subbranch_str(svn_branch_state_t *branch,
+ int eid,
+ apr_pool_t *result_pool)
+{
+ svn_branch_state_t *subbranch
+ = svn_branch_get_subbranch_at_eid(branch, eid, result_pool);
- return SVN_NO_ERROR;
+ return branch_str(subbranch, result_pool);
}
-/* Replay differences between S_LEFT and S_RIGHT into EDITOR:EDIT_BRANCH.
+/* Options to control how strict the merge is about detecting conflicts.
*
- * S_LEFT and/or S_RIGHT may be null meaning an empty set.
+ * The options affect cases that, depending on the user's preference, could
+ * either be considered a conflict or be merged to a deterministic result.
*
- * Non-recursive: single branch only.
+ * The set of options is flexible and may be extended in future.
*/
-static svn_error_t *
-subtree_replay(svn_branch_state_t *edit_branch,
- const svn_element_tree_t *s_left,
- const svn_element_tree_t *s_right,
- apr_pool_t *scratch_pool)
+typedef struct merge_conflict_policy_t
{
- apr_hash_t *diff_left_right;
- apr_hash_index_t *hi;
-
- if (! s_left)
- s_left = svn_element_tree_create(NULL, 0 /*root_eid*/, scratch_pool);
- if (! s_right)
- s_right = svn_element_tree_create(NULL, 0 /*root_eid*/, scratch_pool);
-
- SVN_ERR(element_differences(&diff_left_right,
- s_left, s_right,
- scratch_pool, scratch_pool));
-
- /* Go through the per-element differences. */
- for (hi = apr_hash_first(scratch_pool, diff_left_right);
- hi; hi = apr_hash_next(hi))
- {
- int eid = svn_int_hash_this_key(hi);
- svn_element_content_t **e_pair = apr_hash_this_val(hi);
- svn_element_content_t *e0 = e_pair[0], *e1 = e_pair[1];
-
- SVN_ERR_ASSERT(!e0
- || svn_element_payload_invariants(e0->payload));
- SVN_ERR_ASSERT(!e1
- || svn_element_payload_invariants(e1->payload));
- if (e0 || e1)
- {
- if (e0 && e1)
- {
- SVN_DBG(("replay: alter e%d", eid));
- SVN_ERR(svn_branch_state_alter_one(edit_branch, eid,
- e1->parent_eid, e1->name,
- e1->payload, scratch_pool));
- }
- else if (e0)
- {
- SVN_DBG(("replay: delete e%d", eid));
- SVN_ERR(svn_branch_state_delete_one(edit_branch, eid,
- scratch_pool));
- }
- else
- {
- SVN_DBG(("replay: instan. e%d", eid));
- SVN_ERR(svn_branch_state_alter_one(edit_branch, eid,
- e1->parent_eid, e1->name,
- e1->payload, scratch_pool));
- }
- }
- }
-
- return SVN_NO_ERROR;
-}
+ /* Whether to merge delete-vs-delete */
+ svn_boolean_t merge_double_delete;
+ /* Whether to merge add-vs-add (with same parent/name/payload) */
+ svn_boolean_t merge_double_add;
+ /* Whether to merge reparent-vs-reparent (with same parent) */
+ svn_boolean_t merge_double_reparent;
+ /* Whether to merge rename-vs-rename (with same name) */
+ svn_boolean_t merge_double_rename;
+ /* Whether to merge modify-vs-modify (with same payload) */
+ svn_boolean_t merge_double_modify;
+ /* Possible additional controls: */
+ /* merge (parent, name, props, text) independently or as a group */
+ /* merge (parent, name) independently or as a group */
+ /* merge (props, text) independently or as a group */
+} merge_conflict_policy_t;
-/* */
-static apr_hash_t *
-get_union_of_subbranches(svn_branch_state_t *left_branch,
- svn_branch_state_t *right_branch,
- apr_pool_t *result_pool)
+/* An element-merge conflict description.
+ */
+typedef struct element_merge3_conflict_t
{
- apr_hash_t *all_subbranches;
+ svn_element_content_t *yca;
+ svn_element_content_t *side1;
+ svn_element_content_t *side2;
+} element_merge3_conflict_t;
- svn_branch_subtree_t *s_left
- = left_branch ? svn_branch_get_subtree(left_branch,
- svn_branch_root_eid(left_branch),
- result_pool) : NULL;
- svn_branch_subtree_t *s_right
- = right_branch ? svn_branch_get_subtree(right_branch,
- svn_branch_root_eid(right_branch),
- result_pool) : NULL;
- all_subbranches
- = left_branch ? apr_hash_overlay(result_pool,
- s_left->subbranches, s_right->subbranches)
- : s_right->subbranches;
+static element_merge3_conflict_t *
+element_merge3_conflict_create(svn_element_content_t *yca,
+ svn_element_content_t *side1,
+ svn_element_content_t *side2,
+ apr_pool_t *result_pool)
+{
+ element_merge3_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
- return all_subbranches;
+ c->yca = yca;
+ c->side1 = side1;
+ c->side2 = side2;
+ return c;
}
-/* Replay differences between S_LEFT and S_RIGHT into EDITOR:EDIT_BRANCH.
- *
- * S_LEFT or S_RIGHT (but not both) may be null meaning an empty set.
- *
- * Recurse into subbranches.
+/* A name-clash conflict description.
*/
-static svn_error_t *
-svn_branch_replay(svn_branch_txn_t *edit_txn,
- svn_branch_state_t *edit_branch,
- svn_branch_state_t *left_branch,
- svn_branch_state_t *right_branch,
- apr_pool_t *scratch_pool)
+typedef struct name_clash_conflict_t
{
- assert((left_branch && right_branch)
- ? (svn_branch_root_eid(left_branch) == svn_branch_root_eid(right_branch))
- : (left_branch || right_branch));
-
- if (right_branch)
- {
- /* Replay this branch */
- const svn_element_tree_t *s_left
- = left_branch ? svn_branch_get_element_tree(left_branch) : NULL;
- const svn_element_tree_t *s_right
- = right_branch ? svn_branch_get_element_tree(right_branch) : NULL;
-
- SVN_ERR(subtree_replay(edit_branch, s_left, s_right,
- scratch_pool));
- }
- else
- {
- /* deleted branch LEFT */
- /* nothing to do -- it will go away because we deleted the outer-branch
- element where it was attached */
- }
-
- /* Replay its subbranches, recursively.
- (If we're deleting the current branch, we don't also need to
- explicitly delete its subbranches... do we?) */
- if (right_branch)
- {
- apr_hash_t *all_subbranches
- = get_union_of_subbranches(left_branch, right_branch, scratch_pool);
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(scratch_pool, all_subbranches);
- hi; hi = apr_hash_next(hi))
- {
- int this_eid = svn_int_hash_this_key(hi);
- svn_branch_state_t *left_subbranch
- = left_branch ? svn_branch_get_subbranch_at_eid(
- left_branch, this_eid, scratch_pool) : NULL;
- svn_branch_state_t *right_subbranch
- = right_branch ? svn_branch_get_subbranch_at_eid(
- right_branch, this_eid, scratch_pool) : NULL;
- svn_branch_state_t *edit_subbranch = NULL;
-
- /* If the subbranch is to be edited or added, first look up the
- corresponding edit subbranch, or, if not found, create one. */
- if (right_subbranch)
- {
- const char *new_branch_id
- = svn_branch_id_nest(edit_branch->bid, this_eid, scratch_pool);
-
- SVN_ERR(svn_branch_txn_open_branch(edit_txn, &edit_subbranch,
- right_subbranch->predecessor,
- new_branch_id,
- svn_branch_root_eid(right_subbranch),
- scratch_pool, scratch_pool));
- }
+ int parent_eid;
+ const char *name;
+ /* All EIDs that conflict with each other: hash of (eid -> irrelevant). */
+ apr_hash_t *elements;
+} name_clash_conflict_t;
- /* recurse */
- if (edit_subbranch)
- {
- SVN_ERR(svn_branch_replay(edit_txn, edit_subbranch,
- left_subbranch, right_subbranch,
- scratch_pool));
- }
- }
- }
+static name_clash_conflict_t *
+name_clash_conflict_create(int parent_eid,
+ const char *name,
+ apr_pool_t *result_pool)
+{
+ name_clash_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
- return SVN_NO_ERROR;
+ c->parent_eid = parent_eid;
+ c->name = apr_pstrdup(result_pool, name);
+ c->elements = apr_hash_make(result_pool);
+ return c;
}
-/* Replay differences between LEFT_BRANCH and RIGHT_BRANCH into
- * EDIT_ROOT_BRANCH.
- * (Recurse into subbranches.)
+/* An orphan conflict description.
*/
-static svn_error_t *
-replay(svn_branch_txn_t *edit_txn,
- svn_branch_state_t *edit_root_branch,
- svn_branch_state_t *left_branch,
- svn_branch_state_t *right_branch,
- apr_pool_t *scratch_pool)
+typedef struct orphan_conflict_t
{
- SVN_ERR_ASSERT(left_branch || right_branch);
+ svn_element_content_t *element;
+} orphan_conflict_t;
- SVN_ERR(svn_branch_replay(edit_txn, edit_root_branch,
- left_branch, right_branch, scratch_pool));
- return SVN_NO_ERROR;
-}
+static orphan_conflict_t *
+orphan_conflict_create(svn_element_content_t *element,
+ apr_pool_t *result_pool)
+{
+ orphan_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-static svn_error_t *
-commit_callback(const svn_commit_info_t *commit_info,
- void *baton,
- apr_pool_t *pool);
+ c->element = element;
+ return c;
+}
-/* Baton for commit_callback(). */
-typedef struct commit_callback_baton_t
+/* */
+static conflict_storage_t *
+conflict_storage_create(apr_pool_t *result_pool)
{
- svn_branch_txn_t *edit_txn;
- const char *wc_base_branch_id;
- const char *wc_commit_branch_id;
-
- /* just-committed revision */
- svn_revnum_t revision;
-} commit_callback_baton_t;
-
-static svn_error_t *
-display_diff_of_commit(const commit_callback_baton_t *ccbb,
- apr_pool_t *scratch_pool);
+ conflict_storage_t *c = apr_pcalloc(result_pool, sizeof(*c));
-static svn_error_t *
-do_topbranch(svn_branch_state_t **new_branch_p,
- svn_branch_txn_t *txn,
- svn_branch_rev_bid_eid_t *from,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+ return c;
+}
-/* Allocate the same number of new EIDs in NEW_TXN as are already
- * allocated in OLD_TXN.
- */
-static svn_error_t *
-allocate_eids(svn_branch_txn_t *new_txn,
- const svn_branch_txn_t *old_txn,
- apr_pool_t *scratch_pool)
+/* */
+static const char *
+brief_eid_and_name_or_nil(svn_element_content_t *e,
+ apr_pool_t *result_pool)
{
- int num_new_eids;
- int i;
-
- SVN_ERR(svn_branch_txn_get_num_new_eids(old_txn, &num_new_eids,
- scratch_pool));
- for (i = 0; i < num_new_eids; i++)
- {
- SVN_ERR(svn_branch_txn_new_eid(new_txn, NULL, scratch_pool));
- }
+ return e ? apr_psprintf(result_pool, "%d/%s", e->parent_eid, e->name)
+ : "<nil>";
return SVN_NO_ERROR;
}
-/* Commit the changes from WC into the repository.
- *
- * Open a new commit txn to the repo. Replay the changes from WC into it.
- *
- * Set WC->head_revision and *NEW_REV_P to the committed revision number.
- *
- * If there are no changes to commit, set *NEW_REV_P to SVN_INVALID_REVNUM
- * and do not make a commit and do not change WC->head_revision.
- *
- * NEW_REV_P may be null if not wanted.
- */
-static svn_error_t *
-wc_commit(svn_revnum_t *new_rev_p,
- svnmover_wc_t *wc,
- apr_hash_t *revprops,
- apr_pool_t *scratch_pool)
+svn_error_t *
+svnmover_display_conflicts(conflict_storage_t *conflict_storage,
+ const char *prefix,
+ apr_pool_t *scratch_pool)
{
- const char *branch_info_dir = NULL;
- svn_branch_txn_t *commit_txn;
- commit_callback_baton_t ccbb;
- svn_boolean_t change_detected;
- const char *edit_root_branch_id;
- svn_branch_state_t *edit_root_branch;
+ apr_hash_index_t *hi;
- /* If no log msg provided, use the list of commands */
- if (! svn_hash_gets(revprops, SVN_PROP_REVISION_LOG) && wc->list_of_commands)
+ for (hi = apr_hash_first(scratch_pool,
+ conflict_storage->single_element_conflicts);
+ hi; hi = apr_hash_next(hi))
{
- /* Avoid modifying the passed-in revprops hash */
- revprops = apr_hash_copy(scratch_pool, revprops);
+ int eid = svn_int_hash_this_key(hi);
+ element_merge3_conflict_t *c = apr_hash_this_val(hi);
- svn_hash_sets(revprops, SVN_PROP_REVISION_LOG,
- svn_string_create(wc->list_of_commands, scratch_pool));
+ printf("%ssingle-element conflict: e%d: yca=%s, side1=%s, side2=%s\n",
+ prefix, eid,
+ brief_eid_and_name_or_nil(c->yca, scratch_pool),
+ brief_eid_and_name_or_nil(c->side1, scratch_pool),
+ brief_eid_and_name_or_nil(c->side2, scratch_pool));
}
-
- /* Choose whether to store branching info in a local dir or in revprops.
- (For now, just to exercise the options, we choose local files for
- RA-local and revprops for a remote repo.) */
- if (strncmp(wc->repos_root_url, "file://", 7) == 0)
+ for (hi = apr_hash_first(scratch_pool,
+ conflict_storage->name_clash_conflicts);
+ hi; hi = apr_hash_next(hi))
{
- const char *repos_dir;
-
- SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dir, wc->repos_root_url,
- scratch_pool));
- branch_info_dir = svn_dirent_join(repos_dir, "branch-info", scratch_pool);
- }
+ /*const char *key = apr_hash_this_key(hi);*/
+ name_clash_conflict_t *c = apr_hash_this_val(hi);
+ apr_hash_index_t *hi2;
- /* Start a new editor for the commit. */
- SVN_ERR(svn_ra_get_commit_txn(wc->ra_session,
- &commit_txn,
- revprops,
- commit_callback, &ccbb,
- NULL /*lock_tokens*/, FALSE /*keep_locks*/,
- branch_info_dir,
- scratch_pool));
- /*SVN_ERR(svn_branch_txn__get_debug(&wc->edit_txn, wc->edit_txn, scratch_pool));*/
-
- edit_root_branch_id = wc->working->branch_id;
- edit_root_branch = svn_branch_txn_get_branch_by_id(
- commit_txn, wc->working->branch_id, scratch_pool);
-
- /* We might be creating a new top-level branch in this commit. That is the
- only case in which the working branch will not be found in EDIT_TXN.
- (Creating any other branch can only be done inside a checkout of a
- parent branch.) So, maybe create a new top-level branch. */
- if (! edit_root_branch)
- {
- /* Create a new top-level branch in the edited state. (It will have
- an independent new top-level branch number.) */
- svn_branch_rev_bid_eid_t *from
- = svn_branch_rev_bid_eid_create(wc->base->revision, wc->base->branch_id,
- svn_branch_root_eid(wc->base->branch),
- scratch_pool);
+ printf("%sname-clash conflict: peid %d, name '%s', %d elements\n",
+ prefix, c->parent_eid, c->name, apr_hash_count(c->elements));
+ for (hi2 = apr_hash_first(scratch_pool, c->elements);
+ hi2; hi2 = apr_hash_next(hi2))
+ {
+ int eid = svn_int_hash_this_key(hi2);
- SVN_ERR(do_topbranch(&edit_root_branch, commit_txn,
- from, scratch_pool, scratch_pool));
- edit_root_branch_id = edit_root_branch->bid;
- }
- /* Allocate all the new eids we'll need in this new txn */
- SVN_ERR(allocate_eids(commit_txn, wc->working->branch->txn, scratch_pool));
- SVN_ERR(replay(commit_txn, edit_root_branch,
- wc->base->branch,
- wc->working->branch,
- scratch_pool));
- SVN_ERR(txn_is_changed(commit_txn, &change_detected,
- scratch_pool));
- if (change_detected)
- {
- ccbb.edit_txn = commit_txn;
- ccbb.wc_base_branch_id = wc->base->branch_id;
- ccbb.wc_commit_branch_id = edit_root_branch_id;
-
- SVN_ERR(svn_branch_txn_complete(commit_txn, scratch_pool));
- SVN_ERR(display_diff_of_commit(&ccbb, scratch_pool));
-
- wc->head_revision = ccbb.revision;
- if (new_rev_p)
- *new_rev_p = ccbb.revision;
+ printf("%s element %d\n", prefix, eid);
+ }
}
- else
+ for (hi = apr_hash_first(scratch_pool,
+ conflict_storage->orphan_conflicts);
+ hi; hi = apr_hash_next(hi))
{
- SVN_ERR(svn_branch_txn_abort(commit_txn, scratch_pool));
- if (new_rev_p)
- *new_rev_p = SVN_INVALID_REVNUM;
- }
-
- wc->list_of_commands = NULL;
+ int eid = svn_int_hash_this_key(hi);
+ orphan_conflict_t *c = apr_hash_this_val(hi);
+ printf("%sorphan conflict: element %d/%s: peid %d does not exist\n",
+ prefix, eid, c->element->name, c->element->parent_eid);
+ }
return SVN_NO_ERROR;
}
-typedef enum action_code_t {
- ACTION_INFO_WC,
- ACTION_DIFF,
- ACTION_LOG,
- ACTION_LIST_BRANCHES,
- ACTION_LIST_BRANCHES_R,
- ACTION_LS,
- ACTION_TBRANCH,
- ACTION_BRANCH,
- ACTION_BRANCH_INTO,
- ACTION_MKBRANCH,
- ACTION_MERGE,
- ACTION_MV,
- ACTION_MKDIR,
- ACTION_PUT_FILE,
- ACTION_CAT,
- ACTION_CP,
- ACTION_RM,
- ACTION_CP_RM,
- ACTION_BR_RM,
- ACTION_BR_INTO_RM,
- ACTION_COMMIT,
- ACTION_UPDATE,
- ACTION_SWITCH,
- ACTION_STATUS,
- ACTION_REVERT,
- ACTION_MIGRATE
-} action_code_t;
-
-typedef struct action_defn_t {
- enum action_code_t code;
- const char *name;
- int num_args;
- const char *args_help;
- const char *help;
-} action_defn_t;
-
-#define NL "\n "
-static const action_defn_t action_defn[] =
-{
- {ACTION_INFO_WC, "info-wc", 0, "",
- "print information about the WC"},
- {ACTION_LIST_BRANCHES, "branches", 1, "PATH",
- "list all branches rooted at the same element as PATH"},
- {ACTION_LIST_BRANCHES_R, "ls-br-r", 0, "",
- "list all branches, recursively"},
- {ACTION_LS, "ls", 1, "PATH",
- "list elements in the branch found at PATH"},
- {ACTION_LOG, "log", 2, "FROM@REV TO@REV",
- "show per-revision diffs between FROM and TO"},
- {ACTION_TBRANCH, "tbranch", 1, "SRC",
- "branch the branch-root or branch-subtree at SRC" NL
- "to make a new top-level branch"},
- {ACTION_BRANCH, "branch", 2, "SRC DST",
- "branch the branch-root or branch-subtree at SRC" NL
- "to make a new branch at DST"},
- {ACTION_BRANCH_INTO, "branch-into", 2, "SRC DST",
- "make a branch of the existing subtree SRC appear at" NL
- "DST as part of the existing branch that contains DST" NL
- "(like merging the creation of SRC to DST)"},
- {ACTION_MKBRANCH, "mkbranch", 1, "ROOT",
- "make a directory that's the root of a new subbranch"},
- {ACTION_DIFF, "diff", 2, "LEFT@REV RIGHT@REV",
- "show differences from subtree LEFT to subtree RIGHT"},
- {ACTION_MERGE, "merge", 3, "FROM TO YCA@REV",
- "3-way merge YCA->FROM into TO"},
- {ACTION_CP, "cp", 2, "REV SRC DST",
- "copy SRC@REV to DST"},
- {ACTION_MV, "mv", 2, "SRC DST",
- "move SRC to DST"},
- {ACTION_RM, "rm", 1, "PATH",
- "delete PATH"},
- {ACTION_CP_RM, "copy-and-delete", 2, "SRC DST",
- "copy-and-delete SRC to DST"},
- {ACTION_BR_RM, "branch-and-delete", 2, "SRC DST",
- "branch-and-delete SRC to DST"},
- {ACTION_BR_INTO_RM, "branch-into-and-delete", 2, "SRC DST",
- "merge-and-delete SRC to DST"},
- {ACTION_MKDIR, "mkdir", 1, "PATH",
- "create new directory PATH"},
- {ACTION_PUT_FILE, "put", 2, "LOCAL_FILE PATH",
- "add or modify file PATH with text copied from" NL
- "LOCAL_FILE (use \"-\" to read from standard input)"},
- {ACTION_CAT, "cat", 1, "PATH",
- "display text (for a file) and props (if any) of PATH"},
- {ACTION_COMMIT, "commit", 0, "",
- "commit the changes"},
- {ACTION_UPDATE, "update", 1, ".@REV",
- "update to revision REV, keeping local changes"},
- {ACTION_SWITCH, "switch", 1, "TARGET[@REV]",
- "switch to another branch and/or revision, keeping local changes"},
- {ACTION_STATUS, "status", 0, "",
- "same as 'diff .@base .'"},
- {ACTION_REVERT, "revert", 0, "",
- "revert all uncommitted changes"},
- {ACTION_MIGRATE, "migrate", 1, ".@REV",
- "migrate changes from non-move-tracking revision"},
-};
-
-typedef struct action_t {
- /* The original command words (const char *) by which the action was
- specified */
- apr_array_header_t *action_args;
-
- action_code_t action;
-
- /* argument revisions */
- svn_opt_revision_t rev_spec[3];
-
- const char *branch_id[3];
-
- /* argument paths */
- const char *relpath[3];
-} action_t;
-
-/* ====================================================================== */
-
-/* Find the deepest branch in the repository of which REVNUM:BRANCH_ID:RELPATH
- * is either the root element or a normal, non-sub-branch element.
- *
- * RELPATH is a repository-relative path. REVNUM is a revision number, or
- * SVN_INVALID_REVNUM meaning the current txn.
+/* Merge the payload for one element.
*
- * Return the location of the element in that branch, or with
- * EID=-1 if no element exists there.
+ * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
+ * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
+ * Note that *RESULT_P can be null, indicating a deletion.
*
- * If BRANCH_ID is null, the default is the WC base branch when REVNUM is
- * specified, and the WC working branch when REVNUM is SVN_INVALID_REVNUM.
+ * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
*
- * Return an error if branch BRANCH_ID does not exist in r<REVNUM>; otherwise,
- * the result will never be NULL, as every path is within at least the root
- * branch.
- */
-static svn_error_t *
-find_el_rev_by_rrpath_rev(svn_branch_el_rev_id_t **el_rev_p,
- svnmover_wc_t *wc,
- svn_revnum_t revnum,
- const char *branch_id,
- const char *relpath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- if (SVN_IS_VALID_REVNUM(revnum))
- {
- const svn_branch_repos_t *repos = wc->working->branch->txn->repos;
-
- if (! branch_id)
- branch_id = wc->base->branch_id;
- SVN_ERR(svn_branch_repos_find_el_rev_by_path_rev(el_rev_p, repos,
- revnum,
- branch_id,
- relpath,
- result_pool,
- scratch_pool));
- }
- else
- {
- svn_branch_state_t *branch
- = branch_id ? svn_branch_txn_get_branch_by_id(
- wc->working->branch->txn, branch_id, scratch_pool)
- : wc->working->branch;
- svn_branch_el_rev_id_t *el_rev = apr_palloc(result_pool, sizeof(*el_rev));
-
- if (! branch)
- return svn_error_createf(SVN_ERR_BRANCHING, NULL,
- _("Branch %s not found in working state"),
- branch_id);
- svn_branch_find_nested_branch_element_by_relpath(
- &el_rev->branch, &el_rev->eid,
- branch, relpath, scratch_pool);
- el_rev->rev = SVN_INVALID_REVNUM;
- *el_rev_p = el_rev;
- }
- SVN_ERR_ASSERT(*el_rev_p);
- return SVN_NO_ERROR;
-}
-
-/* Return a string suitable for appending to a displayed element name or
- * element id to indicate that it is a subbranch root element for SUBBRANCH.
- * Return "" if SUBBRANCH is null.
- */
-static const char *
-branch_str(svn_branch_state_t *subbranch,
- apr_pool_t *result_pool)
-{
- if (subbranch)
- return apr_psprintf(result_pool,
- " (branch %s)",
- svn_branch_get_id(subbranch, result_pool));
- return "";
-}
-
-/* Return a string suitable for appending to a displayed element name or
- * element id to indicate that BRANCH:EID is a subbranch root element.
- * Return "" if the element is not a subbranch root element.
+ * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
*/
-static const char *
-subbranch_str(svn_branch_state_t *branch,
+static void
+payload_merge(svn_element_payload_t **result_p,
+ svn_boolean_t *conflict_p,
int eid,
- apr_pool_t *result_pool)
-{
- svn_branch_state_t *subbranch
- = svn_branch_get_subbranch_at_eid(branch, eid, result_pool);
-
- return branch_str(subbranch, result_pool);
-}
-
-/* */
-static const char *
-subtree_subbranch_str(svn_branch_subtree_t *subtree,
- const char *bid,
- int eid,
- apr_pool_t *result_pool)
-{
- svn_branch_subtree_t *subbranch
- = svn_branch_subtree_get_subbranch_at_eid(subtree, eid, result_pool);
-
- if (subbranch)
- return apr_psprintf(result_pool,
- " (branch %s)",
- svn_branch_id_nest(bid, eid, result_pool));
- return "";
-}
-
-/* */
-static const char *
-el_rev_id_to_path(svn_branch_el_rev_id_t *el_rev,
- apr_pool_t *result_pool)
-{
- const char *path
- = svn_branch_get_rrpath_by_eid(el_rev->branch, el_rev->eid, result_pool);
-
- return path;
-}
-
-/* */
-static const char *
-branch_peid_name_to_path(svn_branch_state_t *to_branch,
- int to_parent_eid,
- const char *to_name,
- apr_pool_t *result_pool)
-{
- const char *path
- = svn_relpath_join(svn_branch_get_rrpath_by_eid(to_branch, to_parent_eid,
- result_pool),
- to_name, result_pool);
-
- return path;
-}
-
-/* List the elements in BRANCH, in path notation.
- *
- * List only the elements for which a relpath is known -- that is, elements
- * whose parents exist all the way up to the branch root.
- */
-static svn_error_t *
-list_branch_elements(svn_branch_state_t *branch,
- apr_pool_t *scratch_pool)
+ svn_element_payload_t *side1,
+ svn_element_payload_t *side2,
+ svn_element_payload_t *yca,
+ const merge_conflict_policy_t *policy,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- apr_hash_t *paths_to_eid = apr_hash_make(scratch_pool);
- apr_hash_index_t *hi;
- SVN_ITER_T(int) *pi;
+ svn_boolean_t conflict = FALSE;
+ svn_element_payload_t *result = NULL;
- for (hi = apr_hash_first(scratch_pool, svn_branch_get_elements(branch));
- hi; hi = apr_hash_next(hi))
+ if (yca && side1 && side2)
{
- int eid = svn_int_hash_this_key(hi);
- const char *relpath = svn_branch_get_path_by_eid(branch, eid,
- scratch_pool);
-
- if (relpath)
+ if (svn_element_payload_equal(side1, yca, scratch_pool))
{
- svn_hash_sets(paths_to_eid, relpath, apr_pmemdup(scratch_pool,
- &eid, sizeof(eid)));
+ result = side2;
}
- }
- for (SVN_HASH_ITER_SORTED(pi, paths_to_eid, svn_sort_compare_items_as_paths, scratch_pool))
- {
- const char *relpath = pi->key;
- int eid = *pi->val;
-
- notify(" %-20s%s",
- relpath[0] ? relpath : ".",
- subbranch_str(branch, eid, scratch_pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-/* */
-static int
-sort_compare_items_by_eid(const svn_sort__item_t *a,
- const svn_sort__item_t *b)
-{
- int eid_a = *(const int *)a->key;
- int eid_b = *(const int *)b->key;
-
- return eid_a - eid_b;
-}
-
-static const char *
-peid_name(const svn_element_content_t *element,
- apr_pool_t *scratch_pool)
-{
- if (element->parent_eid == -1)
- return apr_psprintf(scratch_pool, "%3s %-10s", "", ".");
-
- return apr_psprintf(scratch_pool, "%3d/%-10s",
- element->parent_eid, element->name);
-}
-
-static const char elements_by_eid_header[]
- = " eid parent-eid/name\n"
- " --- ----------/----";
-
-/* List all elements in branch BRANCH, in element notation.
- */
-static svn_error_t *
-list_branch_elements_by_eid(svn_branch_state_t *branch,
- apr_pool_t *scratch_pool)
-{
- SVN_ITER_T(svn_element_content_t) *pi;
-
- notify_v("%s", elements_by_eid_header);
- for (SVN_HASH_ITER_SORTED(pi, svn_branch_get_elements(branch),
- sort_compare_items_by_eid, scratch_pool))
- {
- int eid = *(const int *)(pi->key);
- svn_element_content_t *element = pi->val;
-
- if (element)
+ else if (svn_element_payload_equal(side2, yca, scratch_pool))
{
- notify(" e%-3d %21s%s",
- eid,
- peid_name(element, scratch_pool),
- subbranch_str(branch, eid, scratch_pool));
+ result = side1;
}
- }
-
- return SVN_NO_ERROR;
-}
-
-/* */
-static const char *
-branch_id_header_str(const char *prefix,
- apr_pool_t *result_pool)
-{
- if (the_ui_mode == UI_MODE_PATHS)
- {
- return apr_psprintf(result_pool,
- "%sbranch-id root-path\n"
- "%s--------- ---------",
- prefix, prefix);
- }
- else
- {
- return apr_psprintf(result_pool,
- "%sbranch-id branch-name root-eid\n"
- "%s--------- ----------- --------",
- prefix, prefix);
- }
-}
-
-/* Show the id and path or root-eid of BRANCH.
- */
-static const char *
-branch_id_str(svn_branch_state_t *branch,
- apr_pool_t *result_pool)
-{
- apr_pool_t *scratch_pool = result_pool;
-
- if (the_ui_mode == UI_MODE_PATHS)
- {
- return apr_psprintf(result_pool, "%-10s /%s",
- svn_branch_get_id(branch, scratch_pool),
- svn_branch_get_root_rrpath(branch, scratch_pool));
- }
- else
- {
- svn_element_content_t *outer_el = NULL;
- svn_branch_state_t *outer_branch;
- int outer_eid;
-
- svn_branch_get_outer_branch_and_eid(&outer_branch, &outer_eid,
- branch, scratch_pool);
-
- if (outer_branch)
- outer_el = svn_branch_get_element(outer_branch, outer_eid);
-
- return apr_psprintf(result_pool, "%-10s %-12s root=e%d",
- svn_branch_get_id(branch, scratch_pool),
- outer_el ? outer_el->name : "/",
- svn_branch_root_eid(branch));
- }
-}
-
-/* List the branch BRANCH.
- *
- * If WITH_ELEMENTS is true, also list the elements in it.
- */
-static svn_error_t *
-list_branch(svn_branch_state_t *branch,
- svn_boolean_t with_elements,
- apr_pool_t *scratch_pool)
-{
- notify_v(" %s", branch_id_str(branch, scratch_pool));
-
- if (with_elements)
- {
- if (the_ui_mode == UI_MODE_PATHS)
+ else if (policy->merge_double_modify
+ && svn_element_payload_equal(side1, side2, scratch_pool))
{
- SVN_ERR(list_branch_elements(branch, scratch_pool));
+ SVN_DBG(("e%d double modify: ... -> { ... | ... }",
+ eid));
+ result = side1;
}
else
{
- SVN_ERR(list_branch_elements_by_eid(branch, scratch_pool));
+ /* ### Need not conflict if can merge props and text separately. */
+
+ SVN_DBG(("e%d conflict: payload: ... -> { ... | ... }",
+ eid));
+ conflict = TRUE;
}
}
- return SVN_NO_ERROR;
+
+ *result_p = result;
+ *conflict_p = conflict;
}
-/* List all branches rooted at EID.
+/* Merge the content for one element.
+ *
+ * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
+ * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
+ * Note that *RESULT_P can be null, indicating a deletion.
+ *
+ * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
*
- * If WITH_ELEMENTS is true, also list the elements in each branch.
+ * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
*/
-static svn_error_t *
-list_branches(svn_branch_txn_t *txn,
+static void
+element_merge(svn_element_content_t **result_p,
+ element_merge3_conflict_t **conflict_p,
int eid,
- svn_boolean_t with_elements,
+ svn_element_content_t *side1,
+ svn_element_content_t *side2,
+ svn_element_content_t *yca,
+ const merge_conflict_policy_t *policy,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- const apr_array_header_t *branches;
- SVN_ITER_T(svn_branch_state_t) *bi;
- svn_boolean_t printed_header = FALSE;
-
- notify_v("%s", branch_id_header_str(" ", scratch_pool));
-
- branches = svn_branch_txn_get_branches(txn, scratch_pool);
+ svn_boolean_t same1 = svn_element_content_equal(yca, side1, scratch_pool);
+ svn_boolean_t same2 = svn_element_content_equal(yca, side2, scratch_pool);
+ svn_boolean_t conflict = FALSE;
+ svn_element_content_t *result = NULL;
- for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+ if (same1)
{
- svn_branch_state_t *branch = bi->val;
-
- if (svn_branch_root_eid(branch) != eid)
- continue;
-
- SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
- if (with_elements) /* separate branches by a blank line */
- printf("\n");
+ result = side2;
}
-
- for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+ else if (same2)
{
- svn_branch_state_t *branch = bi->val;
-
- if (! svn_branch_get_element(branch, eid)
- || svn_branch_root_eid(branch) == eid)
- continue;
-
- if (! printed_header)
- {
- if (the_ui_mode == UI_MODE_PATHS)
- notify_v("branches containing but not rooted at that element:");
- else
- notify_v("branches containing but not rooted at e%d:", eid);
- printed_header = TRUE;
- }
- SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
- if (with_elements) /* separate branches by a blank line */
- printf("\n");
+ result = side1;
}
-
- return SVN_NO_ERROR;
-}
-
-/* List all branches. If WITH_ELEMENTS is true, also list the elements
- * in each branch.
- */
-static svn_error_t *
-list_all_branches(svn_branch_txn_t *txn,
- svn_boolean_t with_elements,
- apr_pool_t *scratch_pool)
-{
- const apr_array_header_t *branches;
- SVN_ITER_T(svn_branch_state_t) *bi;
-
- branches = svn_branch_txn_get_branches(txn, scratch_pool);
-
- notify_v("branches:");
-
- for (SVN_ARRAY_ITER(bi, branches, scratch_pool))
+ else if (yca && side1 && side2)
{
- svn_branch_state_t *branch = bi->val;
-
- SVN_ERR(list_branch(branch, with_elements, bi->iterpool));
- if (with_elements) /* separate branches by a blank line */
- printf("\n");
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Options to control how strict the merge is about detecting conflicts.
- *
- * The options affect cases that, depending on the user's preference, could
- * either be considered a conflict or be merged to a deterministic result.
- *
- * The set of options is flexible and may be extended in future.
- */
-typedef struct merge_conflict_policy_t
-{
- /* Whether to merge delete-vs-delete */
- svn_boolean_t merge_double_delete;
- /* Whether to merge add-vs-add (with same parent/name/payload) */
- svn_boolean_t merge_double_add;
- /* Whether to merge reparent-vs-reparent (with same parent) */
- svn_boolean_t merge_double_reparent;
- /* Whether to merge rename-vs-rename (with same name) */
- svn_boolean_t merge_double_rename;
- /* Whether to merge modify-vs-modify (with same payload) */
- svn_boolean_t merge_double_modify;
- /* Possible additional controls: */
- /* merge (parent, name, props, text) independently or as a group */
- /* merge (parent, name) independently or as a group */
- /* merge (props, text) independently or as a group */
-} merge_conflict_policy_t;
-
-/* An element-merge conflict description.
- */
-typedef struct element_merge3_conflict_t
-{
- svn_element_content_t *yca;
- svn_element_content_t *side1;
- svn_element_content_t *side2;
-} element_merge3_conflict_t;
-
-static element_merge3_conflict_t *
-element_merge3_conflict_create(svn_element_content_t *yca,
- svn_element_content_t *side1,
- svn_element_content_t *side2,
- apr_pool_t *result_pool)
-{
- element_merge3_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
- c->yca = yca;
- c->side1 = side1;
- c->side2 = side2;
- return c;
-}
-
-/* A name-clash conflict description.
- */
-typedef struct name_clash_conflict_t
-{
- int parent_eid;
- const char *name;
- /* All EIDs that conflict with each other: hash of (eid -> irrelevant). */
- apr_hash_t *elements;
-} name_clash_conflict_t;
-
-static name_clash_conflict_t *
-name_clash_conflict_create(int parent_eid,
- const char *name,
- apr_pool_t *result_pool)
-{
- name_clash_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
- c->parent_eid = parent_eid;
- c->name = apr_pstrdup(result_pool, name);
- c->elements = apr_hash_make(result_pool);
- return c;
-}
-
-/* An orphan conflict description.
- */
-typedef struct orphan_conflict_t
-{
- svn_element_content_t *element;
-} orphan_conflict_t;
-
-static orphan_conflict_t *
-orphan_conflict_create(svn_element_content_t *element,
- apr_pool_t *result_pool)
-{
- orphan_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
- c->element = element;
- return c;
-}
-
-typedef struct conflict_storage_t
-{
- /* Single-element conflicts */
- /* (eid -> element_merge3_conflict_t) */
- apr_hash_t *single_element_conflicts;
-
- /* Name-clash conflicts */
- /* ("%{parent_eid}d/%{name}s" -> name_clash_conflict_t) */
- apr_hash_t *name_clash_conflicts;
-
- /* Orphan conflicts */
- /* (eid -> orphan_conflict_t) */
- apr_hash_t *orphan_conflicts;
-} conflict_storage_t;
-
-/* */
-static conflict_storage_t *
-conflict_storage_create(apr_pool_t *result_pool)
-{
- conflict_storage_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
- return c;
-}
-
-/* */
-static const char *
-brief_eid_and_name_or_nil(svn_element_content_t *e,
- apr_pool_t *result_pool)
-{
- return e ? apr_psprintf(result_pool, "%d/%s", e->parent_eid, e->name)
- : "<nil>";
-
- return SVN_NO_ERROR;
-}
-
-/* */
-static svn_error_t *
-display_conflicts(conflict_storage_t *conflict_storage,
- const char *prefix,
- apr_pool_t *scratch_pool)
-{
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(scratch_pool,
- conflict_storage->single_element_conflicts);
- hi; hi = apr_hash_next(hi))
- {
- int eid = svn_int_hash_this_key(hi);
- element_merge3_conflict_t *c = apr_hash_this_val(hi);
-
- printf("%ssingle-element conflict: e%d: yca=%s, side1=%s, side2=%s\n",
- prefix, eid,
- brief_eid_and_name_or_nil(c->yca, scratch_pool),
- brief_eid_and_name_or_nil(c->side1, scratch_pool),
- brief_eid_and_name_or_nil(c->side2, scratch_pool));
- }
- for (hi = apr_hash_first(scratch_pool,
- conflict_storage->name_clash_conflicts);
- hi; hi = apr_hash_next(hi))
- {
- /*const char *key = apr_hash_this_key(hi);*/
- name_clash_conflict_t *c = apr_hash_this_val(hi);
- apr_hash_index_t *hi2;
-
- printf("%sname-clash conflict: peid %d, name '%s', %d elements\n",
- prefix, c->parent_eid, c->name, apr_hash_count(c->elements));
- for (hi2 = apr_hash_first(scratch_pool, c->elements);
- hi2; hi2 = apr_hash_next(hi2))
- {
- int eid = svn_int_hash_this_key(hi2);
-
- printf("%s element %d\n", prefix, eid);
- }
- }
- for (hi = apr_hash_first(scratch_pool,
- conflict_storage->orphan_conflicts);
- hi; hi = apr_hash_next(hi))
- {
- int eid = svn_int_hash_this_key(hi);
- orphan_conflict_t *c = apr_hash_this_val(hi);
-
- printf("%sorphan conflict: element %d/%s: peid %d does not exist\n",
- prefix, eid, c->element->name, c->element->parent_eid);
- }
- return SVN_NO_ERROR;
-}
-
-/* Merge the payload for one element.
- *
- * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
- * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
- * Note that *RESULT_P can be null, indicating a deletion.
- *
- * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
- *
- * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
- */
-static void
-payload_merge(svn_element_payload_t **result_p,
- svn_boolean_t *conflict_p,
- int eid,
- svn_element_payload_t *side1,
- svn_element_payload_t *side2,
- svn_element_payload_t *yca,
- const merge_conflict_policy_t *policy,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t conflict = FALSE;
- svn_element_payload_t *result = NULL;
-
- if (yca && side1 && side2)
- {
- if (svn_element_payload_equal(side1, yca, scratch_pool))
- {
- result = side2;
- }
- else if (svn_element_payload_equal(side2, yca, scratch_pool))
- {
- result = side1;
- }
- else if (policy->merge_double_modify
- && svn_element_payload_equal(side1, side2, scratch_pool))
- {
- SVN_DBG(("e%d double modify: ... -> { ... | ... }",
- eid));
- result = side1;
- }
- else
- {
- /* ### Need not conflict if can merge props and text separately. */
-
- SVN_DBG(("e%d conflict: payload: ... -> { ... | ... }",
- eid));
- conflict = TRUE;
- }
- }
-
- *result_p = result;
- *conflict_p = conflict;
-}
-
-/* Merge the content for one element.
- *
- * If there is no conflict, set *CONFLICT_P to FALSE and *RESULT_P to the
- * merged element; otherwise set *CONFLICT_P to TRUE and *RESULT_P to NULL.
- * Note that *RESULT_P can be null, indicating a deletion.
- *
- * This handles any case where at least one of (SIDE1, SIDE2, YCA) exists.
- *
- * Allocate the result in RESULT_POOL and/or as pointers to the inputs.
- */
-static void
-element_merge(svn_element_content_t **result_p,
- element_merge3_conflict_t **conflict_p,
- int eid,
- svn_element_content_t *side1,
- svn_element_content_t *side2,
- svn_element_content_t *yca,
- const merge_conflict_policy_t *policy,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t same1 = svn_element_content_equal(yca, side1, scratch_pool);
- svn_boolean_t same2 = svn_element_content_equal(yca, side2, scratch_pool);
- svn_boolean_t conflict = FALSE;
- svn_element_content_t *result = NULL;
-
- if (same1)
- {
- result = side2;
- }
- else if (same2)
- {
- result = side1;
- }
- else if (yca && side1 && side2)
- {
- /* All three sides are different, and all exist */
- result = apr_pmemdup(result_pool, yca, sizeof(*result));
+ /* All three sides are different, and all exist */
+ result = apr_pmemdup(result_pool, yca, sizeof(*result));
/* merge the parent-eid */
if (side1->parent_eid == yca->parent_eid)
@@ -1518,3092 +438,464 @@ element_merge(svn_element_content_t **re
else if (strcmp(side2->name, yca->name) == 0)
{
result->name = side1->name;
- }
- else if (policy->merge_double_rename
- && strcmp(side1->name, side2->name) == 0)
- {
- SVN_DBG(("e%d double rename: %s -> { %s | %s }",
- eid, yca->name, side1->name, side2->name));
- result->name = side1->name;
- }
- else
- {
- SVN_DBG(("e%d conflict: name: %s -> { %s | %s }",
- eid, yca->name, side1->name, side2->name));
- conflict = TRUE;
- }
-
- /* merge the payload */
- {
- svn_boolean_t payload_conflict;
-
- payload_merge(&result->payload, &payload_conflict,
- eid, side1->payload, side2->payload, yca->payload,
- policy, result_pool, scratch_pool);
- if (payload_conflict)
- conflict = TRUE;
- }
- }
- else if (! side1 && ! side2)
- {
- /* Double delete (as we assume at least one of YCA/SIDE1/SIDE2 exists) */
- if (policy->merge_double_delete)
- {
- SVN_DBG(("e%d double delete",
- eid));
- result = side1;
- }
- else
- {
- SVN_DBG(("e%d conflict: delete vs. delete",
- eid));
- conflict = TRUE;
- }
- }
- else if (side1 && side2)
- {
- /* Double add (as we already handled the case where YCA also exists) */
- /* May be allowed for equal content of a normal element (not subbranch) */
- if (policy->merge_double_add
- && !side1->payload->is_subbranch_root
- && !side2->payload->is_subbranch_root
- && svn_element_content_equal(side1, side2, scratch_pool))
- {
- SVN_DBG(("e%d double add",
- eid));
- result = side1;
- }
- else
- {
- SVN_DBG(("e%d conflict: add vs. add (%s)",
- eid,
- svn_element_content_equal(side1, side2, scratch_pool)
- ? "same content" : "different content"));
- conflict = TRUE;
- }
- }
- else
- {
- /* The remaining cases must be delete vs. modify */
- SVN_DBG(("e%d conflict: delete vs. modify: %d -> { %d | %d }",
- eid, !!yca, !!side1, !!side2));
- conflict = TRUE;
- }
-
- *result_p = result;
- *conflict_p
- = conflict ? element_merge3_conflict_create(yca, side1, side2,
- result_pool) : NULL;
-}
-
-static svn_error_t *
-branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
- conflict_storage_t **conflict_storage_p,
- const svn_branch_el_rev_id_t *src,
- const svn_branch_el_rev_id_t *tgt,
- const svn_branch_el_rev_id_t *yca,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
-/* Merge the subbranch of {SRC, TGT, YCA} found at EID.
- */
-static svn_error_t *
-merge_subbranch(svn_branch_txn_t *edit_txn,
- const svn_branch_el_rev_id_t *src,
- const svn_branch_el_rev_id_t *tgt,
- const svn_branch_el_rev_id_t *yca,
- int eid,
- apr_pool_t *scratch_pool)
-{
- svn_branch_state_t *src_subbranch
- = svn_branch_get_subbranch_at_eid(src->branch, eid, scratch_pool);
- svn_branch_state_t *tgt_subbranch
- = svn_branch_get_subbranch_at_eid(tgt->branch, eid, scratch_pool);
- svn_branch_state_t *yca_subbranch
- = svn_branch_get_subbranch_at_eid(yca->branch, eid, scratch_pool);
- svn_branch_el_rev_id_t *subbr_src = NULL;
- svn_branch_el_rev_id_t *subbr_tgt = NULL;
- svn_branch_el_rev_id_t *subbr_yca = NULL;
-
- if (src_subbranch)
- subbr_src = svn_branch_el_rev_id_create(
- src_subbranch, svn_branch_root_eid(src_subbranch),
- src->rev, scratch_pool);
- if (tgt_subbranch)
- subbr_tgt = svn_branch_el_rev_id_create(
- tgt_subbranch, svn_branch_root_eid(tgt_subbranch),
- tgt->rev, scratch_pool);
- if (yca_subbranch)
- subbr_yca = svn_branch_el_rev_id_create(
- yca_subbranch, svn_branch_root_eid(yca_subbranch),
- yca->rev, scratch_pool);
-
- if (subbr_src && subbr_tgt && subbr_yca) /* ?edit vs. ?edit */
- {
- conflict_storage_t *conflict_storage;
-
- /* subbranch possibly changed in source => merge */
- SVN_ERR(branch_merge_subtree_r(edit_txn,
- &conflict_storage,
- subbr_src, subbr_tgt, subbr_yca,
- scratch_pool, scratch_pool));
- /* ### store this branch's conflict_storage somewhere ... */
- }
- else if (subbr_src && subbr_yca) /* ?edit vs. delete */
- {
- /* ### possible conflict (edit vs. delete) */
- }
- else if (subbr_tgt && subbr_yca) /* delete vs. ?edit */
- {
- /* ### possible conflict (delete vs. edit) */
- }
- else if (subbr_src && subbr_tgt) /* double add */
- {
- /* ### conflict */
- }
- else if (subbr_src) /* added on source branch */
- {
- const char *new_branch_id
- = svn_branch_id_nest(svn_branch_get_id(tgt->branch, scratch_pool),
- eid, scratch_pool);
- svn_branch_rev_bid_eid_t *from
- = svn_branch_rev_bid_eid_create(src_subbranch->txn->rev,
- svn_branch_get_id(src_subbranch,
- scratch_pool),
- svn_branch_root_eid(src_subbranch),
- scratch_pool);
-
- SVN_ERR(svn_branch_txn_branch(edit_txn, NULL /*new_branch_id_p*/, from,
- new_branch_id, scratch_pool, scratch_pool));
- }
- else if (subbr_tgt) /* added on target branch */
- {
- /* nothing to do */
- }
- else if (subbr_yca) /* double delete */
- {
- /* ### conflict? policy option? */
- }
-
- return SVN_NO_ERROR;
-}
-
-/* */
-static int
-sort_compare_items_by_peid_and_name(const svn_sort__item_t *a,
- const svn_sort__item_t *b)
-{
- svn_element_content_t *element_a = a->value;
- svn_element_content_t *element_b = b->value;
-
- if (element_a->parent_eid != element_b->parent_eid)
- return element_a->parent_eid - element_b->parent_eid;
- return strcmp(element_a->name, element_b->name);
-}
-
-/* Return all (key -> name_clash_conflict_t) name clash conflicts in BRANCH.
- */
-static svn_error_t *
-detect_clashes(apr_hash_t **clashes_p,
- svn_branch_state_t *branch,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *clashes = apr_hash_make(result_pool);
- SVN_ITER_T(svn_element_content_t) *pi;
- int prev_eid = -1;
- svn_element_content_t *prev_element = NULL;
-
- for (SVN_HASH_ITER_SORTED(pi, svn_branch_get_elements(branch),
- sort_compare_items_by_peid_and_name, scratch_pool))
- {
- int eid = *(const int *)(pi->key);
- svn_element_content_t *element = pi->val;
-
- if (prev_element
- && element->parent_eid == prev_element->parent_eid
- && strcmp(element->name, prev_element->name) == 0)
- {
- const char *key = apr_psprintf(result_pool, "%d/%s",
- element->parent_eid, element->name);
- name_clash_conflict_t *c;
-
- c = svn_hash_gets(clashes, key);
- if (!c)
- {
- c = name_clash_conflict_create(
- element->parent_eid, element->name,
- result_pool);
- svn_hash_sets(clashes, key, c);
- }
- svn_int_hash_set(c->elements, eid, &c);
- svn_int_hash_set(c->elements, prev_eid, &c);
- }
- prev_eid = eid;
- prev_element = element;
- }
-
- *clashes_p = clashes;
- return SVN_NO_ERROR;
-}
-
-/* Return all (eid -> orphan_conflict_t) orphan conflicts in BRANCH.
- */
-static svn_error_t *
-detect_orphans(apr_hash_t **orphans_p,
- svn_branch_state_t *branch,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *orphans = apr_hash_make(result_pool);
- SVN_ITER_T(svn_element_content_t) *pi;
- const svn_element_tree_t *elements = svn_branch_get_element_tree(branch);
-
- for (SVN_HASH_ITER(pi, scratch_pool, elements->e_map))
- {
- int eid = *(const int *)(pi->key);
- svn_element_content_t *element = pi->val;
-
- if (eid != elements->root_eid
- && ! svn_element_tree_get(elements, element->parent_eid))
- {
- orphan_conflict_t *c;
-
- c = orphan_conflict_create(element, result_pool);
- svn_int_hash_set(orphans, eid, c);
-
- notify(" orphan: %d/%s: peid %d does not exist",
- eid, element->name, element->parent_eid);
- }
- }
-
- *orphans_p = orphans;
- return SVN_NO_ERROR;
-}
-
-/* Merge ...
- *
- * Merge any sub-branches in the same way, recursively.
- */
-static svn_error_t *
-branch_merge_subtree_r(svn_branch_txn_t *edit_txn,
- conflict_storage_t **conflict_storage_p,
- const svn_branch_el_rev_id_t *src,
- const svn_branch_el_rev_id_t *tgt,
- const svn_branch_el_rev_id_t *yca,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_branch_subtree_t *s_src, *s_tgt, *s_yca;
- apr_hash_t *diff_yca_src, *diff_yca_tgt;
- apr_hash_t *e_conflicts = apr_hash_make(scratch_pool);
- conflict_storage_t *conflict_storage = conflict_storage_create(result_pool);
- SVN_ITER_T(svn_element_content_t *) *pi;
- apr_hash_t *all_elements;
- const merge_conflict_policy_t policy = { TRUE, TRUE, TRUE, TRUE, TRUE };
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
- SVN_ERR_ASSERT(src->eid == tgt->eid);
- SVN_ERR_ASSERT(src->eid == yca->eid);
-
- SVN_DBG(("merge src: r%2ld %s e%3d",
- src->rev,
- svn_branch_get_id(src->branch, scratch_pool), src->eid));
- SVN_DBG(("merge tgt: r%2ld %s e%3d",
- tgt->rev,
- svn_branch_get_id(tgt->branch, scratch_pool), tgt->eid));
- SVN_DBG(("merge yca: r%2ld %s e%3d",
- yca->rev,
- svn_branch_get_id(yca->branch, scratch_pool), yca->eid));
-
- notify_v("merging into branch %s",
- svn_branch_get_id(tgt->branch, scratch_pool));
- /*
- for (eid, diff1) in element_differences(YCA, FROM):
- diff2 = element_diff(eid, YCA, TO)
- if diff1 and diff2:
- result := element_merge(diff1, diff2)
- elif diff1:
- result := diff1.right
- # else no change
- */
- s_src = svn_branch_get_subtree(src->branch, src->eid, scratch_pool);
- s_tgt = svn_branch_get_subtree(tgt->branch, tgt->eid, scratch_pool);
- s_yca = svn_branch_get_subtree(yca->branch, yca->eid, scratch_pool);
- SVN_ERR(element_differences(&diff_yca_src,
- s_yca->tree, s_src->tree,
- scratch_pool, scratch_pool));
- /* ### We only need to query for YCA:TO differences in elements that are
- different in YCA:FROM, but right now we ask for all differences. */
- SVN_ERR(element_differences(&diff_yca_tgt,
- s_yca->tree, s_tgt->tree,
- scratch_pool, scratch_pool));
-
- all_elements = apr_hash_overlay(scratch_pool,
- svn_branch_get_elements(src->branch),
- svn_branch_get_elements(tgt->branch));
- all_elements = apr_hash_overlay(scratch_pool,
- svn_branch_get_elements(yca->branch),
- all_elements);
- for (SVN_HASH_ITER_SORTED(pi, all_elements,
- sort_compare_items_by_eid, scratch_pool))
- {
- int eid = *(const int *)(pi->key);
- svn_element_content_t **e_yca_src
- = svn_int_hash_get(diff_yca_src, eid);
- svn_element_content_t **e_yca_tgt
- = svn_int_hash_get(diff_yca_tgt, eid);
- svn_element_content_t *e_yca;
- svn_element_content_t *e_src;
- svn_element_content_t *e_tgt;
- svn_element_content_t *result;
- element_merge3_conflict_t *conflict;
-
- svn_pool_clear(iterpool);
-
- /* If an element hasn't changed in the source branch, there is
- no need to do anything with it in the target branch. We could
- use element_merge() for any case where at least one of (SRC,
- TGT, YCA) exists, but we choose to skip it when SRC == YCA. */
- if (! e_yca_src)
- {
- /* Still need to merge any subbranch linked to this element.
- There were no changes to the link element but that doesn't
- mean there were no changes to the linked branch. */
- SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
-
- continue;
- }
-
- e_yca = e_yca_src[0];
- e_src = e_yca_src[1];
- e_tgt = e_yca_tgt ? e_yca_tgt[1] : e_yca_src[0];
-
- element_merge(&result, &conflict,
- eid, e_src, e_tgt, e_yca,
- &policy,
- scratch_pool, scratch_pool);
-
- if (conflict)
- {
- notify_v("! e%d <conflict>", eid);
- svn_int_hash_set(e_conflicts, eid, conflict);
- }
- else if (e_tgt && result)
- {
- notify_v("M/V e%d %s%s",
- eid, result->name,
- subbranch_str(tgt->branch, eid, iterpool));
-
- SVN_ERR(svn_branch_state_alter_one(tgt->branch, eid,
- result->parent_eid, result->name,
- result->payload, iterpool));
-
- SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
- }
- else if (e_tgt)
- {
- notify_v("D e%d %s%s",
- eid, e_yca->name,
- subbranch_str(yca->branch, eid, iterpool));
- SVN_ERR(svn_branch_state_delete_one(tgt->branch, eid, iterpool));
-
- /* ### If this is a subbranch-root element being deleted, shouldn't
- we see if there were any changes to be merged in the subbranch,
- and raise a delete-vs-edit conflict if so? */
- }
- else if (result)
- {
- notify_v("A e%d %s%s",
- eid, result->name,
- subbranch_str(src->branch, eid, iterpool));
-
- /* In BRANCH, create an instance of the element EID with new content.
- *
- * Translated to old language, this means create a new node-copy
- * copied (branched) from the source-right version of the merge
- * (which is not specified here, but will need to be),
- * which may be in this branch or in another branch.
- */
- SVN_ERR(svn_branch_state_alter_one(tgt->branch, eid,
- result->parent_eid, result->name,
- result->payload, iterpool));
-
- SVN_ERR(merge_subbranch(edit_txn, src, tgt, yca, eid, iterpool));
- }
- }
- svn_pool_destroy(iterpool);
-
- /* Detect clashes.
- ### TODO: Detect clashes, cycles and orphans; and report full conflict
- info (including the relevant incoming changes) for each
- kind of conflict. If there are no conflicts, flatten the
- merge result into a tree. */
- conflict_storage->single_element_conflicts = e_conflicts;
- SVN_ERR(detect_clashes(&conflict_storage->name_clash_conflicts,
- tgt->branch,
- result_pool, scratch_pool));
- SVN_ERR(detect_orphans(&conflict_storage->orphan_conflicts,
- tgt->branch,
- result_pool, scratch_pool));
-
- notify_v("merging into branch %s -- finished",
- svn_branch_get_id(tgt->branch, scratch_pool));
-
- *conflict_storage_p = conflict_storage;
- return SVN_NO_ERROR;
-}
-
-/* Merge SRC into TGT, using the common ancestor YCA.
- *
- * Merge the two sets of changes: YCA -> SRC and YCA -> TGT, applying
- * the result to the transaction at TGT.
- *
- * If conflicts arise, just fail.
- *
- * SRC, TGT and YCA must be existing and corresponding (same EID) elements.
- *
- * None of SRC, TGT and YCA is a subbranch root element.
- *
- * Nested subbranches will also be merged.
- */
-static svn_error_t *
-svn_branch_merge(svn_branch_txn_t *edit_txn,
- conflict_storage_t **conflict_storage_p,
- svn_branch_el_rev_id_t *src,
- svn_branch_el_rev_id_t *tgt,
- svn_branch_el_rev_id_t *yca,
- apr_pool_t *scratch_pool)
-{
- /*SVN_ERR(verify_exists_in_branch(from, scratch_pool));*/
- /*SVN_ERR(verify_exists_in_branch(to, scratch_pool));*/
- /*SVN_ERR(verify_exists_in_branch(yca, scratch_pool));*/
- if (src->eid != tgt->eid || src->eid != yca->eid)
- return svn_error_createf(SVN_ERR_BRANCHING, NULL,
- _("Merge branches must all be same element "
- "(from: e%d, to: e%d, yca: e%d)"),
- src->eid, tgt->eid, yca->eid);
- /*SVN_ERR(verify_not_subbranch_root(from, scratch_pool));*/
- /*SVN_ERR(verify_not_subbranch_root(to, scratch_pool));*/
- /*SVN_ERR(verify_not_subbranch_root(yca, scratch_pool));*/
-
- SVN_ERR(branch_merge_subtree_r(edit_txn,
- conflict_storage_p,
- src, tgt, yca,
- scratch_pool, scratch_pool));
-
- return SVN_NO_ERROR;
-}
-
-/* Switch the WC to revision BASE_REVISION (SVN_INVALID_REVNUM means HEAD)
- * and branch BRANCH_ID.
- *
- * Merge any changes in the existing txn into the new txn.
- */
-static svn_error_t *
-do_switch(svnmover_wc_t *wc,
- svn_revnum_t revision,
- svn_branch_state_t *target_branch,
- apr_pool_t *scratch_pool)
-{
- const char *target_branch_id
- = svn_branch_get_id(target_branch, scratch_pool);
- /* Keep hold of the previous WC txn */
- svn_branch_state_t *previous_base_br = wc->base->branch;
- svn_branch_state_t *previous_working_br = wc->working->branch;
- svn_boolean_t has_local_changes = TRUE /* ### wc_has_local_changes(wc) */;
-
- if (has_local_changes
- && svn_branch_root_eid(target_branch)
- != svn_branch_root_eid(previous_base_br))
- {
- return svn_error_createf(SVN_ERR_BRANCHING, NULL,
- _("Cannot switch to branch '%s' and preserve "
- "local changes as the new root element e%d "
- "is not related to the old root element e%d"),
- target_branch_id,
- svn_branch_root_eid(target_branch),
- svn_branch_root_eid(previous_base_br));
- }
-
- /* Complete the old edit drive into the 'WC' txn */
- SVN_ERR(svn_branch_txn_sequence_point(wc->edit_txn, scratch_pool));
-
- /* Check out a new WC, re-using the same data object */
- SVN_ERR(wc_checkout(wc, revision, target_branch_id, scratch_pool));
-
- if (has_local_changes)
- {
- svn_branch_el_rev_id_t *yca, *src, *tgt;
- conflict_storage_t *conflicts;
-
- /* Merge changes from the old into the new WC */
- yca = svn_branch_el_rev_id_create(previous_base_br,
- svn_branch_root_eid(previous_base_br),
- previous_base_br->txn->rev,
- scratch_pool);
- src = svn_branch_el_rev_id_create(previous_working_br,
- svn_branch_root_eid(previous_working_br),
- SVN_INVALID_REVNUM, scratch_pool);
- tgt = svn_branch_el_rev_id_create(wc->working->branch,
- svn_branch_root_eid(wc->working->branch),
- SVN_INVALID_REVNUM, scratch_pool);
- SVN_ERR(svn_branch_merge(wc->edit_txn, &conflicts,
- src, tgt, yca, scratch_pool));
-
- if (apr_hash_count(conflicts->single_element_conflicts)
- || apr_hash_count(conflicts->name_clash_conflicts)
- || apr_hash_count(conflicts->orphan_conflicts))
- {
- SVN_ERR(display_conflicts(conflicts, "switch: ", scratch_pool));
- return svn_error_createf(
- SVN_ERR_BRANCHING, NULL,
- _("Switch failed because of conflicts: "
- "%d single-element conflicts, "
- "%d name-clash conflicts, "
- "%d orphan conflicts"),
- apr_hash_count(conflicts->single_element_conflicts),
- apr_hash_count(conflicts->name_clash_conflicts),
- apr_hash_count(conflicts->orphan_conflicts));
- }
- else
- {
- SVN_DBG(("Switch completed: no conflicts"));
- }
-
- /* ### TODO: If the merge raises conflicts, either revert to the
- pre-update state or store and handle the conflicts. Currently
- this just leaves the merge partially done and raises an error. */
- }
-
- return SVN_NO_ERROR;
-}
-
-/* */
-typedef struct diff_item_t
-{
- int eid;
- svn_element_content_t *e0, *e1;
- const char *relpath0, *relpath1;
- svn_boolean_t modified, reparented, renamed;
-} diff_item_t;
-
-/* Return differences between branch subtrees S_LEFT and S_RIGHT.
- *
- * Set *DIFF_CHANGES to a hash of (eid -> diff_item_t).
- *
- * ### This requires 'subtrees' only in order to produce the 'relpath'
- * fields in the output. Other than that, it would work with arbitrary
- * sets of elements.
- */
-static svn_error_t *
-subtree_diff(apr_hash_t **diff_changes,
- svn_branch_subtree_t *s_left,
- svn_branch_subtree_t *s_right,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *diff_left_right;
- apr_hash_index_t *hi;
-
- *diff_changes = apr_hash_make(result_pool);
-
- SVN_ERR(element_differences(&diff_left_right,
- s_left->tree, s_right->tree,
- result_pool, scratch_pool));
-
- for (hi = apr_hash_first(scratch_pool, diff_left_right);
- hi; hi = apr_hash_next(hi))
- {
- int eid = svn_int_hash_this_key(hi);
- svn_element_content_t **e_pair = apr_hash_this_val(hi);
- svn_element_content_t *e0 = e_pair[0], *e1 = e_pair[1];
-
- if (e0 || e1)
- {
- diff_item_t *item = apr_palloc(result_pool, sizeof(*item));
-
- item->eid = eid;
- item->e0 = e0;
- item->e1 = e1;
- item->relpath0 = e0 ? svn_element_tree_get_path_by_eid(
- s_left->tree, eid, result_pool) : NULL;
- item->relpath1 = e1 ? svn_element_tree_get_path_by_eid(
- s_right->tree, eid, result_pool) : NULL;
- item->reparented = (e0 && e1 && e0->parent_eid != e1->parent_eid);
- item->renamed = (e0 && e1 && strcmp(e0->name, e1->name) != 0);
-
- svn_int_hash_set(*diff_changes, eid, item);
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Find the relative order of diff items A and B, according to the
- * "major path" of each. The major path means its right-hand relpath, if
- * it exists on the right-hand side of the diff, else its left-hand relpath.
- *
- * Return negative/zero/positive when A sorts before/equal-to/after B.
- */
-static int
-diff_ordering_major_paths(const struct svn_sort__item_t *a,
- const struct svn_sort__item_t *b)
-{
- const diff_item_t *item_a = a->value, *item_b = b->value;
- int deleted_a = (item_a->e0 && ! item_a->e1);
- int deleted_b = (item_b->e0 && ! item_b->e1);
- const char *major_path_a = (item_a->e1 ? item_a->relpath1 : item_a->relpath0);
- const char *major_path_b = (item_b->e1 ? item_b->relpath1 : item_b->relpath0);
-
- /* Sort deleted items before all others */
- if (deleted_a != deleted_b)
- return deleted_b - deleted_a;
-
- /* Sort by path */
- return svn_path_compare_paths(major_path_a, major_path_b);
-}
-
-/* Display differences between subtrees LEFT and RIGHT, which are subtrees
- * of branches LEFT_BID and RIGHT_BID respectively.
- *
- * Use EDITOR to fetch content when needed.
- *
- * Write a line containing HEADER before any other output, if it is not
- * null. Write PREFIX at the start of each line of output, including any
- * header line. PREFIX and HEADER should contain no end-of-line characters.
- *
- * The output refers to paths or to elements according to THE_UI_MODE.
- */
-static svn_error_t *
-show_subtree_diff(svn_branch_subtree_t *left,
- const char *left_bid,
- svn_branch_subtree_t *right,
- const char *right_bid,
- const char *prefix,
- const char *header,
- apr_pool_t *scratch_pool)
-{
- apr_hash_t *diff_changes;
- SVN_ITER_T(diff_item_t) *ai;
-
- SVN_ERR_ASSERT(left && left->tree->root_eid >= 0
- && right && right->tree->root_eid >= 0);
-
- SVN_ERR(subtree_diff(&diff_changes, left, right,
- scratch_pool, scratch_pool));
-
- if (header && apr_hash_count(diff_changes))
- notify("%s%s", prefix, header);
-
- for (SVN_HASH_ITER_SORTED(ai, diff_changes,
- (the_ui_mode == UI_MODE_EIDS)
- ? sort_compare_items_by_eid
- : diff_ordering_major_paths,
- scratch_pool))
- {
- diff_item_t *item = ai->val;
- svn_element_content_t *e0 = item->e0, *e1 = item->e1;
- char status_mod = (e0 && e1) ? 'M' : e0 ? 'D' : 'A';
-
- /* For a deleted element whose parent was also deleted, mark it is
- less interesting, somehow. (Or we could omit it entirely.) */
- if (status_mod == 'D')
- {
- diff_item_t *parent_item
- = svn_int_hash_get(diff_changes, e0->parent_eid);
-
- if (parent_item && ! parent_item->e1)
- status_mod = 'd';
- }
-
- if (the_ui_mode == UI_MODE_PATHS)
- {
- const char *major_path = (e1 ? item->relpath1 : item->relpath0);
- const char *from = "";
-
- if (item->reparented || item->renamed)
- {
- if (! item->reparented)
- from = apr_psprintf(scratch_pool,
- " (renamed from .../%s)",
- e0->name);
- else if (! item->renamed)
- from = apr_psprintf(scratch_pool,
- " (moved from %s/...)",
- svn_relpath_dirname(item->relpath0,
- scratch_pool));
- else
- from = apr_psprintf(scratch_pool,
- " (moved+renamed from %s)",
- item->relpath0);
- }
- notify("%s%c%c%c %s%s%s",
- prefix,
- status_mod,
- item->reparented ? 'v' : ' ', item->renamed ? 'r' : ' ',
- major_path,
- subtree_subbranch_str(e0 ? left : right,
- e0 ? left_bid : right_bid,
- item->eid, scratch_pool),
- from);
- }
- else
- {
- notify("%s%c%c%c e%-3d %s%s%s%s%s",
- prefix,
- status_mod,
- item->reparented ? 'v' : ' ', item->renamed ? 'r' : ' ',
- item->eid,
- e1 ? peid_name(e1, scratch_pool) : "",
- subtree_subbranch_str(e0 ? left : right,
- e0 ? left_bid : right_bid,
- item->eid, scratch_pool),
- e0 && e1 ? " (from " : "",
- e0 ? peid_name(e0, scratch_pool) : "",
- e0 && e1 ? ")" : "");
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-typedef svn_error_t *
-svn_branch_diff_func_t(svn_branch_subtree_t *left,
- const char *left_bid,
- svn_branch_subtree_t *right,
- const char *right_bid,
- const char *prefix,
- const char *header,
- apr_pool_t *scratch_pool);
-
-/* Display differences between subtrees LEFT and RIGHT.
- *
- * Recurse into sub-branches.
- */
-static svn_error_t *
-subtree_diff_r(svn_branch_subtree_t *left,
- svn_revnum_t left_rev,
- const char *left_bid,
- const char *left_rrpath,
- svn_branch_subtree_t *right,
- svn_revnum_t right_rev,
- const char *right_bid,
- const char *right_rrpath,
- svn_branch_diff_func_t diff_func,
- const char *prefix,
- apr_pool_t *scratch_pool)
-{
- const char *left_str
- = left ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
- left_rev, left_bid, left->tree->root_eid, left_rrpath)
- : NULL;
- const char *right_str
- = right ? apr_psprintf(scratch_pool, "r%ld:%s:e%d at /%s",
- right_rev, right_bid, right->tree->root_eid, right_rrpath)
- : NULL;
- const char *header;
- apr_hash_t *subbranches_l, *subbranches_r, *subbranches_all;
- apr_hash_index_t *hi;
-
- SVN_DBG(("subtree_diff_r: l='%s' r='%s'",
- left ? left_rrpath : "<nil>",
- right ? right_rrpath : "<nil>"));
-
- if (!left)
- {
- header = apr_psprintf(scratch_pool,
- "--- added branch %s",
- right_str);
- notify("%s%s", prefix, header);
- }
- else if (!right)
- {
- header = apr_psprintf(scratch_pool,
- "--- deleted branch %s",
- left_str);
- notify("%s%s", prefix, header);
- }
- else
- {
- if (strcmp(left_str, right_str) == 0)
- {
- header = apr_psprintf(
- scratch_pool, "--- diff branch %s",
- left_str);
- }
- else
- {
- header = apr_psprintf(
- scratch_pool, "--- diff branch %s : %s",
- left_str, right_str);
- }
- SVN_ERR(diff_func(left, left_bid, right, right_bid,
- prefix, header,
- scratch_pool));
- }
-
- /* recurse into each subbranch that exists in LEFT and/or in RIGHT */
- subbranches_l = left ? left->subbranches : apr_hash_make(scratch_pool);
- subbranches_r = right ? right->subbranches : apr_hash_make(scratch_pool);
- subbranches_all = apr_hash_overlay(scratch_pool,
- subbranches_l, subbranches_r);
-
- for (hi = apr_hash_first(scratch_pool, subbranches_all);
- hi; hi = apr_hash_next(hi))
- {
- int e = svn_int_hash_this_key(hi);
- svn_branch_subtree_t *sub_left = NULL, *sub_right = NULL;
- const char *sub_left_bid = NULL, *sub_right_bid = NULL;
- const char *sub_left_rrpath = NULL, *sub_right_rrpath = NULL;
-
- /* recurse */
- if (left)
- {
- sub_left = svn_branch_subtree_get_subbranch_at_eid(left, e,
- scratch_pool);
- if (sub_left)
- {
- const char *relpath
- = svn_element_tree_get_path_by_eid(left->tree, e, scratch_pool);
-
- sub_left_bid = svn_branch_id_nest(left_bid, e, scratch_pool);
- sub_left_rrpath = svn_relpath_join(left_rrpath, relpath,
- scratch_pool);
- }
- }
- if (right)
- {
- sub_right = svn_branch_subtree_get_subbranch_at_eid(right, e,
- scratch_pool);
- if (sub_right)
- {
[... 2596 lines stripped ...]