You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2012/09/12 16:43:59 UTC

svn commit: r1383980 [2/6] - in /subversion/branches/master-passphrase: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/client-side/emacs/ subversion/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversio...

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs/fs-loader.h?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs/fs-loader.h Wed Sep 12 14:43:54 2012
@@ -107,7 +107,7 @@ typedef struct fs_library_vtable_t
   svn_error_t *(*pack_fs)(svn_fs_t *fs, const char *path,
                           svn_fs_pack_notify_t notify_func, void *notify_baton,
                           svn_cancel_func_t cancel_func, void *cancel_baton,
-                          apr_pool_t *pool);
+                          apr_pool_t *pool, apr_pool_t *common_pool);
 
   /* Provider-specific functions should go here, even if they could go
      in an object vtable, so that they are all kept together. */
@@ -202,6 +202,9 @@ typedef struct fs_vtable_t
                             svn_fs_get_locks_callback_t get_locks_func,
                             void *get_locks_baton,
                             apr_pool_t *pool);
+  svn_error_t *(*freeze)(svn_fs_t *fs,
+                         svn_error_t *(*freeze_body)(void *, apr_pool_t *),
+                         void *baton, apr_pool_t *pool);
   svn_error_t *(*bdb_set_errcall)(svn_fs_t *fs,
                                   void (*handler)(const char *errpfx,
                                                   char *msg));

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_base/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_base/fs.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_base/fs.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_base/fs.c Wed Sep 12 14:43:54 2012
@@ -471,6 +471,14 @@ bdb_write_config(svn_fs_t *fs)
   return svn_io_file_close(dbconfig_file, fs->pool);
 }
 
+static svn_error_t *
+base_bdb_freeze(svn_fs_t *fs,
+                svn_error_t *(*freeze_body)(void *, apr_pool_t *),
+                void *baton,
+                apr_pool_t *pool)
+{
+  SVN__NOT_IMPLEMENTED();
+}
 
 
 /* Creating a new filesystem */
@@ -492,6 +500,7 @@ static fs_vtable_t fs_vtable = {
   svn_fs_base__unlock,
   svn_fs_base__get_lock,
   svn_fs_base__get_locks,
+  base_bdb_freeze,
   base_bdb_set_errcall,
 };
 
