summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/commit_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/commit_util.c')
-rw-r--r--subversion/libsvn_client/commit_util.c1394
1 files changed, 580 insertions, 814 deletions
diff --git a/subversion/libsvn_client/commit_util.c b/subversion/libsvn_client/commit_util.c
index 2661e39..a32ec5d 100644
--- a/subversion/libsvn_client/commit_util.c
+++ b/subversion/libsvn_client/commit_util.c
@@ -44,6 +44,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
+#include "private/svn_client_private.h"
/*** Uncomment this to turn on commit driver debugging. ***/
/*
@@ -181,7 +182,11 @@ fixup_commit_error(const char *local_abspath,
/* 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,
@@ -191,7 +196,10 @@ add_committable(svn_client__committables_t *committables,
svn_revnum_t revision,
const char *copyfrom_relpath,
svn_revnum_t copyfrom_rev,
+ const char *moved_from_abspath,
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)
{
@@ -205,18 +213,15 @@ add_committable(svn_client__committables_t *committables,
/* ### todo: Get the canonical repository for this item, which will
be the real key for the COMMITTABLES hash, instead of the above
bogosity. */
- array = apr_hash_get(committables->by_repository,
- repos_root_url,
- APR_HASH_KEY_STRING);
+ array = svn_hash_gets(committables->by_repository, repos_root_url);
/* E-gads! There is no array for this repository yet! Oh, no
problem, we'll just create (and add to the hash) one. */
if (array == NULL)
{
array = apr_array_make(result_pool, 1, sizeof(new_item));
- apr_hash_set(committables->by_repository,
- apr_pstrdup(result_pool, repos_root_url),
- APR_HASH_KEY_STRING, array);
+ svn_hash_sets(committables->by_repository,
+ apr_pstrdup(result_pool, repos_root_url), array);
}
/* Now update pointer values, ensuring that their allocations live
@@ -238,14 +243,23 @@ add_committable(svn_client__committables_t *committables,
new_item->incoming_prop_changes = apr_array_make(result_pool, 1,
sizeof(svn_prop_t *));
+ if (moved_from_abspath)
+ new_item->moved_from_abspath = apr_pstrdup(result_pool,
+ moved_from_abspath);
+
/* Now, add the commit item to the array. */
APR_ARRAY_PUSH(array, svn_client_commit_item3_t *) = new_item;
/* ... and to the hash. */
- apr_hash_set(committables->by_path,
- new_item->path,
- APR_HASH_KEY_STRING,
- new_item);
+ svn_hash_sets(committables->by_path, new_item->path, new_item);
+
+ if (lock
+ && lock_tokens
+ && (state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN))
+ {
+ svn_hash_sets(lock_tokens, new_item->url,
+ apr_pstrdup(result_pool, lock->token));
+ }
return SVN_NO_ERROR;
}
@@ -258,81 +272,7 @@ look_up_committable(svn_client__committables_t *committables,
apr_pool_t *pool)
{
return (svn_client_commit_item3_t *)
- 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;
+ svn_hash_gets(committables->by_path, path);
}
/* Helper function for svn_client__harvest_committables().
@@ -347,8 +287,8 @@ bail_on_tree_conflicted_ancestor(svn_wc_context_t *wc_ctx,
{
const char *wcroot_abspath;
- SVN_ERR(svn_wc__get_wc_root(&wcroot_abspath, wc_ctx, local_abspath,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
@@ -402,7 +342,7 @@ bail_on_tree_conflicted_ancestor(svn_wc_context_t *wc_ctx,
If COMMIT_RELPATH is not NULL, treat not-added nodes as if it is destined to
be added as COMMIT_RELPATH, and add 'deleted' entries to COMMITTABLES as
items to delete in the copy destination. COPY_MODE_ROOT should be set TRUE
- for the first call for which COPY_MODE is TRUE, i.e. not for for the
+ for the first call for which COPY_MODE is TRUE, i.e. not for the
recursive calls, and FALSE otherwise.
If CHANGELISTS is non-NULL, it is a hash whose keys are const char *
@@ -410,6 +350,12 @@ bail_on_tree_conflicted_ancestor(svn_wc_context_t *wc_ctx,
when harvesting committables; that is, don't add a path to
COMMITTABLES unless it's a member of one of those changelists.
+ IS_EXPLICIT_TARGET should always be passed as TRUE, except when
+ harvest_committables() calls itself in recursion. This provides a way to
+ tell whether LOCAL_ABSPATH was an original target or whether it was reached
+ by recursing deeper into a dir target. (This is used to skip all file
+ externals that aren't explicit commit targets.)
+
DANGLERS is a hash table mapping const char* absolute paths of a parent
to a const char * absolute path of a child. See the comment about
danglers at the top of svn_client__harvest_committables().
@@ -419,19 +365,43 @@ bail_on_tree_conflicted_ancestor(svn_wc_context_t *wc_ctx,
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_committables(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
+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,
apr_hash_t *danglers,
svn_client__check_url_kind_t check_url_func,
void *check_url_baton,
@@ -439,36 +409,223 @@ harvest_committables(svn_wc_context_t *wc_ctx,
void *cancel_baton,
svn_wc_notify_func2_t notify_func,
void *notify_baton,
+ svn_wc_context_t *wc_ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- 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_not_present(&not_present, NULL, NULL, wc_ctx,
+ this_abspath, FALSE, 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_kind2(&kind, wc_ctx, this_abspath,
+ TRUE, 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 */,
+ NULL /* moved_from_abspath */,
+ 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;
+
+ 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;
+ const char *moved_from_abspath = NULL;
+
+ 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 */
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ /* 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))
@@ -477,118 +634,105 @@ harvest_committables(svn_wc_context_t *wc_ctx,
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,
- APR_HASH_KEY_STRING) != NULL));
+ || (status->changelist != NULL
+ && svn_hash_gets(changelists, status->changelist)
+ != 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))
+ /* 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_error_createf
- (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
- _("Entry '%s' has unexpectedly changed special status"),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
+ if (notify_func != NULL)
+ {
+ notify_func(notify_baton,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_failed_conflict,
+ scratch_pool),
+ scratch_pool);
+ }
- if (copy_mode
- && is_update_root
- && db_kind == svn_node_file)
+ 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)
{
- if (copy_mode)
- return SVN_NO_ERROR;
+ /* A node's type has changed before attempting to commit.
+ This also catches symlink vs non symlink changes */
+
+ 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 NODE is in our changelist, then examine it for conflicts. We
- need to bail out if any conflicts exist. */
- if (conflicted && matches_changelists)
+ 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
@@ -597,42 +741,6 @@ harvest_committables(svn_wc_context_t *wc_ctx,
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_revnum_t revision;
- const char *repos_relpath;
- svn_node_kind_t kind;
-
- /* Determine from what parent we would be the deleted child */
- SVN_ERR(svn_wc__node_get_origin(NULL, &revision, &repos_relpath,
- NULL, NULL, NULL, wc_ctx,
- svn_dirent_dirname(local_abspath,
- scratch_pool),
- FALSE, scratch_pool, scratch_pool));
-
- repos_relpath = svn_relpath_join(repos_relpath,
- svn_dirent_basename(local_abspath,
- NULL),
- scratch_pool);
-
- SVN_ERR(check_url_func(check_url_baton, &kind,
- svn_path_url_add_component2(repos_root_url,
- repos_relpath,
- scratch_pool),
- revision, 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)
@@ -646,142 +754,120 @@ harvest_committables(svn_wc_context_t *wc_ctx,
state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY;
cf_relpath = original_relpath;
cf_rev = original_rev;
+
+ if (status->moved_from_abspath && !copy_mode)
+ {
+ state_flags |= SVN_CLIENT_COMMIT_ITEM_MOVED_HERE;
+ moved_from_abspath = status->moved_from_abspath;
+ }
}
}
- /* 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));
+ svn_revnum_t dir_rev = SVN_INVALID_REVNUM;
+
+ if (!copy_mode_root && !status->switched && !is_added)
+ SVN_ERR(svn_wc__node_get_base(NULL, &dir_rev, NULL, NULL, NULL, NULL,
+ wc_ctx, svn_dirent_dirname(local_abspath,
+ scratch_pool),
+ FALSE /* ignore_enoent */,
+ FALSE /* show_hidden */,
+ scratch_pool, scratch_pool));
- if (copy_mode_root || node_rev != dir_rev)
+ if (copy_mode_root || status->switched || 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,
- scratch_pool, scratch_pool));
+ state_flags |= (SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_IS_COPY);
- if (cf_relpath)
- state_flags |= 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));
- }
+ 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;
+ /* 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,
+ moved_from_abspath,
+ 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);
@@ -794,22 +880,35 @@ harvest_committables(svn_wc_context_t *wc_ctx,
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 && 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 || (is_deleted && is_op_root && status->copied))
+ && 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)
{
@@ -827,73 +926,33 @@ harvest_committables(svn_wc_context_t *wc_ctx,
if (parent_is_copy)
parent_abspath = copy_root_abspath;
- if (!apr_hash_get(danglers, parent_abspath, APR_HASH_KEY_STRING))
+ if (!svn_hash_gets(danglers, parent_abspath))
{
- apr_hash_set(danglers,
- apr_pstrdup(result_pool, parent_abspath),
- APR_HASH_KEY_STRING,
- apr_pstrdup(result_pool, local_abspath));
+ svn_hash_sets(danglers, apr_pstrdup(result_pool, parent_abspath),
+ apr_pstrdup(result_pool, local_abspath));
}
}
}
- 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));
+ if (is_deleted && !is_added)
+ {
+ /* Skip all descendants */
+ if (status->kind == svn_node_dir)
+ baton->skip_below_abspath = apr_pstrdup(baton->result_pool,
+ local_abspath);
+ return SVN_NO_ERROR;
+ }
/* 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))
+ 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)
{
- 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(wc_ctx, 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),
- NULL, /* danglers */
- check_url_func, check_url_baton,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- result_pool,
- iterpool));
- }
-
- svn_pool_destroy(iterpool);
+ 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;
@@ -907,17 +966,19 @@ struct handle_descendants_baton
void *cancel_baton;
svn_client__check_url_kind_t check_url_func;
void *check_url_baton;
+ svn_client__committables_t *committables;
};
/* Helper for the commit harvesters */
static svn_error_t *
handle_descendants(void *baton,
- const void *key, apr_ssize_t klen, void *val,
- apr_pool_t *pool)
+ const void *key, apr_ssize_t klen, void *val,
+ apr_pool_t *pool)
{
struct handle_descendants_baton *hdb = baton;
apr_array_header_t *commit_items = val;
apr_pool_t *iterpool = svn_pool_create(pool);
+ const char *repos_root_url = key;
int i;
for (i = 0; i < commit_items->nelts; i++)
@@ -943,32 +1004,64 @@ handle_descendants(void *baton,
for (j = 0; j < absent_descendants->nelts; j++)
{
- int k;
- svn_boolean_t found_item = FALSE;
svn_node_kind_t kind;
+ svn_client_commit_item3_t *desc_item;
const char *relpath = APR_ARRAY_IDX(absent_descendants, j,
const char *);
const char *local_abspath = svn_dirent_join(item->path, relpath,
iterpool);
- /* If the path has a commit operation, we do nothing.
- (It will be deleted by the operation) */
- for (k = 0; k < commit_items->nelts; k++)
+ /* ### Need a sub-iterpool? */
+
+
+ /* We found a 'not present' descendant during a copy (at op_depth>0),
+ this is most commonly caused by copying some mixed revision tree.
+
+ In this case not present can imply that the node does not exist
+ in the parent revision, or that the node does. But we want to copy
+ the working copy state in which it does not exist, but might be
+ replaced. */
+
+ desc_item = svn_hash_gets(hdb->committables->by_path, local_abspath);
+
+ /* If the path has a commit operation (possibly at an higher
+ op_depth, we might want to turn an add in a replace. */
+ if (desc_item)
{
- svn_client_commit_item3_t *cmt_item =
- APR_ARRAY_IDX(commit_items, k, svn_client_commit_item3_t *);
+ const char *dir;
+ svn_boolean_t found_intermediate = FALSE;
+
+ if (desc_item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ continue; /* We already have a delete or replace */
+ else if (!(desc_item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ continue; /* Not a copy/add, just a modification */
- if (! strcmp(cmt_item->path, local_abspath))
+ dir = svn_dirent_dirname(local_abspath, iterpool);
+
+ while (strcmp(dir, item->path))
{
- found_item = TRUE;
- break;
+ svn_client_commit_item3_t *i_item;
+
+ i_item = svn_hash_gets(hdb->committables->by_path, dir);
+
+ if (i_item)
+ {
+ if ((i_item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ || (i_item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ {
+ found_intermediate = TRUE;
+ break;
+ }
+ }
+ dir = svn_dirent_dirname(dir, iterpool);
}
- }
- if (found_item)
- continue; /* We have an explicit delete or replace for this path */
+ if (found_intermediate)
+ continue; /* Some intermediate ancestor is an add or delete */
- /* ### Need a sub-iterpool? */
+ /* Fall through to detect if we need to turn the add in a
+ replace. */
+ }
if (hdb->check_url_func)
{
@@ -986,25 +1079,35 @@ handle_descendants(void *baton,
else
kind = svn_node_unknown; /* 'Ok' for a delete of something */
- {
- /* Add a new commit item that describes the delete */
- apr_pool_t *result_pool = commit_items->pool;
- svn_client_commit_item3_t *new_item
- = svn_client_commit_item3_create(result_pool);
-
- new_item->path = svn_dirent_join(item->path, relpath,
- result_pool);
- new_item->kind = kind;
- new_item->url = svn_path_url_add_component2(item->url, relpath,
- result_pool);
- new_item->revision = SVN_INVALID_REVNUM;
- new_item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
- new_item->incoming_prop_changes = apr_array_make(result_pool, 1,
- sizeof(svn_prop_t *));
-
- APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *)
- = new_item;
- }
+ if (desc_item)
+ {
+ /* Extend the existing add/copy item to create a replace */
+ desc_item->state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE;
+ continue;
+ }
+
+ /* Add a new commit item that describes the delete */
+
+ SVN_ERR(add_committable(hdb->committables,
+ svn_dirent_join(item->path, relpath,
+ iterpool),
+ kind,
+ repos_root_url,
+ svn_uri_skip_ancestor(
+ repos_root_url,
+ svn_path_url_add_component2(item->url,
+ relpath,
+ iterpool),
+ iterpool),
+ SVN_INVALID_REVNUM,
+ NULL /* copyfrom_relpath */,
+ SVN_INVALID_REVNUM,
+ NULL /* moved_from_abspath */,
+ SVN_CLIENT_COMMIT_ITEM_DELETE,
+ NULL /* lock tokens */,
+ NULL /* lock */,
+ commit_items->pool,
+ iterpool));
}
}
@@ -1029,6 +1132,7 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
apr_hash_t **lock_tokens,
const char *base_dir_abspath,
const apr_array_header_t *targets,
+ int depth_empty_start,
svn_depth_t depth,
svn_boolean_t just_locked,
const apr_array_header_t *changelists,
@@ -1041,7 +1145,6 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
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;
@@ -1086,8 +1189,6 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
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);
@@ -1096,34 +1197,6 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
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. */
@@ -1132,18 +1205,19 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
ctx->notify_baton2,
iterpool));
- SVN_ERR(harvest_committables(ctx->wc_ctx, target_abspath,
+ /* Are the remaining items externals with depth empty? */
+ if (i == depth_empty_start)
+ depth = svn_depth_empty;
+
+ 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,
danglers,
check_url_func, check_url_baton,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2,
- result_pool, iterpool));
+ ctx->wc_ctx, result_pool, iterpool));
}
hdb.wc_ctx = ctx->wc_ctx;
@@ -1151,6 +1225,7 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
hdb.cancel_baton = ctx->cancel_baton;
hdb.check_url_func = check_url_func;
hdb.check_url_baton = check_url_baton;
+ hdb.committables = *committables;
SVN_ERR(svn_iter_apr_hash(NULL, (*committables)->by_repository,
handle_descendants, &hdb, iterpool));
@@ -1215,7 +1290,8 @@ harvest_copy_committables(void *baton, void *item, apr_pool_t *pool)
/* Read the entry for this SRC. */
SVN_ERR_ASSERT(svn_dirent_is_absolute(pair->src_abspath_or_url));
- SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, NULL, btn->ctx->wc_ctx,
+ SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &repos_root_url, NULL,
+ btn->ctx->wc_ctx,
pair->src_abspath_or_url,
pool, pool));
@@ -1223,16 +1299,12 @@ harvest_copy_committables(void *baton, void *item, apr_pool_t *pool)
pair->dst_abspath_or_url, pool);
/* Handle this SRC. */
- SVN_ERR(harvest_committables(btn->ctx->wc_ctx,
- pair->src_abspath_or_url,
+ 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 */
+ NULL /* changelists */,
NULL,
btn->check_url_func,
btn->check_url_baton,
@@ -1240,13 +1312,14 @@ harvest_copy_committables(void *baton, void *item, apr_pool_t *pool)
btn->ctx->cancel_baton,
btn->ctx->notify_func2,
btn->ctx->notify_baton2,
- 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;
hdb.cancel_baton = btn->ctx->cancel_baton;
hdb.check_url_func = btn->check_url_func;
hdb.check_url_baton = btn->check_url_baton;
+ hdb.committables = btn->committables;
SVN_ERR(svn_iter_apr_hash(NULL, btn->committables->by_repository,
handle_descendants, &hdb, pool));
@@ -1357,14 +1430,9 @@ svn_client__condense_commit_items(const char **base_url,
{
svn_client_commit_item3_t *this_item
= APR_ARRAY_IDX(ci, i, svn_client_commit_item3_t *);
- size_t url_len = strlen(this_item->url);
- size_t base_url_len = strlen(*base_url);
- if (url_len > base_url_len)
- this_item->session_relpath = svn_uri__is_child(*base_url,
- this_item->url, pool);
- else
- this_item->session_relpath = "";
+ this_item->session_relpath = svn_uri_skip_ancestor(*base_url,
+ this_item->url, pool);
}
#ifdef SVN_CLIENT_COMMIT_DEBUG
/* ### TEMPORARY CODE ### */
@@ -1437,9 +1505,8 @@ do_item_commit(void **dir_baton,
apr_pool_t *pool)
{
struct item_commit_baton *icb = callback_baton;
- const svn_client_commit_item3_t *item = apr_hash_get(icb->commit_items,
- path,
- APR_HASH_KEY_STRING);
+ const svn_client_commit_item3_t *item = svn_hash_gets(icb->commit_items,
+ path);
svn_node_kind_t kind = item->kind;
void *file_baton = NULL;
apr_pool_t *file_pool = NULL;
@@ -1567,10 +1634,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. */
@@ -1594,10 +1658,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)
@@ -1610,14 +1671,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;
}
}
}
@@ -1635,11 +1699,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
@@ -1659,11 +1719,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;
}
}
@@ -1676,10 +1732,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)
@@ -1693,14 +1746,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;
}
}
}
@@ -1721,16 +1777,13 @@ 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. */
mod->item = item;
mod->file_baton = file_baton;
- apr_hash_set(file_mods, item->session_relpath, APR_HASH_KEY_STRING, mod);
+ svn_hash_sets(file_mods, item->session_relpath, mod);
}
else if (file_baton)
{
@@ -1739,25 +1792,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,
@@ -1765,7 +1810,6 @@ svn_client__do_commit(const char *base_url,
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,
@@ -1780,17 +1824,7 @@ svn_client__do_commit(const char *base_url,
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);
@@ -1802,7 +1836,7 @@ svn_client__do_commit(const char *base_url,
svn_client_commit_item3_t *item =
APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
const char *path = item->session_relpath;
- apr_hash_set(items_hash, path, APR_HASH_KEY_STRING, item);
+ svn_hash_sets(items_hash, path, item);
APR_ARRAY_PUSH(paths, const char *) = path;
}
@@ -1816,9 +1850,8 @@ svn_client__do_commit(const char *base_url,
cb_baton.base_url = base_url;
/* Drive the commit editor! */
- SVN_ERR(svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
- paths, do_item_commit, &cb_baton,
- scratch_pool));
+ SVN_ERR(svn_delta_path_driver2(editor, edit_baton, paths, TRUE,
+ do_item_commit, &cb_baton, scratch_pool));
/* Transmit outstanding text deltas. */
for (hi = apr_hash_first(scratch_pool, file_mods);
@@ -1870,12 +1903,8 @@ svn_client__do_commit(const char *base_url,
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);
+ svn_hash_sets(*sha1_checksums, item->path, new_text_base_sha1_checksum);
}
svn_pool_destroy(iterpool);
@@ -1885,269 +1914,6 @@ svn_client__do_commit(const char *base_url,
}
-#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,
@@ -2254,8 +2020,8 @@ svn_client__ensure_revprop_table(apr_hash_t **revprop_table_out,
{
new_revprop_table = apr_hash_make(pool);
}
- apr_hash_set(new_revprop_table, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
- svn_string_create(log_msg, pool));
+ svn_hash_sets(new_revprop_table, SVN_PROP_REVISION_LOG,
+ svn_string_create(log_msg, pool));
*revprop_table_out = new_revprop_table;
return SVN_NO_ERROR;
}