[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH]commit from multiple wc

From: HuiHuang <yellow.flying_at_yahoo.com.cn>
Date: Thu, 16 Jul 2009 19:17:42 +0800

Hey,

My patch does not use the new function svn_wc__adm_open_in_context(), but I will make a
new patch which use the new function instead of svn_wc_adm_open3() as soon as possible.

log message:
     
    [[[
       This change try to make svn_client_commit5() do multiple commits, one for each target
       working copy. Essentially:
       svn commit wc1; svn commit wc2; ...; svn commit wcN;
       The solution will be discarded later when commit functionality is rebased on WC-NG.
      
       * 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 38429)
+++ subversion/include/svn_client.h (working copy)
@@ -1705,54 +1705,75 @@
  */
 
 /**
- * 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.
- * Set @a *commit_info_p to the results of the commit, allocated in @a 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.
+*
+* @a targets is an array of <tt>const char *</tt> paths to commit. They
+* need not be canonicalized nor condensed; this function will take care of
+* that. If @a targets has zero elements, then do nothing and return
+* immediately without error.
+*
+* If non-NULL, @a revprop_table is a hash table holding additional,
+* custom revision properties (<tt>const char *</tt> names mapped to
+* <tt>svn_string_t *</tt> values) to be set on the new revision.
+* This table cannot contain any standard Subversion properties.
+*
+* If @a ctx->notify_func2 is non-NULL, then call @a ctx->notify_func2 with
+* @a ctx->notify_baton2 as the commit progresses, with any of the following
+* actions: @c svn_wc_notify_commit_modified, @c svn_wc_notify_commit_added,
+* @c svn_wc_notify_commit_deleted, @c svn_wc_notify_commit_replaced,
+* @c svn_wc_notify_commit_postfix_txdelta.
+*
+* If @a depth is @c svn_depth_infinity, commit all changes to and
+* below named targets. If @a depth is @c svn_depth_empty, commit
+* only named targets (that is, only property changes on named
+* directory targets, and property and content changes for named file
+* targets). If @a depth is @c svn_depth_files, behave as above for
+* named file targets, and for named directory targets, commit
+* property changes on a named directory and all changes to files
+* directly inside that directory. If @c svn_depth_immediates, behave
+* as for @c svn_depth_files, and for subdirectories of any named
+* directory target commit as though for @c svn_depth_empty.
+*
+* Unlock paths in the repository, unless @a keep_locks is TRUE.
+*
+* @a changelists is an array of <tt>const char *</tt> changelist
+* names, used as a restrictive filter on items that are committed;
+* that is, don't commit anything unless it's a member of one of those
+* changelists. After the commit completes successfully, remove
+* changelist associations from the targets, unless @a
+* keep_changelists is set. If @a changelists is
+* empty (or altogether @c NULL), no changelist filtering occurs.
+*
+* Use @a pool for any temporary allocations.
+*
+* When @a allow_multiple_working_copies is TRUE, commits from multiple
+* working copies are allowed, one commit for each working copy.
+* Essentially:
+* svn commit wc1; svn commit wc2; ...; svn commit wcN;
+* When @a allow_multiple_working_copies is FALSE, only commit from one
+* single working copy is allowed.
+*
+* Also it returns an array @a commit_info_p of svn_commit_info_t, allocated in
+* @a pool. One svn_commit_info_t for each commit.
+*/
+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 allow_multiple_working_copies,
+ 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);
+
+/**
+ * Like svn_client_commit5(), but with @a allow_multiple_working_copies
+ * always set to FALSE.
  *
- * @a targets is an array of <tt>const char *</tt> paths to commit. They
- * need not be canonicalized nor condensed; this function will take care of
- * that. If @a targets has zero elements, then do nothing and return
- * immediately without error.
- *
- * If non-NULL, @a revprop_table is a hash table holding additional,
- * custom revision properties (<tt>const char *</tt> names mapped to
- * <tt>svn_string_t *</tt> values) to be set on the new revision.
- * This table cannot contain any standard Subversion properties.
- *
- * If @a ctx->notify_func2 is non-NULL, then call @a ctx->notify_func2 with
- * @a ctx->notify_baton2 as the commit progresses, with any of the following
- * actions: @c svn_wc_notify_commit_modified, @c svn_wc_notify_commit_added,
- * @c svn_wc_notify_commit_deleted, @c svn_wc_notify_commit_replaced,
- * @c svn_wc_notify_commit_postfix_txdelta.
- *
- * If @a depth is @c svn_depth_infinity, commit all changes to and
- * below named targets. If @a depth is @c svn_depth_empty, commit
- * only named targets (that is, only property changes on named
- * directory targets, and property and content changes for named file
- * targets). If @a depth is @c svn_depth_files, behave as above for
- * named file targets, and for named directory targets, commit
- * property changes on a named directory and all changes to files
- * directly inside that directory. If @c svn_depth_immediates, behave
- * as for @c svn_depth_files, and for subdirectories of any named
- * directory target commit as though for @c svn_depth_empty.
- *
- * Unlock paths in the repository, unless @a keep_locks is TRUE.
- *
- * @a changelists is an array of <tt>const char *</tt> changelist
- * names, used as a restrictive filter on items that are committed;
- * that is, don't commit anything unless it's a member of one of those
- * changelists. After the commit completes successfully, remove
- * changelist associations from the targets, unless @a
- * keep_changelists is set. If @a changelists is
- * empty (or altogether @c NULL), no changelist filtering occurs.
- *
- * Use @a pool for any temporary allocations.
- *
- * If no error is returned and @a (*commit_info_p)->revision is set to
- * @c SVN_INVALID_REVNUM, then the commit was a no-op; nothing needed to
- * be committed.
- *
  * @since New in 1.5.
  */
 svn_error_t *
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c (revision 38429)
+++ subversion/libsvn_client/commit.c (working copy)
@@ -73,7 +73,39 @@
 
 } import_ctx_t;
 
