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.