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

[PATCH] "svn commit" performance

From: Chia-liang Kao <clkao_at_clkao.org>
Date: 2003-07-10 15:34:44 CEST

ok, so here's the patch. (I've run the basic and commit regression tests)

On Wed, Jul 09, 2003 at 04:24:34AM +0800, Chia-liang Kao wrote:
> ah, this looks cleaner than what I was doing with entry url
> and path checking. I'll probably svn revert my stuff.

Treelock indivudual targets instead of the base dir in commit. This
fixes the case when you issue "svn commit dir", every directory under
cwd is treelocked. this makes it very slow when you have lots of things
outside dir.

* libsvn_subr/sorts.c include/svn_sort.h
  (svn_sort_compare_paths): New function.

* libsvn_client/commit.c
  (svn_client_commit): Do not treelock base_dir. Iterate over the
    sorted targets and use svn_wc_adm_open_descendent to tree lock
    them.

* include/svn_wc.h
* subversion/libsvn_wc/lock.c
  (svn_wc_adm_open_descendent): New function.

Index: libsvn_subr/sorts.c
===================================================================
--- libsvn_subr/sorts.c (revision 6432)
+++ libsvn_subr/sorts.c (working copy)
@@ -83,7 +83,16 @@
   return a_rev < b_rev ? 1 : -1;
 }
 
+int
+svn_sort_compare_paths (const void *a, const void *b)
+{
+ const char *astr = *(const char **)a;
+ const char *bstr = *(const char **)b;
 
+ return svn_path_compare_paths (astr, bstr);
+}
+
+
 #ifndef apr_hash_sort_keys
 
 /* see svn_sorts.h for documentation */
Index: include/svn_sorts.h
===================================================================
--- include/svn_sorts.h (revision 6432)
+++ include/svn_sorts.h (working copy)
@@ -76,7 +76,12 @@
  */
 int svn_sort_compare_revisions (const void *a, const void *b);
 
+/** Helper function for sorting @c apr_array_header_t with paths
+ * into alphabetical order.
+ */
+int svn_sort_compare_paths (const void *a, const void *b);
 
+
 #ifndef apr_hash_sorted_keys
 /** Sort @a ht according to its keys, return an @c apr_array_header_t
  * containing @c svn_item_t structures holding those keys and values
Index: libsvn_client/commit.c
===================================================================
--- libsvn_client/commit.c (revision 6432)
+++ libsvn_client/commit.c (working copy)
@@ -39,6 +39,7 @@
 #include "svn_io.h"
 #include "svn_md5.h"
 #include "svn_time.h"
+#include "svn_sorts.h"
 
 #include "client.h"
 
@@ -812,7 +813,7 @@
   apr_array_header_t *rel_targets;
   apr_hash_t *committables, *tempfiles = NULL;
   svn_wc_adm_access_t *base_dir_access;
- apr_array_header_t *commit_items;
+ apr_array_header_t *commit_items, *lock_targets;
   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;
@@ -862,22 +863,55 @@
         }
     }
 
- SVN_ERR (svn_wc_adm_open (&base_dir_access, NULL, base_dir, TRUE, TRUE,
+ /* Extract the path of targets */
+ lock_targets = apr_array_make (pool, targets->nelts,
+ sizeof (const char *));
+
+ for (i = 0; i < targets->nelts; ++i)
+ {
+ const char *target;
+ svn_node_kind_t kind;
+
+ SVN_ERR (svn_path_get_absolute (&target,
+ ((const char **)targets->elts)[i],
+ pool));
+
+ SVN_ERR (svn_io_check_path (target, &kind, pool));
+ if (kind != svn_node_dir)
+ target = svn_path_dirname (target, pool);
+
+ *((const char **) apr_array_push (lock_targets)) = target;
+ }
+
+ SVN_ERR (svn_wc_adm_open (&base_dir_access, NULL, base_dir, TRUE, FALSE,
                             pool));
 
+ /* Sort the targets so parents will be locked first, descendants will
+ retrieve from parents. Also if base_dir is one of the lock_targets,
+ re-lock it with treelock */
+
+ qsort (lock_targets->elts, lock_targets->nelts, lock_targets->elt_size,
+ svn_sort_compare_paths);
+
   /* 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 */
