You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/06/27 17:13:42 UTC
svn commit: r1354571 [5/37] - in /subversion/branches/master-passphrase: ./
build/ build/ac-macros/ build/generator/ build/generator/templates/
build/win32/ contrib/client-side/emacs/ contrib/server-side/ notes/
notes/api-errata/1.8/ notes/directory-in...
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/commit_util.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/commit_util.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/commit_util.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/commit_util.c Wed Jun 27 15:12:37 2012
@@ -182,7 +182,11 @@ fixup_commit_error(const char *local_abs
/* Add a new commit candidate (described by all parameters except
`COMMITTABLES') to the COMMITTABLES hash. All of the commit item's
- members are allocated out of RESULT_POOL. */
+ members are allocated out of RESULT_POOL.
+
+ If the state flag specifies that a lock must be used, store the token in LOCK
+ in lock_tokens.
+ */
static svn_error_t *
add_committable(svn_client__committables_t *committables,
const char *local_abspath,
@@ -193,6 +197,8 @@ add_committable(svn_client__committables
const char *copyfrom_relpath,
svn_revnum_t copyfrom_rev,
apr_byte_t state_flags,
+ apr_hash_t *lock_tokens,
+ const svn_lock_t *lock,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -248,6 +254,16 @@ add_committable(svn_client__committables
APR_HASH_KEY_STRING,
new_item);
+ if (lock
+ && lock_tokens
+ && (state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN))
+ {
+ apr_hash_set(lock_tokens,
+ new_item->url,
+ APR_HASH_KEY_STRING,
+ apr_pstrdup(result_pool, lock->token));
+ }
+
return SVN_NO_ERROR;
}
@@ -262,80 +278,6 @@ look_up_committable(svn_client__committa
apr_hash_get(committables->by_path, path, APR_HASH_KEY_STRING);
}
-/* Helper for harvest_committables().
- * If ENTRY is a dir, return an SVN_ERR_WC_FOUND_CONFLICT error when
- * encountering a tree-conflicted immediate child node. However, do
- * not consider immediate children that are outside the bounds of DEPTH.
- *
- * TODO ### WC_CTX and LOCAL_ABSPATH ...
- * ENTRY, DEPTH, CHANGELISTS and POOL are the same ones
- * originally received by harvest_committables().
- *
- * Tree-conflicts information is stored in the victim's immediate parent.
- * In some cases of an absent tree-conflicted victim, the tree-conflict
- * information in its parent dir is the only indication that the node
- * is under version control. This function is necessary for this
- * particular case. In all other cases, this simply bails out a little
- * bit earlier. */
-static svn_error_t *
-bail_on_tree_conflicted_children(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_node_kind_t kind,
- svn_depth_t depth,
- apr_hash_t *changelists,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *pool)
-{
- apr_hash_t *conflicts;
- apr_hash_index_t *hi;
-
- if ((depth == svn_depth_empty)
- || (kind != svn_node_dir))
- /* There can't possibly be tree-conflicts information here. */
- return SVN_NO_ERROR;
-
- SVN_ERR(svn_wc__get_all_tree_conflicts(&conflicts, wc_ctx, local_abspath,
- pool, pool));
- if (!conflicts)
- return SVN_NO_ERROR;
-
- for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
- {
- const svn_wc_conflict_description2_t *conflict =
- svn__apr_hash_index_val(hi);
-
- if ((conflict->node_kind == svn_node_dir) &&
- (depth == svn_depth_files))
- continue;
-
- /* So we've encountered a conflict that is included in DEPTH.
- Bail out. But if there are CHANGELISTS, avoid bailing out
- on an item that doesn't match the CHANGELISTS. */
- if (!svn_wc__changelist_match(wc_ctx, local_abspath, changelists, pool))
- continue;
-
- /* At this point, a conflict was found, and either there were no
- changelists, or the changelists matched. Bail out already! */
-
- if (notify_func != NULL)
- {
- notify_func(notify_baton,
- svn_wc_create_notify(local_abspath,
- svn_wc_notify_failed_conflict,
- pool),
- pool);
- }
-
- return svn_error_createf(
- SVN_ERR_WC_FOUND_CONFLICT, NULL,
- _("Aborting commit: '%s' remains in conflict"),
- svn_dirent_local_style(conflict->local_abspath, pool));
- }
-
- return SVN_NO_ERROR;
-}
-
/* Helper function for svn_client__harvest_committables().
* Determine whether we are within a tree-conflicted subtree of the
* working copy and return an SVN_ERR_WC_FOUND_CONFLICT error if so. */
@@ -426,19 +368,43 @@ bail_on_tree_conflicted_ancestor(svn_wc_
Any items added to COMMITTABLES are allocated from the COMITTABLES
hash pool, not POOL. SCRATCH_POOL is used for temporary allocations. */
+
+struct harvest_baton
+{
+ /* Static data */
+ const char *root_abspath;
+ svn_client__committables_t *committables;
+ apr_hash_t *lock_tokens;
+ const char *commit_relpath; /* Valid for the harvest root */
+ svn_depth_t depth;
+ svn_boolean_t just_locked;
+ apr_hash_t *changelists;
+ apr_hash_t *danglers;
+ svn_client__check_url_kind_t check_url_func;
+ void *check_url_baton;
+ svn_wc_notify_func2_t notify_func;
+ void *notify_baton;
+ svn_wc_context_t *wc_ctx;
+ apr_pool_t *result_pool;
+
+ /* Harvester state */
+ const char *skip_below_abspath; /* If non-NULL, skip everything below */
+};
+
+static svn_error_t *
+harvest_status_callback(void *status_baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool);
+
static svn_error_t *
harvest_committables(const char *local_abspath,
svn_client__committables_t *committables,
apr_hash_t *lock_tokens,
- const char *repos_root_url,
- const char *commit_relpath,
- svn_boolean_t copy_mode_root,
+ const char *copy_mode_relpath,
svn_depth_t depth,
svn_boolean_t just_locked,
apr_hash_t *changelists,
- svn_boolean_t skip_files,
- svn_boolean_t skip_dirs,
- svn_boolean_t is_explicit_target,
apr_hash_t *danglers,
svn_client__check_url_kind_t check_url_func,
void *check_url_baton,
@@ -446,38 +412,221 @@ harvest_committables(const char *local_a
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
- svn_client_ctx_t *ctx,
+ svn_wc_context_t *wc_ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_wc_context_t *wc_ctx = ctx->wc_ctx;
- svn_boolean_t text_mod = FALSE;
- svn_boolean_t prop_mod = FALSE;
+ struct harvest_baton baton;
+
+ SVN_ERR_ASSERT((just_locked && lock_tokens) || !just_locked);
+
+ baton.root_abspath = local_abspath;
+ baton.committables = committables;
+ baton.lock_tokens = lock_tokens;
+ baton.commit_relpath = copy_mode_relpath;
+ baton.depth = depth;
+ baton.just_locked = just_locked;
+ baton.changelists = changelists;
+ baton.danglers = danglers;
+ baton.check_url_func = check_url_func;
+ baton.check_url_baton = check_url_baton;
+ baton.notify_func = notify_func;
+ baton.notify_baton = notify_baton;
+ baton.wc_ctx = wc_ctx;
+ baton.result_pool = result_pool;
+
+ baton.skip_below_abspath = NULL;
+
+ SVN_ERR(svn_wc_walk_status(wc_ctx,
+ local_abspath,
+ depth,
+ (copy_mode_relpath != NULL) /* get_all */,
+ FALSE /* no_ignore */,
+ FALSE /* ignore_text_mods */,
+ NULL /* ignore_patterns */,
+ harvest_status_callback,
+ &baton,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+harvest_not_present_for_copy(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ svn_client__committables_t *committables,
+ const char *repos_root_url,
+ const char *commit_relpath,
+ svn_client__check_url_kind_t check_url_func,
+ void *check_url_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const apr_array_header_t *children;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int i;
+
+ /* A function to retrieve not present children would be nice to have */
+ SVN_ERR(svn_wc__node_get_children_of_working_node(
+ &children, wc_ctx, local_abspath, TRUE,
+ scratch_pool, iterpool));
+
+ for (i = 0; i < children->nelts; i++)
+ {
+ const char *this_abspath = APR_ARRAY_IDX(children, i, const char *);
+ const char *name = svn_dirent_basename(this_abspath, NULL);
+ const char *this_commit_relpath;
+ svn_boolean_t not_present;
+ svn_node_kind_t kind;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc__node_is_status_not_present(¬_present, wc_ctx,
+ this_abspath, scratch_pool));
+
+ if (!not_present)
+ continue;
+
+ if (commit_relpath == NULL)
+ this_commit_relpath = NULL;
+ else
+ this_commit_relpath = svn_relpath_join(commit_relpath, name,
+ iterpool);
+
+ /* We should check if we should really add a delete operation */
+ if (check_url_func)
+ {
+ svn_revnum_t parent_rev;
+ const char *parent_repos_relpath;
+ const char *parent_repos_root_url;
+ const char *node_url;
+
+ /* Determine from what parent we would be the deleted child */
+ SVN_ERR(svn_wc__node_get_origin(
+ NULL, &parent_rev, &parent_repos_relpath,
+ &parent_repos_root_url, NULL, NULL,
+ wc_ctx,
+ svn_dirent_dirname(this_abspath,
+ scratch_pool),
+ FALSE, scratch_pool, scratch_pool));
+
+ node_url = svn_path_url_add_component2(
+ svn_path_url_add_component2(parent_repos_root_url,
+ parent_repos_relpath,
+ scratch_pool),
+ svn_dirent_basename(this_abspath, NULL),
+ iterpool);
+
+ SVN_ERR(check_url_func(check_url_baton, &kind,
+ node_url, parent_rev, iterpool));
+
+ if (kind == svn_node_none)
+ continue; /* This node can't be deleted */
+ }
+ else
+ SVN_ERR(svn_wc_read_kind(&kind, wc_ctx, this_abspath, TRUE,
+ scratch_pool));
+
+ SVN_ERR(add_committable(committables, this_abspath, kind,
+ repos_root_url,
+ this_commit_relpath,
+ SVN_INVALID_REVNUM,
+ NULL /* copyfrom_relpath */,
+ SVN_INVALID_REVNUM /* copyfrom_rev */,
+ SVN_CLIENT_COMMIT_ITEM_DELETE,
+ NULL, NULL,
+ result_pool, scratch_pool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_wc_status_func4_t */
+static svn_error_t *
+harvest_status_callback(void *status_baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
apr_byte_t state_flags = 0;
- svn_node_kind_t working_kind;
- svn_node_kind_t db_kind;
- const char *node_relpath;
- const char *node_lock_token;
svn_revnum_t node_rev;
const char *cf_relpath = NULL;
svn_revnum_t cf_rev = SVN_INVALID_REVNUM;
svn_boolean_t matches_changelists;
- svn_boolean_t is_special;
svn_boolean_t is_added;
svn_boolean_t is_deleted;
svn_boolean_t is_replaced;
- svn_boolean_t is_not_present;
- svn_boolean_t is_excluded;
svn_boolean_t is_op_root;
- svn_boolean_t is_symlink;
- svn_boolean_t conflicted;
- const char *node_changelist;
- svn_boolean_t is_update_root;
svn_revnum_t original_rev;
const char *original_relpath;
- svn_boolean_t copy_mode = (commit_relpath != NULL);
+ svn_boolean_t copy_mode;
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ struct harvest_baton *baton = status_baton;
+ svn_boolean_t is_harvest_root =
+ (strcmp(baton->root_abspath, local_abspath) == 0);
+ svn_client__committables_t *committables = baton->committables;
+ const char *repos_root_url = status->repos_root_url;
+ const char *commit_relpath = NULL;
+ svn_boolean_t copy_mode_root = (baton->commit_relpath && is_harvest_root);
+ svn_boolean_t just_locked = baton->just_locked;
+ apr_hash_t *changelists = baton->changelists;
+ svn_wc_notify_func2_t notify_func = baton->notify_func;
+ void *notify_baton = baton->notify_baton;
+ svn_wc_context_t *wc_ctx = baton->wc_ctx;
+ apr_pool_t *result_pool = baton->result_pool;
+
+ if (baton->commit_relpath)
+ commit_relpath = svn_relpath_join(
+ baton->commit_relpath,
+ svn_dirent_skip_ancestor(baton->root_abspath,
+ local_abspath),
+ scratch_pool);
+
+ copy_mode = (commit_relpath != NULL);
+
+ if (baton->skip_below_abspath
+ && svn_dirent_is_ancestor(baton->skip_below_abspath, local_abspath))
+ {
+ return SVN_NO_ERROR;
+ }
+ else
+ baton->skip_below_abspath = NULL; /* We have left the skip tree */
+
+ /* Return early for nodes that don't have a committable status */
+ switch (status->node_status)
+ {
+ case svn_wc_status_unversioned:
+ case svn_wc_status_ignored:
+ case svn_wc_status_external:
+ case svn_wc_status_none:
+ /* Unversioned nodes aren't committable, but are reported by the status
+ walker.
+ But if the unversioned node is the root of the walk, we have a user
+ error */
+ if (is_harvest_root)
+ return svn_error_createf(
+ SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not under version control"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
+ return SVN_NO_ERROR;
+ case svn_wc_status_normal:
+ /* Status normal nodes aren't modified, so we don't have to commit them
+ when we perform a normal commit. But if a node is conflicted we want
+ to stop the commit and if we are collecting lock tokens we want to
+ look further anyway.
+
+ When in copy mode we need to compare the revision of the node against
+ the parent node to copy mixed-revision base nodes properly */
+ if (!copy_mode && !status->conflicted
+ && !(just_locked && status->lock))
+ return SVN_NO_ERROR;
+ break;
+ default:
+ /* Fall through */
+ break;
+ }
/* Early out if the item is already marked as committable. */
if (look_up_committable(committables, local_abspath, scratch_pool))
@@ -486,132 +635,105 @@ harvest_committables(const char *local_a
SVN_ERR_ASSERT((copy_mode && commit_relpath)
|| (! copy_mode && ! commit_relpath));
SVN_ERR_ASSERT((copy_mode_root && copy_mode) || ! copy_mode_root);
- SVN_ERR_ASSERT((just_locked && lock_tokens) || !just_locked);
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- /* Return error on unknown path kinds. We check both the entry and
- the node itself, since a path might have changed kind since its
- entry was written. */
- SVN_ERR(svn_wc__node_get_commit_status(&db_kind, &is_added, &is_deleted,
- &is_replaced,
- &is_not_present, &is_excluded,
- &is_op_root, &is_symlink,
- &node_rev, &node_relpath,
- &original_rev, &original_relpath,
- &conflicted,
- &node_changelist,
- &prop_mod, &is_update_root,
- &node_lock_token,
- wc_ctx, local_abspath,
- scratch_pool, scratch_pool));
-
- if ((skip_files && db_kind == svn_node_file) || is_excluded)
- return SVN_NO_ERROR;
-
- if (!node_relpath && commit_relpath)
- node_relpath = commit_relpath;
-
- SVN_ERR(svn_io_check_special_path(local_abspath, &working_kind, &is_special,
- scratch_pool));
-
- /* ### In 1.6 an obstructed dir would fail when locking before we
- got here. Locking now doesn't fail so perhaps we should do
- some sort of checking here. */
-
- if ((working_kind != svn_node_file)
- && (working_kind != svn_node_dir)
- && (working_kind != svn_node_none))
- {
- return svn_error_createf
- (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
- _("Unknown entry kind for '%s'"),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
/* Save the result for reuse. */
matches_changelists = ((changelists == NULL)
- || (node_changelist != NULL
- && apr_hash_get(changelists, node_changelist,
+ || (status->changelist != NULL
+ && apr_hash_get(changelists, status->changelist,
APR_HASH_KEY_STRING) != NULL));
/* Early exit. */
- if (working_kind != svn_node_dir && working_kind != svn_node_none
- && ! matches_changelists)
+ if (status->kind != svn_node_dir && ! matches_changelists)
{
return SVN_NO_ERROR;
}
- /* Verify that the node's type has not changed before attempting to
- commit. */
- if ((((!is_symlink) && (is_special))
-#ifdef HAVE_SYMLINK
- || (is_symlink && (! is_special))
-#endif /* HAVE_SYMLINK */
- ) && (working_kind != svn_node_none))
- {
- return svn_error_createf
- (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
- _("Entry '%s' has unexpectedly changed special status"),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
-
- /* Handle file externals.
- * (IS_UPDATE_ROOT is more generally defined, but at the moment this
- * condition matches only file externals.)
- *
- * Don't copy files that svn:externals brought into the WC. So in copy_mode,
- * even explicit targets are skipped.
- *
- * Exclude file externals from recursion. Hande file externals only when
- * passed as explicit target. Note that svn_client_commit6() passes all
- * committable externals in as explicit targets iff they count.
- *
- * Also note that dir externals will never be reached recursively by this
- * function, since svn_wc__node_get_children_of_working_node() (used below
- * to recurse) does not return switched subdirs. */
- if (is_update_root
- && db_kind == svn_node_file
- && (copy_mode
- || ! is_explicit_target))
+ /* If NODE is in our changelist, then examine it for conflicts. We
+ need to bail out if any conflicts exist.
+ The status walker checked for conflict marker removal. */
+ if (status->conflicted && matches_changelists)
{
- return SVN_NO_ERROR;
+ if (notify_func != NULL)
+ {
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_failed_conflict,
+ scratch_pool),
+ scratch_pool);
+ }
+
+ return svn_error_createf(
+ SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ _("Aborting commit: '%s' remains in conflict"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
}
+ else if (status->node_status == svn_wc_status_obstructed)
+ {
+ /* A node's type has changed before attempting to commit.
+ This also catches symlink vs non symlink changes */
- /* If NODE is in our changelist, then examine it for conflicts. We
- need to bail out if any conflicts exist. */
- if (conflicted && matches_changelists)
+ if (notify_func != NULL)
+ {
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_failed_obstruction,
+ scratch_pool),
+ scratch_pool);
+ }
+
+ return svn_error_createf(
+ SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+ _("Node '%s' has unexpectedly changed kind"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
+ }
+
+ if (status->conflicted && status->kind == svn_node_unknown)
+ return SVN_NO_ERROR; /* Ignore delete-delete conflict */
+
+ /* Return error on unknown path kinds. We check both the entry and
+ the node itself, since a path might have changed kind since its
+ entry was written. */
+ SVN_ERR(svn_wc__node_get_commit_status(&is_added, &is_deleted,
+ &is_replaced,
+ &is_op_root,
+ &node_rev,
+ &original_rev, &original_relpath,
+ wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Hande file externals only when passed as explicit target. Note that
+ * svn_client_commit6() passes all committable externals in as explicit
+ * targets iff they count. */
+ if (status->file_external && !is_harvest_root)
{
- svn_boolean_t tc, pc, treec;
+ return SVN_NO_ERROR;
+ }
- SVN_ERR(svn_wc_conflicted_p3(&tc, &pc, &treec, wc_ctx,
- local_abspath, scratch_pool));
- if (tc || pc || treec)
+ if (status->node_status == svn_wc_status_missing && matches_changelists)
+ {
+ /* Added files and directories must exist. See issue #3198. */
+ if (is_added && is_op_root)
{
if (notify_func != NULL)
{
notify_func(notify_baton,
svn_wc_create_notify(local_abspath,
- svn_wc_notify_failed_conflict,
+ svn_wc_notify_failed_missing,
scratch_pool),
scratch_pool);
}
-
return svn_error_createf(
- SVN_ERR_WC_FOUND_CONFLICT, NULL,
- _("Aborting commit: '%s' remains in conflict"),
- svn_dirent_local_style(local_abspath, scratch_pool));
+ SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+ _("'%s' is scheduled for addition, but is missing"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
}
+
+ return SVN_NO_ERROR;
}
if (is_deleted && !is_op_root /* && !is_added */)
return SVN_NO_ERROR; /* Not an operational delete and not an add. */
- if (node_relpath == NULL)
- SVN_ERR(svn_wc__node_get_repos_relpath(&node_relpath,
- wc_ctx, local_abspath,
- scratch_pool, scratch_pool));
/* Check for the deletion case.
* We delete explicitly deleted nodes (duh!)
* We delete not-present children of copies
@@ -620,36 +742,6 @@ harvest_committables(const char *local_a
if (is_deleted || is_replaced)
state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE;
- else if (is_not_present)
- {
- if (! copy_mode)
- return SVN_NO_ERROR;
-
- /* We should check if we should really add a delete operation */
- if (check_url_func)
- {
- svn_client__pathrev_t *origin;
- const char *repos_url;
- svn_node_kind_t kind;
-
- /* Determine from what parent we would be the deleted child */
- SVN_ERR(svn_client__wc_node_get_origin(
- &origin, svn_dirent_dirname(local_abspath, scratch_pool),
- ctx, scratch_pool, scratch_pool));
-
- repos_url = svn_path_url_add_component2(
- origin->url, svn_dirent_basename(local_abspath, NULL),
- scratch_pool);
-
- SVN_ERR(check_url_func(check_url_baton, &kind, repos_url, origin->rev,
- scratch_pool));
-
- if (kind == svn_node_none)
- return SVN_NO_ERROR; /* This node can't be deleted */
- }
-
- state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE;
- }
/* Check for adds and copies */
if (is_added && is_op_root)
@@ -666,139 +758,108 @@ harvest_committables(const char *local_a
}
}
- /* Further additions occur in copy mode. */
- if (copy_mode
- && (!is_added || copy_mode_root)
- && !(state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
+ /* Further copies may occur in copy mode. */
+ else if (copy_mode
+ && !(state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
{
svn_revnum_t dir_rev;
- if (!copy_mode_root)
- SVN_ERR(svn_wc__node_get_base_rev(&dir_rev, wc_ctx,
- svn_dirent_dirname(local_abspath,
- scratch_pool),
- scratch_pool));
-
- if (copy_mode_root || node_rev != dir_rev)
- {
- state_flags |= SVN_CLIENT_COMMIT_ITEM_ADD;
-
- SVN_ERR(svn_wc__node_get_origin(NULL, &cf_rev,
- &cf_relpath, NULL,
- NULL, NULL,
- wc_ctx, local_abspath, FALSE,
+ if (!copy_mode_root && !status->switched)
+ SVN_ERR(svn_wc__node_get_base(&dir_rev, NULL, NULL, NULL, wc_ctx,
+ svn_dirent_dirname(local_abspath,
+ scratch_pool),
scratch_pool, scratch_pool));
- if (cf_relpath)
- state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY;
+ if (copy_mode_root || status->switched || node_rev != dir_rev)
+ {
+ state_flags |= (SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_IS_COPY);
+
+ if (status->copied)
+ {
+ /* Copy from original location */
+ cf_rev = original_rev;
+ cf_relpath = original_relpath;
+ }
+ else
+ {
+ /* Copy BASE location, to represent a mixed-rev or switch copy */
+ cf_rev = status->revision;
+ cf_relpath = status->repos_relpath;
+ }
}
}
- /* If an add is scheduled to occur, dig around for some more
- information about it. */
- if (state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
+ if (!(state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ || (state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
{
- /* First of all, the working file or directory must exist.
- See issue #3198. */
- if (working_kind == svn_node_none)
+ svn_boolean_t text_mod = FALSE;
+ svn_boolean_t prop_mod = FALSE;
+
+ if (status->kind == svn_node_file)
{
- if (notify_func != NULL)
+ /* Check for text modifications on files */
+ if ((state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
+ && ! (state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY))
{
- notify_func(notify_baton,
- svn_wc_create_notify(local_abspath,
- svn_wc_notify_failed_missing,
- scratch_pool),
- scratch_pool);
+ text_mod = TRUE; /* Local added files are always modified */
}
- return svn_error_createf(
- SVN_ERR_WC_PATH_NOT_FOUND, NULL,
- _("'%s' is scheduled for addition, but is missing"),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
-
- /* Regular adds of files have text mods, but for copies we have
- to test for textual mods. Directories simply don't have text! */
- if (db_kind == svn_node_file)
- {
- /* Check for text mods. */
- if (state_flags & SVN_CLIENT_COMMIT_ITEM_IS_COPY)
- SVN_ERR(svn_wc_text_modified_p2(&text_mod, wc_ctx, local_abspath,
- FALSE, scratch_pool));
else
- text_mod = TRUE;
+ text_mod = (status->text_status != svn_wc_status_normal);
}
- }
- /* Else, if we aren't deleting this item, we'll have to look for
- local text or property mods to determine if the path might be
- committable. */
- else if (! (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
- {
- /* Check for text mods on files. If EOL_PROP_CHANGED is TRUE,
- then we need to force a translated byte-for-byte comparison
- against the text-base so that a timestamp comparison won't
- bail out early. Depending on how the svn:eol-style prop was
- changed, we might have to send new text to the server to
- match the new newline style. */
- if (db_kind == svn_node_file)
- SVN_ERR(svn_wc_text_modified_p2(&text_mod, wc_ctx, local_abspath,
- FALSE, scratch_pool));
- }
-
- /* Set text/prop modification flags accordingly. */
- if (text_mod)
- state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS;
- if (prop_mod)
- state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+ prop_mod = (status->prop_status != svn_wc_status_normal
+ && status->prop_status != svn_wc_status_none);
+
+ /* Set text/prop modification flags accordingly. */
+ if (text_mod)
+ state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS;
+ if (prop_mod)
+ state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+ }
/* If the entry has a lock token and it is already a commit candidate,
or the caller wants unmodified locked items to be treated as
such, note this fact. */
- if (node_lock_token && lock_tokens && (state_flags || just_locked))
+ if (status->lock && baton->lock_tokens && (state_flags || just_locked))
{
state_flags |= SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN;
}
/* Now, if this is something to commit, add it to our list. */
- if (state_flags)
+ if (matches_changelists
+ && state_flags)
{
- if (matches_changelists)
- {
- /* Finally, add the committable item. */
- SVN_ERR(add_committable(committables, local_abspath, db_kind,
- repos_root_url,
- copy_mode
+ /* Finally, add the committable item. */
+ SVN_ERR(add_committable(committables, local_abspath,
+ status->kind,
+ repos_root_url,
+ copy_mode
? commit_relpath
- : node_relpath,
- copy_mode
+ : status->repos_relpath,
+ copy_mode
? SVN_INVALID_REVNUM
: node_rev,
- cf_relpath,
- cf_rev,
- state_flags,
- result_pool, scratch_pool));
- if (state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)
- apr_hash_set(lock_tokens,
- svn_path_url_add_component2(
- repos_root_url, node_relpath,
- apr_hash_pool_get(lock_tokens)),
- APR_HASH_KEY_STRING,
- apr_pstrdup(apr_hash_pool_get(lock_tokens),
- node_lock_token));
- }
+ cf_relpath,
+ cf_rev,
+ state_flags,
+ baton->lock_tokens, status->lock,
+ result_pool, scratch_pool));
}
- /* Fetch lock tokens for descendants of deleted nodes. */
- if (lock_tokens
- && (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
+ /* Fetch lock tokens for descendants of deleted BASE nodes. */
+ if (matches_changelists
+ && (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ && !copy_mode
+ && SVN_IS_VALID_REVNUM(node_rev) /* && BASE-kind = dir */
+ && baton->lock_tokens)
{
apr_hash_t *local_relpath_tokens;
apr_hash_index_t *hi;
- apr_pool_t *token_pool = apr_hash_pool_get(lock_tokens);
SVN_ERR(svn_wc__node_get_lock_tokens_recursive(
&local_relpath_tokens, wc_ctx, local_abspath,
- token_pool, scratch_pool));
+ result_pool, scratch_pool));
/* Add tokens to existing hash. */
for (hi = apr_hash_first(scratch_pool, local_relpath_tokens);
@@ -811,22 +872,35 @@ harvest_committables(const char *local_a
apr_hash_this(hi, &key, &klen, &val);
- apr_hash_set(lock_tokens, key, klen, val);
+ apr_hash_set(baton->lock_tokens, key, klen, val);
}
}
- /* Make sure we check for dangling children on additions */
- if (state_flags && is_added && is_explicit_target && danglers)
+ /* Make sure we check for dangling children on additions
+
+ We perform this operation on the harvest root, and on roots caused by
+ changelist filtering.
+ */
+ if (matches_changelists
+ && (is_harvest_root || baton->changelists)
+ && state_flags
+ && is_added
+ && baton->danglers)
{
- /* If a node is added, it's parent must exist in the repository at the
+ /* If a node is added, its parent must exist in the repository at the
time of committing */
-
+ apr_hash_t *danglers = baton->danglers;
svn_boolean_t parent_added;
const char *parent_abspath = svn_dirent_dirname(local_abspath,
scratch_pool);
- SVN_ERR(svn_wc__node_is_added(&parent_added, wc_ctx, parent_abspath,
- scratch_pool));
+ /* First check if parent is already in the list of commits
+ (Common case for GUI clients that provide a list of commit targets) */
+ if (look_up_committable(committables, parent_abspath, scratch_pool))
+ parent_added = FALSE; /* Skip all expensive checks */
+ else
+ SVN_ERR(svn_wc__node_is_added(&parent_added, wc_ctx, parent_abspath,
+ scratch_pool));
if (parent_added)
{
@@ -854,63 +928,25 @@ harvest_committables(const char *local_a
}
}
- if (db_kind != svn_node_dir || depth <= svn_depth_empty)
- return SVN_NO_ERROR;
-
- SVN_ERR(bail_on_tree_conflicted_children(wc_ctx, local_abspath,
- db_kind, depth, changelists,
- notify_func, notify_baton,
- scratch_pool));
-
- /* Recursively handle each node according to depth, except when the
- node is only being deleted. */
- if ((! (state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
- || (state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ if (is_deleted && !is_added)
{
- const apr_array_header_t *children;
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- int i;
- svn_depth_t depth_below_here = depth;
-
- if (depth < svn_depth_infinity)
- depth_below_here = svn_depth_empty; /* Stop recursing */
-
- SVN_ERR(svn_wc__node_get_children_of_working_node(
- &children, wc_ctx, local_abspath, copy_mode,
- scratch_pool, iterpool));
- for (i = 0; i < children->nelts; i++)
- {
- const char *this_abspath = APR_ARRAY_IDX(children, i, const char *);
- const char *name = svn_dirent_basename(this_abspath, NULL);
- const char *this_commit_relpath;
-
- svn_pool_clear(iterpool);
-
- if (commit_relpath == NULL)
- this_commit_relpath = NULL;
- else
- this_commit_relpath = svn_relpath_join(commit_relpath, name,
- iterpool);
-
- SVN_ERR(harvest_committables(this_abspath,
- committables, lock_tokens,
- repos_root_url,
- this_commit_relpath,
- FALSE, /* COPY_MODE_ROOT */
- depth_below_here,
- just_locked,
- changelists,
- (depth < svn_depth_files),
- (depth < svn_depth_immediates),
- FALSE, /* IS_EXPLICIT_TARGET */
- danglers,
- check_url_func, check_url_baton,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- ctx, result_pool, iterpool));
- }
+ /* Skip all descendants */
+ if (status->kind == svn_node_dir)
+ baton->skip_below_abspath = apr_pstrdup(baton->result_pool,
+ local_abspath);
+ return SVN_NO_ERROR;
+ }
- svn_pool_destroy(iterpool);
+ /* Recursively handle each node according to depth, except when the
+ node is only being deleted, or is in an added tree (as added trees
+ use the normal commit handling). */
+ if (copy_mode && !is_added && !is_deleted && status->kind == svn_node_dir)
+ {
+ SVN_ERR(harvest_not_present_for_copy(wc_ctx, local_abspath, committables,
+ repos_root_url, commit_relpath,
+ baton->check_url_func,
+ baton->check_url_baton,
+ result_pool, scratch_pool));
}
return SVN_NO_ERROR;
@@ -1058,7 +1094,6 @@ svn_client__harvest_committables(svn_cli
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_t *changelist_hash = NULL;
- svn_wc_context_t *wc_ctx = ctx->wc_ctx;
struct handle_descendants_baton hdb;
apr_hash_index_t *hi;
@@ -1103,8 +1138,6 @@ svn_client__harvest_committables(svn_cli
for (i = 0; i < targets->nelts; ++i)
{
const char *target_abspath;
- svn_node_kind_t kind;
- const char *repos_root_url;
svn_pool_clear(iterpool);
@@ -1113,34 +1146,6 @@ svn_client__harvest_committables(svn_cli
APR_ARRAY_IDX(targets, i, const char *),
iterpool);
- SVN_ERR(svn_wc_read_kind(&kind, wc_ctx, target_abspath,
- FALSE, /* show_hidden */
- iterpool));
- if (kind == svn_node_none)
- {
- /* If a target of the commit is a tree-conflicted node that
- * has no entry (e.g. locally deleted), issue a proper tree-
- * conflicts error instead of a "not under version control". */
- const svn_wc_conflict_description2_t *conflict;
- SVN_ERR(svn_wc__get_tree_conflict(&conflict, wc_ctx, target_abspath,
- iterpool, iterpool));
- if (conflict != NULL)
- return svn_error_createf(
- SVN_ERR_WC_FOUND_CONFLICT, NULL,
- _("Aborting commit: '%s' remains in conflict"),
- svn_dirent_local_style(conflict->local_abspath,
- iterpool));
- else
- return svn_error_createf(
- SVN_ERR_ILLEGAL_TARGET, NULL,
- _("'%s' is not under version control"),
- svn_dirent_local_style(target_abspath, iterpool));
- }
-
- SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, NULL, wc_ctx,
- target_abspath,
- result_pool, iterpool));
-
/* Handle our TARGET. */
/* Make sure this isn't inside a working copy subtree that is
* marked as tree-conflicted. */
@@ -1151,17 +1156,13 @@ svn_client__harvest_committables(svn_cli
SVN_ERR(harvest_committables(target_abspath,
*committables, *lock_tokens,
- repos_root_url,
- NULL /* COMMIT_RELPATH */,
- FALSE /* COPY_MODE_ROOT */,
+ NULL /* COPY_MODE_RELPATH */,
depth, just_locked, changelist_hash,
- FALSE, FALSE,
- TRUE /* IS_EXPLICIT_TARGET */,
danglers,
check_url_func, check_url_baton,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
- ctx, result_pool, iterpool));
+ ctx->wc_ctx, result_pool, iterpool));
}
hdb.wc_ctx = ctx->wc_ctx;
@@ -1243,14 +1244,10 @@ harvest_copy_committables(void *baton, v
/* Handle this SRC. */
SVN_ERR(harvest_committables(pair->src_abspath_or_url,
btn->committables, NULL,
- repos_root_url,
commit_relpath,
- TRUE, /* COPY_MODE_ROOT */
svn_depth_infinity,
FALSE, /* JUST_LOCKED */
- NULL,
- FALSE, FALSE, /* skip files, dirs */
- TRUE, /* IS_EXPLICIT_TARGET (don't care) */
+ NULL /* changelists */,
NULL,
btn->check_url_func,
btn->check_url_baton,
@@ -1258,7 +1255,7 @@ harvest_copy_committables(void *baton, v
btn->ctx->cancel_baton,
btn->ctx->notify_func2,
btn->ctx->notify_baton2,
- btn->ctx, btn->result_pool, pool));
+ btn->ctx->wc_ctx, btn->result_pool, pool));
hdb.wc_ctx = btn->ctx->wc_ctx;
hdb.cancel_func = btn->ctx->cancel_func;
@@ -1580,10 +1577,7 @@ do_item_commit(void **dir_baton,
parent_baton, pool);
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, item->kind,
- err, ctx, pool));
+ goto fixup_error;
}
/* If this item is supposed to be added, do so. */
@@ -1607,10 +1601,7 @@ do_item_commit(void **dir_baton,
}
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind, err,
- ctx, pool));
+ goto fixup_error;
/* Set other prop-changes, if available in the baton */
if (item->outgoing_prop_changes)
@@ -1623,14 +1614,17 @@ do_item_commit(void **dir_baton,
prop = APR_ARRAY_IDX(prop_changes, ctr, svn_prop_t *);
if (kind == svn_node_file)
{
- editor->change_file_prop(file_baton, prop->name,
- prop->value, pool);
+ err = editor->change_file_prop(file_baton, prop->name,
+ prop->value, pool);
}
else
{
- editor->change_dir_prop(*dir_baton, prop->name,
- prop->value, pool);
+ err = editor->change_dir_prop(*dir_baton, prop->name,
+ prop->value, pool);
}
+
+ if (err)
+ goto fixup_error;
}
}
}
@@ -1648,11 +1642,7 @@ do_item_commit(void **dir_baton,
file_pool, &file_baton);
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind,
- err, ctx,
- pool));
+ goto fixup_error;
}
}
else
@@ -1672,11 +1662,7 @@ do_item_commit(void **dir_baton,
}
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind,
- err, ctx,
- pool));
+ goto fixup_error;
}
}
@@ -1689,10 +1675,7 @@ do_item_commit(void **dir_baton,
(kind == svn_node_dir) ? *dir_baton : file_baton, pool);
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind, err,
- ctx, pool));
+ goto fixup_error;
/* Make any additional client -> repository prop changes. */
if (item->outgoing_prop_changes)
@@ -1706,14 +1689,17 @@ do_item_commit(void **dir_baton,
svn_prop_t *);
if (kind == svn_node_file)
{
- editor->change_file_prop(file_baton, prop->name,
+ err = editor->change_file_prop(file_baton, prop->name,
prop->value, pool);
}
else
{
- editor->change_dir_prop(*dir_baton, prop->name,
+ err = editor->change_dir_prop(*dir_baton, prop->name,
prop->value, pool);
}
+
+ if (err)
+ goto fixup_error;
}
}
}
@@ -1734,10 +1720,7 @@ do_item_commit(void **dir_baton,
file_pool, &file_baton);
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind,
- err, ctx, pool));
+ goto fixup_error;
}
/* Add this file mod to the FILE_MODS hash. */
@@ -1752,25 +1735,17 @@ do_item_commit(void **dir_baton,
err = editor->close_file(file_baton, NULL, file_pool);
if (err)
- return svn_error_trace(fixup_commit_error(local_abspath,
- icb->base_url,
- path, kind,
- err, ctx, pool));
+ goto fixup_error;
}
return SVN_NO_ERROR;
-}
-
-#ifdef SVN_CLIENT_COMMIT_DEBUG
-/* Prototype for function below */
-static svn_error_t *get_test_editor(const svn_delta_editor_t **editor,
- void **edit_baton,
- const svn_delta_editor_t *real_editor,
- void *real_eb,
- const char *base_url,
- apr_pool_t *pool);
-#endif /* SVN_CLIENT_COMMIT_DEBUG */
+fixup_error:
+ return svn_error_trace(fixup_commit_error(local_abspath,
+ icb->base_url,
+ path, kind,
+ err, ctx, pool));
+}
svn_error_t *
svn_client__do_commit(const char *base_url,
@@ -1778,7 +1753,6 @@ svn_client__do_commit(const char *base_u
const svn_delta_editor_t *editor,
void *edit_baton,
const char *notify_path_prefix,
- apr_hash_t **md5_checksums,
apr_hash_t **sha1_checksums,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
@@ -1793,17 +1767,7 @@ svn_client__do_commit(const char *base_u
apr_array_header_t *paths =
apr_array_make(scratch_pool, commit_items->nelts, sizeof(const char *));
-#ifdef SVN_CLIENT_COMMIT_DEBUG
- {
- SVN_ERR(get_test_editor(&editor, &edit_baton,
- editor, edit_baton,
- base_url, scratch_pool));
- }
-#endif /* SVN_CLIENT_COMMIT_DEBUG */
-
/* Ditto for the checksums. */
- if (md5_checksums)
- *md5_checksums = apr_hash_make(result_pool);
if (sha1_checksums)
*sha1_checksums = apr_hash_make(result_pool);
@@ -1883,9 +1847,6 @@ svn_client__do_commit(const char *base_u
err, ctx, scratch_pool));
}
- if (md5_checksums)
- apr_hash_set(*md5_checksums, item->path, APR_HASH_KEY_STRING,
- new_text_base_md5_checksum);
if (sha1_checksums)
apr_hash_set(*sha1_checksums, item->path, APR_HASH_KEY_STRING,
new_text_base_sha1_checksum);
@@ -1898,269 +1859,6 @@ svn_client__do_commit(const char *base_u
}
-#ifdef SVN_CLIENT_COMMIT_DEBUG
-
-/*** Temporary test editor ***/
-
-struct edit_baton
-{
- const char *path;
-
- const svn_delta_editor_t *real_editor;
- void *real_eb;
-};
-
-struct item_baton
-{
- struct edit_baton *eb;
- void *real_baton;
-
- const char *path;
-};
-
-static struct item_baton *
-make_baton(struct edit_baton *eb,
- void *real_baton,
- const char *path,
- apr_pool_t *pool)
-{
- struct item_baton *new_baton = apr_pcalloc(pool, sizeof(*new_baton));
- new_baton->eb = eb;
- new_baton->real_baton = real_baton;
- new_baton->path = apr_pstrdup(pool, path);
- return new_baton;
-}
-
-static svn_error_t *
-set_target_revision(void *edit_baton,
- svn_revnum_t target_revision,
- apr_pool_t *pool)
-{
- struct edit_baton *eb = edit_baton;
- return (*eb->real_editor->set_target_revision)(eb->real_eb,
- target_revision,
- pool);
-}
-
-static svn_error_t *
-open_root(void *edit_baton,
- svn_revnum_t base_revision,
- apr_pool_t *dir_pool,
- void **root_baton)
-{
- struct edit_baton *eb = edit_baton;
- struct item_baton *new_baton = make_baton(eb, NULL, eb->path, dir_pool);
- fprintf(stderr, "TEST EDIT STARTED (base URL=%s)\n", eb->path);
- *root_baton = new_baton;
- return (*eb->real_editor->open_root)(eb->real_eb,
- base_revision,
- dir_pool,
- &new_baton->real_baton);
-}
-
-static svn_error_t *
-add_file(const char *path,
- void *parent_baton,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- apr_pool_t *pool,
- void **baton)
-{
- struct item_baton *db = parent_baton;
- struct item_baton *new_baton = make_baton(db->eb, NULL, path, pool);
- const char *copystuffs = "";
- if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision))
- copystuffs = apr_psprintf(pool,
- " (copied from %s:%ld)",
- copyfrom_path,
- copyfrom_revision);
- fprintf(stderr, " Adding : %s%s\n", path, copystuffs);
- *baton = new_baton;
- return (*db->eb->real_editor->add_file)(path, db->real_baton,
- copyfrom_path, copyfrom_revision,
- pool, &new_baton->real_baton);
-}
-
-static svn_error_t *
-delete_entry(const char *path,
- svn_revnum_t revision,
- void *parent_baton,
- apr_pool_t *pool)
-{
- struct item_baton *db = parent_baton;
- fprintf(stderr, " Deleting: %s\n", path);
- return (*db->eb->real_editor->delete_entry)(path, revision,
- db->real_baton, pool);
-}
-
-static svn_error_t *
-open_file(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *pool,
- void **baton)
-{
- struct item_baton *db = parent_baton;
- struct item_baton *new_baton = make_baton(db->eb, NULL, path, pool);
- fprintf(stderr, " Opening : %s\n", path);
- *baton = new_baton;
- return (*db->eb->real_editor->open_file)(path, db->real_baton,
- base_revision, pool,
- &new_baton->real_baton);
-}
-
-static svn_error_t *
-close_file(void *baton, const char *text_checksum, apr_pool_t *pool)
-{
- struct item_baton *fb = baton;
- fprintf(stderr, " Closing : %s\n", fb->path);
- return (*fb->eb->real_editor->close_file)(fb->real_baton,
- text_checksum, pool);
-}
-
-
-static svn_error_t *
-change_file_prop(void *file_baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- struct item_baton *fb = file_baton;
- fprintf(stderr, " PropSet (%s=%s)\n", name, value ? value->data : "");
- return (*fb->eb->real_editor->change_file_prop)(fb->real_baton,
- name, value, pool);
-}
-
-static svn_error_t *
-apply_textdelta(void *file_baton,
- const char *base_checksum,
- apr_pool_t *pool,
- svn_txdelta_window_handler_t *handler,
- void **handler_baton)
-{
- struct item_baton *fb = file_baton;
- fprintf(stderr, " Transmitting text...\n");
- return (*fb->eb->real_editor->apply_textdelta)(fb->real_baton,
- base_checksum, pool,
- handler, handler_baton);
-}
-
-static svn_error_t *
-close_edit(void *edit_baton, apr_pool_t *pool)
-{
- struct edit_baton *eb = edit_baton;
- fprintf(stderr, "TEST EDIT COMPLETED\n");
- return (*eb->real_editor->close_edit)(eb->real_eb, pool);
-}
-
-static svn_error_t *
-add_directory(const char *path,
- void *parent_baton,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- apr_pool_t *pool,
- void **baton)
-{
- struct item_baton *db = parent_baton;
- struct item_baton *new_baton = make_baton(db->eb, NULL, path, pool);
- const char *copystuffs = "";
- if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision))
- copystuffs = apr_psprintf(pool,
- " (copied from %s:%ld)",
- copyfrom_path,
- copyfrom_revision);
- fprintf(stderr, " Adding : %s%s\n", path, copystuffs);
- *baton = new_baton;
- return (*db->eb->real_editor->add_directory)(path,
- db->real_baton,
- copyfrom_path,
- copyfrom_revision,
- pool,
- &new_baton->real_baton);
-}
-
-static svn_error_t *
-open_directory(const char *path,
- void *parent_baton,
- svn_revnum_t base_revision,
- apr_pool_t *pool,
- void **baton)
-{
- struct item_baton *db = parent_baton;
- struct item_baton *new_baton = make_baton(db->eb, NULL, path, pool);
- fprintf(stderr, " Opening : %s\n", path);
- *baton = new_baton;
- return (*db->eb->real_editor->open_directory)(path, db->real_baton,
- base_revision, pool,
- &new_baton->real_baton);
-}
-
-static svn_error_t *
-change_dir_prop(void *dir_baton,
- const char *name,
- const svn_string_t *value,
- apr_pool_t *pool)
-{
- struct item_baton *db = dir_baton;
- fprintf(stderr, " PropSet (%s=%s)\n", name, value ? value->data : "");
- return (*db->eb->real_editor->change_dir_prop)(db->real_baton,
- name, value, pool);
-}
-
-static svn_error_t *
-close_directory(void *baton, apr_pool_t *pool)
-{
- struct item_baton *db = baton;
- fprintf(stderr, " Closing : %s\n", db->path);
- return (*db->eb->real_editor->close_directory)(db->real_baton, pool);
-}
-
-static svn_error_t *
-abort_edit(void *edit_baton, apr_pool_t *pool)
-{
- struct edit_baton *eb = edit_baton;
- fprintf(stderr, "TEST EDIT ABORTED\n");
- return (*eb->real_editor->abort_edit)(eb->real_eb, pool);
-}
-
-static svn_error_t *
-get_test_editor(const svn_delta_editor_t **editor,
- void **edit_baton,
- const svn_delta_editor_t *real_editor,
- void *real_eb,
- const char *base_url,
- apr_pool_t *pool)
-{
- svn_delta_editor_t *ed = svn_delta_default_editor(pool);
- struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
-
- eb->path = apr_pstrdup(pool, base_url);
- eb->real_editor = real_editor;
- eb->real_eb = real_eb;
-
- /* We don't implement absent_file() or absent_directory() in this
- editor, because presumably commit would never send that. */
- ed->set_target_revision = set_target_revision;
- ed->open_root = open_root;
- ed->add_directory = add_directory;
- ed->open_directory = open_directory;
- ed->close_directory = close_directory;
- ed->add_file = add_file;
- ed->open_file = open_file;
- ed->close_file = close_file;
- ed->delete_entry = delete_entry;
- ed->apply_textdelta = apply_textdelta;
- ed->change_dir_prop = change_dir_prop;
- ed->change_file_prop = change_file_prop;
- ed->close_edit = close_edit;
- ed->abort_edit = abort_edit;
-
- *editor = ed;
- *edit_baton = eb;
- return SVN_NO_ERROR;
-}
-#endif /* SVN_CLIENT_COMMIT_DEBUG */
-
svn_error_t *
svn_client__get_log_msg(const char **log_msg,
const char **tmp_file,
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/copy.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/copy.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/copy.c Wed Jun 27 15:12:37 2012
@@ -70,56 +70,6 @@
/*** Code. ***/
-/* Obtain the implied mergeinfo and the existing mergeinfo of the
- source path, combine them and return the result in
- *TARGET_MERGEINFO. One of LOCAL_ABSPATH and SRC_URL must be valid,
- the other must be NULL. */
-static svn_error_t *
-calculate_target_mergeinfo(svn_ra_session_t *ra_session,
- apr_hash_t **target_mergeinfo,
- const char *local_abspath,
- const char *src_url,
- svn_revnum_t src_revnum,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
-{
- svn_boolean_t locally_added = FALSE;
- apr_hash_t *src_mergeinfo = NULL;
-
- SVN_ERR_ASSERT((local_abspath && !src_url) || (!local_abspath && src_url));
-
- /* If we have a schedule-add WC path (which was not copied from
- elsewhere), it doesn't have any repository mergeinfo, so don't
- bother checking. */
- if (local_abspath)
- {
- svn_client__pathrev_t *origin;
-
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
- SVN_ERR(svn_client__wc_node_get_origin(&origin,
- local_abspath, ctx,
- pool, pool));
- src_revnum = origin->rev;
- src_url = origin->url;
-
- if (! src_url)
- locally_added = TRUE;
- }
-
- if (! locally_added)
- {
- /* Fetch any existing (explicit) mergeinfo. */
- SVN_ERR(svn_client__get_repos_mergeinfo(&src_mergeinfo, ra_session,
- src_url, src_revnum,
- svn_mergeinfo_inherited,
- TRUE, pool));
- }
-
- *target_mergeinfo = src_mergeinfo;
- return SVN_NO_ERROR;
-}
-
/* Extend the mergeinfo for the single WC path TARGET_WCPATH, adding
MERGEINFO to any mergeinfo pre-existing in the WC. */
static svn_error_t *
@@ -747,7 +697,6 @@ repos_to_repos_copy(const apr_array_head
apr_array_header_t *path_infos;
const char *top_url, *top_url_all, *top_url_dst;
const char *message, *repos_root;
- svn_revnum_t youngest = SVN_INVALID_REVNUM;
svn_ra_session_t *ra_session = NULL;
const svn_delta_editor_t *editor;
void *edit_baton;
@@ -803,9 +752,10 @@ repos_to_repos_copy(const apr_array_head
/* Go ahead and grab mergeinfo from the source, too. */
SVN_ERR(svn_ra_reparent(ra_session, pair->src_abspath_or_url, pool));
- SVN_ERR(calculate_target_mergeinfo(ra_session, &mergeinfo, NULL,
- pair->src_abspath_or_url,
- pair->src_revnum, ctx, pool));
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ &mergeinfo, ra_session,
+ pair->src_abspath_or_url, pair->src_revnum,
+ svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool));
if (mergeinfo)
SVN_ERR(svn_mergeinfo_to_string(&info->mergeinfo, mergeinfo, pool));
@@ -1000,7 +950,7 @@ repos_to_repos_copy(const apr_array_head
/* Figure out the basename that will result from this operation,
and ensure that we aren't trying to overwrite existing paths. */
dst_rel = svn_uri_skip_ancestor(top_url, pair->dst_abspath_or_url, pool);
- SVN_ERR(svn_ra_check_path(ra_session, dst_rel, youngest,
+ SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
&dst_kind, pool));
if (dst_kind != svn_node_none)
return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
@@ -1116,7 +1066,7 @@ repos_to_repos_copy(const apr_array_head
cb_baton.is_move = is_move;
/* Call the path-based editor driver. */
- err = svn_delta_path_driver(editor, edit_baton, youngest, paths,
+ err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM, paths,
path_driver_cb_func, &cb_baton, pool);
if (err)
{
@@ -1185,7 +1135,7 @@ wc_to_repos_copy(const apr_array_header_
const char *top_src_abspath;
svn_ra_session_t *ra_session;
const svn_delta_editor_t *editor;
- const char *common_wc_abspath = NULL;
+ apr_hash_t *relpath_map = NULL;
void *edit_baton;
svn_client__committables_t *committables;
apr_array_header_t *commit_items;
@@ -1204,21 +1154,6 @@ wc_to_repos_copy(const apr_array_header_
iterpool = svn_pool_create(pool);
- /* Verify that all the source paths exist, are versioned, etc.
- We'll do so by querying the base revisions of those things (which
- we'll need to know later anyway).
- ### Should we use the 'origin' revision instead of 'base'?
- */
- for (i = 0; i < copy_pairs->nelts; i++)
- {
- svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
- svn_client__copy_pair_t *);
- svn_pool_clear(iterpool);
-
- SVN_ERR(svn_wc__node_get_base_rev(&pair->src_revnum, ctx->wc_ctx,
- pair->src_abspath_or_url, iterpool));
- }
-
/* Determine the longest common ancestor for the destinations, and open an RA
session to that location. */
/* ### But why start by getting the _parent_ of the first one? */
@@ -1369,17 +1304,26 @@ wc_to_repos_copy(const apr_array_header_
svn_client__copy_pair_t *);
svn_client_commit_item3_t *item =
APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
+ svn_client__pathrev_t *src_origin;
svn_pool_clear(iterpool);
+ SVN_ERR(svn_client__wc_node_get_origin(&src_origin,
+ pair->src_abspath_or_url,
+ ctx, iterpool, iterpool));
+
/* Set the mergeinfo for the destination to the combined merge
info known to the WC and the repository. */
item->outgoing_prop_changes = apr_array_make(pool, 1,
sizeof(svn_prop_t *));
- SVN_ERR(calculate_target_mergeinfo(ra_session, &mergeinfo,
- pair->src_abspath_or_url,
- NULL, SVN_INVALID_REVNUM,
- ctx, iterpool));
+ /* Repository mergeinfo (or NULL if it's locally added)... */
+ if (src_origin)
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ &mergeinfo, ra_session, src_origin->url, src_origin->rev,
+ svn_mergeinfo_inherited, TRUE /*sqelch_inc.*/, iterpool));
+ else
+ mergeinfo = NULL;
+ /* ... and WC mergeinfo. */
SVN_ERR(svn_client__parse_mergeinfo(&wc_mergeinfo, ctx->wc_ctx,
pair->src_abspath_or_url,
iterpool, iterpool));
@@ -1413,22 +1357,25 @@ wc_to_repos_copy(const apr_array_header_
commit_items, pool));
#ifdef ENABLE_EV2_SHIMS
- for (i = 0; !common_wc_abspath && i < commit_items->nelts; i++)
+ if (commit_items)
{
- common_wc_abspath = APR_ARRAY_IDX(commit_items, i,
- svn_client_commit_item3_t *)->path;
- }
+ relpath_map = apr_hash_make(pool);
+ for (i = 0; i < commit_items->nelts; i++)
+ {
+ svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i,
+ svn_client_commit_item3_t *);
+ const char *relpath;
- for (; i < commit_items->nelts; i++)
- {
- svn_client_commit_item3_t *item =
- APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
+ if (!item->path)
+ continue;
- if (!item->path)
- continue;
-
- common_wc_abspath = svn_dirent_get_longest_ancestor(common_wc_abspath,
- item->path, pool);
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
+ ctx->wc_ctx, item->path, FALSE, pool,
+ iterpool));
+ if (relpath)
+ apr_hash_set(relpath_map, relpath, APR_HASH_KEY_STRING, item->path);
+ }
}
#endif
@@ -1439,8 +1386,7 @@ wc_to_repos_copy(const apr_array_header_
/* Fetch RA commit editor. */
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
- svn_client__get_shim_callbacks(ctx->wc_ctx,
- common_wc_abspath,
+ svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map,
pool)));
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
@@ -1453,7 +1399,7 @@ wc_to_repos_copy(const apr_array_header_
SVN_ERR_W(svn_client__do_commit(top_dst_url, commit_items,
editor, edit_baton,
0, /* ### any notify_path_offset needed? */
- NULL, NULL, ctx, pool, pool),
+ NULL, ctx, pool, pool),
_("Commit failed (details follow):"));
/* Sleep to ensure timestamp integrity. */
@@ -1513,13 +1459,14 @@ repos_to_wc_copy_single(svn_client__copy
if (pair->src_kind == svn_node_dir)
{
svn_boolean_t sleep_needed = FALSE;
- const char *tmp_abspath;
+ const char *tmpdir_abspath, *tmp_abspath;
- /* Find a temporary location in which to check out the copy source.
- * (This function is deprecated, but we intend to replace this whole
- * code path with something else.) */
- SVN_ERR(svn_wc_create_tmp_file2(NULL, &tmp_abspath, dst_abspath,
- svn_io_file_del_on_close, pool));
+ /* Find a temporary location in which to check out the copy source. */
+ SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
+ pool, pool));
+
+ SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
+ svn_io_file_del_on_close, pool, pool));
/* Make a new checkout of the requested source. While doing so,
* resolve pair->src_revnum to an actual revision number in case it
@@ -1622,9 +1569,10 @@ repos_to_wc_copy_single(svn_client__copy
/* Record the implied mergeinfo (before the notification callback
is invoked for the root node). */
- SVN_ERR(calculate_target_mergeinfo(ra_session, &src_mergeinfo, NULL,
- pair->src_abspath_or_url,
- pair->src_revnum, ctx, pool));
+ SVN_ERR(svn_client__get_repos_mergeinfo(
+ &src_mergeinfo, ra_session,
+ pair->src_abspath_or_url, pair->src_revnum,
+ svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool));
SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool));
/* Do our own notification for the root node, even if we could possibly
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/delete.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/delete.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/delete.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/delete.c Wed Jun 27 15:12:37 2012
@@ -47,12 +47,12 @@
/*** Code. ***/
-/* An svn_client_status_func_t callback function for finding
+/* An svn_wc_status_func4_t callback function for finding
status structures which are not safely deletable. */
static svn_error_t *
find_undeletables(void *baton,
const char *path,
- const svn_client_status_t *status,
+ const svn_wc_status3_t *status,
apr_pool_t *pool)
{
/* Check for error-ful states. */
@@ -80,20 +80,19 @@ find_undeletables(void *baton,
return SVN_NO_ERROR;
}
-
-svn_error_t *
-svn_client__can_delete(const char *path,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
+/* Verify that the path can be deleted without losing stuff,
+ i.e. ensure that there are no modified or unversioned resources
+ under PATH. This is similar to checking the output of the status
+ command. CTX is used for the client's config options. POOL is
+ used for all temporary allocations. */
+static svn_error_t *
+can_delete_node(const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- svn_opt_revision_t revision;
svn_node_kind_t external_kind;
const char *defining_abspath;
- const char* local_abspath;
-
- revision.kind = svn_opt_revision_unspecified;
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
+ apr_array_header_t *ignores;
/* A file external should not be deleted since the file external is
implemented as a switched file and it would delete the file the
@@ -121,11 +120,19 @@ svn_client__can_delete(const char *path,
status callback function find_undeletables() makes the
determination, returning an error if it finds anything that shouldn't
be deleted. */
- return svn_error_trace(svn_client_status5(NULL, ctx, path, &revision,
- svn_depth_infinity, FALSE,
- FALSE, FALSE, FALSE, FALSE,
- NULL,
+
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool));
+
+ return svn_error_trace(svn_wc_walk_status(ctx->wc_ctx,
+ local_abspath,
+ svn_depth_infinity,
+ FALSE /* get_all */,
+ FALSE /* no_ignore */,
+ FALSE /* ignore_text_mod */,
+ ignores,
find_undeletables, NULL,
+ ctx->cancel_func,
+ ctx->cancel_baton,
scratch_pool));
}
@@ -327,7 +334,7 @@ svn_client__wc_delete(const char *path,
if (!force && !keep_local)
/* Verify that there are no "awkward" files */
- SVN_ERR(svn_client__can_delete(local_abspath, ctx, pool));
+ SVN_ERR(can_delete_node(local_abspath, ctx, pool));
if (!dry_run)
/* Mark the entry for commit deletion and perform wc deletion */
@@ -363,7 +370,7 @@ svn_client__wc_delete_many(const apr_arr
if (!force && !keep_local)
/* Verify that there are no "awkward" files */
- SVN_ERR(svn_client__can_delete(local_abspath, ctx, pool));
+ SVN_ERR(can_delete_node(local_abspath, ctx, pool));
}
if (!dry_run)
@@ -459,16 +466,19 @@ svn_client_delete4(const apr_array_heade
/* Delete the targets from each working copy in turn. */
for (hi = apr_hash_first(pool, wcroots); hi; hi = apr_hash_next(hi))
{
- const char *wcroot_abspath = svn__apr_hash_index_key(hi);
+ const char *root_abspath;
const apr_array_header_t *targets = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
+ SVN_ERR(svn_dirent_condense_targets(&root_abspath, NULL, targets,
+ FALSE, iterpool, iterpool));
+
SVN_WC__CALL_WITH_WRITE_LOCK(
svn_client__wc_delete_many(targets, force, FALSE, keep_local,
ctx->notify_func2, ctx->notify_baton2,
ctx, iterpool),
- ctx->wc_ctx, wcroot_abspath, TRUE /* lock_anchor */,
+ ctx->wc_ctx, root_abspath, TRUE /* lock_anchor */,
iterpool);
}
svn_pool_destroy(iterpool);
Modified: subversion/branches/master-passphrase/subversion/libsvn_client/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_client/deprecated.c?rev=1354571&r1=1354570&r2=1354571&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_client/deprecated.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_client/deprecated.c Wed Jun 27 15:12:37 2012
@@ -886,7 +886,7 @@ svn_client_diff5(const apr_array_header_
revision2, relative_to_dir, depth,
ignore_ancestry, no_diff_deleted,
show_copies_as_adds, ignore_content_type, FALSE,
- use_git_diff_format, header_encoding,
+ FALSE, use_git_diff_format, header_encoding,
outstream, errstream, changelists, ctx, pool);
}
@@ -1014,6 +1014,7 @@ svn_client_diff_peg5(const apr_array_hea
show_copies_as_adds,
ignore_content_type,
FALSE,
+ FALSE,
use_git_diff_format,
header_encoding,
outstream,
@@ -1642,7 +1643,10 @@ svn_client_propset3(svn_commit_info_t **
{
if (svn_path_is_url(target))
{
- struct capture_baton_t cb = { commit_info_p, pool };
+ struct capture_baton_t cb;
+
+ cb.info = commit_info_p;
+ cb.pool = pool;
SVN_ERR(svn_client_propset_remote(propname, propval, target, skip_checks,
base_revision_for_url, revprop_table,
@@ -1955,8 +1959,11 @@ svn_client_status4(svn_revnum_t *result_
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- struct status4_wrapper_baton swb = { ctx->wc_ctx, status_func,
- status_baton };
+ struct status4_wrapper_baton swb;
+
+ swb.wc_ctx = ctx->wc_ctx;
+ swb.old_func = status_func;
+ swb.old_baton = status_baton;
return svn_client_status5(result_rev, ctx, path, revision, depth, get_all,
update, no_ignore, ignore_externals, TRUE,
@@ -2558,6 +2565,34 @@ svn_client_url_from_path(const char **ur
/*** From mergeinfo.c ***/
svn_error_t *
+svn_client_mergeinfo_log(svn_boolean_t finding_merged,
+ const char *target_path_or_url,
+ const svn_opt_revision_t *target_peg_revision,
+ const char *source_path_or_url,
+ const svn_opt_revision_t *source_peg_revision,
+ svn_log_entry_receiver_t receiver,
+ void *receiver_baton,
+ svn_boolean_t discover_changed_paths,
+ svn_depth_t depth,
+ const apr_array_header_t *revprops,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_opt_revision_t start_revision, end_revision;
+
+ start_revision.kind = svn_opt_revision_unspecified;
+ end_revision.kind = svn_opt_revision_unspecified;
+
+ return svn_client_mergeinfo_log2(finding_merged,
+ target_path_or_url, target_peg_revision,
+ source_path_or_url, source_peg_revision,
+ &start_revision, &end_revision,
+ receiver, receiver_baton,
+ discover_changed_paths, depth, revprops,
+ ctx, scratch_pool);
+}
+
+svn_error_t *
svn_client_mergeinfo_log_merged(const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const char *merge_source_path_or_url,