summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/commit.c')
-rw-r--r--subversion/libsvn_client/commit.c266
1 files changed, 104 insertions, 162 deletions
diff --git a/subversion/libsvn_client/commit.c b/subversion/libsvn_client/commit.c
index 07fdce19cdd0..4a945c887aa9 100644
--- a/subversion/libsvn_client/commit.c
+++ b/subversion/libsvn_client/commit.c
@@ -45,6 +45,7 @@
#include "client.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
#include "svn_private_config.h"
@@ -110,7 +111,8 @@ get_ra_editor(const svn_delta_editor_t **editor,
continue;
svn_pool_clear(iterpool);
- SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL,
+ NULL, NULL,
ctx->wc_ctx, item->path, FALSE, pool,
iterpool));
if (relpath)
@@ -203,8 +205,8 @@ collect_lock_tokens(apr_hash_t **result,
for (hi = apr_hash_first(pool, all_tokens); hi; hi = apr_hash_next(hi))
{
- const char *url = svn__apr_hash_index_key(hi);
- const char *token = svn__apr_hash_index_val(hi);
+ const char *url = apr_hash_this_key(hi);
+ const char *token = apr_hash_this_val(hi);
const char *relpath = svn_uri_skip_ancestor(base_url, url, pool);
if (relpath)
@@ -238,91 +240,30 @@ post_process_commit_item(svn_wc_committed_queue_t *queue,
loop_recurse = TRUE;
remove_lock = (! keep_locks && (item->state_flags
- & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
+ & (SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN
+ | SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_DELETE)));
- /* When the node was deleted (or replaced), we need to always remove the
- locks, as they're invalidated on the server. We cannot honor the
+ /* When the node was deleted (or replaced), we need to always remove the
+ locks, as they're invalidated on the server. We cannot honor the
SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN flag here because it does not tell
us whether we have locked children. */
if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
remove_lock = TRUE;
- return svn_wc_queue_committed3(queue, wc_ctx, item->path,
- loop_recurse, item->incoming_prop_changes,
+ return svn_error_trace(
+ svn_wc_queue_committed4(queue, wc_ctx, item->path,
+ loop_recurse,
+ 0 != (item->state_flags &
+ (SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_DELETE
+ | SVN_CLIENT_COMMIT_ITEM_TEXT_MODS
+ | SVN_CLIENT_COMMIT_ITEM_PROP_MODS)),
+ item->incoming_prop_changes,
remove_lock, !keep_changelists,
- sha1_checksum, scratch_pool);
-}
-
-
-static svn_error_t *
-check_nonrecursive_dir_delete(svn_wc_context_t *wc_ctx,
- const char *target_abspath,
- svn_depth_t depth,
- apr_pool_t *scratch_pool)
-{
- svn_node_kind_t kind;
-
- SVN_ERR_ASSERT(depth != svn_depth_infinity);
-
- SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, target_abspath,
- TRUE, FALSE, scratch_pool));
-
-
- /* ### TODO(sd): This check is slightly too strict. It should be
- ### possible to:
- ###
- ### * delete a directory containing only files when
- ### depth==svn_depth_files;
- ###
- ### * delete a directory containing only files and empty
- ### subdirs when depth==svn_depth_immediates.
- ###
- ### But for now, we insist on svn_depth_infinity if you're
- ### going to delete a directory, because we're lazy and
- ### trying to get depthy commits working in the first place.
- ###
- ### This would be fairly easy to fix, though: just, well,
- ### check the above conditions!
- ###
- ### GJS: I think there may be some confusion here. there is
- ### the depth of the commit, and the depth of a checked-out
- ### directory in the working copy. Delete, by its nature, will
- ### always delete all of its children, so it seems a bit
- ### strange to worry about what is in the working copy.
- */
- if (kind == svn_node_dir)
- {
- svn_wc_schedule_t schedule;
-
- /* ### Looking at schedule is probably enough, no need for
- pristine compare etc. */
- SVN_ERR(svn_wc__node_get_schedule(&schedule, NULL,
- wc_ctx, target_abspath,
- scratch_pool));
-
- if (schedule == svn_wc_schedule_delete
- || schedule == svn_wc_schedule_replace)
- {
- const apr_array_header_t *children;
-
- SVN_ERR(svn_wc__node_get_children(&children, wc_ctx,
- target_abspath, TRUE,
- scratch_pool, scratch_pool));
-
- if (children->nelts > 0)
- return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Cannot delete the directory '%s' "
- "in a non-recursive commit "
- "because it has children"),
- svn_dirent_local_style(target_abspath,
- scratch_pool));
- }
- }
-
- return SVN_NO_ERROR;
+ sha1_checksum, scratch_pool));
}
-
/* Given a list of committables described by their common base abspath
BASE_ABSPATH and a list of relative dirents TARGET_RELPATHS determine
which absolute paths must be locked to commit all these targets and
@@ -393,8 +334,8 @@ determine_lock_targets(apr_array_header_t **lock_targets,
hi = apr_hash_next(hi))
{
const char *common;
- const char *wcroot_abspath = svn__apr_hash_index_key(hi);
- apr_array_header_t *wc_targets = svn__apr_hash_index_val(hi);
+ const char *wcroot_abspath = apr_hash_this_key(hi);
+ apr_array_header_t *wc_targets = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -420,8 +361,7 @@ determine_lock_targets(apr_array_header_t **lock_targets,
SVN_ERR(svn_dirent_condense_targets(&common, &wc_targets, wc_targets,
FALSE, iterpool, iterpool));
- qsort(wc_targets->elts, wc_targets->nelts, wc_targets->elt_size,
- svn_sort_compare_paths);
+ svn_sort__array(wc_targets, svn_sort_compare_paths);
if (wc_targets->nelts == 0
|| !svn_path_is_empty(APR_ARRAY_IDX(wc_targets, 0, const char*))
@@ -599,6 +539,7 @@ svn_client_commit6(const apr_array_header_t *targets,
const char *current_abspath;
const char *notify_prefix;
int depth_empty_after = -1;
+ apr_hash_t *move_youngest = NULL;
int i;
SVN_ERR_ASSERT(depth != svn_depth_unknown && depth != svn_depth_exclude);
@@ -673,26 +614,6 @@ svn_client_commit6(const apr_array_header_t *targets,
base_abspath,
pool);
- /* If a non-recursive commit is desired, do not allow a deleted directory
- as one of the targets. */
- if (depth != svn_depth_infinity && ! commit_as_operations)
- for (i = 0; i < rel_targets->nelts; i++)
- {
- const char *relpath = APR_ARRAY_IDX(rel_targets, i, const char *);
- const char *target_abspath;
-
- svn_pool_clear(iterpool);
-
- target_abspath = svn_dirent_join(base_abspath, relpath, iterpool);
-
- cmt_err = svn_error_trace(
- check_nonrecursive_dir_delete(ctx->wc_ctx, target_abspath,
- depth, iterpool));
-
- if (cmt_err)
- goto cleanup;
- }
-
/* Crawl the working copy for commit items. */
{
struct check_url_kind_baton cukb;
@@ -741,7 +662,7 @@ svn_client_commit6(const apr_array_header_t *targets,
apr_hash_index_t *hi = apr_hash_first(iterpool,
committables->by_repository);
- commit_items = svn__apr_hash_index_val(hi);
+ commit_items = apr_hash_this_val(hi);
}
/* If our array of targets contains only locks (and no actual file
@@ -789,62 +710,12 @@ svn_client_commit6(const apr_array_header_t *targets,
if (cmt_err)
goto cleanup;
- if (moved_from_abspath && delete_op_root_abspath &&
- strcmp(moved_from_abspath, delete_op_root_abspath) == 0)
-
+ if (moved_from_abspath && delete_op_root_abspath)
{
- svn_boolean_t found_delete_half =
- (svn_hash_gets(committables->by_path, delete_op_root_abspath)
- != NULL);
-
- if (!found_delete_half)
- {
- const char *delete_half_parent_abspath;
-
- /* The delete-half isn't in the commit target list.
- * However, it might itself be the child of a deleted node,
- * either because of another move or a deletion.
- *
- * For example, consider: mv A/B B; mv B/C C; commit;
- * C's moved-from A/B/C is a child of the deleted A/B.
- * A/B/C does not appear in the commit target list, but
- * A/B does appear.
- * (Note that moved-from information is always stored
- * relative to the BASE tree, so we have 'C moved-from
- * A/B/C', not 'C moved-from B/C'.)
- *
- * An example involving a move and a delete would be:
- * mv A/B C; rm A; commit;
- * Now C is moved-from A/B which does not appear in the
- * commit target list, but A does appear.
- */
-
- /* Scan upwards for a deletion op-root from the
- * delete-half's parent directory. */
- delete_half_parent_abspath =
- svn_dirent_dirname(delete_op_root_abspath, iterpool);
- if (strcmp(delete_op_root_abspath,
- delete_half_parent_abspath) != 0)
- {
- const char *parent_delete_op_root_abspath;
-
- cmt_err = svn_error_trace(
- svn_wc__node_get_deleted_ancestor(
- &parent_delete_op_root_abspath,
- ctx->wc_ctx, delete_half_parent_abspath,
- iterpool, iterpool));
- if (cmt_err)
- goto cleanup;
-
- if (parent_delete_op_root_abspath)
- found_delete_half =
- (svn_hash_gets(committables->by_path,
- parent_delete_op_root_abspath)
- != NULL);
- }
- }
+ svn_client_commit_item3_t *delete_half =
+ svn_hash_gets(committables->by_path, delete_op_root_abspath);
- if (!found_delete_half)
+ if (!delete_half)
{
cmt_err = svn_error_createf(
SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -854,8 +725,32 @@ svn_client_commit6(const apr_array_header_t *targets,
svn_dirent_local_style(item->path, iterpool),
svn_dirent_local_style(delete_op_root_abspath,
iterpool));
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(
+ delete_op_root_abspath,
+ svn_wc_notify_failed_requires_target,
+ iterpool);
+ notify->err = cmt_err;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
+ }
+
goto cleanup;
}
+ else if (delete_half->revision == item->copyfrom_rev)
+ {
+ /* Ok, now we know that we perform an out-of-date check
+ on the copyfrom location. Remember this for a fixup
+ round right before committing. */
+
+ if (!move_youngest)
+ move_youngest = apr_hash_make(pool);
+
+ svn_hash_sets(move_youngest, item->path, item);
+ }
}
}
@@ -885,6 +780,19 @@ svn_client_commit6(const apr_array_header_t *targets,
svn_dirent_local_style(item->path, iterpool),
svn_dirent_local_style(copy_op_root_abspath,
iterpool));
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(
+ copy_op_root_abspath,
+ svn_wc_notify_failed_requires_target,
+ iterpool);
+ notify->err = cmt_err;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
+ }
+
goto cleanup;
}
}
@@ -941,6 +849,37 @@ svn_client_commit6(const apr_array_header_t *targets,
if (cmt_err)
goto cleanup;
+ if (move_youngest != NULL)
+ {
+ apr_hash_index_t *hi;
+ svn_revnum_t youngest;
+
+ SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest, pool));
+
+ for (hi = apr_hash_first(iterpool, move_youngest);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_client_commit_item3_t *item = apr_hash_this_val(hi);
+
+ /* We delete the original side with its original revision and will
+ receive an out-of-date error if that node changed since that
+ revision.
+
+ The copy is of that same revision and we know that this revision
+ didn't change between this revision and youngest. So we can just
+ as well commit a copy from youngest.
+
+ Note that it is still possible to see gaps between the delete and
+ copy revisions as the repository might handle multiple commits
+ at the same time (or when an out of date proxy is involved), but
+ in general it should decrease the number of gaps. */
+
+ if (item->copyfrom_rev < youngest)
+ item->copyfrom_rev = youngest;
+ }
+ }
+
cmt_err = svn_error_trace(
get_ra_editor(&editor, &edit_baton, ra_session, ctx,
log_msg, commit_items, revprop_table,
@@ -996,6 +935,9 @@ svn_client_commit6(const apr_array_header_t *targets,
commit_info->author,
ctx->cancel_func, ctx->cancel_baton,
iterpool);
+
+ if (bump_err)
+ goto cleanup;
}
cleanup:
@@ -1004,16 +946,16 @@ svn_client_commit6(const apr_array_header_t *targets,
working copies. */
if (timestamp_sleep)
{
- const char *wcroot_abspath;
- svn_error_t *err = svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+ const char *sleep_abspath;
+ svn_error_t *err = svn_wc__get_wcroot(&sleep_abspath, ctx->wc_ctx,
base_abspath, pool, pool);
if (err)
{
svn_error_clear(err);
- wcroot_abspath = NULL;
+ sleep_abspath = base_abspath;
}
- svn_io_sleep_for_timestamps(wcroot_abspath, pool);
+ svn_io_sleep_for_timestamps(sleep_abspath, pool);
}
/* Abort the commit if it is still in progress. */