Hey Stefan,
I write a new patch as you want. I have tested it on my machine and you can also test it to
see whether it can pass the test. Thank you:)
log message:
[[[
* subversion/include/svn_client.h
(svn_client_commit5): New API. Which do commit from multiple working copies.
It does one single commit for each working copy.
* subversion/libsvn_client/commit.c
(commit_packet_t): New struct. It is used to store information needed by commit.
(create_commit_packet): New function.It is used to create commit_packet_t.
(do_one_single_commit): New function. It is used to do one single commit for each
collected commit packet.
(do_commit): New function. When locking root failed, this function call
collect_commit_packets to collect commit packets and call do_one_single_commit
to do one single commit for each working copy.
(collect_commit_packets): New function. It is used to collect commit_packet_t
when locking root failed.
(svn_client_commit5): It is renamed from svn_client_commit4 and is implement of
API svn_client_commit5() in subversion/include/svn_client.h.
(svn_client_commit4): New function. It does the same work as svn_client_commit4
did originally.
]]]
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 38371)
+++ subversion/include/svn_client.h (working copy)
@@ -1705,6 +1705,25 @@
*/
/**
+* This function is the same as svn_client_commit4 except that it does
+* multiple commits when @a multiple_commit is set TRUE, one for each
+* target working copy. Essentially:
+* svn commit wc1; svn commit wc2; ...; svn commit wcN;
+* Also it returns an array @a commit_info_p.
+*/
+svn_error_t *
+svn_client_commit5(apr_array_header_t **commit_info_p,
+ const apr_array_header_t *targets,
+ svn_depth_t depth,
+ svn_boolean_t multiple_commit,
+ svn_boolean_t keep_locks,
+ svn_boolean_t keep_changelists,
+ const apr_array_header_t *changelists,
+ const apr_hash_t *revprop_table,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool);
+
+/**
* Commit files or directories into repository, authenticating with
* the authentication baton cached in @a ctx, and using
* @a ctx->log_msg_func3/@a ctx->log_msg_baton3 to obtain the log message.
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c (revision 38371)
+++ subversion/libsvn_client/commit.c (working copy)
@@ -72,7 +72,46 @@
} import_ctx_t;
+typedef struct commit_packet_t
+{
+ /* Working copy root of a wc */
+ const char *base_dir;
+ /* Targets under base_dir */
+ apr_array_header_t *targets;
+
+ /* Relative paths of targets base on base_dir */
+ apr_array_header_t *rel_targets;
+
+ svn_wc_adm_access_t *base_dir_access;
+
+} commit_packet_t;
+
+static commit_packet_t *
+create_commit_packet(const char *base_dir,
+ const char *target,
+ const char *rel_target,
+ svn_wc_adm_access_t *base_dir_access,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *targets;
+ apr_array_header_t *rel_targets;
+ commit_packet_t *commit_packet;
+
+ targets = apr_array_make(pool, 1, sizeof(char *));
+ rel_targets = apr_array_make(pool, 1, sizeof(char *));
+ APR_ARRAY_PUSH(targets, const char *) = target;
+ APR_ARRAY_PUSH(rel_targets, const char *) = rel_target;
+
+ commit_packet = apr_pcalloc(pool, sizeof(commit_packet_t));
+ commit_packet->base_dir = base_dir;
+ commit_packet->base_dir_access = base_dir_access;
+ commit_packet->rel_targets = rel_targets;
+ commit_packet->targets = targets;
+
+ return commit_packet;
+}
+
/* Apply PATH's contents (as a delta against the empty string) to
FILE_BATON in EDITOR. Use POOL for any temporary allocation.
PROPERTIES is the set of node properties set on this file.
@@ -1292,10 +1331,391 @@
return SVN_NO_ERROR;
}
+static svn_error_t *
+do_one_single_commit(svn_commit_info_t **commit_info_p,
+ svn_wc_adm_access_t *base_dir_access,
+ const char *base_dir,
+ const apr_array_header_t *rel_targets,
+ svn_depth_t depth,
+ svn_boolean_t keep_locks,
+ svn_boolean_t keep_changelists,
+ const apr_array_header_t *changelists,
+ const apr_hash_t *revprop_table,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ const svn_delta_editor_t *editor;
+ void *edit_baton;
+ svn_ra_session_t *ra_session;
+ const char *log_msg;
+ const char *base_url;
+ apr_hash_t *committables;
+ apr_hash_t *lock_tokens;
+ apr_hash_t *tempfiles = NULL;
+ apr_hash_t *checksums;
+ apr_array_header_t *commit_items;
+ svn_error_t *cmt_err = SVN_NO_ERROR, *unlock_err = SVN_NO_ERROR;
+ svn_error_t *bump_err = SVN_NO_ERROR, *cleanup_err = SVN_NO_ERROR;
+ svn_boolean_t commit_in_progress = FALSE;
+ const char *current_dir = "";
+ const char *notify_prefix;
+
+ /* Crawl the working copy for commit items. */
+ if ((cmt_err = svn_client__harvest_committables(&committables,
+ &lock_tokens,
+ base_dir_access,
+ rel_targets,
+ depth,
+ ! keep_locks,
+ changelists,
+ ctx,
+ pool)))
+ goto cleanup;
+
+ /* ### todo: Currently there should be only one hash entry, which
+ has a hacked name until we have the entries files storing
+ canonical repository URLs. Then, the hacked name can go away
+ and be replaced with a canonical repos URL, and from there we
+ are poised to started handling nested working copies. See
+ http://subversion.tigris.org/issues/show_bug.cgi?id=960. */
+ if (! ((commit_items = apr_hash_get(committables,
+ SVN_CLIENT__SINGLE_REPOS_NAME,
+ APR_HASH_KEY_STRING))))
+ goto cleanup;
+
+ /* If our array of targets contains only locks (and no actual file
+ or prop modifications), then we return here to avoid committing a
+ revision with no changes. */
+ {
+ svn_boolean_t not_found_changed_path = TRUE;
+
+
+ cmt_err = svn_iter_apr_array(¬_found_changed_path,
+ commit_items,
+ commit_item_is_changed, NULL, pool);
+ if (not_found_changed_path || cmt_err)
+ goto cleanup;
+ }
+
+ /* Go get a log message. If an error occurs, or no log message is
+ specified, abort the operation. */
+ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
+ {
+ const char *tmp_file;
+ cmt_err = svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
+ ctx, pool);
+
+ if (cmt_err || (! log_msg))
+ goto cleanup;
+ }
+ else
+ log_msg = "";
+
+ /* Sort and condense our COMMIT_ITEMS. */
+ if ((cmt_err = svn_client__condense_commit_items(&base_url,
+ commit_items,
+ pool)))
+ goto cleanup;
+
+ /* Collect our lock tokens with paths relative to base_url. */
+ if ((cmt_err = collect_lock_tokens(&lock_tokens, lock_tokens, base_url,
+ pool)))
+ goto cleanup;
+
+ if ((cmt_err = get_ra_editor(&ra_session,
+ &editor, &edit_baton, ctx,
+ base_url, base_dir, base_dir_access, log_msg,
+ commit_items, revprop_table, commit_info_p,
+ TRUE, lock_tokens, keep_locks, pool)))
+ goto cleanup;
+
+ /* Make a note that we have a commit-in-progress. */
+ commit_in_progress = TRUE;
+
+ if ((cmt_err = svn_dirent_get_absolute(¤t_dir,
+ current_dir, pool)))
+ goto cleanup;
+
+ /* Determine prefix to strip from the commit notify messages */
+ notify_prefix = svn_dirent_get_longest_ancestor(current_dir, base_dir, pool);
+
+ /* Perform the commit. */
+ cmt_err = svn_client__do_commit(base_url, commit_items, base_dir_access,
+ editor, edit_baton,
+ notify_prefix,
+ &tempfiles, &checksums, ctx, pool);
+
+ /* Handle a successful commit. */
+ if ((! cmt_err)
+ || (cmt_err->apr_err == SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
+ {
+ svn_wc_committed_queue_t *queue = svn_wc_committed_queue_create(pool);
+ struct post_commit_baton btn;
+
+ btn.queue = queue;
+ btn.qpool = pool;
+ btn.base_dir_access = base_dir_access;
+ btn.keep_changelists = keep_changelists;
+ btn.keep_locks = keep_locks;
+ btn.checksums = checksums;
+
+ /* Make a note that our commit is finished. */
+ commit_in_progress = FALSE;
+
+ bump_err = svn_iter_apr_array(NULL, commit_items,
+ post_process_commit_item, &btn,
+ pool);
+ if (bump_err)
+ goto cleanup;
+
+ SVN_ERR_ASSERT(*commit_info_p);
+ bump_err
+ = svn_wc_process_committed_queue(queue, base_dir_access,
+ (*commit_info_p)->revision,
+ (*commit_info_p)->date,
+ (*commit_info_p)->author,
+ pool);
+ }
+
+ /* Sleep to ensure timestamp integrity. */
+ svn_io_sleep_for_timestamps(base_dir, pool);
+
+ cleanup:
+ /* Abort the commit if it is still in progress. */
+ if (commit_in_progress)
+ svn_error_clear(editor->abort_edit(edit_baton, pool));
+
+ /* A bump error is likely to occur while running a working copy log file,
+ explicitly unlocking and removing temporary files would be wrong in
+ that case. A commit error (cmt_err) should only occur before any
+ attempt to modify the working copy, so it doesn't prevent explicit
+ clean-up. */
+ if (! bump_err)
+ {
+ unlock_err = svn_wc_adm_close2(base_dir_access, pool);
+
+ if (! unlock_err)
+ cleanup_err = remove_tmpfiles(tempfiles, pool);
+ }
+
+ /* As per our promise, if *commit_info_p isn't set, provide a default where
+ rev = SVN_INVALID_REVNUM. */
+ if (! *commit_info_p)
+ *commit_info_p = svn_create_commit_info(pool);
+
+ return reconcile_errors(cmt_err, unlock_err, bump_err, cleanup_err, pool);
+}
+
+static svn_error_t *
+collect_commit_packets(apr_array_header_t **commit_packets,
+ const apr_array_header_t *targets,
+ const char *base_dir,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ commit_packet_t *commit_packet;
+ apr_array_header_t *components;
+ apr_pool_t *iterpool, *iterpool1;
+ svn_boolean_t found_parent = FALSE, error_happened = FALSE;
+ svn_wc_adm_access_t *base_dir_access;
+ svn_wc_adm_access_t *adm_access;
+ svn_error_t *lock_err = SVN_NO_ERROR, *check_err = SVN_NO_ERROR;
+ const char *rel_target;
+ const char *target;
+ const char *new_base_dir;
+ const char *component;
+ int i, j;
+
+ *commit_packets = apr_array_make(result_pool, 0, sizeof(commit_packet_t *));
+ iterpool = svn_pool_create(scratch_pool);
+
+ for (i = 0; i < targets->nelts; i++)
+ {
+ svn_pool_clear(iterpool);
+ target = APR_ARRAY_IDX(targets, i, const char *);
+ found_parent = FALSE;
+
+ /* Try to find target's locked parent. */
+ iterpool1 = svn_pool_create(iterpool);
+ for (j = 0; j < (*commit_packets)->nelts; j++)
+ {
+ svn_pool_clear(iterpool1);
+ commit_packet = APR_ARRAY_IDX(*commit_packets, j, commit_packet_t *);
+
+ /* Check whether target is under base_dir_access. */
+ check_err = svn_wc_adm_probe_retrieve(&adm_access,
+ commit_packet->base_dir_access,
+ target, iterpool1);
+ if (check_err)
+ {
+ if (check_err->apr_err = SVN_ERR_WC_NOT_LOCKED)
+ {
+ svn_error_clear(check_err);
+ continue;
+ }
+ else
+ return check_err;
+ }
+ rel_target = svn_dirent_is_child(commit_packet->base_dir,
+ target, iterpool1);
+ if (rel_target == NULL)
+ rel_target = "";
+ APR_ARRAY_PUSH(commit_packet->targets, const char *) = target;
+ APR_ARRAY_PUSH(commit_packet->rel_targets, const char *)
+ = apr_pstrdup(result_pool, rel_target);
+ found_parent = TRUE;
+ break;
+ }
+ svn_pool_destroy(iterpool1);
+
+ /* If no suitable working copy root was found for the current target path.
+ * Walk the current target path downwards, starting from the common
+ * root (the root which we could not lock, in the code this is often
+ * called the "base_dir").
+ * Try to lock the current directory at each step.
+ * If locking succeeds, we have found a new WC root!
+ * Store its access baton in the set of known working copy roots.
+ * Put the current target path into the group of the root we just found.
+ */
+ if (! found_parent)
+ {
+ new_base_dir = apr_pstrdup(iterpool, base_dir);
+ rel_target = svn_dirent_is_child(new_base_dir, target,
+ iterpool);
+
+ /* If the wc are nested, we should lock from the root( base_dir here).
+ * So we add "" to the components firstly.
+ */
+ components = apr_array_make(iterpool, 1, sizeof (const char *));
+ APR_ARRAY_PUSH(components, const char *) = "";
+ if (rel_target)
+ {
+ apr_array_header_t *temp_components
+ = svn_path_decompose(rel_target, iterpool);
+ for (j = 0; j < temp_components->nelts; j++)
+ {
+ component = APR_ARRAY_IDX(temp_components, j, const char *);
+ if(component)
+ APR_ARRAY_PUSH(components, const char *) = component;
+ }
+ }
+
+ iterpool1 = svn_pool_create(iterpool);
+ for (j = 0; j < components->nelts; j++)
+ {
+ svn_pool_clear(iterpool1);
+ component = APR_ARRAY_IDX(components, j, const char *);
+ new_base_dir = svn_dirent_join(new_base_dir, component,
+ iterpool1);
+ lock_err = svn_wc_adm_open3(&base_dir_access, NULL,
+ new_base_dir,
+ TRUE, /* Write lock */
+ -1, /* lock levels */
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ result_pool);
+ if (lock_err)
+ {
+ if (lock_err->apr_err == SVN_ERR_WC_NOT_DIRECTORY
+ || lock_err->apr_err == SVN_ERR_WC_LOCKED)
+ svn_error_clear(lock_err);
+ else
+ {
+ error_happened = TRUE;
+ break;
+ }
+ }
+ else
+ {
+ check_err = svn_wc_adm_probe_retrieve(&adm_access,
+ base_dir_access,
+ target,
+ iterpool1);
+ if (! check_err)
+ {
+ rel_target = svn_dirent_is_child(new_base_dir, target,
+ iterpool);
+ if (rel_target == NULL)
+ rel_target = "";
+ commit_packet = create_commit_packet
+ (apr_pstrdup(result_pool, new_base_dir),
+ target,
+ apr_pstrdup(result_pool, rel_target),
+ base_dir_access, result_pool);
+ APR_ARRAY_PUSH(*commit_packets, commit_packet_t *)
+ = commit_packet;
+ break;
+ }
+ svn_error_clear(check_err);
+ SVN_ERR(svn_wc_adm_close2(base_dir_access, iterpool1));
+ }
+ }
+
+ /* If we have tried all ancestors but still can not lock the
+ * target. */
+ if (j >= components->nelts)
+ return svn_error_createf
+ (SVN_ERR_BASE, NULL,
+ _("'%s' cannot be locked"), target);
+ svn_pool_destroy(iterpool1);
+ }
+ if (error_happened)
+ break;
+ }
+ svn_pool_destroy(iterpool);
+ return error_happened ? lock_err : SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+do_commit(apr_array_header_t **commit_info,
+ const apr_array_header_t *targets,
+ const char *base_dir,
+ svn_depth_t depth,
+ svn_boolean_t keep_locks,
+ svn_boolean_t keep_changelists,
+ const apr_array_header_t *changelists,
+ const apr_hash_t *revprop_table,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ svn_commit_info_t *commit_info_p;
+ apr_array_header_t *commit_packets;
+ apr_pool_t *scratch_pool;
+ commit_packet_t *commit_packet;
+ int i;
+
+ scratch_pool = svn_pool_create(pool);
+ //commit_packets = apr_array_make(pool, 0, sizeof(commit_packet_t *));
+ SVN_ERR(collect_commit_packets(&commit_packets, targets, base_dir,
+ ctx, pool, scratch_pool));
+ svn_pool_destroy(scratch_pool);
+
+ /* Commit in turn */
+ for (i = 0; i < commit_packets->nelts; i++)
+ {
+ commit_info_p = NULL;
+ commit_packet = APR_ARRAY_IDX(commit_packets, i, commit_packet_t *);
+
+ SVN_ERR(do_one_single_commit(&commit_info_p,
+ commit_packet->base_dir_access,
+ commit_packet->base_dir,
+ commit_packet->rel_targets,
+ depth, keep_locks, keep_changelists,
+ changelists, revprop_table, ctx,
+ pool));
+ if (commit_info_p)
+ APR_ARRAY_PUSH(*commit_info, svn_commit_info_t *) = commit_info_p;
+ }
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
-svn_client_commit4(svn_commit_info_t **commit_info_p,
+svn_client_commit5(apr_array_header_t **commit_info,
const apr_array_header_t *targets,
svn_depth_t depth,
+ svn_boolean_t multiple_commit,
svn_boolean_t keep_locks,
svn_boolean_t keep_changelists,
const apr_array_header_t *changelists,
@@ -1303,30 +1723,20 @@
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const svn_delta_editor_t *editor;
- void *edit_baton;
- svn_ra_session_t *ra_session;
- const char *log_msg;
const char *base_dir;
- const char *base_url;
const char *target;
apr_array_header_t *rel_targets;
apr_array_header_t *dirs_to_lock;
apr_array_header_t *dirs_to_lock_recursive;
+ apr_pool_t *iterpool;
svn_boolean_t lock_base_dir_recursive = FALSE;
- apr_hash_t *committables;
- apr_hash_t *lock_tokens;
- apr_hash_t *tempfiles = NULL;
- apr_hash_t *checksums;
svn_wc_adm_access_t *base_dir_access;
- apr_array_header_t *commit_items;
- svn_error_t *cmt_err = SVN_NO_ERROR, *unlock_err = SVN_NO_ERROR;
- svn_error_t *bump_err = SVN_NO_ERROR, *cleanup_err = SVN_NO_ERROR;
- svn_boolean_t commit_in_progress = FALSE;
- const char *current_dir = "";
- const char *notify_prefix;
+ svn_commit_info_t *commit_info_p;
+ svn_error_t *lock_err = SVN_NO_ERROR, *cmt_err = SVN_NO_ERROR;
int i;
+ *commit_info = apr_array_make(pool, 1, sizeof(commit_info_p));
+
/* Committing URLs doesn't make sense, so error if it's tried. */
for (i = 0; i < targets->nelts; i++)
{
@@ -1344,8 +1754,15 @@
/* No targets means nothing to commit, so just return. */
if (! base_dir)
- goto cleanup;
+ {
+ /* As per our promise, if *commit_info isn't set, provide a
+ * default where rev = SVN_INVALID_REVNUM. */
+ commit_info_p = svn_create_commit_info(pool);
+ APR_ARRAY_PUSH(*commit_info, svn_commit_info_t *) = commit_info_p;
+ return SVN_NO_ERROR;
+ }
+
/* When svn_path_condense_targets() was written, we didn't have real
* depths, we just had recursive / nonrecursive.
*
@@ -1457,7 +1874,7 @@
}
else if (! lock_base_dir_recursive)
{
- apr_pool_t *subpool = svn_pool_create(pool);
+ iterpool = svn_pool_create(pool);
SVN_ERR(adjust_rel_targets(&base_dir, &rel_targets,
base_dir, rel_targets,
@@ -1467,13 +1884,13 @@
{
svn_node_kind_t kind;
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
target = svn_dirent_join(base_dir,
APR_ARRAY_IDX(rel_targets, i, const char *),
- subpool);
+ iterpool);
- SVN_ERR(svn_io_check_path(target, &kind, subpool));
+ SVN_ERR(svn_io_check_path(target, &kind, iterpool));
/* If the final target is a dir, we want to lock it */
if (kind == svn_node_dir)
@@ -1498,7 +1915,7 @@
Do nothing if target is already the base_dir. */
if (strcmp(target, base_dir) != 0)
{
- target = svn_dirent_dirname(target, subpool);
+ target = svn_dirent_dirname(target, iterpool);
while (strcmp(target, base_dir) != 0)
{
@@ -1507,7 +1924,7 @@
APR_ARRAY_PUSH(dirs_to_lock,
const char *) = apr_pstrdup(pool, target);
- parent_dir = svn_dirent_dirname(target, subpool);
+ parent_dir = svn_dirent_dirname(target, iterpool);
if (strcmp(parent_dir, target) == 0)
break; /* Reached root directory */
@@ -1517,16 +1934,16 @@
}
}
- svn_pool_destroy(subpool);
+ svn_pool_destroy(iterpool);
}
- SVN_ERR(svn_wc_adm_open3(&base_dir_access, NULL, base_dir,
+ lock_err = svn_wc_adm_open3(&base_dir_access, NULL, base_dir,
TRUE, /* Write lock */
lock_base_dir_recursive ? -1 : 0, /* lock levels */
ctx->cancel_func, ctx->cancel_baton,
- pool));
+ pool);
- if (!lock_base_dir_recursive)
+ if (!lock_err && !lock_base_dir_recursive)
{
apr_array_header_t *unique_dirs_to_lock;
struct lock_dirs_baton btn;
@@ -1566,164 +1983,89 @@
lock_dirs_for_commit, &btn, pool));
}
- /* One day we might support committing from multiple working copies, but
- we don't yet. This check ensures that we don't silently commit a
- subset of the targets.
+ /* If you do not want to do multiple commit or no error happens, just
+ * try to commit all the targets. */
+ if (!multiple_commit || !lock_err)
+ {
+ if (lock_err)
+ return lock_err;
+ commit_info_p = NULL;
- At the same time, if a non-recursive commit is desired, do not
- allow a deleted directory as one of the targets. */
- {
- struct check_dir_delete_baton btn;
+ /* Check whether all targets are under base_dir_access.
+ * At the same time, if a non-recursive commit is desired, do not
+ * allow a deleted directory as one of the targets. */
+ {
+ struct check_dir_delete_baton btn;
- btn.base_dir_access = base_dir_access;
- btn.depth = depth;
- SVN_ERR(svn_iter_apr_array(NULL, targets,
- check_nonrecursive_dir_delete, &btn,
- pool));
- }
-
- /* Crawl the working copy for commit items. */
- if ((cmt_err = svn_client__harvest_committables(&committables,
- &lock_tokens,
- base_dir_access,
- rel_targets,
- depth,
- ! keep_locks,
- changelists,
- ctx,
- pool)))
- goto cleanup;
-
- /* ### todo: Currently there should be only one hash entry, which
- has a hacked name until we have the entries files storing
- canonical repository URLs. Then, the hacked name can go away
- and be replaced with a canonical repos URL, and from there we
- are poised to started handling nested working copies. See
- http://subversion.tigris.org/issues/show_bug.cgi?id=960. */
- if (! ((commit_items = apr_hash_get(committables,
- SVN_CLIENT__SINGLE_REPOS_NAME,
- APR_HASH_KEY_STRING))))
- goto cleanup;
-
- /* If our array of targets contains only locks (and no actual file
- or prop modifications), then we return here to avoid committing a
- revision with no changes. */
- {
- svn_boolean_t not_found_changed_path = TRUE;
-
-
- cmt_err = svn_iter_apr_array(¬_found_changed_path,
- commit_items,
- commit_item_is_changed, NULL, pool);
- if (not_found_changed_path || cmt_err)
- goto cleanup;
- }
-
- /* Go get a log message. If an error occurs, or no log message is
- specified, abort the operation. */
- if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
+ btn.base_dir_access = base_dir_access;
+ btn.depth = depth;
+ cmt_err = svn_iter_apr_array(NULL, targets,
+ check_nonrecursive_dir_delete, &btn,
+ pool);
+ }
+ if (!multiple_commit || ! cmt_err)
+ {
+ if (cmt_err)
+ return cmt_err;
+ SVN_ERR(do_one_single_commit(&commit_info_p, base_dir_access,
+ base_dir, rel_targets,
+ depth, keep_locks, keep_changelists,
+ changelists, revprop_table, ctx, pool));
+ if (commit_info_p)
+ APR_ARRAY_PUSH(*commit_info, svn_commit_info_t *) = commit_info_p;
+ return SVN_NO_ERROR;
+ }
+ SVN_ERR(svn_wc_adm_close2(base_dir_access, pool));
+ }
+ /* If SVN_ERR_WC_NOT_DIRECTORY error happened, we should try to lock each wc
+ * independently and commit from each working copy in turn */
+ else if (lock_err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
{
- const char *tmp_file;
- cmt_err = svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
- ctx, pool);
-
- if (cmt_err || (! log_msg))
- goto cleanup;
+ svn_error_clear(lock_err);
+ SVN_ERR(do_commit(commit_info, targets, base_dir, depth, keep_locks,
+ keep_changelists, changelists, revprop_table, ctx,
+ pool));
+ return SVN_NO_ERROR;
}
+ /* If other error happened, just return the error. */
else
- log_msg = "";
+ return lock_err;
- /* Sort and condense our COMMIT_ITEMS. */
- if ((cmt_err = svn_client__condense_commit_items(&base_url,
- commit_items,
- pool)))
- goto cleanup;
-
- /* Collect our lock tokens with paths relative to base_url. */
- if ((cmt_err = collect_lock_tokens(&lock_tokens, lock_tokens, base_url,
- pool)))
- goto cleanup;
-
- if ((cmt_err = get_ra_editor(&ra_session,
- &editor, &edit_baton, ctx,
- base_url, base_dir, base_dir_access, log_msg,
- commit_items, revprop_table, commit_info_p,
- TRUE, lock_tokens, keep_locks, pool)))
- goto cleanup;
-
- /* Make a note that we have a commit-in-progress. */
- commit_in_progress = TRUE;
-
- if ((cmt_err = svn_dirent_get_absolute(¤t_dir,
- current_dir, pool)))
- goto cleanup;
-
- /* Determine prefix to strip from the commit notify messages */
- notify_prefix = svn_dirent_get_longest_ancestor(current_dir, base_dir, pool);
-
- /* Perform the commit. */
- cmt_err = svn_client__do_commit(base_url, commit_items, base_dir_access,
- editor, edit_baton,
- notify_prefix,
- &tempfiles, &checksums, ctx, pool);
-
- /* Handle a successful commit. */
- if ((! cmt_err)
- || (cmt_err->apr_err == SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
+ if (cmt_err->apr_err == SVN_ERR_WC_NOT_LOCKED)
{
- svn_wc_committed_queue_t *queue = svn_wc_committed_queue_create(pool);
- struct post_commit_baton btn;
-
- btn.queue = queue;
- btn.qpool = pool;
- btn.base_dir_access = base_dir_access;
- btn.keep_changelists = keep_changelists;
- btn.keep_locks = keep_locks;
- btn.checksums = checksums;
-
- /* Make a note that our commit is finished. */
- commit_in_progress = FALSE;
-
- bump_err = svn_iter_apr_array(NULL, commit_items,
- post_process_commit_item, &btn,
- pool);
- if (bump_err)
- goto cleanup;
-
- SVN_ERR_ASSERT(*commit_info_p);
- bump_err
- = svn_wc_process_committed_queue(queue, base_dir_access,
- (*commit_info_p)->revision,
- (*commit_info_p)->date,
- (*commit_info_p)->author,
- pool);
+ svn_error_clear(cmt_err);
+ SVN_ERR(do_commit(commit_info, targets, base_dir, depth, keep_locks,
+ keep_changelists, changelists, revprop_table, ctx,
+ pool));
+ return SVN_NO_ERROR;
}
+ else
+ return cmt_err;
+}
- /* Sleep to ensure timestamp integrity. */
- svn_io_sleep_for_timestamps(base_dir, pool);
-
- cleanup:
- /* Abort the commit if it is still in progress. */
- if (commit_in_progress)
- svn_error_clear(editor->abort_edit(edit_baton, pool));
-
- /* A bump error is likely to occur while running a working copy log file,
- explicitly unlocking and removing temporary files would be wrong in
- that case. A commit error (cmt_err) should only occur before any
- attempt to modify the working copy, so it doesn't prevent explicit
- clean-up. */
- if (! bump_err)
- {
- unlock_err = svn_wc_adm_close2(base_dir_access, pool);
-
- if (! unlock_err)
- cleanup_err = remove_tmpfiles(tempfiles, pool);
- }
-
- /* As per our promise, if *commit_info_p isn't set, provide a default where
- rev = SVN_INVALID_REVNUM. */
- if (! *commit_info_p)
- *commit_info_p = svn_create_commit_info(pool);
-
- return reconcile_errors(cmt_err, unlock_err, bump_err, cleanup_err, pool);
+svn_error_t *
+svn_client_commit4(svn_commit_info_t **commit_info_p,
+ const apr_array_header_t *targets,
+ svn_depth_t depth,
+ svn_boolean_t keep_locks,
+ svn_boolean_t keep_changelists,
+ const apr_array_header_t *changelists,
+ const apr_hash_t *revprop_table,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *commit_info;
+ SVN_ERR(svn_client_commit5(&commit_info,
+ targets,
+ depth,
+ FALSE,
+ keep_locks,
+ keep_changelists,
+ changelists,
+ revprop_table,
+ ctx,
+ pool));
+ if (commit_info->nelts >= 1)
+ *commit_info_p = APR_ARRAY_IDX(commit_info, 0, svn_commit_info_t *);
+ return SVN_NO_ERROR;
}
Huihuang
------------------
yellow.flying
2009-07-15
__________________________________________________
赶快注册雅虎超大容量免费邮箱?
http://cn.mail.yahoo.com
------------------------------------------------------
http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2371375
Received on 2009-07-15 04:05:02 CEST