Due to popular demand, I gave it a go and replaced the
r0 template in FSFS with a bunch of code to generate
its content. The code is not too complicated but long-ish.

If people a in favour of committing this, I will. Otherwise,
I'll leave things as they are.

-- Stefan^2.
Index: subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.c	(revision 1649582)
+++ subversion/libsvn_fs_fs/fs_fs.c	(working copy)
@@ -36,6 +36,7 @@
 #include "cached_data.h"
 #include "id.h"
 #include "index.h"
+#include "low_level.h"
 #include "rep-cache.h"
 #include "revprops.h"
 #include "transaction.h"
@@ -1554,6 +1555,103 @@ svn_fs_fs__rep_copy(representation_t *re
   return apr_pmemdup(pool, rep, sizeof(*rep));
 }
 
+/* Set *noderev_str to the serialized form of the root noderev of r0 in FS.
+ * The text representation of the root directory is given in TEXT, it may
+ * be prefixed in r0 with TEXT_PREFIX (currently an empty string).  The
+ * noderev itself is prefixed with NODEREV_PREFIX (i.e. everything in front
+ * of the noderev in the r0 file).  Prefixes are only used to calculate
+ * offsets and are not copied in the result.
+ *
+ * Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporaries.
+ */
+static svn_error_t *
+construct_r0_noderev(const char **noderev_str,
+                     svn_fs_t *fs,
+                     const char *text,
+                     const char *text_prefix,
+                     const char *noderev_prefix,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  svn_checksum_t *md5;
+  svn_fs_id_t *id;
+  svn_stringbuf_t *result = svn_stringbuf_create_empty(result_pool);
+  svn_stream_t *stream;
+  int format;
+
+  svn_fs_fs__id_part_t node_id = { 0 };  /* "0-0" */
+  svn_fs_fs__id_part_t copy_id = { 0 };  /* "0-0" */
+  svn_fs_fs__id_part_t rev_item = { 0 }; /* "0-0" */
+
+  representation_t data_rep = { 0 };
+  node_revision_t noderev = { 0 };
+
+  /* Checksum the contents. */
+  SVN_ERR(svn_checksum(&md5, svn_checksum_md5, text, strlen(text),
+                       scratch_pool));
+
+  /* Construct the noderev's full ID.  Use log. or phys. location. */
+  rev_item.number = svn_fs_fs__use_log_addressing(fs)
+                  ? SVN_FS_FS__ITEM_INDEX_ROOT_NODE
+                  : strlen(noderev_prefix);
+  id = svn_fs_fs__id_rev_create(&node_id, &copy_id, &rev_item, scratch_pool);
+
+  /* Assign all non-NULL fields in DATA_REP. */
+  data_rep.item_index = svn_fs_fs__use_log_addressing(fs)
+                      ? SVN_FS_FS__ITEM_INDEX_FIRST_USER
+                      : strlen(text_prefix);
+  data_rep.size = strlen(text);
+  data_rep.expanded_size = strlen(text);
+  svn_fs_fs__id_txn_reset(&data_rep.txn_id);
+  memcpy(data_rep.md5_digest, md5->digest, sizeof(data_rep.md5_digest));
+
+  /* Tie everything together in the root NODEREV. */
+  noderev.kind = svn_node_dir;
+  noderev.copyfrom_rev = SVN_INVALID_REVNUM;
+  noderev.created_path = "/";
+  noderev.copyroot_path = noderev.created_path;
+  noderev.id = id;
+  noderev.data_rep = &data_rep;
+
+  /* Serialize that into a buffer - using the oldest format possible. */
+  format = svn_fs_fs__use_log_addressing(fs)
+         ? SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT
+         : 1;
+
+  stream = svn_stream_from_stringbuf(result, scratch_pool);
+  SVN_ERR(svn_fs_fs__write_noderev(stream, &noderev, format, FALSE,
+                                   scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
+
+  /* Finally done ... */
+  *noderev_str = result->data;
+
+  return SVN_NO_ERROR;
+}
+
+/* Set *CHANGES_STR to the serialized form of the changed paths list of r0
+ * in FS.  Allocate the result in RESULT_POOL and use SCRATCH_POOL for
+ * temporaries. */
+static svn_error_t *
+construct_r0_changes(const char **changes_str,
+                     svn_fs_t *fs,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  svn_stringbuf_t *result = svn_stringbuf_create_empty(result_pool);
+  svn_stream_t *stream;
+
+  /* There are no changes in r0, so serialize an empty hash into a buffer. */
+  stream = svn_stream_from_stringbuf(result, scratch_pool);
+  SVN_ERR(svn_fs_fs__write_changes(stream, fs, apr_hash_make(scratch_pool),
+                                   TRUE, scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
+
+  /* Done ... */
+  *changes_str = result->data;
+
+  return SVN_NO_ERROR;
+};
 
 /* Write out the zeroth revision for filesystem FS.
    Perform temporary allocations in SCRATCH_POOL. */
@@ -1567,58 +1665,69 @@ write_revision_zero(svn_fs_t *fs,
   const char *path_revision_zero = svn_fs_fs__path_rev(fs, 0, subpool);
   apr_hash_t *proplist;
   svn_string_t date;
+  const char *dir_str, *dir_rep, *noderev, *changes, *r0_contents;
+  svn_fs_fs__revision_file_t *rev_file;
+  apr_off_t dir_len, noderev_len;
+
+  /* Directory /@0 contains no entries.  It is terminated like a hash
+   * followed by a newline.  This is its (plain) representation. */
+  dir_str = SVN_HASH_TERMINATOR "\n";
+
+  /* On disk, we wrap it with a header and a footer. */
+  dir_rep = apr_psprintf(subpool, "PLAIN\n%sENDREP\n", dir_str);
+  dir_len = (apr_off_t)strlen(dir_rep);
+
+  /* Construct the two other items in r0. */
+  SVN_ERR(construct_r0_noderev(&noderev, fs, dir_str, "", dir_rep,
+                               subpool, subpool));
+  SVN_ERR(construct_r0_changes(&changes, fs, subpool, subpool));
+  noderev_len = (apr_off_t)strlen(noderev);
+
+  /* Chain them into a skeleton r0 template. */
+  r0_contents = apr_psprintf(subpool, "%s%s%s", dir_rep, noderev, changes);
 
-  /* Write out a rev file for revision 0. */
+  /* Write the skeleton r0. */
+  SVN_ERR(svn_io_file_create(path_revision_zero, r0_contents, subpool));
+
+  /* Append index & trailer.  Now re-open r0 to do that. */
+  SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0,
+                                                    subpool, subpool));
   if (svn_fs_fs__use_log_addressing(fs))
     {
       apr_array_header_t *index_entries;
       svn_fs_fs__p2l_entry_t *entry;
-      svn_fs_fs__revision_file_t *rev_file;
       const char *l2p_proto_index, *p2l_proto_index;
 
-      /* Write a skeleton r0 with no indexes. */
-      SVN_ERR(svn_io_file_create(path_revision_zero,
-                    "PLAIN\nEND\nENDREP\n"
-                    "id: 0.0.r0/2\n"
-                    "type: dir\n"
-                    "count: 0\n"
-                    "text: 0 3 4 4 "
-                    "2d2977d1c96f487abe4a1e202dd03b4e\n"
-                    "cpath: /\n"
-                    "\n\n", subpool));
-
       /* Construct the index P2L contents: describe the 3 items we have.
          Be sure to create them in on-disk order. */
       index_entries = apr_array_make(subpool, 3, sizeof(entry));
 
       entry = apr_pcalloc(subpool, sizeof(*entry));
       entry->offset = 0;
-      entry->size = 17;
+      entry->size = dir_len;
       entry->type = SVN_FS_FS__ITEM_TYPE_DIR_REP;
       entry->item.revision = 0;
       entry->item.number = SVN_FS_FS__ITEM_INDEX_FIRST_USER;
       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
 
       entry = apr_pcalloc(subpool, sizeof(*entry));
-      entry->offset = 17;
-      entry->size = 89;
+      entry->offset = dir_len;
+      entry->size = noderev_len;
       entry->type = SVN_FS_FS__ITEM_TYPE_NODEREV;
       entry->item.revision = 0;
       entry->item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE;
       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
 
       entry = apr_pcalloc(subpool, sizeof(*entry));
-      entry->offset = 106;
-      entry->size = 1;
+      entry->offset = dir_len + noderev_len;
+      entry->size = (apr_off_t)strlen(changes);
       entry->type = SVN_FS_FS__ITEM_TYPE_CHANGES;
       entry->item.revision = 0;
       entry->item.number = SVN_FS_FS__ITEM_INDEX_CHANGES;
       APR_ARRAY_PUSH(index_entries, svn_fs_fs__p2l_entry_t *) = entry;
 
-      /* Now re-open r0, create proto-index files from our entries and
-         rewrite the index section of r0. */
-      SVN_ERR(svn_fs_fs__open_pack_or_rev_file_writable(&rev_file, fs, 0,
-                                                        subpool, subpool));
+      /* Create proto-index files from our entries and append the index
+       * section to r0. */
       SVN_ERR(svn_fs_fs__p2l_index_from_p2l_entries(&p2l_proto_index, fs,
                                                     rev_file, index_entries,
                                                     subpool, subpool));
@@ -1627,19 +1736,20 @@ write_revision_zero(svn_fs_t *fs,
                                                     subpool, subpool));
       SVN_ERR(svn_fs_fs__add_index_data(fs, rev_file->file, l2p_proto_index,
                                         p2l_proto_index, 0, subpool));
-      SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
     }
   else
-    SVN_ERR(svn_io_file_create(path_revision_zero,
-                               "PLAIN\nEND\nENDREP\n"
-                               "id: 0.0.r0/17\n"
-                               "type: dir\n"
-                               "count: 0\n"
-                               "text: 0 0 4 4 "
-                               "2d2977d1c96f487abe4a1e202dd03b4e\n"
-                               "cpath: /\n"
-                               "\n\n17 107\n", subpool));
+    {
+      apr_off_t offset = 0;
+      svn_stringbuf_t *trailer = svn_fs_fs__unparse_revision_trailer
+                                  (dir_len, dir_len + noderev_len, subpool);
+
+      SVN_ERR(svn_io_file_seek(rev_file->file, APR_END, &offset, subpool));
+      SVN_ERR(svn_io_file_write_full(rev_file->file, trailer->data,
+                                     trailer->len, NULL, subpool));
+    }
 
+  /* Close & seal the file. */
+  SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
   SVN_ERR(svn_io_set_file_read_only(path_revision_zero, FALSE, subpool));
 
   /* Set a date on revision 0. */
Index: subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
===================================================================
--- subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c	(revision 1649582)
+++ subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c	(working copy)
@@ -27,6 +27,7 @@
 #include "../svn_test.h"
 #include "../../libsvn_fs_fs/fs.h"
 #include "../../libsvn_fs_fs/fs_fs.h"
+#include "../../libsvn_fs_fs/util.h"
 
 #include "svn_hash.h"
 #include "svn_pools.h"
@@ -1243,6 +1244,58 @@ id_parser_test(const svn_test_opts_t *op
 
 #undef REPO_NAME
 
+/* ------------------------------------------------------------------------ */
+
+#define REPO_NAME "test-repo-compare_r0_template"
+static svn_error_t *
+compare_r0_template(const svn_test_opts_t *opts,
+                    apr_pool_t *pool)
+{
+  svn_fs_t *fs;
+  svn_stringbuf_t *r0;
+  const char *expected;
+
+  /* Create a filesystem and read its r0. */
+  SVN_ERR(svn_test__create_fs(&fs, REPO_NAME, opts, pool));
+  SVN_ERR(svn_stringbuf_from_file2(&r0,
+                                   svn_fs_fs__path_rev_absolute(fs, 0, pool),
+                                   pool));
+
+  /* Compare to our template. */
+  if (svn_fs_fs__use_log_addressing(fs))
+    {
+      /* Compare only the non-binary part of formats 7 and up. */
+      expected = "PLAIN\nEND\nENDREP\n"
+                 "id: 0.0.r0/2\n"
+                 "type: dir\n"
+                 "count: 0\n"
+                 "text: 0 3 4 4 "
+                 "2d2977d1c96f487abe4a1e202dd03b4e\n"
+                 "cpath: /\n"
+                 "\n\nL2P-INDEX\n";
+
+      svn_stringbuf_remove(r0, strlen(expected), r0->len);
+    }
+  else
+    {
+      /* Full comparison for formats 1 .. 6 */
+      expected = "PLAIN\nEND\nENDREP\n"
+                 "id: 0.0.r0/17\n"
+                 "type: dir\n"
+                 "count: 0\n"
+                 "text: 0 0 4 4 "
+                 "2d2977d1c96f487abe4a1e202dd03b4e\n"
+                 "cpath: /\n"
+                 "\n\n17 107\n";
+    }
+
+  SVN_TEST_STRING_ASSERT(r0->data, expected);
+
+  return SVN_NO_ERROR;
+}
+
+#undef REPO_NAME
+
 
 /* The test table.  */
 
@@ -1285,6 +1338,8 @@ static struct svn_test_descriptor_t test
                        "change revprops with enabled and disabled caching"),
     SVN_TEST_OPTS_PASS(id_parser_test,
                        "id parser test"),
+    SVN_TEST_OPTS_PASS(compare_r0_template,
+                       "expect r0 to match the template"),
     SVN_TEST_NULL
   };
 
Generate r0 in FSFS to the degree possible without having a proper txn.

Add a regression test that verifies we still end up with the same
quirky content as before.

* subversion/libsvn_fs_fs/fs_fs.c
  (construct_r0_noderev,
   construct_r0_changes): New utilities.
  (write_revision_zero): Use the new utilities to piece r0 together
                         from its parts.  Avoid literals if feasible.

* subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
  (compare_r0_template): New test.
  (test_funcs): Register new test.

Reply via email to