- for (i = 0; i < targets->nelts; ++i)
+
+ for (i = 0; i < lock_targets->nelts; ++i)
     {
+ const char *target = APR_ARRAY_IDX (lock_targets, i, const char *);
       svn_wc_adm_access_t *adm_access;
- const char *target;
- SVN_ERR (svn_path_get_absolute (&target,
- ((const char **)targets->elts)[i],
- pool));
- SVN_ERR_W (svn_wc_adm_probe_retrieve (&adm_access, base_dir_access,
- target, pool),
- "Are all the targets part of the same working copy?");
+
+ if (strcmp (target, base_dir) == 0)
+ {
+ svn_wc_adm_close (base_dir_access);
+ SVN_ERR (svn_wc_adm_open (&base_dir_access, NULL, base_dir,
+ TRUE, TRUE, pool));
+ }
+ else
+ SVN_ERR_W (svn_wc_adm_open_descendant (&adm_access, base_dir_access,
+ target, TRUE, TRUE, pool),
+ "Are all the targets part of the same working copy?");
     }
 
   /* Crawl the working copy for commit items. */
Index: include/svn_wc.h
===================================================================
--- include/svn_wc.h (revision 6432)
+++ include/svn_wc.h (working copy)
@@ -127,6 +127,20 @@
                                     svn_boolean_t tree_lock,
                                     apr_pool_t *pool);
 
+/** Checks if @a path is in the same working copy as the adm_access
+ * @associated. Acts like @c svn_wc_adm_open if true, otherwise
+ * returns SVN_ERR_ENTRY_NOT_FOUND.
+ */
+
+svn_error_t *
+svn_wc_adm_open_descendant (svn_wc_adm_access_t **adm_access,
+ svn_wc_adm_access_t *associated,
+ const char *path,
+ svn_boolean_t write_lock,
+ svn_boolean_t tree_lock,
+ apr_pool_t *pool);
+
+
 /** Return, in @a *adm_access, a pointer to an existing access baton associated
  * with @a path. @a path must be a directory that is locked as part of the
  * set containing the @a associated access baton.
Index: libsvn_wc/lock.c
===================================================================
--- libsvn_wc/lock.c (revision 6432)
+++ libsvn_wc/lock.c (working copy)
@@ -439,9 +439,55 @@
 {
   return do_open (adm_access, NULL, path, TRUE, FALSE, TRUE, pool);
 }
-
 
 svn_error_t *
+svn_wc_adm_open_descendant (svn_wc_adm_access_t **adm_access,
+ svn_wc_adm_access_t *associated,
+ const char *path,
+ svn_boolean_t write_lock,
+ svn_boolean_t tree_lock,
+ apr_pool_t *pool)
+{
+ const char *parent, *relative;
+ apr_array_header_t *paths;
+ int i;
+
+ parent = apr_pstrdup(pool, associated->path);
+ relative = svn_path_is_child (parent, path, pool);
+
+ if (relative == NULL)
+ return svn_error_createf (SVN_ERR_ENTRY_NOT_FOUND, NULL,
+ "%s is not descendant of %s", path, parent);
+
+ paths = svn_path_decompose (relative, pool);
+
+ for (i = 0; i < paths->nelts; ++i)
+ {
+ const char *component = APR_ARRAY_IDX(paths, i, const char *);
+ svn_wc_adm_access_t *parent_access;
+ apr_hash_t *entries;
+
+ char *child = svn_path_join (parent, component, pool);
+
+ SVN_ERR (svn_wc_adm_probe_try (&parent_access, associated,
+ parent, FALSE, FALSE, pool));
+ SVN_ERR (svn_wc_entries_read (&entries, parent_access, FALSE, pool));
+ if (apr_hash_get (entries, component, APR_HASH_KEY_STRING) == NULL)
+ return svn_error_createf (SVN_ERR_ENTRY_NOT_FOUND, NULL,
+ "%s is not descendant of %s", path, parent);
+
+ parent = child;
+ }
+
+ SVN_ERR (svn_wc_adm_probe_try (adm_access, associated, path,
+ write_lock, tree_lock, pool));
+
+ assert ((*adm_access)->type == svn_wc__adm_access_write_lock);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_wc_adm_probe_open (svn_wc_adm_access_t **adm_access,
                        svn_wc_adm_access_t *associated,
                        const char *path,

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Thu Jul 10 15:35:28 2003

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

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.