@@ -911,7 +920,8 @@ base_bdb_pack(svn_fs_t *fs,
               void *notify_baton,
               svn_cancel_func_t cancel,
               void *cancel_baton,
-              apr_pool_t *pool)
+              apr_pool_t *pool,
+              apr_pool_t *common_pool)
 {
   /* Packing is currently a no op for BDB. */
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/caching.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/caching.c Wed Sep 12 14:43:54 2012
@@ -429,6 +429,19 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
 
   SVN_ERR(init_callbacks(ffd->node_revision_cache, fs, no_handler, pool));
 
+  /* initialize node change list cache, if caching has been enabled */
+  SVN_ERR(create_cache(&(ffd->changes_cache),
+                       NULL,
+                       membuffer,
+                       0, 0, /* Do not use inprocess cache */
+                       svn_fs_fs__serialize_changes,
+                       svn_fs_fs__deserialize_changes,
+                       sizeof(svn_revnum_t),
+                       apr_pstrcat(pool, prefix, "CHANGES", (char *)NULL),
+                       fs->pool));
+
+  SVN_ERR(init_callbacks(ffd->changes_cache, fs, no_handler, pool));
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.c Wed Sep 12 14:43:54 2012
@@ -38,6 +38,7 @@
 #include "tree.h"
 #include "lock.h"
 #include "id.h"
+#include "rep-cache.h"
 #include "svn_private_config.h"
 #include "private/svn_fs_util.h"
 
@@ -123,6 +124,46 @@ fs_set_errcall(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+struct fs_freeze_baton_t {
+  svn_fs_t *fs;
+  svn_error_t *(*freeze_body)(void *, apr_pool_t *);
+  void *baton;
+};
+
+static svn_error_t *
+fs_freeze_body(void *baton,
+               apr_pool_t *pool)
+{
+  struct fs_freeze_baton_t *b = baton;
+  svn_boolean_t exists;
+
+  SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, b->fs, pool));
+  if (exists)
+    SVN_ERR(svn_fs_fs__lock_rep_cache(b->fs, pool));
+
+  SVN_ERR(b->freeze_body(b->baton, pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fs_freeze(svn_fs_t *fs,
+          svn_error_t *(*freeze_body)(void *, apr_pool_t *),
+          void *baton,
+          apr_pool_t *pool)
+{
+  struct fs_freeze_baton_t b;
+
+  b.fs = fs;
+  b.freeze_body = freeze_body;
+  b.baton = baton;
+
+  SVN_ERR(svn_fs__check_fs(fs, TRUE));
+  SVN_ERR(svn_fs_fs__with_write_lock(fs, fs_freeze_body, &b, pool));
+
+  return SVN_NO_ERROR;
+}
+
 
 
 /* The vtable associated with a specific open filesystem. */
@@ -143,6 +184,7 @@ static fs_vtable_t fs_vtable = {
   svn_fs_fs__unlock,
   svn_fs_fs__get_lock,
   svn_fs_fs__get_locks,
+  fs_freeze,
   fs_set_errcall
 };
 
@@ -263,13 +305,14 @@ fs_pack(svn_fs_t *fs,
         void *notify_baton,
         svn_cancel_func_t cancel_func,
         void *cancel_baton,
-        apr_pool_t *pool)
+        apr_pool_t *pool,
+        apr_pool_t *common_pool)
 {
   SVN_ERR(svn_fs__check_fs(fs, FALSE));
   SVN_ERR(initialize_fs_struct(fs));
   SVN_ERR(svn_fs_fs__open(fs, path, pool));
   SVN_ERR(svn_fs_fs__initialize_caches(fs, pool));
-  SVN_ERR(fs_serialized_init(fs, pool, pool));
+  SVN_ERR(fs_serialized_init(fs, common_pool, pool));
   return svn_fs_fs__pack(fs, notify_func, notify_baton,
                          cancel_func, cancel_baton, pool);
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.h?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs.h Wed Sep 12 14:43:54 2012
@@ -285,6 +285,10 @@ typedef struct fs_fs_data_t
   /* Cache for node_revision_t objects; the key is (revision, id offset) */
   svn_cache__t *node_revision_cache;
 
+  /* Cache for change lists as APR arrays of change_t * objects; the key
+     is the revision */
+  svn_cache__t *changes_cache;
+
   /* If set, there are or have been more than one concurrent transaction */
   svn_boolean_t concurrent_transactions;
 

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs_fs.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/fs_fs.c Wed Sep 12 14:43:54 2012
@@ -2285,6 +2285,10 @@ get_node_revision_body(node_revision_t *
                                   svn_stream_from_aprfile2(revision_file, FALSE,
                                                            pool),
                                   pool));
+  /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+  if (svn_fs_fs__id_txn_id(id) == NULL)
+    (*noderev_p)->is_fresh_txn_root = FALSE;
+
 
   /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
   return set_cached_node_revision_body(*noderev_p, fs, id, pool);
@@ -3681,8 +3685,19 @@ get_revision_proplist(apr_hash_t **propl
    * non-packed shard.  If that fails, we will fall through to packed
    * shard reads. */
   if (!is_packed_revprop(fs, rev))
-    SVN_ERR(read_non_packed_revprop(proplist_p, fs, rev, generation,
-                                    pool));
+    {
+      svn_error_t *err = read_non_packed_revprop(proplist_p, fs, rev,
+                                                 generation, pool);
+      if (err)
+        {
+          if (!APR_STATUS_IS_ENOENT(err->apr_err)
+              || ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
+            return svn_error_trace(err);
+
+          svn_error_clear(err);
+          *proplist_p = NULL; /* in case read_non_packed_revprop changed it */
+        }
+    }
 
   /* if revprop packing is available and we have not read the revprops, yet,
    * try reading them from a packed shard.  If that fails, REV is most
@@ -4188,6 +4203,8 @@ struct rep_state
 static svn_error_t *
 create_rep_state_body(struct rep_state **rep_state,
                       struct rep_args **rep_args,
+                      apr_file_t **file_hint,
+                      svn_revnum_t *rev_hint,
                       representation_t *rep,
                       svn_fs_t *fs,
                       apr_pool_t *pool)
@@ -4197,7 +4214,43 @@ create_rep_state_body(struct rep_state *
   struct rep_args *ra;
   unsigned char buf[4];
 
-  SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
+  /* If the hint is
+   * - given,
+   * - refers to a packed revision,
+   * - as does the rep we want to read, and
+   * - refers to the same pack file as the rep
+   * ...
+   */
+  if (   file_hint && rev_hint && *file_hint
+      && *rev_hint < ffd->min_unpacked_rev
+      && rep->revision < ffd->min_unpacked_rev
+      && (   (*rev_hint / ffd->max_files_per_dir)
+          == (rep->revision / ffd->max_files_per_dir)))
+    {
+      /* ... we can re-use the same, already open file object
+       */
+      apr_off_t offset;
+      SVN_ERR(get_packed_offset(&offset, fs, rep->revision, pool));
+
+      offset += rep->offset;
+      SVN_ERR(svn_io_file_seek(*file_hint, APR_SET, &offset, pool));
+
+      rs->file = *file_hint;
+    }
+  else
+    {
+      /* otherwise, create a new file object
+       */
+      SVN_ERR(open_and_seek_representation(&rs->file, fs, rep, pool));
+    }
+
+  /* remember the current file, if suggested by the caller */
+  if (file_hint)
+    *file_hint = rs->file;
+  if (rev_hint)
+    *rev_hint = rep->revision;
+
+  /* continue constructing RS and RA */
   rs->window_cache = ffd->txdelta_window_cache;
   rs->combined_cache = ffd->combined_window_cache;
 
@@ -4229,15 +4282,26 @@ create_rep_state_body(struct rep_state *
 
 /* Read the rep args for REP in filesystem FS and create a rep_state
    for reading the representation.  Return the rep_state in *REP_STATE
-   and the rep args in *REP_ARGS, both allocated in POOL. */
+   and the rep args in *REP_ARGS, both allocated in POOL.
+
+   When reading multiple reps, i.e. a skip delta chain, you may provide
+   non-NULL FILE_HINT and REV_HINT.  (If FILE_HINT is not NULL, in the first
+   call it should be a pointer to NULL.)  The function will use these variables
+   to store the previous call results and tries to re-use them.  This may
+   result in significant savings in I/O for packed files.
+ */
 static svn_error_t *
 create_rep_state(struct rep_state **rep_state,
                  struct rep_args **rep_args,
+                 apr_file_t **file_hint,
+                 svn_revnum_t *rev_hint,
                  representation_t *rep,
                  svn_fs_t *fs,
                  apr_pool_t *pool)
 {
-  svn_error_t *err = create_rep_state_body(rep_state, rep_args, rep, fs, pool);
+  svn_error_t *err = create_rep_state_body(rep_state, rep_args,
+                                           file_hint, rev_hint,
+                                           rep, fs, pool);
   if (err && err->apr_err == SVN_ERR_FS_CORRUPT)
     {
       fs_fs_data_t *ffd = fs->fsap_data;
@@ -4504,13 +4568,16 @@ build_rep_list(apr_array_header_t **list
   struct rep_state *rs;
   struct rep_args *rep_args;
   svn_boolean_t is_cached = FALSE;
+  apr_file_t *last_file = NULL;
+  svn_revnum_t last_revision;
 
   *list = apr_array_make(pool, 1, sizeof(struct rep_state *));
   rep = *first_rep;
 
   while (1)
     {
-      SVN_ERR(create_rep_state(&rs, &rep_args, &rep, fs, pool));
+      SVN_ERR(create_rep_state(&rs, &rep_args, &last_file,
+                               &last_revision, &rep, fs, pool));
       SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached, pool));
       if (is_cached)
         {
@@ -4602,6 +4669,10 @@ read_window(svn_txdelta_window_t **nwin,
 
   SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
 
+  /* RS->FILE may be shared between RS instances -> make sure we point
+   * to the right data. */
+  SVN_ERR(svn_io_file_seek(rs->file, APR_SET, &rs->off, pool));
+
   /* Skip windows to reach the current chunk if we aren't there yet. */
   while (rs->chunk_index < this_chunk)
     {
@@ -4988,8 +5059,8 @@ svn_fs_fs__get_file_delta_stream(svn_txd
       struct rep_args *rep_args;
 
       /* Read target's base rep if any. */
-      SVN_ERR(create_rep_state(&rep_state, &rep_args, target->data_rep,
-                               fs, pool));
+      SVN_ERR(create_rep_state(&rep_state, &rep_args, NULL, NULL,
+                               target->data_rep, fs, pool));
       /* If that matches source, then use this delta as is. */
       if (rep_args->is_delta
           && (rep_args->is_delta_vs_empty
@@ -5050,10 +5121,27 @@ get_dir_contents(apr_hash_t *entries,
     }
   else if (noderev->data_rep)
     {
+      /* use a temporary pool for temp objects.
+       * Also undeltify content before parsing it. Otherwise, we could only
+       * parse it byte-by-byte.
+       */
+      apr_pool_t *text_pool = svn_pool_create(pool);
+      apr_size_t len = noderev->data_rep->expanded_size
+                     ? noderev->data_rep->expanded_size
+                     : noderev->data_rep->size;
+      svn_stringbuf_t *text = svn_stringbuf_create_ensure(len, text_pool);
+      text->len = len;
+
       /* The representation is immutable.  Read it normally. */
-      SVN_ERR(read_representation(&contents, fs, noderev->data_rep, pool));
-      SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
+      SVN_ERR(read_representation(&contents, fs, noderev->data_rep, text_pool));
+      SVN_ERR(svn_stream_read(contents, text->data, &text->len));
       SVN_ERR(svn_stream_close(contents));
+
+      /* de-serialize hash */
+      contents = svn_stream_from_stringbuf(text, text_pool);
+      SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
+
+      svn_pool_destroy(text_pool);
     }
 
   return SVN_NO_ERROR;
@@ -5737,7 +5825,7 @@ read_change(change_t **change_p,
   return SVN_NO_ERROR;
 }
 
-/* Fetch all the changed path entries from FILE and store then in
+/* Examine all the changed path entries in CHANGES and store them in
    *CHANGED_PATHS.  Folding is done to remove redundant or unnecessary
    *data.  Store a hash of paths to copyfrom "REV PATH" strings in
    COPYFROM_HASH if it is non-NULL.  If PREFOLDED is true, assume that
@@ -5746,22 +5834,22 @@ read_change(change_t **change_p,
    remove children of replaced or deleted directories.  Do all
    allocations in POOL. */
 static svn_error_t *
-fetch_all_changes(apr_hash_t *changed_paths,
-                  apr_hash_t *copyfrom_cache,
-                  apr_file_t *file,
-                  svn_boolean_t prefolded,
-                  apr_pool_t *pool)
+process_changes(apr_hash_t *changed_paths,
+                apr_hash_t *copyfrom_cache,
+                apr_array_header_t *changes,
+                svn_boolean_t prefolded,
+                apr_pool_t *pool)
 {
-  change_t *change;
   apr_pool_t *iterpool = svn_pool_create(pool);
+  int i;
 
   /* Read in the changes one by one, folding them into our local hash
      as necessary. */
 
-  SVN_ERR(read_change(&change, file, iterpool));
-
-  while (change)
+  for (i = 0; i < changes->nelts; ++i)
     {
+      change_t *change = APR_ARRAY_IDX(changes, i, change_t *);
+      
       SVN_ERR(fold_change(changed_paths, change, copyfrom_cache));
 
       /* Now, if our change was a deletion or replacement, we have to
@@ -5815,8 +5903,6 @@ fetch_all_changes(apr_hash_t *changed_pa
 
       /* Clear the per-iteration subpool. */
       svn_pool_clear(iterpool);
-
-      SVN_ERR(read_change(&change, file, iterpool));
     }
 
   /* Destroy the per-iteration subpool. */
@@ -5825,6 +5911,29 @@ fetch_all_changes(apr_hash_t *changed_pa
   return SVN_NO_ERROR;
 }
 
+/* Fetch all the changes from FILE and store them in *CHANGES.  Do all
+   allocations in POOL. */
+static svn_error_t *
+read_all_changes(apr_array_header_t **changes,
+                 apr_file_t *file,
+                 apr_pool_t *pool)
+{
+  change_t *change;
+
+  /* pre-allocate enough room for most change lists
+     (will be auto-expanded as necessary) */
+  *changes = apr_array_make(pool, 30, sizeof(change_t *));
+  
+  SVN_ERR(read_change(&change, file, pool));
+  while (change)
+    {
+      APR_ARRAY_PUSH(*changes, change_t*) = change;
+      SVN_ERR(read_change(&change, file, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_fs__txn_changes_fetch(apr_hash_t **changed_paths_p,
                              svn_fs_t *fs,
@@ -5833,11 +5942,15 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t 
 {
   apr_file_t *file;
   apr_hash_t *changed_paths = apr_hash_make(pool);
+  apr_array_header_t *changes;
+  apr_pool_t *scratch_pool = svn_pool_create(pool);
 
   SVN_ERR(svn_io_file_open(&file, path_txn_changes(fs, txn_id, pool),
                            APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
 
-  SVN_ERR(fetch_all_changes(changed_paths, NULL, file, FALSE, pool));
+  SVN_ERR(read_all_changes(&changes, file, scratch_pool));
+  SVN_ERR(process_changes(changed_paths, NULL, changes, FALSE, pool));
+  svn_pool_destroy(scratch_pool);
 
   SVN_ERR(svn_io_file_close(file, pool));
 
@@ -5846,17 +5959,32 @@ svn_fs_fs__txn_changes_fetch(apr_hash_t 
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_fs_fs__paths_changed(apr_hash_t **changed_paths_p,
-                         svn_fs_t *fs,
-                         svn_revnum_t rev,
-                         apr_hash_t *copyfrom_cache,
-                         apr_pool_t *pool)
+/* Fetch the list of change in revision REV in FS and return it in *CHANGES.
+ * Allocate the result in POOL.
+ */
+static svn_error_t *
+get_changes(apr_array_header_t **changes,
+            svn_fs_t *fs,
+            svn_revnum_t rev,
+            apr_pool_t *pool)
 {
   apr_off_t changes_offset;
-  apr_hash_t *changed_paths;
   apr_file_t *revision_file;
+  svn_boolean_t found;
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  /* try cache lookup first */
 
+  if (ffd->changes_cache)
+    {
+      SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
+                             &rev, pool));
+      if (found)
+        return SVN_NO_ERROR;
+    }
+
+  /* read changes from revision file */
+  
   SVN_ERR(ensure_revision_exists(fs, rev, pool));
 
   SVN_ERR(open_pack_or_rev_file(&revision_file, fs, rev, pool));
@@ -5865,14 +5993,37 @@ svn_fs_fs__paths_changed(apr_hash_t **ch
                                   rev, pool));
 
   SVN_ERR(svn_io_file_seek(revision_file, APR_SET, &changes_offset, pool));
+  SVN_ERR(read_all_changes(changes, revision_file, pool));
+  
+  SVN_ERR(svn_io_file_close(revision_file, pool));
 
-  changed_paths = apr_hash_make(pool);
+  /* cache for future reference */
+  
+  if (ffd->changes_cache)
+    SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
 
-  SVN_ERR(fetch_all_changes(changed_paths, copyfrom_cache, revision_file,
-                            TRUE, pool));
+  return SVN_NO_ERROR;
+}
 
-  /* Close the revision file. */
-  SVN_ERR(svn_io_file_close(revision_file, pool));
+
+svn_error_t *
+svn_fs_fs__paths_changed(apr_hash_t **changed_paths_p,
+                         svn_fs_t *fs,
+                         svn_revnum_t rev,
+                         apr_hash_t *copyfrom_cache,
+                         apr_pool_t *pool)
+{
+  apr_hash_t *changed_paths;
+  apr_array_header_t *changes;
+  apr_pool_t *scratch_pool = svn_pool_create(pool);
+
+  SVN_ERR(get_changes(&changes, fs, rev, scratch_pool));
+
+  changed_paths = apr_hash_make(pool);
+
+  SVN_ERR(process_changes(changed_paths, copyfrom_cache, changes,
+                          TRUE, pool));
+  svn_pool_destroy(scratch_pool);
 
   *changed_paths_p = changed_paths;
 
@@ -6632,11 +6783,14 @@ rep_write_contents(void *baton,
 
 /* Given a node-revision NODEREV in filesystem FS, return the
    representation in *REP to use as the base for a text representation
-   delta.  Perform temporary allocations in *POOL. */
+   delta if PROPS is FALSE.  If PROPS has been set, a suitable props
+   base representation will be returned.  Perform temporary allocations
+   in *POOL. */
 static svn_error_t *
 choose_delta_base(representation_t **rep,
                   svn_fs_t *fs,
                   node_revision_t *noderev,
+                  svn_boolean_t props,
                   apr_pool_t *pool)
 {
   int count;
@@ -6685,7 +6839,8 @@ choose_delta_base(representation_t **rep
     SVN_ERR(svn_fs_fs__get_node_revision(&base, fs,
                                          base->predecessor_id, pool));
 
-  *rep = base->data_rep;
+  /* return a suitable base representation */
+  *rep = props ? base->prop_rep : base->data_rep;
 
   return SVN_NO_ERROR;
 }
@@ -6732,7 +6887,7 @@ rep_write_get_baton(struct rep_write_bat
   SVN_ERR(get_file_offset(&b->rep_offset, file, b->pool));
 
   /* Get the base for this delta. */
-  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, b->pool));
+  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, FALSE, b->pool));
   SVN_ERR(read_representation(&source, fs, base_rep, b->pool));
 
   /* Write out the rep header. */
@@ -7151,7 +7306,8 @@ write_hash_rep(representation_t *rep,
    is not NULL, it will be used in addition to the on-disk cache to find
    earlier reps with the same content.  When such existing reps can be found,
    we will truncate the one just written from the file and return the existing
-   rep.  Perform temporary allocations in POOL. */
+   rep.  If PROPS is set, assume that we want to a props representation as
+   the base for our delta.  Perform temporary allocations in POOL. */
 static svn_error_t *
 write_hash_delta_rep(representation_t *rep,
                      apr_file_t *file,
@@ -7159,6 +7315,7 @@ write_hash_delta_rep(representation_t *r
                      svn_fs_t *fs,
                      node_revision_t *noderev,
                      apr_hash_t *reps_hash,
+                     svn_boolean_t props,
                      apr_pool_t *pool)
 {
   svn_txdelta_window_handler_t diff_wh;
@@ -7179,7 +7336,7 @@ write_hash_delta_rep(representation_t *r
   int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
 
   /* Get the base for this delta. */
-  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, pool));
+  SVN_ERR(choose_delta_base(&base_rep, fs, noderev, props, pool));
   SVN_ERR(read_representation(&source, fs, base_rep, pool));
 
   SVN_ERR(get_file_offset(&rep->offset, file, pool));
@@ -7414,7 +7571,7 @@ write_final_rev(const svn_fs_id_t **new_
           if (ffd->deltify_directories)
             SVN_ERR(write_hash_delta_rep(noderev->data_rep, file,
                                          str_entries, fs, noderev, NULL,
-                                         pool));
+                                         FALSE, pool));
           else
             SVN_ERR(write_hash_rep(noderev->data_rep, file, str_entries,
                                    fs, NULL, pool));
@@ -7453,7 +7610,7 @@ write_final_rev(const svn_fs_id_t **new_
       if (ffd->deltify_properties)
         SVN_ERR(write_hash_delta_rep(noderev->prop_rep, file,
                                      proplist, fs, noderev, reps_hash,
-                                     pool));
+                                     TRUE, pool));
       else
         SVN_ERR(write_hash_rep(noderev->prop_rep, file, proplist,
                                fs, reps_hash, pool));
@@ -9694,7 +9851,7 @@ verify_walker(representation_t *rep,
   struct rep_args *rep_args;
 
   /* ### Should this be using read_rep_line() directly? */
-  SVN_ERR(create_rep_state(&rs, &rep_args, rep, fs, scratch_pool));
+  SVN_ERR(create_rep_state(&rs, &rep_args, NULL, NULL, rep, fs, scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache-db.sql
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache-db.sql?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache-db.sql (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache-db.sql Wed Sep 12 14:43:54 2012
@@ -57,3 +57,11 @@ FROM rep_cache
 -- STMT_DEL_REPS_YOUNGER_THAN_REV
 DELETE FROM rep_cache
 WHERE revision > ?1
+
+/* An INSERT takes an SQLite reserved lock that prevents other writes
+   but doesn't block reads.  The incomplete transaction means that no
+   permanent change is made to the database and the transaction is
+   removed when the database is closed.  */
+-- STMT_LOCK_REP
+BEGIN TRANSACTION;
+INSERT INTO rep_cache VALUES ('dummy', 0, 0, 0, 0)

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.c Wed Sep 12 14:43:54 2012
@@ -351,3 +351,17 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
+                          apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  if (! ffd->rep_cache_db)
+    SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
+
+  SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_LOCK_REP));
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.h?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/rep-cache.h Wed Sep 12 14:43:54 2012
@@ -88,6 +88,12 @@ svn_fs_fs__del_rep_reference(svn_fs_t *f
                              svn_revnum_t youngest,
                              apr_pool_t *pool);
 
+/* Start a transaction to take an SQLite reserved lock that prevents
+   other writes. */
+svn_error_t *
+svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
+                          apr_pool_t *pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.c Wed Sep 12 14:43:54 2012
@@ -1079,3 +1079,135 @@ svn_fs_fs__replace_dir_entry(void **data
 
   return SVN_NO_ERROR;
 }
+
+/* Utility function to serialize change CHANGE_P in the given serialization
+ * CONTEXT.
+ */
+static void
+serialize_change(svn_temp_serializer__context_t *context,
+                 change_t * const *change_p)
+{
+  const change_t * change = *change_p;
+  if (change == NULL)
+    return;
+
+  /* serialize the change struct itself */
+  svn_temp_serializer__push(context,
+                            (const void * const *)change_p,
+                            sizeof(*change));
+
+  /* serialize sub-structures */
+  svn_fs_fs__id_serialize(context, &change->noderev_id);
+
+  svn_temp_serializer__add_string(context, &change->path);
+  svn_temp_serializer__add_string(context, &change->copyfrom_path);
+
+  /* return to the caller's nesting level */
+  svn_temp_serializer__pop(context);
+}
+
+/* Utility function to serialize the CHANGE_P within the given
+ * serialization CONTEXT.
+ */
+static void
+deserialize_change(void *buffer, change_t **change_p)
+{
+  change_t * change;
+
+  /* fix-up of the pointer to the struct in question */
+  svn_temp_deserializer__resolve(buffer, (void **)change_p);
+
+  change = *change_p;
+  if (change == NULL)
+    return;
+
+  /* fix-up of sub-structures */
+  svn_fs_fs__id_deserialize(change, (svn_fs_id_t **)&change->noderev_id);
+
+  svn_temp_deserializer__resolve(change, (void **)&change->path);
+  svn_temp_deserializer__resolve(change, (void **)&change->copyfrom_path);
+}
+
+/* Auxiliary structure representing the content of a change_t array.
+   This structure is much easier to (de-)serialize than an APR array.
+ */
+typedef struct changes_data_t
+{
+  /* number of entries in the array */
+  int count;
+
+  /* reference to the changes */
+  change_t **changes;
+} changes_data_t;
+
+svn_error_t *
+svn_fs_fs__serialize_changes(void **data,
+                             apr_size_t *data_len,
+                             void *in,
+                             apr_pool_t *pool)
+{
+  apr_array_header_t *array = in;
+  changes_data_t changes;
+  svn_temp_serializer__context_t *context;
+  svn_stringbuf_t *serialized;
+  int i;
+
+  /* initialize our auxiliary data structure */
+  changes.count = array->nelts;
+  changes.changes = apr_palloc(pool, sizeof(change_t*) * changes.count);
+
+  /* populate it with the array elements */
+  for (i = 0; i < changes.count; ++i)
+    changes.changes[i] = APR_ARRAY_IDX(array, i, change_t*);
+
+  /* serialize it and all its elements */
+  context = svn_temp_serializer__init(&changes,
+                                      sizeof(changes),
+                                      changes.count * 100,
+                                      pool);
+
+  svn_temp_serializer__push(context,
+                            (const void * const *)&changes.changes,
+                            changes.count * sizeof(change_t*));
+
+  for (i = 0; i < changes.count; ++i)
+    serialize_change(context, &changes.changes[i]);
+
+  svn_temp_serializer__pop(context);
+
+  /* return the serialized result */
+  serialized = svn_temp_serializer__get(context);
+
+  *data = serialized->data;
+  *data_len = serialized->len;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__deserialize_changes(void **out,
+                               void *data,
+                               apr_size_t data_len,
+                               apr_pool_t *pool)
+{
+  int i;
+  changes_data_t *changes = (changes_data_t *)data;
+  apr_array_header_t *array = apr_array_make(pool, changes->count,
+                                             sizeof(change_t *));
+
+  /* de-serialize our auxiliary data structure */
+  svn_temp_deserializer__resolve(changes, (void**)&changes->changes);
+
+  /* de-serialize each entry and add it to the array */
+  for (i = 0; i < changes->count; ++i)
+    {
+      deserialize_change((void*)changes->changes,
+                         (change_t **)&changes->changes[i]);
+      APR_ARRAY_PUSH(array, change_t *) = changes->changes[i];
+    }
+
+  /* done */
+  *out = array;
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.h?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/temp_serializer.h Wed Sep 12 14:43:54 2012
@@ -235,4 +235,24 @@ svn_fs_fs__replace_dir_entry(void **data
                              void *baton,
                              apr_pool_t *pool);
 
+/**
+ * Implements #svn_cache__serialize_func_t for an #apr_array_header_t of
+ * #change_t *.
+ */
+svn_error_t *
+svn_fs_fs__serialize_changes(void **data,
+                             apr_size_t *data_len,
+                             void *in,
+                             apr_pool_t *pool);
+
+/**
+ * Implements #svn_cache__deserialize_func_t for an #apr_array_header_t of
+ * #change_t *.
+ */
+svn_error_t *
+svn_fs_fs__deserialize_changes(void **out,
+                               void *data,
+                               apr_size_t data_len,
+                               apr_pool_t *pool);
+
 #endif

Modified: subversion/branches/master-passphrase/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_fs_fs/tree.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_fs_fs/tree.c Wed Sep 12 14:43:54 2012
@@ -44,7 +44,6 @@
 #include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_error.h"
-#include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_mergeinfo.h"
 #include "svn_fs.h"
@@ -229,7 +228,7 @@ find_descendents_in_cache(void *baton,
   struct fdic_baton *b = baton;
   const char *item_path = key;
 
-  if (svn_dirent_is_ancestor(b->path, item_path))
+  if (svn_fspath__skip_ancestor(b->path, item_path))
     APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
 
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_local/split_url.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_local/split_url.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_local/split_url.c Wed Sep 12 14:43:54 2012
@@ -74,5 +74,8 @@ svn_ra_local__split_URL(svn_repos_t **re
                              - svn_path_component_count(repos_root_dirent));
   *repos_url = urlbuf->data;
 
+  /* Configure hook script environment variables. */
+  SVN_ERR(svn_repos_hooks_setenv(*repos, NULL, pool, pool));
+
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/commit.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/commit.c Wed Sep 12 14:43:54 2012
@@ -38,6 +38,7 @@
 #include "svn_private_config.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
+#include "private/svn_skel.h"
 
 #include "ra_serf.h"
 #include "../libsvn_ra/ra_loader.h"
@@ -320,6 +321,49 @@ checkout_node(const char **working_url,
 }
 
 
+/* This is a wrapper around checkout_node() (which see for
+   documentation) which simply retries the CHECKOUT request when it
+   fails due to an SVN_ERR_APMOD_BAD_BASELINE error return from the
+   server.
+
+   See http://subversion.tigris.org/issues/show_bug.cgi?id=4127 for
+   details.
+*/
+static svn_error_t *
+retry_checkout_node(const char **working_url,
+                    const commit_context_t *commit_ctx,
+                    const char *node_url,
+                    apr_pool_t *result_pool,
+                    apr_pool_t *scratch_pool)
+{
+  svn_error_t *err = SVN_NO_ERROR;
+  int retry_count = 5; /* Magic, arbitrary number. */
+
+  do
+    {
+      svn_error_clear(err);
+
+      err = checkout_node(working_url, commit_ctx, node_url,
+                          result_pool, scratch_pool);
+
+      /* There's a small chance of a race condition here if Apache is
+         experiencing heavy commit concurrency or if the network has
+         long latency.  It's possible that the value of HEAD changed
+         between the time we fetched the latest baseline and the time
+         we try to CHECKOUT that baseline.  If that happens, Apache
+         will throw us a BAD_BASELINE error (deltaV says you can only
+         checkout the latest baseline).  We just ignore that specific
+         error and retry a few times, asking for the latest baseline
+         again. */
+      if (err && (err->apr_err != SVN_ERR_APMOD_BAD_BASELINE))
+        return err;
+    }
+  while (err && retry_count--);
+
+  return err;
+}
+
+
 static svn_error_t *
 checkout_dir(dir_context_t *dir,
              apr_pool_t *scratch_pool)
@@ -365,8 +409,8 @@ checkout_dir(dir_context_t *dir,
     }
 
   /* Checkout our directory into the activity URL now. */
-  err = checkout_node(working, dir->commit, checkout_url,
-                      dir->pool, scratch_pool);
+  err = retry_checkout_node(working, dir->commit, checkout_url,
+                            dir->pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_FS_CONFLICT)
@@ -503,8 +547,8 @@ checkout_file(file_context_t *file,
                           NULL, scratch_pool, scratch_pool));
 
   /* Checkout our file into the activity URL now. */
-  err = checkout_node(&file->working_url, file->commit, checkout_url,
-                      file->pool, scratch_pool);
+  err = retry_checkout_node(&file->working_url, file->commit, checkout_url,
+                            file->pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_FS_CONFLICT)
@@ -718,36 +762,62 @@ proppatch_walker(void *baton,
   return SVN_NO_ERROR;
 }
 
+/* Possible add the lock-token "If:" precondition header to HEADERS if
+   an examination of COMMIT_CTX and RELPATH indicates that this is the
+   right thing to do.
+
+   Generally speaking, if the client provided a lock token for
+   RELPATH, it's the right thing to do.  There is a notable instance
+   where this is not the case, however.  If the file at RELPATH was
+   explicitly deleted in this commit already, then mod_dav removed its
+   lock token when it fielded the DELETE request, so we don't want to
+   set the lock precondition again.  (See
+   http://subversion.tigris.org/issues/show_bug.cgi?id=3674 for details.)
+*/
 static svn_error_t *
-setup_proppatch_headers(serf_bucket_t *headers,
-                        void *baton,
-                        apr_pool_t *pool)
+maybe_set_lock_token_header(serf_bucket_t *headers,
+                            commit_context_t *commit_ctx,
+                            const char *relpath,
+                            apr_pool_t *pool)
 {
-  proppatch_context_t *proppatch = baton;
+  const char *token;
 
-  if (SVN_IS_VALID_REVNUM(proppatch->base_revision))
-    {
-      serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
-                              apr_psprintf(pool, "%ld",
-                                           proppatch->base_revision));
-    }
+  if (! (relpath && commit_ctx->lock_tokens))
+    return SVN_NO_ERROR;
 
-  if (proppatch->relpath && proppatch->commit->lock_tokens)
+  if (! apr_hash_get(commit_ctx->deleted_entries, relpath,
+                     APR_HASH_KEY_STRING))
     {
-      const char *token;
-
-      token = apr_hash_get(proppatch->commit->lock_tokens, proppatch->relpath,
+      token = apr_hash_get(commit_ctx->lock_tokens, relpath,
                            APR_HASH_KEY_STRING);
-
       if (token)
         {
           const char *token_header;
 
           token_header = apr_pstrcat(pool, "(<", token, ">)", (char *)NULL);
-
           serf_bucket_headers_set(headers, "If", token_header);
         }
     }
+  
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+setup_proppatch_headers(serf_bucket_t *headers,
+                        void *baton,
+                        apr_pool_t *pool)
+{
+  proppatch_context_t *proppatch = baton;
+
+  if (SVN_IS_VALID_REVNUM(proppatch->base_revision))
+    {
+      serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER,
+                              apr_psprintf(pool, "%ld",
+                                           proppatch->base_revision));
+    }
+
+  SVN_ERR(maybe_set_lock_token_header(headers, proppatch->commit,
+                                      proppatch->relpath, pool));
 
   return SVN_NO_ERROR;
 }
@@ -950,22 +1020,8 @@ setup_put_headers(serf_bucket_t *headers
                               ctx->result_checksum);
     }
 
-  if (ctx->commit->lock_tokens)
-    {
-      const char *token;
-
-      token = apr_hash_get(ctx->commit->lock_tokens, ctx->relpath,
-                           APR_HASH_KEY_STRING);
-
-      if (token)
-        {
-          const char *token_header;
-
-          token_header = apr_pstrcat(pool, "(<", token, ">)", (char *)NULL);
-
-          serf_bucket_headers_set(headers, "If", token_header);
-        }
-    }
+  SVN_ERR(maybe_set_lock_token_header(headers, ctx->commit,
+                                      ctx->relpath, pool));
 
   return APR_SUCCESS;
 }
@@ -1108,7 +1164,26 @@ create_txn_post_body(serf_bucket_t **bod
                      serf_bucket_alloc_t *alloc,
                      apr_pool_t *pool)
 {
-  *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
+  apr_hash_t *revprops = baton;
+  svn_skel_t *request_skel;
+  svn_stringbuf_t *skel_str;
+
+  request_skel = svn_skel__make_empty_list(pool);
+  if (revprops)
+    {
+      svn_skel_t *proplist_skel;
+
+      SVN_ERR(svn_skel__unparse_proplist(&proplist_skel, revprops, pool));
+      svn_skel__prepend(proplist_skel, request_skel);
+      svn_skel__prepend_str("create-txn-with-props", request_skel, pool);
+      skel_str = svn_skel__unparse(request_skel, pool);
+      *body_bkt = SERF_BUCKET_SIMPLE_STRING(skel_str->data, alloc);
+    }
+  else
+    {
+      *body_bkt = SERF_BUCKET_SIMPLE_STRING("( create-txn )", alloc);
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1213,12 +1288,15 @@ open_root(void *edit_baton,
   proppatch_context_t *proppatch_ctx;
   dir_context_t *dir;
   apr_hash_index_t *hi;
-  const char *proppatch_target;
+  const char *proppatch_target = NULL;
 
   if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->session))
     {
       post_response_ctx_t *prc;
       const char *rel_path;
+      svn_boolean_t post_with_revprops =
+        apr_hash_get(ctx->session->supported_posts, "create-txn-with-props",
+                     APR_HASH_KEY_STRING) ? TRUE : FALSE;
 
       /* Create our activity URL now on the server. */
       handler = apr_pcalloc(ctx->pool, sizeof(*handler));
@@ -1226,7 +1304,8 @@ open_root(void *edit_baton,
       handler->method = "POST";
       handler->body_type = SVN_SKEL_MIME_TYPE;
       handler->body_delegate = create_txn_post_body;
-      handler->body_delegate_baton = NULL;
+      handler->body_delegate_baton =
+        post_with_revprops ? ctx->revprop_table : NULL;
       handler->header_delegate = setup_post_headers;
       handler->header_delegate_baton = NULL;
       handler->path = ctx->session->me_resource;
@@ -1288,7 +1367,9 @@ open_root(void *edit_baton,
       dir->removed_props = apr_hash_make(dir->pool);
       dir->url = apr_pstrdup(dir->pool, ctx->txn_root_url);
 
-      proppatch_target = ctx->txn_url;
+      /* If we included our revprops in the POST, we need not
+         PROPPATCH them. */
+      proppatch_target = post_with_revprops ? NULL : ctx->txn_url;
     }
   else
     {
@@ -1369,45 +1450,50 @@ open_root(void *edit_baton,
       proppatch_target = ctx->baseline_url;
     }
 
+  /* Unless this is NULL -- which means we don't need to PROPPATCH the
+     transaction with our revprops -- then, you know, PROPPATCH the
+     transaction with our revprops.  */
+  if (proppatch_target)
+    {
+      proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
+      proppatch_ctx->pool = dir_pool;
+      proppatch_ctx->commit = ctx;
+      proppatch_ctx->path = proppatch_target;
+      proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
+      proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
+      proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
+
+      for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
+           hi = apr_hash_next(hi))
+        {
+          const void *key;
+          void *val;
+          const char *name;
+          svn_string_t *value;
+          const char *ns;
+
+          apr_hash_this(hi, &key, NULL, &val);
+          name = key;
+          value = val;
 
-  /* PROPPATCH our revprops and pass them along.  */
-  proppatch_ctx = apr_pcalloc(ctx->pool, sizeof(*proppatch_ctx));
-  proppatch_ctx->pool = dir_pool;
-  proppatch_ctx->commit = ctx;
-  proppatch_ctx->path = proppatch_target;
-  proppatch_ctx->changed_props = apr_hash_make(proppatch_ctx->pool);
-  proppatch_ctx->removed_props = apr_hash_make(proppatch_ctx->pool);
-  proppatch_ctx->base_revision = SVN_INVALID_REVNUM;
-
-  for (hi = apr_hash_first(ctx->pool, ctx->revprop_table); hi;
-       hi = apr_hash_next(hi))
-    {
-      const void *key;
-      void *val;
-      const char *name;
-      svn_string_t *value;
-      const char *ns;
-
-      apr_hash_this(hi, &key, NULL, &val);
-      name = key;
-      value = val;
+          if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
+            {
+              ns = SVN_DAV_PROP_NS_SVN;
+              name += sizeof(SVN_PROP_PREFIX) - 1;
+            }
+          else
+            {
+              ns = SVN_DAV_PROP_NS_CUSTOM;
+            }
 
-      if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX) - 1) == 0)
-        {
-          ns = SVN_DAV_PROP_NS_SVN;
-          name += sizeof(SVN_PROP_PREFIX) - 1;
-        }
-      else
-        {
-          ns = SVN_DAV_PROP_NS_CUSTOM;
+          svn_ra_serf__set_prop(proppatch_ctx->changed_props,
+                                proppatch_ctx->path,
+                                ns, name, value, proppatch_ctx->pool);
         }
 
-      svn_ra_serf__set_prop(proppatch_ctx->changed_props, proppatch_ctx->path,
-                            ns, name, value, proppatch_ctx->pool);
+      SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
     }
 
-  SVN_ERR(proppatch_resource(proppatch_ctx, dir->commit, ctx->pool));
-
   *root_baton = dir;
 
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/options.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/options.c Wed Sep 12 14:43:54 2012
@@ -204,10 +204,21 @@ capabilities_headers_iterator_callback(v
   /* SVN-specific headers -- if present, server supports HTTP protocol v2 */
   else if (strncmp(key, "SVN", 3) == 0)
     {
+      /* If we've not yet seen any information about supported POST
+         requests, we'll initialize the list/hash with "create-txn"
+         (which we know is supported by virtue of the server speaking
+         HTTPv2 at all. */
+      if (! session->supported_posts)
+        {
+          session->supported_posts = apr_hash_make(session->pool);
+          apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1);
+        }
+
       if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
         {
           session->repos_root = session->session_url;
-          session->repos_root.path = apr_pstrdup(session->pool, val);
+          session->repos_root.path =
+            (char *)svn_fspath__canonicalize(val, session->pool);
           session->repos_root_str =
             svn_urlpath__canonicalize(
                 apr_uri_unparse(session->pool, &session->repos_root, 0),
@@ -257,6 +268,21 @@ capabilities_headers_iterator_callback(v
         {
           opt_ctx->youngest_rev = SVN_STR_TO_REV(val);
         }
+      else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0)
+        {
+          /* May contain multiple values, separated by commas. */
+          int i;
+          apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
+                                                       opt_ctx->pool);
+
+          for (i = 0; i < vals->nelts; i++)
+            {
+              const char *post_val = APR_ARRAY_IDX(vals, i, const char *);
+
+              apr_hash_set(session->supported_posts, post_val,
+                           APR_HASH_KEY_STRING, (void *)1);
+            }
+        }
     }
 
   return 0;

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/ra_serf.h Wed Sep 12 14:43:54 2012
@@ -62,6 +62,10 @@ extern "C" {
                    APR_STRINGIFY(SERF_MINOR_VERSION) "." \
                    APR_STRINGIFY(SERF_PATCH_VERSION)
 
+/** Wait duration (in microseconds) used in calls to serf_context_run() */
+#define SVN_RA_SERF__CONTEXT_RUN_DURATION 500000
+
+
 
 /* Forward declarations. */
 typedef struct svn_ra_serf__session_t svn_ra_serf__session_t;
@@ -89,7 +93,9 @@ typedef struct svn_ra_serf__connection_t
 
 } svn_ra_serf__connection_t;
 
-/** Max. number of connctions we'll open to the server. */
+/** Max. number of connctions we'll open to the server. 
+ *  Note: minimum 2 connections are required for ra_serf to function correctly!
+ */
 #define MAX_NR_OF_CONNS 4
 
 /*
@@ -209,6 +215,10 @@ struct svn_ra_serf__session_t {
   const char *vtxn_stub;        /* for accessing transactions (i.e. txnprops) */
   const char *vtxn_root_stub;   /* for accessing TXN/PATH pairs */
 
+  /* Hash mapping const char * server-supported POST types to
+     disinteresting-but-non-null values. */
+  apr_hash_t *supported_posts;
+
   /*** End HTTP v2 stuff ***/
 
   svn_ra_serf__blncache_t *blncache;

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/replay.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/replay.c Wed Sep 12 14:43:54 2012
@@ -732,6 +732,7 @@ svn_ra_serf__replay_range(svn_ra_session
   svn_revnum_t rev = start_revision;
   const char *report_target;
   int active_reports = 0;
+  apr_short_interval_time_t waittime_left = session->timeout;
 
   SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
 
@@ -855,18 +856,36 @@ svn_ra_serf__replay_range(svn_ra_session
          ### ahead and apply it here, too, in case serf eventually uses
          ### that parameter.
       */
-      status = serf_context_run(session->context, session->timeout,
+      status = serf_context_run(session->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
                                 pool);
 
       err = session->pending_error;
       session->pending_error = NULL;
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = session->timeout;
         }
 
       /* Substract the number of completely handled responses from our

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/serf.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/serf.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/serf.c Wed Sep 12 14:43:54 2012
@@ -381,10 +381,12 @@ svn_ra_serf__open(svn_ra_session_t *sess
                                _("Illegal URL '%s'"),
                                session_URL);
     }
-  /* Contrary to what the comment for apr_uri_t.path says in apr-util 1.2.12 and
-     older, for root paths url.path will be "", where serf requires "/". */
+  /* Depending the version of apr-util in use, for root paths url.path
+     will be NULL or "", where serf requires "/". */
   if (url.path == NULL || url.path[0] == '\0')
-    url.path = apr_pstrdup(serf_sess->pool, "/");
+    {
+      url.path = apr_pstrdup(serf_sess->pool, "/");
+    }
   if (!url.port)
     {
       url.port = apr_uri_port_of_scheme(url.scheme);
@@ -478,9 +480,18 @@ svn_ra_serf__reparent(svn_ra_session_t *
                                _("Illegal repository URL '%s'"), url);
     }
 
-  /* Maybe we should use a string buffer for these strings so we don't
-     allocate memory in the session on every reparent? */
-  session->session_url.path = apr_pstrdup(session->pool, new_url.path);
+  /* Depending the version of apr-util in use, for root paths url.path
+     will be NULL or "", where serf requires "/". */
+  /* ### Maybe we should use a string buffer for these strings so we
+     ### don't allocate memory in the session on every reparent? */
+  if (new_url.path == NULL || new_url.path[0] == '\0')
+    {
+      session->session_url.path = apr_pstrdup(session->pool, "/");
+    }
+  else
+    {
+      session->session_url.path = apr_pstrdup(session->pool, new_url.path);
+    }
   session->session_url_str = apr_pstrdup(session->pool, url);
 
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/update.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/update.c Wed Sep 12 14:43:54 2012
@@ -313,6 +313,10 @@ struct report_context_t {
   /* Do we want the server to send copyfrom args or not? */
   svn_boolean_t send_copyfrom_args;
 
+  /* Is the server including properties inline for newly added
+     files/dirs? */
+  svn_boolean_t add_props_included;
+
   /* Path -> lock token mapping. */
   apr_hash_t *lock_path_tokens;
 
@@ -358,6 +362,9 @@ struct report_context_t {
   /* Are we done parsing the REPORT response? */
   svn_boolean_t done;
 
+  /* Did we get a complete (non-truncated) report? */
+  svn_boolean_t report_completed;
+
   /* The XML parser context for the REPORT response.  */
   svn_ra_serf__xml_parser_t *parser_ctx;
 };
@@ -1216,9 +1223,9 @@ fetch_file(report_context_t *ctx, report
 
   if (!info->url)
     {
-      return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
-                        _("The OPTIONS response did not include the "
-                          "requested checked-in value"));
+      return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                              _("The REPORT or PROPFIND response did not "
+                                "include the requested checked-in value"));
     }
 
   /* If needed, create the PROPFIND to retrieve the file's properties. */
@@ -1388,7 +1395,13 @@ start_report(svn_ra_serf__xml_parser_t *
 
   state = parser->state->current_state;
 
-  if (state == NONE && strcmp(name.name, "target-revision") == 0)
+  if (state == NONE && strcmp(name.name, "update-report") == 0)
+    {
+      const char *val = svn_xml_get_attr_value("inline-props", attrs);
+      if (val && (strcmp(val, "true") == 0))
+        ctx->add_props_included = TRUE;
+    }
+  else if (state == NONE && strcmp(name.name, "target-revision") == 0)
     {
       const char *rev;
 
@@ -1528,7 +1541,11 @@ start_report(svn_ra_serf__xml_parser_t *
       /* Mark that we don't have a base. */
       info->base_rev = SVN_INVALID_REVNUM;
       dir->base_rev = info->base_rev;
-      dir->fetch_props = TRUE;
+
+      /* If the server isn't included properties for added items,
+         we'll need to fetch them ourselves. */
+      if (! ctx->add_props_included)
+        dir->fetch_props = TRUE;
 
       dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
                                             dir->base_name, dir->pool);
@@ -1585,9 +1602,13 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, ADD_FILE);
 
       info->base_rev = SVN_INVALID_REVNUM;
-      info->fetch_props = TRUE;
       info->fetch_file = TRUE;
 
+      /* If the server isn't included properties for added items,
+         we'll need to fetch them ourselves. */
+      if (! ctx->add_props_included)
+        info->fetch_props = TRUE;
+
       info->base_name = apr_pstrdup(info->pool, file_name);
       info->name = NULL;
 
@@ -1862,8 +1883,15 @@ end_report(svn_ra_serf__xml_parser_t *pa
 
   if (state == NONE)
     {
-      /* nothing to close yet. */
-      return SVN_NO_ERROR;
+      if (strcmp(name.name, "update-report") == 0)
+        {
+          ctx->report_completed = TRUE;
+        }
+      else
+        {
+          /* nothing to close yet. */
+          return SVN_NO_ERROR;
+        }
     }
 
   if (((state == OPEN_DIR && (strcmp(name.name, "open-directory") == 0)) ||
@@ -1886,9 +1914,9 @@ end_report(svn_ra_serf__xml_parser_t *pa
       if (!checked_in_url &&
           (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props))
         {
-          return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
-                                  _("The OPTIONS response did not include the "
-                                    "requested checked-in value"));
+          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                                  _("The REPORT or PROPFIND response did not "
+                                    "include the requested checked-in value"));
         }
 
       info->dir->url = checked_in_url;
@@ -1896,7 +1924,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
       /* At this point, we should have the checked-in href.
        * If needed, create the PROPFIND to retrieve the dir's properties.
        */
-      if (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props)
+      if (info->dir->fetch_props)
         {
           /* Unconditionally set fetch_props now. */
           info->dir->fetch_props = TRUE;
@@ -2353,6 +2381,7 @@ finish_report(void *report_baton,
   svn_stringbuf_t *buf = NULL;
   apr_pool_t *iterpool = svn_pool_create(pool);
   svn_error_t *err;
+  apr_short_interval_time_t waittime_left = sess->timeout;
 
   svn_xml_make_close_tag(&buf, iterpool, "S:update-report");
   SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
@@ -2435,7 +2464,9 @@ finish_report(void *report_baton,
          and what items are allocated within.  */
       iterpool_inner = svn_pool_create(iterpool);
 
-      status = serf_context_run(sess->context, sess->timeout, iterpool_inner);
+      status = serf_context_run(sess->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
+                                iterpool_inner);
 
       err = sess->pending_error;
       sess->pending_error = SVN_NO_ERROR;
@@ -2445,12 +2476,29 @@ finish_report(void *report_baton,
           err = handler->server_error->error;
         }
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = sess->timeout;
         }
 
       SVN_ERR(err);
@@ -2607,7 +2655,12 @@ finish_report(void *report_baton,
       SVN_ERR(close_all_dirs(report->root_dir));
     }
 
-  err = report->update_editor->close_edit(report->update_baton, iterpool);
+  /* If we got a complete report, close the edit.  Otherwise, abort it. */
+  if (report->report_completed)
+    err = report->update_editor->close_edit(report->update_baton, iterpool);
+  else
+    err = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                           _("Missing update-report close tag"));
 
   svn_pool_destroy(iterpool);
   return svn_error_trace(err);
@@ -2750,6 +2803,10 @@ make_update_reporter(svn_ra_session_t *r
       make_simple_xml_tag(&buf, "S:recursive", "no", scratch_pool);
     }
 
+  /* Subversion 1.8+ servers can be told to send properties for newly
+     added items inline even when doing a skelta response. */
+  make_simple_xml_tag(&buf, "S:include-props", "yes", scratch_pool);
+
   make_simple_xml_tag(&buf, "S:depth", svn_depth_to_word(depth), scratch_pool);
 
   SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,

Modified: subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_ra_serf/util.c Wed Sep 12 14:43:54 2012
@@ -707,7 +707,8 @@ svn_ra_serf__context_run_wait(svn_boolea
                               apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool;
-
+  apr_short_interval_time_t waittime_left = sess->timeout;
+  
   assert(sess->pending_error == SVN_NO_ERROR);
 
   iterpool = svn_pool_create(scratch_pool);
@@ -722,17 +723,36 @@ svn_ra_serf__context_run_wait(svn_boolea
       if (sess->cancel_func)
         SVN_ERR((*sess->cancel_func)(sess->cancel_baton));
 
-      status = serf_context_run(sess->context, sess->timeout, iterpool);
+      status = serf_context_run(sess->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
+                                iterpool);
 
       err = sess->pending_error;
       sess->pending_error = SVN_NO_ERROR;
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = sess->timeout;
         }
 
       SVN_ERR(err);
@@ -2212,7 +2232,8 @@ svn_ra_serf__discover_vcc(const char **v
 
       /* Now recreate the root_url. */
       session->repos_root = session->session_url;
-      session->repos_root.path = apr_pstrdup(session->pool, url_buf->data);
+      session->repos_root.path =
+        (char *)svn_fspath__canonicalize(url_buf->data, session->pool);
       session->repos_root_str =
         svn_urlpath__canonicalize(apr_uri_unparse(session->pool,
                                                   &session->repos_root, 0),

Modified: subversion/branches/master-passphrase/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_repos/commit.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_repos/commit.c Wed Sep 12 14:43:54 2012
@@ -1334,12 +1334,8 @@ svn_repos__get_commit_ev2(svn_editor_t *
   /* Can the user modify the repository at all?  */
   /* ### check against AUTHZ.  */
 
-  /* Okay... some access is allowed. Let's run the start-commit hook.  */
   author = apr_hash_get(revprops, SVN_PROP_REVISION_AUTHOR,
                         APR_HASH_KEY_STRING);
-  SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
-                                        repos->client_capabilities,
-                                        scratch_pool));
 
   eb = apr_palloc(result_pool, sizeof(*eb));
   eb->repos = repos;
@@ -1357,6 +1353,11 @@ svn_repos__get_commit_ev2(svn_editor_t *
   /* The TXN has been created. Go ahead and apply all revision properties.  */
   SVN_ERR(apply_revprops(repos->fs, eb->txn_name, revprops, scratch_pool));
 
+  /* Okay... some access is allowed. Let's run the start-commit hook.  */
+  SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
+                                        repos->client_capabilities,
+                                        eb->txn_name, scratch_pool));
+
   /* Wrap the FS editor within our editor.  */
   SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton,
                             result_pool, scratch_pool));

Modified: subversion/branches/master-passphrase/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_repos/fs-wrap.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_repos/fs-wrap.c Wed Sep 12 14:43:54 2012
@@ -83,23 +83,29 @@ svn_repos_fs_begin_txn_for_commit2(svn_f
                                    apr_hash_t *revprop_table,
                                    apr_pool_t *pool)
 {
-  svn_string_t *author = apr_hash_get(revprop_table, SVN_PROP_REVISION_AUTHOR,
-                                      APR_HASH_KEY_STRING);
   apr_array_header_t *revprops;
+  const char *txn_name;
+  svn_string_t *author = apr_hash_get(revprop_table,
+                                      SVN_PROP_REVISION_AUTHOR,
+                                      APR_HASH_KEY_STRING);
 
-  /* Run start-commit hooks. */
-  SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
-                                        repos->client_capabilities, pool));
-
-  /* Begin the transaction, ask for the fs to do on-the-fly lock checks. */
+  /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
+     We fetch its name, too, so the start-commit hook can use it.  */
   SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev,
                             SVN_FS_TXN_CHECK_LOCKS, pool));
+  SVN_ERR(svn_fs_txn_name(&txn_name, *txn_p, pool));
 
   /* We pass the revision properties to the filesystem by adding them
      as properties on the txn.  Later, when we commit the txn, these
      properties will be copied into the newly created revision. */
   revprops = svn_prop_hash_to_array(revprop_table, pool);
-  return svn_repos_fs_change_txn_props(*txn_p, revprops, pool);
+  SVN_ERR(svn_repos_fs_change_txn_props(*txn_p, revprops, pool));
+
+  /* Run start-commit hooks. */
+  SVN_ERR(svn_repos__hooks_start_commit(repos, author ? author->data : NULL,
+                                        repos->client_capabilities, txn_name,
+                                        pool));
+  return SVN_NO_ERROR;
 }
 
 

Modified: subversion/branches/master-passphrase/subversion/libsvn_repos/hooks.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_repos/hooks.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_repos/hooks.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_repos/hooks.c Wed Sep 12 14:43:54 2012
@@ -215,6 +215,7 @@ run_hook_cmd(svn_string_t **result,
   svn_error_t *err;
   apr_proc_t cmd_proc = {0};
   apr_pool_t *cmd_pool;
+  apr_hash_t *hook_env = NULL;
 
   if (result)
     {
@@ -234,8 +235,19 @@ run_hook_cmd(svn_string_t **result,
    * destroy in order to clean up the stderr pipe opened for the process. */
   cmd_pool = svn_pool_create(pool);
 
+  /* Check if a custom environment is defined for this hook, or else
+   * whether a default environment is defined. */
+  if (hooks_env)
+    {
+      hook_env = apr_hash_get(hooks_env, name, APR_HASH_KEY_STRING);
+      if (hook_env == NULL)
+        hook_env = apr_hash_get(hooks_env,
+                                SVN_REPOS__HOOKS_ENV_DEFAULT_SECTION,
+                                APR_HASH_KEY_STRING);
+    }
+    
   err = svn_io_start_cmd3(&cmd_proc, ".", cmd, args,
-                          env_from_env_hash(hooks_env, pool, pool),
+                          env_from_env_hash(hook_env, pool, pool),
                           FALSE, FALSE, stdin_handle, result != NULL,
                           null_handle, TRUE, NULL, cmd_pool);
   if (!err)
@@ -357,6 +369,7 @@ svn_error_t *
 svn_repos__hooks_start_commit(svn_repos_t *repos,
                               const char *user,
                               const apr_array_header_t *capabilities,
+                              const char *txn_name,
                               apr_pool_t *pool)
 {
   const char *hook = svn_repos_start_commit_hook(repos, pool);
@@ -368,7 +381,7 @@ svn_repos__hooks_start_commit(svn_repos_
     }
   else if (hook)
     {
-      const char *args[5];
+      const char *args[6];
       char *capabilities_string;
 
       if (capabilities)
@@ -388,7 +401,8 @@ svn_repos__hooks_start_commit(svn_repos_
       args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
       args[2] = user ? user : "";
       args[3] = capabilities_string;
-      args[4] = NULL;
+      args[4] = txn_name;
+      args[5] = NULL;
 
       SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_START_COMMIT, hook, args,
                            repos->hooks_env, NULL, pool));

Modified: subversion/branches/master-passphrase/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_repos/repos.c?rev=1383980&r1=1383979&r2=1383980&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_repos/repos.c Wed Sep 12 14:43:54 2012
@@ -34,6 +34,7 @@
 #include "svn_repos.h"
 #include "svn_hash.h"
 #include "svn_version.h"
+#include "svn_config.h"
 
 #include "private/svn_repos_private.h"
 #include "private/svn_subr_private.h"
@@ -166,13 +167,6 @@ svn_repos_post_revprop_change_hook(svn_r
                        pool);
 }
 
-void
-svn_repos_hooks_setenv(svn_repos_t *repos,
-                       apr_hash_t *hooks_env)
-{
-  repos->hooks_env = hooks_env;
-}
-
 static svn_error_t *
 create_repos_dir(const char *path, apr_pool_t *pool)
 {
@@ -311,16 +305,17 @@ create_hooks(svn_repos_t *repos, apr_poo
 ""                                                                           NL
 "# START-COMMIT HOOK"                                                        NL
 "#"                                                                          NL
-"# The start-commit hook is invoked before a Subversion txn is created"      NL
-"# in the process of doing a commit.  Subversion runs this hook"             NL
-"# by invoking a program (script, executable, binary, etc.) named"           NL
-"# '"SCRIPT_NAME"' (for which this file is a template)"                      NL
-"# with the following ordered arguments:"                                    NL
+"# The start-commit hook is invoked immediately after a Subversion txn is"   NL
+"# created and populated with initial revprops in the process of doing a"    NL
+"# commit. Subversion runs this hook by invoking a program (script, "        NL
+"# executable, binary, etc.) named '"SCRIPT_NAME"' (for which this file"     NL
+"# is a template) with the following ordered arguments:"                     NL
 "#"                                                                          NL
 "#   [1] REPOS-PATH   (the path to this repository)"                         NL
 "#   [2] USER         (the authenticated user attempting to commit)"         NL
 "#   [3] CAPABILITIES (a colon-separated list of capabilities reported"      NL
 "#                     by the client; see note below)"                       NL
+"#   [4] TXN-NAME     (the name of the commit txn just created)"             NL
 "#"                                                                          NL
 "# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5"       NL
 "# clients will typically report at least the \""                            \
@@ -329,6 +324,11 @@ create_hooks(svn_repos_t *repos, apr_poo
 "# e.g.: \"" SVN_RA_CAPABILITY_MERGEINFO ":some-other-capability\" "         \
   "(the order is undefined)."                                                NL
 "#"                                                                          NL
+"# Note: The TXN-NAME parameter is new in Subversion 1.8.  Prior to version" NL
+"# 1.8, the start-commit hook was invoked before the commit txn was even"    NL
+"# created, so the ability to inspect the commit txn and its metadata from"  NL
+"# within the start-commit hook was not possible."                           NL
+"# "                                                                         NL
 "# The list is self-reported by the client.  Therefore, you should not"      NL
 "# make security assumptions based on the capabilities list, nor should"     NL
 "# you assume that clients reliably report every capability they have."      NL
@@ -1035,6 +1035,13 @@ create_conf(svn_repos_t *repos, apr_pool
 "### \"none\" (to compare usernames as-is without case conversion, which"    NL
 "### is the default behavior)."                                              NL
 "# force-username-case = none"                                               NL
+"### The hooks-env options specifies a path to the hook script environment " NL
+"### configuration file. This option overrides the per-repository default"   NL
+"### and can be used to configure the hook script environment for multiple " NL
+"### repositories in a single file, if an absolute path is specified."       NL
+"### Unless you specify an absolute path, the file's location is relative"   NL
+"### to the directory containing this file."                                 NL
+"# hooks-env = " SVN_REPOS__CONF_HOOKS_ENV                                   NL
 ""                                                                           NL
 "[sasl]"                                                                     NL
 "### This option specifies whether you want to use the Cyrus SASL"           NL
@@ -1049,17 +1056,7 @@ create_conf(svn_repos_t *repos, apr_pool
 "### to the effective key length for encryption (e.g. 128 means 128-bit"     NL
 "### encryption). The values below are the defaults."                        NL
 "# min-encryption = 0"                                                       NL
-"# max-encryption = 256"                                                     NL
-""                                                                           NL
-"[hooks-env]"                                                                NL
-"### Options in this section define environment variables for use by"        NL
-"### hook scripts run by svnserve."                                          NL
-#ifdef WIN32
-"# PATH = C:\\Program Files\\Subversion\\bin"                                NL
-#else
-"# PATH = /bin:/sbin:/usr/bin:/usr/sbin"                                     NL
-#endif
-"# LC_CTYPE = en_US.UTF-8"                                                   NL;
+"# max-encryption = 256"                                                     NL;
 
     SVN_ERR_W(svn_io_file_create(svn_repos_svnserve_conf(repos, pool),
                                  svnserve_conf_contents, pool),
@@ -1126,6 +1123,129 @@ create_conf(svn_repos_t *repos, apr_pool
               _("Creating authz file"));
   }
 
+  {
+    static const char * const hooks_env_contents =
+"### This file is an example hook script environment configuration file."    NL
+"### Hook scripts run in an empty environment by default."                   NL
+"### As shown below each section defines environment variables for a"        NL
+"### particular hook script. The [default] section defines environment"      NL
+"### variables for all hook scripts, unless overridden by a hook-specific"   NL
+"### section."                                                               NL
+""                                                                           NL
+"### This example configures a UTF-8 locale for all hook scripts, so that "  NL
+"### special characters, such as umlauts, may be printed to stderr."         NL
+"### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must" NL
+"### also be set to 'yes' in httpd.conf."                                    NL
+"### With svnserve, the LANG environment variable of the svnserve process"   NL
+"### must be set to the same value as given here."                           NL
+"[default]"                                                                  NL
+"# LANG = en_US.UTF-8"                                                       NL
+""                                                                           NL
+"### This sets the PATH environment variable for the pre-commit hook."       NL
+"# [pre-commit]"                                                             NL
+"# PATH = /usr/local/bin:/usr/bin:/usr/sbin"                                 NL;
+
+    SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path,
+                                                 SVN_REPOS__CONF_HOOKS_ENV,
+                                                 pool),
+                                 hooks_env_contents, pool),
+              _("Creating hooks-env file"));
+  }
+
+  return SVN_NO_ERROR;
+}
+
+/* Baton for parse_hooks_env_option. */
+struct parse_hooks_env_option_baton {
+  /* The name of the section being parsed. If not the default section,
+   * the section name should match the name of a hook to which the
+   * options apply. */
+  const char *section;
+  apr_hash_t *hooks_env;
+} parse_hooks_env_option_baton;
+
+/* An implementation of svn_config_enumerator2_t.
+ * Set environment variable NAME to value VALUE in the environment for
+ * all hooks (in case the current section is the default section),
+ * or the hook with the name corresponding to the current section's name. */
+static svn_boolean_t
+parse_hooks_env_option(const char *name, const char *value,
+                       void *baton, apr_pool_t *pool)
+{
+  struct parse_hooks_env_option_baton *bo = baton;
+  apr_pool_t *result_pool = apr_hash_pool_get(bo->hooks_env);
+  apr_hash_t *hook_env;
+  
+  hook_env = apr_hash_get(bo->hooks_env, bo->section, APR_HASH_KEY_STRING);
+  if (hook_env == NULL)
+    {
+      hook_env = apr_hash_make(result_pool);
+      apr_hash_set(bo->hooks_env, apr_pstrdup(result_pool, bo->section),
+                   APR_HASH_KEY_STRING, hook_env);
+    }
+  apr_hash_set(hook_env, apr_pstrdup(result_pool, name),
+               APR_HASH_KEY_STRING, apr_pstrdup(result_pool, value));
+
+  return TRUE;
+}
+
+struct parse_hooks_env_section_baton {
+  svn_config_t *cfg;
+  apr_hash_t *hooks_env;
+} parse_hooks_env_section_baton;
+
+/* An implementation of svn_config_section_enumerator2_t. */
+static svn_boolean_t
+parse_hooks_env_section(const char *name, void *baton, apr_pool_t *pool)
+{
+  struct parse_hooks_env_section_baton *b = baton;
+  struct parse_hooks_env_option_baton bo;
+
+  bo.section = name;
+  bo.hooks_env = b->hooks_env;
+
+  (void)svn_config_enumerate2(b->cfg, name, parse_hooks_env_option, &bo, pool);
+
+  return TRUE;
+}
+
+/* Parse the hooks env file for this repository. */
+static svn_error_t *
+parse_hooks_env(svn_repos_t *repos,
+                const char *local_abspath,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+{
+  svn_config_t *cfg;
+  int n;
+  struct parse_hooks_env_section_baton b;
+
+  SVN_ERR(svn_config_read2(&cfg, local_abspath, FALSE, TRUE, scratch_pool));
+  b.cfg = cfg;
+  b.hooks_env = apr_hash_make(result_pool);
+  n = svn_config_enumerate_sections2(cfg, parse_hooks_env_section, &b,
+                                     scratch_pool);
+  if (n > 0)
+    repos->hooks_env = b.hooks_env;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_repos_hooks_setenv(svn_repos_t *repos,
+                       const char *hooks_env_path,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
+{
+  if (hooks_env_path == NULL)
+    hooks_env_path = svn_dirent_join(repos->conf_path,
+                                     SVN_REPOS__CONF_HOOKS_ENV, scratch_pool);
+  else if (!svn_dirent_is_absolute(hooks_env_path))
+    hooks_env_path = svn_dirent_join(repos->conf_path, hooks_env_path,
+                                     scratch_pool);
+
+  SVN_ERR(parse_hooks_env(repos, hooks_env_path, result_pool, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1145,6 +1265,7 @@ create_svn_repos_t(const char *path, apr
   repos->hook_path = svn_dirent_join(path, SVN_REPOS__HOOK_DIR, pool);
   repos->lock_path = svn_dirent_join(path, SVN_REPOS__LOCK_DIR, pool);
   repos->repository_capabilities = apr_hash_make(pool);
+  repos->hooks_env = NULL;
 
   return repos;
 }
@@ -1685,6 +1806,47 @@ svn_repos_recover4(const char *path,
   return SVN_NO_ERROR;
 }
 
+/* For BDB we fall back on BDB's repos layer lock which means that the
+   repository is unreadable while frozen.
+
+   For FSFS we delegate to the FS layer which uses the FSFS write-lock
+   and an SQLite reserved lock which means the repository is readable
+   while frozen. */
+svn_error_t *
+svn_repos_freeze(const char *path,
+                 svn_error_t *(*freeze_body)(void *, apr_pool_t *),
+                 void *baton,
+                 apr_pool_t *pool)
+{
+  svn_repos_t *repos;
+
+  /* Using a subpool as the only way to unlock the repos lock used by
+     BDB is to clear the pool used to take the lock. */
+  apr_pool_t *subpool = svn_pool_create(pool);
+
+  SVN_ERR(get_repos(&repos, path,
+                    TRUE  /* exclusive */,
+                    FALSE /* non-blocking */,
+                    FALSE /* open-fs */,
+                    NULL, subpool));
+
+  if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0)
+    {
+      svn_error_t *err = freeze_body(baton, subpool);
+      svn_pool_destroy(subpool);
+      return err;
+    }
+  else
+    {
+      SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, NULL, subpool));
+      SVN_ERR(svn_fs_freeze(svn_repos_fs(repos), freeze_body, baton, subpool));
+    }
+
+  svn_pool_destroy(subpool);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *svn_repos_db_logfiles(apr_array_header_t **logfiles,
                                    const char *path,
                                    svn_boolean_t only_unused,