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, ©_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.