Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/stats.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/stats.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/stats.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/stats.c Tue Nov 25 16:26:48 2014 @@ -71,13 +71,13 @@ typedef enum rep_kind_t typedef struct rep_stats_t { /* absolute offset in the file */ - apr_size_t offset; + apr_off_t offset; /* item length in bytes */ - apr_size_t size; + apr_uint64_t size; /* item length after de-deltification */ - apr_size_t expanded_size; + apr_uint64_t expanded_size; /* revision that contains this representation * (may be referenced by other revisions, though) */ @@ -104,34 +104,35 @@ typedef struct revision_info_t /* pack file offset (manifest value), 0 for non-packed files */ apr_off_t offset; - /* offset of the changes list relative to OFFSET */ - apr_size_t changes; - /* length of the changes list on bytes */ - apr_size_t changes_len; + apr_uint64_t changes_len; /* offset of the changes list relative to OFFSET */ - apr_size_t change_count; + apr_uint64_t change_count; /* first offset behind the revision data in the pack file (file length * for non-packed revs) */ apr_off_t end; /* number of directory noderevs in this revision */ - apr_size_t dir_noderev_count; + apr_uint64_t dir_noderev_count; /* number of file noderevs in this revision */ - apr_size_t file_noderev_count; + apr_uint64_t file_noderev_count; /* total size of directory noderevs (i.e. the structs - not the rep) */ - apr_size_t dir_noderev_size; + apr_uint64_t dir_noderev_size; /* total size of file noderevs (i.e. the structs - not the rep) */ - apr_size_t file_noderev_size; + apr_uint64_t file_noderev_size; /* all rep_stats_t of this revision (in no particular order), * i.e. those that point back to this struct */ apr_array_header_t *representations; + + /* Temporary rev / pack file access object, used in phys. addressing + * mode only. NULL when done reading this revision. */ + svn_fs_fs__revision_file_t *rev_file; } revision_info_t; /* Root data structure containing all information about a given repository. @@ -175,97 +176,45 @@ typedef struct query_t void *cancel_baton; } query_t; -/* Open the file containing revision REV in QUERY and return it in *FILE. +/* Return the length of REV_FILE in *FILE_SIZE. + * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * -open_rev_or_pack_file(apr_file_t **file, - query_t *query, - svn_revnum_t rev, - apr_pool_t *pool) -{ - svn_fs_fs__revision_file_t *rev_file; - SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, rev, - pool, pool)); - - *file = rev_file->file; - return SVN_NO_ERROR; -} - -/* Return the length of FILE in *FILE_SIZE. Use POOL for allocations. -*/ -static svn_error_t * get_file_size(apr_off_t *file_size, - apr_file_t *file, - apr_pool_t *pool) + svn_fs_fs__revision_file_t *rev_file, + apr_pool_t *scratch_pool) { apr_finfo_t finfo; - SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, file, pool)); + SVN_ERR(svn_io_file_info_get(&finfo, APR_FINFO_SIZE, rev_file->file, + scratch_pool)); *file_size = finfo.size; return SVN_NO_ERROR; } -/* Get the file content of revision REVISION in QUERY and return it in - * *CONTENT. Read the LEN bytes starting at file OFFSET. When provided, - * use FILE as packed or plain rev file. - * Use POOL for temporary allocations. - */ -static svn_error_t * -get_content(svn_stringbuf_t **content, - apr_file_t *file, - query_t *query, - svn_revnum_t revision, - apr_off_t offset, - apr_size_t len, - apr_pool_t *pool) -{ - apr_pool_t * file_pool = svn_pool_create(pool); - apr_size_t large_buffer_size = 0x10000; - - if (file == NULL) - SVN_ERR(open_rev_or_pack_file(&file, query, revision, file_pool)); - - *content = svn_stringbuf_create_ensure(len, pool); - (*content)->len = len; - - /* for better efficiency use larger buffers on large reads */ - if ( (len >= large_buffer_size) - && (apr_file_buffer_size_get(file) < large_buffer_size)) - apr_file_buffer_set(file, - apr_palloc(apr_file_pool_get(file), - large_buffer_size), - large_buffer_size); - - SVN_ERR(svn_io_file_seek(file, APR_SET, &offset, pool)); - SVN_ERR(svn_io_file_read_full2(file, (*content)->data, len, - NULL, NULL, pool)); - svn_pool_destroy(file_pool); - - return SVN_NO_ERROR; -} - /* Initialize the LARGEST_CHANGES member in STATS with a capacity of COUNT - * entries. Use POOL for allocations. + * entries. Allocate the result in RESULT_POOL. */ static void initialize_largest_changes(svn_fs_fs__stats_t *stats, apr_size_t count, - apr_pool_t *pool) + apr_pool_t *result_pool) { apr_size_t i; - stats->largest_changes = apr_pcalloc(pool, sizeof(*stats->largest_changes)); + stats->largest_changes = apr_pcalloc(result_pool, + sizeof(*stats->largest_changes)); stats->largest_changes->count = count; stats->largest_changes->min_size = 1; stats->largest_changes->changes - = apr_palloc(pool, count * sizeof(*stats->largest_changes->changes)); + = apr_palloc(result_pool, count * sizeof(*stats->largest_changes->changes)); /* allocate *all* entries before the path stringbufs. This increases * cache locality and enhances performance significantly. */ for (i = 0; i < count; ++i) stats->largest_changes->changes[i] - = apr_palloc(pool, sizeof(**stats->largest_changes->changes)); + = apr_palloc(result_pool, sizeof(**stats->largest_changes->changes)); /* now initialize them and allocate the stringbufs */ for (i = 0; i < count; ++i) @@ -273,7 +222,7 @@ initialize_largest_changes(svn_fs_fs__st stats->largest_changes->changes[i]->size = 0; stats->largest_changes->changes[i]->revision = SVN_INVALID_REVNUM; stats->largest_changes->changes[i]->path - = svn_stringbuf_create_ensure(1024, pool); + = svn_stringbuf_create_ensure(1024, result_pool); } } @@ -398,75 +347,19 @@ add_change(svn_fs_fs__stats_t *stats, } } -/* Read header information for the revision stored in FILE_CONTENT (one - * whole revision). Return the offsets within FILE_CONTENT for the - * *ROOT_NODEREV, the list of *CHANGES and its len in *CHANGES_LEN. - * Use POOL for temporary allocations. */ -static svn_error_t * -read_revision_header(apr_size_t *changes, - apr_size_t *changes_len, - apr_size_t *root_noderev, - svn_stringbuf_t *file_content, - apr_pool_t *pool) -{ - char buf[64]; - const char *line; - char *space; - apr_uint64_t val; - apr_size_t len; - - /* Read in this last block, from which we will identify the last line. */ - len = sizeof(buf); - if (len > file_content->len) - len = file_content->len; - - memcpy(buf, file_content->data + file_content->len - len, len); - - /* The last byte should be a newline. */ - if (buf[(apr_ssize_t)len - 1] != '\n') - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Revision lacks trailing newline")); - - /* Look for the next previous newline. */ - buf[len - 1] = 0; - line = strrchr(buf, '\n'); - if (line == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file longer " - "than 64 characters")); - - space = strchr(line, ' '); - if (space == NULL) - return svn_error_create(SVN_ERR_FS_CORRUPT, NULL, - _("Final line in revision file missing space")); - - /* terminate the header line */ - *space = 0; - - /* extract information */ - SVN_ERR(svn_cstring_strtoui64(&val, line+1, 0, APR_SIZE_MAX, 10)); - *root_noderev = (apr_size_t)val; - SVN_ERR(svn_cstring_strtoui64(&val, space+1, 0, APR_SIZE_MAX, 10)); - *changes = (apr_size_t)val; - *changes_len = file_content->len - *changes - (buf + len - line) + 1; - - return SVN_NO_ERROR; -} - /* Comparator used for binary search comparing the absolute file offset * of a representation to some other offset. DATA is a *rep_stats_t, - * KEY is a pointer to an apr_size_t. + * KEY is a pointer to an apr_off_t. */ static int compare_representation_offsets(const void *data, const void *key) { - apr_ssize_t diff = (*(const rep_stats_t *const *)data)->offset - - *(const apr_size_t *)key; + apr_off_t lhs = (*(const rep_stats_t *const *)data)->offset; + apr_off_t rhs = *(const apr_off_t *)key; - /* sizeof(int) may be < sizeof(ssize_t) */ - if (diff < 0) + if (lhs < rhs) return -1; - return diff > 0 ? 1 : 0; + return (lhs > rhs ? 1 : 0); } /* Find the revision_info_t object to the given REVISION in QUERY and @@ -483,7 +376,7 @@ find_representation(int *idx, query_t *query, revision_info_t **revision_info, svn_revnum_t revision, - apr_size_t offset) + apr_off_t offset) { revision_info_t *info; *idx = -1; @@ -519,19 +412,17 @@ find_representation(int *idx, } /* Find / auto-construct the representation stats for REP in QUERY and - * return it in *REPRESENTATION. To be able to parse the base rep, we - * pass the FILE_CONTENT as well. + * return it in *REPRESENTATION. * - * If necessary, allocate the result in POOL; use SCRATCH_POOL for temp. - * allocations. + * If necessary, allocate the result in RESULT_POOL; use SCRATCH_POOL for + * temporary allocations. */ static svn_error_t * parse_representation(rep_stats_t **representation, query_t *query, - svn_stringbuf_t *file_content, representation_t *rep, revision_info_t *revision_info, - apr_pool_t *pool, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { rep_stats_t *result; @@ -541,31 +432,32 @@ parse_representation(rep_stats_t **repre /* look it up */ result = find_representation(&idx, query, &revision_info, rep->revision, - (apr_size_t)rep->item_index); + (apr_off_t)rep->item_index); if (!result) { /* not parsed, yet (probably a rep in the same revision). * Create a new rep object and determine its base rep as well. */ - result = apr_pcalloc(pool, sizeof(*result)); + result = apr_pcalloc(result_pool, sizeof(*result)); result->revision = rep->revision; - result->expanded_size = (apr_size_t)(rep->expanded_size - ? rep->expanded_size - : rep->size); - result->offset = (apr_size_t)rep->item_index; - result->size = (apr_size_t)rep->size; - - if (!svn_fs_fs__use_log_addressing(query->fs, rep->revision)) + result->expanded_size = (rep->expanded_size ? rep->expanded_size + : rep->size); + result->offset = (apr_off_t)rep->item_index; + result->size = rep->size; + + /* In phys. addressing mode, follow link to the actual representation. + * In log. addressing mode, we will find it already as part of our + * linear walk through the whole file. */ + if (!svn_fs_fs__use_log_addressing(query->fs)) { svn_fs_fs__rep_header_t *header; - svn_stream_t *stream; + apr_off_t offset = revision_info->offset + result->offset; - svn_string_t content; - content.data = file_content->data + (apr_size_t)rep->item_index; - content.len = file_content->len - (apr_size_t)rep->item_index; - - stream = svn_stream_from_string(&content, scratch_pool); - SVN_ERR(svn_fs_fs__read_rep_header(&header, stream, + SVN_ERR_ASSERT(revision_info->rev_file); + SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET, + &offset, scratch_pool)); + SVN_ERR(svn_fs_fs__read_rep_header(&header, + revision_info->rev_file->stream, scratch_pool, scratch_pool)); result->header_size = header->header_size; @@ -583,33 +475,79 @@ parse_representation(rep_stats_t **repre /* forward declaration */ static svn_error_t * read_noderev(query_t *query, - svn_stringbuf_t *file_content, - apr_size_t offset, + svn_stringbuf_t *noderev_str, revision_info_t *revision_info, - apr_pool_t *pool, + apr_pool_t *result_pool, apr_pool_t *scratch_pool); -/* Starting at the directory in NODEREV's text in FILE_CONTENT, read all - * DAG nodes, directories and representations linked in that tree structure. +/* Read the noderev item at OFFSET in REVISION_INFO from the filesystem + * provided by QUERY. Return it in *NODEREV, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. + * + * The textual representation of the noderev will be used to determine + * the on-disk size of the noderev. Only called in phys. addressing mode. + */ +static svn_error_t * +read_phsy_noderev(svn_stringbuf_t **noderev, + query_t *query, + apr_off_t offset, + revision_info_t *revision_info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_stringbuf_t *noderev_str = svn_stringbuf_create_empty(result_pool); + svn_stringbuf_t *line; + svn_boolean_t eof; + + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + /* Navigate the file stream to the start of noderev. */ + SVN_ERR_ASSERT(revision_info->rev_file); + + offset += revision_info->offset; + SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET, + &offset, scratch_pool)); + + /* Read it (terminated by an empty line) */ + do + { + svn_pool_clear(iterpool); + + SVN_ERR(svn_stream_readline(revision_info->rev_file->stream, &line, + "\n", &eof, iterpool)); + svn_stringbuf_appendstr(noderev_str, line); + svn_stringbuf_appendbyte(noderev_str, '\n'); + } + while (line->len > 0 && !eof); + + /* Return the result. */ + *noderev = noderev_str; + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + +/* Starting at the directory in NODEREV's text, read all DAG nodes, + * directories and representations linked in that tree structure. * Store them in QUERY and REVISION_INFO. Also, read them only once. * - * Use POOL for persistent allocations and SCRATCH_POOL for temporaries. + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * parse_dir(query_t *query, - svn_stringbuf_t *file_content, node_revision_t *noderev, revision_info_t *revision_info, - apr_pool_t *pool, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); - apr_pool_t *subpool = svn_pool_create(scratch_pool); int i; apr_array_header_t *entries; SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, query->fs, noderev, - subpool, subpool)); + scratch_pool, scratch_pool)); for (i = 0; i < entries->nelts; ++i) { @@ -617,55 +555,49 @@ parse_dir(query_t *query, if (svn_fs_fs__id_rev(dirent->id) == revision_info->revision) { + svn_stringbuf_t *noderev_str; svn_pool_clear(iterpool); - SVN_ERR(read_noderev(query, file_content, - (apr_size_t)svn_fs_fs__id_item(dirent->id), - revision_info, pool, iterpool)); + + SVN_ERR(read_phsy_noderev(&noderev_str, query, + svn_fs_fs__id_item(dirent->id), + revision_info, iterpool, iterpool)); + SVN_ERR(read_noderev(query, noderev_str, revision_info, + result_pool, iterpool)); } } svn_pool_destroy(iterpool); - svn_pool_destroy(subpool); return SVN_NO_ERROR; } -/* Starting at the noderev at OFFSET in FILE_CONTENT, read all DAG nodes, - * directories and representations linked in that tree structure. Store - * them in QUERY and REVISION_INFO. Also, read them only once. Return the - * result in *NODEREV. +/* Parse the noderev given as NODEREV_STR and store the info in QUERY and + * REVISION_INFO. In phys. addressing mode, continue reading all DAG nodes, + * directories and representations linked in that tree structure. * - * Use POOL for persistent allocations and SCRATCH_POOL for temporaries. + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_noderev(query_t *query, - svn_stringbuf_t *file_content, - apr_size_t offset, + svn_stringbuf_t *noderev_str, revision_info_t *revision_info, - apr_pool_t *pool, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - const char *end_marker = "\n\n"; - const char *noderev_end = strstr(file_content->data + offset, end_marker); - apr_size_t noderev_len = noderev_end ? (noderev_end - file_content->data - - offset + strlen(end_marker)) - : (file_content->len - offset); - rep_stats_t *text = NULL; rep_stats_t *props = NULL; - node_revision_t *noderev; - apr_pool_t *subpool = svn_pool_create(scratch_pool); - svn_stream_t *stream = svn_stream_from_stringbuf(file_content, subpool); - svn_stream_skip(stream, offset); - SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, subpool, subpool)); + svn_stream_t *stream = svn_stream_from_stringbuf(noderev_str, scratch_pool); + SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, scratch_pool, + scratch_pool)); if (noderev->data_rep) { - SVN_ERR(parse_representation(&text, query, file_content, + SVN_ERR(parse_representation(&text, query, noderev->data_rep, revision_info, - pool, subpool)); + result_pool, scratch_pool)); /* if we are the first to use this rep, mark it as "text rep" */ if (++text->ref_count == 1) @@ -674,9 +606,9 @@ read_noderev(query_t *query, if (noderev->prop_rep) { - SVN_ERR(parse_representation(&props, query, file_content, + SVN_ERR(parse_representation(&props, query, noderev->prop_rep, revision_info, - pool, subpool)); + result_pool, scratch_pool)); /* if we are the first to use this rep, mark it as "prop rep" */ if (++props->ref_count == 1) @@ -697,67 +629,121 @@ read_noderev(query_t *query, /* if this is a directory and has not been processed, yet, read and * process it recursively */ if ( noderev->kind == svn_node_dir && text && text->ref_count == 1 - && !svn_fs_fs__use_log_addressing(query->fs, revision_info->revision)) - SVN_ERR(parse_dir(query, file_content, noderev, revision_info, - pool, subpool)); + && !svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(parse_dir(query, noderev, revision_info, result_pool, + scratch_pool)); /* update stats */ if (noderev->kind == svn_node_dir) { - revision_info->dir_noderev_size += noderev_len; + revision_info->dir_noderev_size += noderev_str->len; revision_info->dir_noderev_count++; } else { - revision_info->file_noderev_size += noderev_len; + revision_info->file_noderev_size += noderev_str->len; revision_info->file_noderev_count++; } - svn_pool_destroy(subpool); return SVN_NO_ERROR; } -/* Given the unparsed changes list in CHANGES with LEN chars, return the - * number of changed paths encoded in it. +/* For the revision given as REVISION_INFO within QUERY, determine the number + * of entries in its changed paths list and store that info in REVISION_INFO. + * Use SCRATCH_POOL for temporary allocations. */ -static apr_size_t -get_change_count(const char *changes, - apr_size_t len) -{ - apr_size_t lines = 0; - const char *end = changes + len; +static svn_error_t * +get_phys_change_count(query_t *query, + revision_info_t *revision_info, + apr_pool_t *scratch_pool) +{ + /* We are going to use our own sub-pool here because the changes object + * may well be >100MB and SCRATCH_POOL may not get cleared until all other + * info has been read by read_phys_revision(). Therefore, tidy up early. + */ + apr_pool_t *subpool = svn_pool_create(scratch_pool); + apr_array_header_t *changes; - /* line count */ - for (; changes < end; ++changes) - if (*changes == '\n') - ++lines; + SVN_ERR(svn_fs_fs__get_changes(&changes, query->fs, + revision_info->revision, subpool)); + revision_info->change_count = changes->nelts; - /* two lines per change */ - return lines / 2; + /* Release potentially tons of memory. */ + svn_pool_destroy(subpool); + + return SVN_NO_ERROR; +} + +/* Read header information for the revision stored in FILE_CONTENT (one + * whole revision). Return the offsets within FILE_CONTENT for the + * *ROOT_NODEREV, the list of *CHANGES and its len in *CHANGES_LEN. + * Use POOL for temporary allocations. */ +static svn_error_t * +read_phys_revision(query_t *query, + revision_info_t *info, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + char buf[64]; + apr_off_t root_node_offset; + apr_off_t changes_offset; + svn_stringbuf_t *trailer; + svn_stringbuf_t *noderev_str; + + /* Read the last 64 bytes of the revision (if long enough). */ + apr_off_t start = MAX(info->offset, info->end - sizeof(buf)); + apr_size_t len = (apr_size_t)(info->end - start); + SVN_ERR(svn_io_file_seek(info->rev_file->file, APR_SET, &start, + scratch_pool)); + SVN_ERR(svn_io_file_read_full2(info->rev_file->file, buf, len, NULL, NULL, + scratch_pool)); + trailer = svn_stringbuf_ncreate(buf, len, scratch_pool); + + /* Parse that trailer. */ + SVN_ERR(svn_fs_fs__parse_revision_trailer(&root_node_offset, + &changes_offset, trailer, + info->revision)); + SVN_ERR(get_phys_change_count(query, info, scratch_pool)); + + /* Calculate the length of the changes list. */ + trailer = svn_fs_fs__unparse_revision_trailer(root_node_offset, + changes_offset, + scratch_pool); + info->changes_len = info->end - info->offset - changes_offset + - trailer->len; + + /* Recursively read nodes added in this rev. */ + SVN_ERR(read_phsy_noderev(&noderev_str, query, root_node_offset, info, + scratch_pool, scratch_pool)); + SVN_ERR(read_noderev(query, noderev_str, info, result_pool, scratch_pool)); + + return SVN_NO_ERROR; } /* Read the content of the pack file staring at revision BASE physical - * addressing mode and store it in QUERY. Use POOL for allocations. + * addressing mode and store it in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_phys_pack_file(query_t *query, svn_revnum_t base, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_pool_t *local_pool = svn_pool_create(pool); - apr_pool_t *iterpool = svn_pool_create(local_pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); int i; apr_off_t file_size = 0; - apr_file_t *file; + svn_fs_fs__revision_file_t *rev_file; - SVN_ERR(open_rev_or_pack_file(&file, query, base, local_pool)); - SVN_ERR(get_file_size(&file_size, file, local_pool)); + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base, + scratch_pool, scratch_pool)); + SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool)); /* process each revision in the pack file */ for (i = 0; i < query->shard_size; ++i) { - apr_size_t root_node_offset; - svn_stringbuf_t *rev_content; revision_info_t *info; /* cancellation support */ @@ -765,133 +751,139 @@ read_phys_pack_file(query_t *query, SVN_ERR(query->cancel_func(query->cancel_baton)); /* create the revision info for the current rev */ - info = apr_pcalloc(pool, sizeof(*info)); - info->representations = apr_array_make(iterpool, 4, sizeof(rep_stats_t*)); + info = apr_pcalloc(result_pool, sizeof(*info)); + info->representations = apr_array_make(result_pool, 4, + sizeof(rep_stats_t*)); + info->rev_file = rev_file; info->revision = base + i; SVN_ERR(svn_fs_fs__get_packed_offset(&info->offset, query->fs, base + i, iterpool)); if (i + 1 == query->shard_size) - SVN_ERR(svn_io_file_seek(file, APR_END, &info->end, iterpool)); + info->end = file_size; else SVN_ERR(svn_fs_fs__get_packed_offset(&info->end, query->fs, base + i + 1, iterpool)); - SVN_ERR(get_content(&rev_content, file, query, info->revision, - info->offset, - info->end - info->offset, - iterpool)); - - SVN_ERR(read_revision_header(&info->changes, - &info->changes_len, - &root_node_offset, - rev_content, - iterpool)); - - info->change_count - = get_change_count(rev_content->data + info->changes, - info->changes_len); - SVN_ERR(read_noderev(query, rev_content, - root_node_offset, info, pool, iterpool)); + SVN_ERR(read_phys_revision(query, info, result_pool, iterpool)); + + info->representations = apr_array_copy(result_pool, + info->representations); - info->representations = apr_array_copy(pool, info->representations); + /* Done with this revision. */ + info->rev_file = NULL; + + /* put it into our container */ APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; /* destroy temps */ svn_pool_clear(iterpool); } + /* Done with this pack file. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + /* one more pack file processed */ if (query->progress_func) - query->progress_func(base, query->progress_baton, local_pool); - - svn_pool_destroy(local_pool); + query->progress_func(base, query->progress_baton, scratch_pool); return SVN_NO_ERROR; } /* Read the content of the file for REVISION in physical addressing mode - * and store its contents in QUERY. Use POOL for allocations. + * and store its contents in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_phys_revision_file(query_t *query, svn_revnum_t revision, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - apr_size_t root_node_offset; - apr_pool_t *local_pool = svn_pool_create(pool); - svn_stringbuf_t *rev_content; - revision_info_t *info = apr_pcalloc(pool, sizeof(*info)); + revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info)); apr_off_t file_size = 0; - apr_file_t *file; + svn_fs_fs__revision_file_t *rev_file; /* cancellation support */ if (query->cancel_func) SVN_ERR(query->cancel_func(query->cancel_baton)); /* read the whole pack file into memory */ - SVN_ERR(open_rev_or_pack_file(&file, query, revision, local_pool)); - SVN_ERR(get_file_size(&file_size, file, local_pool)); + SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, revision, + scratch_pool, scratch_pool)); + SVN_ERR(get_file_size(&file_size, rev_file, scratch_pool)); /* create the revision info for the current rev */ - info->representations = apr_array_make(pool, 4, sizeof(rep_stats_t*)); + info->representations = apr_array_make(result_pool, 4, sizeof(rep_stats_t*)); + info->rev_file = rev_file; info->revision = revision; info->offset = 0; info->end = file_size; - SVN_ERR(get_content(&rev_content, file, query, revision, 0, file_size, - local_pool)); + SVN_ERR(read_phys_revision(query, info, result_pool, scratch_pool)); - SVN_ERR(read_revision_header(&info->changes, - &info->changes_len, - &root_node_offset, - rev_content, - local_pool)); + /* Done with this revision. */ + SVN_ERR(svn_fs_fs__close_revision_file(rev_file)); + info->rev_file = NULL; - /* put it into our containers */ + /* put it into our container */ APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; - info->change_count - = get_change_count(rev_content->data + info->changes, - info->changes_len); - - /* parse the revision content recursively. */ - SVN_ERR(read_noderev(query, rev_content, - root_node_offset, info, - pool, local_pool)); - /* show progress every 1000 revs or so */ if (query->progress_func) { if (query->shard_size && (revision % query->shard_size == 0)) - query->progress_func(revision, query->progress_baton, local_pool); + query->progress_func(revision, query->progress_baton, scratch_pool); if (!query->shard_size && (revision % 1000 == 0)) - query->progress_func(revision, query->progress_baton, local_pool); + query->progress_func(revision, query->progress_baton, scratch_pool); } - svn_pool_destroy(local_pool); - return SVN_NO_ERROR; } -/* Read the item described by ENTRY from the REV_FILE and return - * the respective byte sequence in *CONTENTS allocated in POOL. +/* Given the unparsed changes list in CHANGES with LEN chars, return the + * number of changed paths encoded in it. Only used in log. addressing + * mode. + */ +static apr_uint64_t +get_log_change_count(const char *changes, + apr_size_t len) +{ + apr_size_t lines = 0; + const char *end = changes + len; + + /* line count */ + for (; changes < end; ++changes) + if (*changes == '\n') + ++lines; + + /* two lines per change */ + return lines / 2; +} + +/* Read the item described by ENTRY from the REV_FILE and return the + * respective byte sequence in *CONTENTS, allocated in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations */ static svn_error_t * read_item(svn_stringbuf_t **contents, svn_fs_fs__revision_file_t *rev_file, svn_fs_fs__p2l_entry_t *entry, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - svn_stringbuf_t *item = svn_stringbuf_create_ensure(entry->size, pool); + svn_stringbuf_t *item = svn_stringbuf_create_ensure(entry->size, + result_pool); item->len = entry->size; item->data[item->len] = 0; SVN_ERR(svn_io_file_aligned_seek(rev_file->file, rev_file->block_size, - NULL, entry->offset, pool)); + NULL, entry->offset, scratch_pool)); SVN_ERR(svn_io_file_read_full2(rev_file->file, item->data, item->len, - NULL, NULL, pool)); + NULL, NULL, scratch_pool)); *contents = item; @@ -899,17 +891,20 @@ read_item(svn_stringbuf_t **contents, } /* Process the logically addressed revision contents of revisions BASE to - * BASE + COUNT - 1 in QUERY. Use POOL for allocations. + * BASE + COUNT - 1 in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_log_rev_or_packfile(query_t *query, svn_revnum_t base, int count, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { fs_fs_data_t *ffd = query->fs->fsap_data; - apr_pool_t *iterpool = svn_pool_create(pool); - apr_pool_t *localpool = svn_pool_create(pool); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_off_t max_offset; apr_off_t offset = 0; int i; @@ -919,8 +914,9 @@ read_log_rev_or_packfile(query_t *query, for (i = 0; i < count; ++i) { /* create the revision info for the current rev */ - revision_info_t *info = apr_pcalloc(pool, sizeof(*info)); - info->representations = apr_array_make(pool, 4, sizeof(rep_stats_t*)); + revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info)); + info->representations = apr_array_make(result_pool, 4, + sizeof(rep_stats_t*)); info->revision = base + i; APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info; @@ -928,9 +924,9 @@ read_log_rev_or_packfile(query_t *query, /* open the pack / rev file that is covered by the p2l index */ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base, - localpool, iterpool)); + scratch_pool, iterpool)); SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, query->fs, rev_file, - base, localpool)); + base, scratch_pool)); /* record the whole pack size in the first rev so the total sum will still be correct */ @@ -974,8 +970,8 @@ read_log_rev_or_packfile(query_t *query, revision_info_t *info = APR_ARRAY_IDX(query->revisions, entry->item.revision, revision_info_t*); - SVN_ERR(read_item(&item, rev_file, entry, iterpool)); - SVN_ERR(read_noderev(query, item, 0, info, pool, iterpool)); + SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool)); + SVN_ERR(read_noderev(query, item, info, result_pool, iterpool)); } else if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES) { @@ -983,9 +979,9 @@ read_log_rev_or_packfile(query_t *query, revision_info_t *info = APR_ARRAY_IDX(query->revisions, entry->item.revision, revision_info_t*); - SVN_ERR(read_item(&item, rev_file, entry, iterpool)); + SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool)); info->change_count - = get_change_count(item->data + 0, item->len); + = get_log_change_count(item->data + 0, item->len); info->changes_len += entry->size; } @@ -996,74 +992,99 @@ read_log_rev_or_packfile(query_t *query, /* clean up and close file handles */ svn_pool_destroy(iterpool); - svn_pool_destroy(localpool); return SVN_NO_ERROR; } /* Read the content of the pack file staring at revision BASE logical - * addressing mode and store it in QUERY. Use POOL for allocations. + * addressing mode and store it in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_log_pack_file(query_t *query, svn_revnum_t base, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - SVN_ERR(read_log_rev_or_packfile(query, base, query->shard_size, pool)); + SVN_ERR(read_log_rev_or_packfile(query, base, query->shard_size, + result_pool, scratch_pool)); /* one more pack file processed */ if (query->progress_func) - query->progress_func(base, query->progress_baton, pool); + query->progress_func(base, query->progress_baton, scratch_pool); return SVN_NO_ERROR; } /* Read the content of the file for REVISION in logical addressing mode - * and store its contents in QUERY. Use POOL for allocations. + * and store its contents in QUERY. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_log_revision_file(query_t *query, svn_revnum_t revision, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { - SVN_ERR(read_log_rev_or_packfile(query, revision, 1, pool)); + SVN_ERR(read_log_rev_or_packfile(query, revision, 1, + result_pool, scratch_pool)); /* show progress every 1000 revs or so */ if (query->progress_func) { if (query->shard_size && (revision % query->shard_size == 0)) - query->progress_func(revision, query->progress_baton, pool); + query->progress_func(revision, query->progress_baton, scratch_pool); if (!query->shard_size && (revision % 1000 == 0)) - query->progress_func(revision, query->progress_baton, pool); + query->progress_func(revision, query->progress_baton, scratch_pool); } return SVN_NO_ERROR; } /* Read the repository and collect the stats info in QUERY. - * Use POOL for allocations. + * + * Use RESULT_POOL for persistent allocations and SCRATCH_POOL for + * temporaries. */ static svn_error_t * read_revisions(query_t *query, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { + apr_pool_t *iterpool = svn_pool_create(scratch_pool); svn_revnum_t revision; /* read all packed revs */ for ( revision = 0 ; revision < query->min_unpacked_rev ; revision += query->shard_size) - if (svn_fs_fs__use_log_addressing(query->fs, revision)) - SVN_ERR(read_log_pack_file(query, revision, pool)); - else - SVN_ERR(read_phys_pack_file(query, revision, pool)); + { + svn_pool_clear(iterpool); + + if (svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(read_log_pack_file(query, revision, result_pool, iterpool)); + else + SVN_ERR(read_phys_pack_file(query, revision, result_pool, iterpool)); + } /* read non-packed revs */ for ( ; revision <= query->head; ++revision) - if (svn_fs_fs__use_log_addressing(query->fs, revision)) - SVN_ERR(read_log_revision_file(query, revision, pool)); - else - SVN_ERR(read_phys_revision_file(query, revision, pool)); + { + svn_pool_clear(iterpool); + + if (svn_fs_fs__use_log_addressing(query->fs)) + SVN_ERR(read_log_revision_file(query, revision, result_pool, + iterpool)); + else + SVN_ERR(read_phys_revision_file(query, revision, result_pool, + iterpool)); + } + + svn_pool_destroy(iterpool); return SVN_NO_ERROR; } @@ -1232,7 +1253,7 @@ svn_fs_fs__get_stats(svn_fs_fs__stats_t SVN_ERR(create_query(&query, fs, *stats, progress_func, progress_func, cancel_func, cancel_baton, scratch_pool, scratch_pool)); - SVN_ERR(read_revisions(query, scratch_pool)); + SVN_ERR(read_revisions(query, scratch_pool, scratch_pool)); aggregate_stats(query->revisions, *stats); return SVN_NO_ERROR;
Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/structure URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/structure?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/structure (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/structure Tue Nov 25 16:26:48 2014 @@ -44,9 +44,7 @@ repository) is: <rev>.<count> Pack file, if the repository has been packed (see below) manifest Pack manifest file, if a pack file exists (see below) revprops.db SQLite database of the packed revprops (format 5 only) - transactions/ Subdirectory containing transactions (format 1 to 6) - <txnid>.txn/ Directory containing transaction <txnid> - transactions-la/ Subdirectory containing transactions (format 7+) + transactions/ Subdirectory containing transactions <txnid>.txn/ Directory containing transaction <txnid> txn-protorevs/ Subdirectory containing transaction proto-revision files <txnid>.rev Proto-revision file for transaction <txnid> @@ -216,8 +214,8 @@ Filesystem format options Currently, the only recognised format options are "layout" and "addressing". The first specifies the paths that will be used to store the revision -files and revision property files. The second specifies for which -revisions address translation is required. +files and revision property files. The second specifies that logical to +physical address translation is required. The "layout" option is followed by the name of the filesystem layout and any required parameters. The default layout, if no "layout" @@ -257,11 +255,10 @@ The supported modes, and the parameters pairs with "offset" being the byte offset relative to the beginning of the revision in the respective rev or pack file. -"logical <first-revision-to-use-it>" - 'first-revision-to-use-it' specifies the first revision to use logical - addressing, must coincide with the beginning of a shard and may be a - future revision. All earlier revisions use physical addressing. It is - illegal to use logical addressing on non-sharded repositories. +"logical" + All existing and future revision files will use logical + addressing. It is illegal to use logical addressing on non-sharded + repositories. Addressing modes Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/transaction.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/transaction.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/transaction.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/transaction.c Tue Nov 25 16:26:48 2014 @@ -391,7 +391,7 @@ auto_truncate_proto_rev(svn_fs_t *fs, apr_pool_t *pool) { /* Only relevant for newer FSFS formats. */ - if (svn_fs_fs__use_log_addressing(fs, txn_id->revision)) + if (svn_fs_fs__use_log_addressing(fs)) { /* Determine file range covered by the proto-index so far. Note that we always append to both file, i.e. the last index entry also @@ -1612,21 +1612,16 @@ svn_fs_fs__add_change(svn_fs_t *fs, /* If the transaction TXN_ID in FS uses logical addressing, store the * (ITEM_INDEX, OFFSET) pair in the txn's log-to-phys proto index file. - * If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether - * to actually write to the proto-index. Use POOL for allocations. + * Use POOL for allocations. */ static svn_error_t * store_l2p_index_entry(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, - svn_revnum_t final_revision, apr_off_t offset, apr_uint64_t item_index, apr_pool_t *pool) { - if (final_revision == SVN_INVALID_REVNUM) - final_revision = txn_id->revision + 1; - - if (svn_fs_fs__use_log_addressing(fs, final_revision)) + if (svn_fs_fs__use_log_addressing(fs)) { const char *path = svn_fs_fs__path_l2p_proto_index(fs, txn_id, pool); apr_file_t *file; @@ -1641,20 +1636,15 @@ store_l2p_index_entry(svn_fs_t *fs, /* If the transaction TXN_ID in FS uses logical addressing, store ENTRY * in the phys-to-log proto index file of transaction TXN_ID. - * If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether - * to actually write to the proto-index. Use POOL for allocations. + * Use POOL for allocations. */ static svn_error_t * store_p2l_index_entry(svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, - svn_revnum_t final_revision, svn_fs_fs__p2l_entry_t *entry, apr_pool_t *pool) { - if (final_revision == SVN_INVALID_REVNUM) - final_revision = txn_id->revision + 1; - - if (svn_fs_fs__use_log_addressing(fs, final_revision)) + if (svn_fs_fs__use_log_addressing(fs)) { const char *path = svn_fs_fs__path_p2l_proto_index(fs, txn_id, pool); apr_file_t *file; @@ -1670,22 +1660,16 @@ store_p2l_index_entry(svn_fs_t *fs, * of file system FS and return it in *ITEM_INDEX. For old formats, it * will simply return the offset as item index; in new formats, it will * increment the txn's item index counter file and store the mapping in - * the proto index file. If FINAL_REVISION is not SVN_INVALID_REVNUM, use - * it to determine whether to actually write to the proto-index. - * Use POOL for allocations. + * the proto index file. Use POOL for allocations. */ static svn_error_t * allocate_item_index(apr_uint64_t *item_index, svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, - svn_revnum_t final_revision, apr_off_t my_offset, apr_pool_t *pool) { - if (final_revision == SVN_INVALID_REVNUM) - final_revision = txn_id->revision + 1; - - if (svn_fs_fs__use_log_addressing(fs, final_revision)) + if (svn_fs_fs__use_log_addressing(fs)) { apr_file_t *file; char buffer[SVN_INT64_BUFFER_SIZE] = { 0 }; @@ -1712,8 +1696,7 @@ allocate_item_index(apr_uint64_t *item_i SVN_ERR(svn_io_file_close(file, pool)); /* write log-to-phys index */ - SVN_ERR(store_l2p_index_entry(fs, txn_id, final_revision, - my_offset, *item_index, pool)); + SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, *item_index, pool)); } else { @@ -2309,8 +2292,7 @@ rep_write_contents_close(void *baton) /* Write out our cosmetic end marker. */ SVN_ERR(svn_stream_puts(b->rep_stream, "ENDREP\n")); SVN_ERR(allocate_item_index(&rep->item_index, b->fs, &rep->txn_id, - SVN_INVALID_REVNUM, b->rep_offset, - b->scratch_pool)); + b->rep_offset, b->scratch_pool)); b->noderev->data_rep = rep; } @@ -2336,8 +2318,8 @@ rep_write_contents_close(void *baton) b->scratch_pool)); SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->scratch_pool)); - SVN_ERR(store_p2l_index_entry(b->fs, &rep->txn_id, SVN_INVALID_REVNUM, - &entry, b->scratch_pool)); + SVN_ERR(store_p2l_index_entry(b->fs, &rep->txn_id, &entry, + b->scratch_pool)); } SVN_ERR(svn_io_file_close(b->file, b->scratch_pool)); @@ -2519,9 +2501,7 @@ write_directory_to_stream(svn_stream_t * 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. If FINAL_REVISION is not SVN_INVALID_REVNUM, use it - to determine whether to write to the proto-index files. - Perform temporary allocations in SCRATCH_POOL. */ + the existing rep. Perform temporary allocations in SCRATCH_POOL. */ static svn_error_t * write_container_rep(representation_t *rep, apr_file_t *file, @@ -2530,7 +2510,6 @@ write_container_rep(representation_t *re svn_fs_t *fs, apr_hash_t *reps_hash, apr_uint32_t item_type, - svn_revnum_t final_revision, apr_pool_t *scratch_pool) { svn_stream_t *stream; @@ -2582,7 +2561,7 @@ write_container_rep(representation_t *re SVN_ERR(svn_stream_puts(whb->stream, "ENDREP\n")); SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id, - final_revision, offset, scratch_pool)); + offset, scratch_pool)); entry.offset = offset; SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); @@ -2594,8 +2573,7 @@ write_container_rep(representation_t *re fnv1a_checksum_ctx, scratch_pool)); - SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, final_revision, - &entry, scratch_pool)); + SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool)); /* update the representation */ rep->size = whb->size; @@ -2616,8 +2594,6 @@ write_container_rep(representation_t *re If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume that we want to a props representation as the base for our delta. - If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether - to write to the proto-index files. Perform temporary allocations in SCRATCH_POOL. */ static svn_error_t * @@ -2629,7 +2605,6 @@ write_container_delta_rep(representation node_revision_t *noderev, apr_hash_t *reps_hash, apr_uint32_t item_type, - svn_revnum_t final_revision, apr_pool_t *scratch_pool) { svn_txdelta_window_handler_t diff_wh; @@ -2726,7 +2701,7 @@ write_container_delta_rep(representation SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n")); SVN_ERR(allocate_item_index(&rep->item_index, fs, &rep->txn_id, - final_revision, offset, scratch_pool)); + offset, scratch_pool)); entry.offset = offset; SVN_ERR(svn_fs_fs__get_file_offset(&offset, file, scratch_pool)); @@ -2738,8 +2713,7 @@ write_container_delta_rep(representation fnv1a_checksum_ctx, scratch_pool)); - SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, final_revision, - &entry, scratch_pool)); + SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool)); /* update the representation */ rep->expanded_size = whb->size; @@ -2926,12 +2900,11 @@ write_final_rev(const svn_fs_id_t **new_ write_directory_to_stream, fs, noderev, NULL, SVN_FS_FS__ITEM_TYPE_DIR_REP, - rev, pool)); + pool)); else SVN_ERR(write_container_rep(noderev->data_rep, file, entries, write_directory_to_stream, fs, NULL, - SVN_FS_FS__ITEM_TYPE_DIR_REP, rev, - pool)); + SVN_FS_FS__ITEM_TYPE_DIR_REP, pool)); reset_txn_in_rep(noderev->data_rep); } @@ -2947,7 +2920,7 @@ write_final_rev(const svn_fs_id_t **new_ reset_txn_in_rep(noderev->data_rep); noderev->data_rep->revision = rev; - if (!svn_fs_fs__use_log_addressing(fs, rev)) + if (!svn_fs_fs__use_log_addressing(fs)) { /* See issue 3845. Some unknown mechanism caused the protorev file to get truncated, so check for that @@ -2976,11 +2949,11 @@ write_final_rev(const svn_fs_id_t **new_ if (ffd->deltify_properties) SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist, write_hash_to_stream, fs, noderev, - reps_hash, item_type, rev, pool)); + reps_hash, item_type, pool)); else SVN_ERR(write_container_rep(noderev->prop_rep, file, proplist, write_hash_to_stream, fs, reps_hash, - item_type, rev, pool)); + item_type, pool)); reset_txn_in_rep(noderev->prop_rep); } @@ -2996,15 +2969,15 @@ write_final_rev(const svn_fs_id_t **new_ /* root nodes have a fixed ID in log addressing mode */ SVN_ERR(svn_fs_fs__get_file_offset(&my_offset, file, pool)); - if (svn_fs_fs__use_log_addressing(fs, rev) && at_root) + if (svn_fs_fs__use_log_addressing(fs) && at_root) { /* reference the root noderev from the log-to-phys index */ rev_item.number = SVN_FS_FS__ITEM_INDEX_ROOT_NODE; - SVN_ERR(store_l2p_index_entry(fs, txn_id, rev, my_offset, + SVN_ERR(store_l2p_index_entry(fs, txn_id, my_offset, rev_item.number, pool)); } else - SVN_ERR(allocate_item_index(&rev_item.number, fs, txn_id, rev, + SVN_ERR(allocate_item_index(&rev_item.number, fs, txn_id, my_offset, pool)); rev_item.revision = rev; @@ -3062,7 +3035,7 @@ write_final_rev(const svn_fs_id_t **new_ pool)); /* reference the root noderev from the log-to-phys index */ - if (svn_fs_fs__use_log_addressing(fs, rev)) + if (svn_fs_fs__use_log_addressing(fs)) { svn_fs_fs__p2l_entry_t entry; rev_item.revision = SVN_INVALID_REVNUM; @@ -3076,7 +3049,7 @@ write_final_rev(const svn_fs_id_t **new_ fnv1a_checksum_ctx, pool)); - SVN_ERR(store_p2l_index_entry(fs, txn_id, rev, &entry, pool)); + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool)); } /* Return our ID that references the revision file. */ @@ -3086,17 +3059,15 @@ write_final_rev(const svn_fs_id_t **new_ } /* Write the changed path info CHANGED_PATHS from transaction TXN_ID to the - permanent rev-file FILE representing NEW_REV in filesystem FS. *OFFSET_P - is set the to offset in the file of the beginning of this information. - NEW_REV is the revision currently being committed. - Perform temporary allocations in POOL. */ + permanent rev-file FILE in filesystem FS. *OFFSET_P is set the to offset + in the file of the beginning of this information. Perform temporary + allocations in POOL. */ static svn_error_t * write_final_changed_path_info(apr_off_t *offset_p, apr_file_t *file, svn_fs_t *fs, const svn_fs_fs__id_part_t *txn_id, apr_hash_t *changed_paths, - svn_revnum_t new_rev, apr_pool_t *pool) { apr_off_t offset; @@ -3114,7 +3085,7 @@ write_final_changed_path_info(apr_off_t *offset_p = offset; /* reference changes from the indexes */ - if (svn_fs_fs__use_log_addressing(fs, new_rev)) + if (svn_fs_fs__use_log_addressing(fs)) { svn_fs_fs__p2l_entry_t entry; @@ -3128,8 +3099,8 @@ write_final_changed_path_info(apr_off_t fnv1a_checksum_ctx, pool)); - SVN_ERR(store_p2l_index_entry(fs, txn_id, new_rev, &entry, pool)); - SVN_ERR(store_l2p_index_entry(fs, txn_id, new_rev, entry.offset, + SVN_ERR(store_p2l_index_entry(fs, txn_id, &entry, pool)); + SVN_ERR(store_l2p_index_entry(fs, txn_id, entry.offset, SVN_FS_FS__ITEM_INDEX_CHANGES, pool)); } @@ -3286,242 +3257,6 @@ verify_locks(svn_fs_t *fs, return SVN_NO_ERROR; } -/* Return TRUE, if transaction TXN_ID in FS definitively uses logical - * addressing mode. Use POOL for temporary allocations. - */ -static svn_boolean_t -using_log_addressing(svn_fs_t *fs, - const svn_fs_fs__id_part_t *txn_id, - apr_pool_t *pool) -{ - /* As long as we don't write new data representations, we won't allocate - IDs and there is no difference between log & phys mode. - - After the first ID got allocated, it is logical mode and the proto- - index file does exist. - */ - svn_node_kind_t kind; - const char *path = svn_fs_fs__path_l2p_proto_index(fs, txn_id, pool); - - svn_error_t *err = svn_io_check_path(path, &kind, pool); - if (err) - { - /* We couldn't check for the presence of the index file. - - So, we probably won't be able to access it during later stages - of the commit. - */ - svn_error_clear(err); - return FALSE; - } - - return kind == svn_node_file; -} - -/* Return TRUE, if the file with FILENAME contains a node revision. - */ -static svn_boolean_t -is_noderev_file(const char *filename) -{ - apr_size_t dot_count = 0; - - /* all interesting files start with "node." */ - if (strncmp(filename, PATH_PREFIX_NODE, strlen(PATH_PREFIX_NODE))) - return FALSE; - - for (; *filename; ++filename) - if (*filename == '.') - ++dot_count; - - return dot_count == 2; -} - -/* Determine the checksum for the SIZE bytes of data starting at START - * in FILE and return the result in *FNV1_CHECKSUM. - * Use POOL for tempoary allocations. - */ -static svn_error_t * -fnv1a_checksum_on_file_range(apr_uint32_t *fnv1_checksum, - apr_file_t *file, - apr_off_t start, - apr_off_t size, - apr_pool_t *pool) -{ - char *buffer = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); - - svn_checksum_ctx_t *checksum_ctx - = svn_checksum_ctx_create(svn_checksum_fnv1a_32x4, pool); - - SVN_ERR(svn_io_file_seek(file, APR_SET, &start, pool)); - while (size > 0) - { - apr_size_t to_read = MIN(size, sizeof(buffer)); - - SVN_ERR(svn_io_file_read_full2(file, buffer, to_read, &to_read, - NULL, pool)); - SVN_ERR(svn_checksum_update(checksum_ctx, buffer, to_read)); - size -= to_read; - } - SVN_ERR(fnv1a_checksum_finalize(fnv1_checksum, checksum_ctx, pool)); - - return SVN_NO_ERROR; -} - -/* qsort()-compatible comparison function sorting svn_fs_fs__p2l_entry_t - * by offset. - */ -static int -compare_sort_p2l_entry(const void *a, - const void *b) -{ - apr_off_t lhs = ((const svn_fs_fs__p2l_entry_t *)a)->offset; - apr_off_t rhs = ((const svn_fs_fs__p2l_entry_t *)b)->offset; - - return lhs < rhs ? -1 : rhs == lhs ? 0 : 1; -} - - -/* Upgrade the transaction TXN_ID in FS from physical addressing mode - * to logical addressing mode. FINAL_REVISION is the revision that this - * txn is being committed to. Use POOL for temporary allocations. - */ -static svn_error_t * -upgrade_transaction(svn_fs_t *fs, - const svn_fs_fs__id_part_t *txn_id, - svn_revnum_t final_revision, - apr_file_t *proto_file, - apr_pool_t *pool) -{ - fs_fs_data_t *ffd = fs->fsap_data; - apr_hash_t *dirents; - int i; - apr_hash_index_t* hi; - - /* we allocate large temporary data and want to release it asap */ - - apr_pool_t *subpool = svn_pool_create(pool); - apr_pool_t *iterpool = svn_pool_create(pool); - - apr_hash_t *id_map = apr_hash_make(subpool); - const char *txn_dir = svn_fs_fs__path_txn_dir(fs, txn_id, subpool); - apr_array_header_t *p2l_entries - = apr_array_make(subpool, 16, sizeof(svn_fs_fs__p2l_entry_t)); - - /* scan the txn directory for noderev files and patch them up */ - - SVN_ERR(svn_io_get_dirents3(&dirents, txn_dir, TRUE, subpool, iterpool)); - for (hi = apr_hash_first(subpool, dirents); hi; hi = apr_hash_next(hi)) - { - apr_file_t *file; - const char *filename; - node_revision_t *noderev; - svn_stream_t *stream; - const char *name; - apr_uint64_t *old_index, *item_index, *new_index; - - svn_pool_clear(iterpool); - - /* We are only interested in file data reps of this txns. - Older IDs remain valid because they are already committed. - Other IDs (noderevs and their usage in directories) will only be - assigned later anyways. */ - - name = apr_hash_this_key(hi); - if (!is_noderev_file(name)) - continue; - - filename = svn_dirent_join(txn_dir, name, iterpool); - SVN_ERR(svn_io_file_open(&file, filename, - APR_READ | APR_WRITE | APR_BUFFERED, - APR_OS_DEFAULT, - iterpool)); - stream = svn_stream_from_aprfile2(file, TRUE, iterpool); - SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, iterpool, iterpool)); - if ( noderev->data_rep == NULL - || noderev->data_rep->revision != SVN_INVALID_REVNUM - || noderev->kind != svn_node_file) - continue; - - /* We need to assign an id. - We might already have one because of rep sharing. */ - - item_index = &noderev->data_rep->item_index; - new_index = apr_hash_get(id_map, item_index, sizeof(*item_index)); - - if (new_index) - { - *item_index = *new_index; - } - else - { - svn_fs_fs__rep_header_t *header; - svn_fs_fs__p2l_entry_t *entry; - - /* assign a logical ID and write the L2P proto-index */ - - old_index = apr_pmemdup(subpool, item_index, sizeof(*item_index)); - SVN_ERR(allocate_item_index(item_index, fs, txn_id, final_revision, - *old_index, iterpool)); - - new_index = apr_pmemdup(subpool, item_index, sizeof(*item_index)); - apr_hash_set(id_map, old_index, sizeof(*old_index), new_index); - - /* we need to know the length of the representation header - because it is not accounted for by the representation length */ - - entry = apr_array_push(p2l_entries); - entry->offset = *old_index; - SVN_ERR(svn_io_file_seek(proto_file, APR_SET, &entry->offset, - iterpool)); - SVN_ERR(svn_fs_fs__read_rep_header(&header, - svn_stream_from_aprfile2(proto_file, TRUE, iterpool), - iterpool, iterpool)); - - /* Create the corresponding entry for the P2L proto-index. - - We need to write that proto-index in strict offset order but - we have no control over the order in which we traverse the - data reps. Thus, we collect the entries in an array. */ - - entry->size = noderev->data_rep->size + header->header_size + 7; - /* 7 for the "ENDREP\n" */ - entry->type = SVN_FS_FS__ITEM_TYPE_FILE_REP; - entry->item.revision = SVN_INVALID_REVNUM; - entry->item.number = *new_index; - SVN_ERR(fnv1a_checksum_on_file_range(&entry->fnv1_checksum, - proto_file, - entry->offset, entry->size, - iterpool)); - } - - /* write the updated noderev to disk */ - - SVN_ERR(svn_io_file_trunc(file, 0, iterpool)); - SVN_ERR(svn_fs_fs__write_noderev(stream, noderev, ffd->format, - TRUE, iterpool)); - } - - /* Finally, write all P2L proto-index entries ordered by item offset. */ - - qsort(p2l_entries->elts, p2l_entries->nelts, p2l_entries->elt_size, - compare_sort_p2l_entry); - for (i = 0; i < p2l_entries->nelts; ++i) - { - svn_fs_fs__p2l_entry_t *entry; - - svn_pool_clear(iterpool); - - entry = &APR_ARRAY_IDX(p2l_entries, i, svn_fs_fs__p2l_entry_t); - SVN_ERR(store_p2l_index_entry(fs, txn_id, final_revision, - entry, iterpool)); - } - - svn_pool_clear(iterpool); - svn_pool_clear(subpool); - - return SVN_NO_ERROR; -} - /* Return in *PATH the path to a file containing the properties that make up the final revision properties file. This involves setting svn:date and removing any temporary properties associated with the @@ -3701,18 +3436,6 @@ commit_body(void *baton, apr_pool_t *poo cb->fs, txn_id, pool)); SVN_ERR(svn_fs_fs__get_file_offset(&initial_offset, proto_file, pool)); - /* Make sure that we don't try to commit an old txn that used physical - addressing but will be committed into the revision range that requires - logical addressing. - */ - if (svn_fs_fs__use_log_addressing(cb->fs, new_rev) - && !svn_fs_fs__use_log_addressing(cb->fs, txn_id->revision) - && !using_log_addressing(cb->fs, txn_id, pool)) - { - SVN_ERR(upgrade_transaction(cb->fs, txn_id, new_rev, proto_file, pool)); - SVN_ERR(svn_io_file_seek(proto_file, APR_SET, &initial_offset, pool)); - } - /* Write out all the node-revisions and directory contents. */ root_id = svn_fs_fs__id_txn_create_root(txn_id, pool); SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id, @@ -3723,9 +3446,17 @@ commit_body(void *baton, apr_pool_t *poo /* Write the changed-path information. */ SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file, cb->fs, txn_id, changed_paths, - new_rev, pool)); + pool)); - if (!svn_fs_fs__use_log_addressing(cb->fs, new_rev)) + if (svn_fs_fs__use_log_addressing(cb->fs)) + { + /* Append the index data to the rev file. */ + SVN_ERR(svn_fs_fs__add_index_data(cb->fs, proto_file, + svn_fs_fs__path_l2p_proto_index(cb->fs, txn_id, pool), + svn_fs_fs__path_p2l_proto_index(cb->fs, txn_id, pool), + new_rev, pool)); + } + else { /* Write the final line. */ @@ -3737,15 +3468,6 @@ commit_body(void *baton, apr_pool_t *poo SVN_ERR(svn_io_file_write_full(proto_file, trailer->data, trailer->len, NULL, pool)); } - else - { - /* Append the index data to the rev file. */ - SVN_ERR(svn_fs_fs__add_index_data(cb->fs, proto_file, - svn_fs_fs__path_l2p_proto_index(cb->fs, txn_id, pool), - svn_fs_fs__path_p2l_proto_index(cb->fs, txn_id, pool), - new_rev, pool)); - } - SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool)); SVN_ERR(svn_io_file_close(proto_file, pool)); Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/tree.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/tree.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/tree.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/tree.c Tue Nov 25 16:26:48 2014 @@ -2387,10 +2387,7 @@ fs_dir_optimal_order(apr_array_header_t apr_hash_t *entries, apr_pool_t *pool) { - *ordered_p - = svn_fs_fs__order_dir_entries(root->fs, entries, - root->rev, - pool); + *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, pool); return SVN_NO_ERROR; } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.c Tue Nov 25 16:26:48 2014 @@ -237,11 +237,7 @@ const char * svn_fs_fs__path_txns_dir(svn_fs_t *fs, apr_pool_t *pool) { - fs_fs_data_t *ffd = fs->fsap_data; - - return ffd->format >= SVN_FS_FS__MIN_LOG_ADDRESSING_FORMAT - ? svn_dirent_join(fs->path, PATH_TXNS_LA_DIR, pool) - : svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); + return svn_dirent_join(fs->path, PATH_TXNS_DIR, pool); } const char * @@ -691,10 +687,8 @@ svn_fs_fs__move_into_place(const char *o } svn_boolean_t -svn_fs_fs__use_log_addressing(svn_fs_t *fs, - svn_revnum_t rev) +svn_fs_fs__use_log_addressing(svn_fs_t *fs) { fs_fs_data_t *ffd = fs->fsap_data; - return ffd->min_log_addressing_rev != SVN_INVALID_REVNUM - && ffd->min_log_addressing_rev <= rev; + return ffd->use_log_addressing; } Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/util.h Tue Nov 25 16:26:48 2014 @@ -401,9 +401,8 @@ svn_fs_fs__move_into_place(const char *o const char *perms_reference, apr_pool_t *pool); -/* Return TRUE, iff revision REV in FS requires logical addressing. */ +/* Return TRUE, iff FS uses logical addressing. */ svn_boolean_t -svn_fs_fs__use_log_addressing(svn_fs_t *fs, - svn_revnum_t rev); +svn_fs_fs__use_log_addressing(svn_fs_t *fs); #endif Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_fs/verify.c Tue Nov 25 16:26:48 2014 @@ -22,6 +22,7 @@ #include "svn_sorts.h" #include "svn_checksum.h" +#include "svn_time.h" #include "private/svn_subr_private.h" #include "verify.h" @@ -404,7 +405,7 @@ compare_p2l_to_l2p_index(svn_fs_t *fs, "refers to revision %ld outside " "the rev / pack file (%ld-%ld)"), apr_off_t_toa(pool, entry->offset), - entry->item.number, + (long)entry->item.number, start, start + count - 1); } else @@ -702,6 +703,49 @@ compare_p2l_to_rev(svn_fs_t *fs, return SVN_NO_ERROR; } +/* Verify that the revprops of the revisions START to END in FS can be + * accessed. Invoke CANCEL_FUNC with CANCEL_BATON at regular intervals. + * + * The values of START and END have already been auto-selected and + * verified. + */ +static svn_error_t * +verify_revprops(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + svn_revnum_t revision; + apr_pool_t *iterpool = svn_pool_create(pool); + + for (revision = start; revision < end; ++revision) + { + svn_string_t *date; + apr_time_t timetemp; + + svn_pool_clear(iterpool); + + /* Access the svn:date revprop. + * This implies parsing all revprops for that revision. */ + SVN_ERR(svn_fs_fs__revision_prop(&date, fs, revision, + SVN_PROP_REVISION_DATE, iterpool)); + + /* The time stamp is the only revprop that, if given, needs to + * have a valid content. */ + if (date) + SVN_ERR(svn_time_from_cstring(&timetemp, date->data, iterpool)); + + if (cancel_func) + SVN_ERR(cancel_func(cancel_baton)); + } + + svn_pool_destroy(iterpool); + + return SVN_NO_ERROR; +} + static svn_revnum_t pack_size(svn_fs_t *fs, svn_revnum_t rev) { @@ -710,22 +754,24 @@ pack_size(svn_fs_t *fs, svn_revnum_t rev return rev < ffd->min_unpacked_rev ? ffd->max_files_per_dir : 1; } -/* Verify that the log-to-phys indexes and phys-to-log indexes are - * consistent with each other. The function signature is similar to - * svn_fs_fs__verify. +/* Verify that on-disk representation has not been tempered with (in a way + * that leaves the repository in a corrupted state). This compares log-to- + * phys with phys-to-log indexes, verifies the low-level checksums and + * checks that all revprops are available. The function signature is + * similar to svn_fs_fs__verify. * * The values of START and END have already been auto-selected and * verified. You may call this for format7 or higher repos. */ static svn_error_t * -verify_index_consistency(svn_fs_t *fs, - svn_revnum_t start, - svn_revnum_t end, - svn_fs_progress_notify_func_t notify_func, - void *notify_baton, - svn_cancel_func_t cancel_func, - void *cancel_baton, - apr_pool_t *pool) +verify_f7_metadata_consistency(svn_fs_t *fs, + svn_revnum_t start, + svn_revnum_t end, + svn_fs_progress_notify_func_t notify_func, + void *notify_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; svn_revnum_t revision, next_revision; @@ -761,6 +807,11 @@ verify_index_consistency(svn_fs_t *fs, err = compare_p2l_to_rev(fs, pack_start, pack_end - pack_start, cancel_func, cancel_baton, iterpool); + /* ensure that revprops are available and accessible */ + if (!err) + err = verify_revprops(fs, pack_start, pack_end, + cancel_func, cancel_baton, iterpool); + /* concurrent packing is one of the reasons why verification may fail. Make sure, we operate on up-to-date information. */ if (err) @@ -811,11 +862,10 @@ svn_fs_fs__verify(svn_fs_t *fs, /* log/phys index consistency. We need to check them first to make sure we can access the rev / pack files in format7. */ - if (svn_fs_fs__use_log_addressing(fs, end)) - SVN_ERR(verify_index_consistency(fs, - MAX(start, ffd->min_log_addressing_rev), - end, notify_func, notify_baton, - cancel_func, cancel_baton, pool)); + if (svn_fs_fs__use_log_addressing(fs)) + SVN_ERR(verify_f7_metadata_consistency(fs, start, end, + notify_func, notify_baton, + cancel_func, cancel_baton, pool)); /* rep cache consistency */ if (ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT) Modified: subversion/branches/move-tracking-2/subversion/libsvn_fs_util/fs-util.c URL: http://svn.apache.org/viewvc/subversion/branches/move-tracking-2/subversion/libsvn_fs_util/fs-util.c?rev=1641647&r1=1641646&r2=1641647&view=diff ============================================================================== --- subversion/branches/move-tracking-2/subversion/libsvn_fs_util/fs-util.c (original) +++ subversion/branches/move-tracking-2/subversion/libsvn_fs_util/fs-util.c Tue Nov 25 16:26:48 2014 @@ -199,6 +199,7 @@ svn_fs__path_change_create_internal(cons change->node_rev_id = node_rev_id; change->change_kind = change_kind; change->mergeinfo_mod = svn_tristate_unknown; + change->copyfrom_rev = SVN_INVALID_REVNUM; return change; } Propchange: subversion/branches/move-tracking-2/subversion/libsvn_fs_x/ ------------------------------------------------------------------------------ Merged /subversion/trunk/subversion/libsvn_fs_fs:r1603487,1603499,1603605,1604128,1604414,1604416-1604417,1604421,1604717,1604802,1604824,1604836,1604902-1604903,1604911,1604925,1604933,1604947,1605059-1605060,1605064-1605065,1605068,1605071-1605073,1605075,1605123,1605188-1605189,1605191,1605197,1605444,1605633,1606132,1606142,1606144,1606514,1606526,1606528,1606551,1606554,1606564,1606598-1606599,1606656,1606658,1606662,1606744,1606840,1607572,1612810,1613872,1614611,1615348,1615351-1615352,1616338-1616339,1616613,1617586,1617688,1618138,1618151,1618153,1618641,1618653,1618662,1619358,1619774,1620602,1620909,1620912,1620928,1620930,1621275,1621635,1622931,1622937,1622942,1622946,1622959-1622960,1622963,1622987,1623007,1623368,1623373,1623377,1623379,1623381,1623398,1623402,1624011,1624512,1626246,1626871,1626873,1626886,1627497-1627498,1627502,1627947,1627949,1627966,1628093,1628159,1628161,1628392-1628393,1628415,1628427,1628738,1628762,1629854-1629855,1629857,1629865,1629873,16 29875,1629879,1630067,1630070,1631049-1631051,1631075,1631185-1631186,1631196-1631197,1631239-1631240,1631548,1631550,1631563,1631567,1631588,1631598,1632776,1632849,1632851-1632853,1632856-1632857,1632868,1632908,1633232,1633617-1633618,1634872,1634920,1636478,1636483,1636629 Merged /subversion/trunk/subversion/libsvn_fs_x:r1635153-1641646 Merged /subversion/branches/log-addressing/subversion/libsvn_fs_fs:r1531982