+typedef struct commit_packet_t
+{
+ /* Working copy root of a wc */
+ const char *base_dir;
 
+ /* Target paths, relative to 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 *rel_target,
+ svn_wc_adm_access_t *base_dir_access,
+ apr_pool_t *pool)
+{
+ apr_array_header_t *rel_targets;
+ commit_packet_t *commit_packet;
+
+ rel_targets = apr_array_make(pool, 1, sizeof(char *));
+ 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;
+
+ 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.
@@ -1277,10 +1309,390 @@
   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(&not_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(&current_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);
+ SVN_ERR(svn_dirent_get_absolute(&target,
+ APR_ARRAY_IDX(targets, i, const char *),
+ iterpool));
+ 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->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),
+ 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);
+ 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 allow_multiple_working_copies,
                    svn_boolean_t keep_locks,
                    svn_boolean_t keep_changelists,
                    const apr_array_header_t *changelists,
@@ -1288,30 +1700,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++)
     {
@@ -1329,8 +1731,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.
    *
@@ -1442,7 +1851,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,
@@ -1452,13 +1861,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)
@@ -1483,7 +1892,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)
                 {
@@ -1492,7 +1901,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 */
@@ -1501,19 +1910,16 @@
                 }
             }
         }
-
- svn_pool_destroy(subpool);
+ svn_pool_destroy(iterpool);
     }
 
- SVN_ERR(svn_wc__adm_open_in_context(&base_dir_access,
- ctx->wc_ctx,
- base_dir,
- TRUE, /* Write lock */
- lock_base_dir_recursive ? -1 : 0,
- ctx->cancel_func, ctx->cancel_baton,
- pool));
+ 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);
 
- 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;
@@ -1553,164 +1959,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 (!allow_multiple_working_copies || !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(&not_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 (!allow_multiple_working_copies || ! 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(&current_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;
 }

Thank you~
Huihuang

------------------
yellow.flying
2009-07-16

__________________________________________________
赶快注册雅虎超大容量免费邮箱?
http://cn.mail.yahoo.com

------------------------------------------------------
http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2371716
Received on 2009-07-16 13:19:18 CEST

This is an archived mail posted to the Subversion Dev mailing list.