Index: subversion/libsvn_fs_fs/fs.h =================================================================== --- subversion/libsvn_fs_fs/fs.h (revision 29068) +++ subversion/libsvn_fs_fs/fs.h (working copy) @@ -45,7 +45,6 @@ extern "C" { #define PATH_REVS_DIR "revs" /* Directory of revisions */ #define PATH_REVPROPS_DIR "revprops" /* Directory of revprops */ #define PATH_TXNS_DIR "transactions" /* Directory of transactions */ -#define PATH_NODE_ORIGINS_DIR "node-origins" /* Lazy node-origin cache */ #define PATH_TXN_PROTOS_DIR "txn-protorevs" /* Directory of proto-revs */ #define PATH_TXN_CURRENT "txn-current" /* File with next txn key */ #define PATH_TXN_CURRENT_LOCK "txn-current-lock" /* Lock for txn-current */ @@ -85,6 +84,9 @@ extern "C" { /* The minimum format number that stores protorevs in a separate directory. */ #define SVN_FS_FS__MIN_PROTOREVS_DIR_FORMAT 3 +/* The minimum format number that doesn't keep node and copy ID counters. */ +#define SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT 3 + /* Maximum number of directories to cache dirents for. This *must* be a power of 2 for DIR_CACHE_ENTRIES_MASK to work. */ Index: subversion/libsvn_fs_fs/fs_fs.c =================================================================== --- subversion/libsvn_fs_fs/fs_fs.c (revision 29068) +++ subversion/libsvn_fs_fs/fs_fs.c (working copy) @@ -303,13 +303,6 @@ path_txn_node_children(svn_fs_t *fs, con PATH_EXT_CHILDREN, NULL); } -static APR_INLINE const char * -path_node_origin(svn_fs_t *fs, const char *node_id, apr_pool_t *pool) -{ - return svn_path_join_many(pool, fs->path, PATH_NODE_ORIGINS_DIR, - node_id, NULL); -} - /* Functions for working with shared transaction data. */ @@ -1206,14 +1199,6 @@ svn_fs_fs__hotcopy(const char *src_path, PATH_LOCKS_DIR, TRUE, NULL, NULL, pool)); - /* Now copy the node-origins cache tree. */ - src_subdir = svn_path_join(src_path, PATH_NODE_ORIGINS_DIR, pool); - SVN_ERR(svn_io_check_path(src_subdir, &kind, pool)); - if (kind == svn_node_dir) - SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_path, - PATH_NODE_ORIGINS_DIR, TRUE, NULL, - NULL, pool)); - /* Copy the txn-current file. */ if (format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, PATH_TXN_CURRENT, pool)); @@ -4564,8 +4549,8 @@ write_hash_rep(svn_filesize_t *size, transaction into the permanent rev-file FILE. Return the offset of the new node-revision in *OFFSET. If this is a directory, all children are copied as well. START_NODE_ID and START_COPY_ID are - the first available node and copy ids for this filesystem. - Temporary allocations are from POOL. */ + the first available node and copy ids for this filesystem, for older + FS formats. Temporary allocations are from POOL. */ static svn_error_t * write_final_rev(const svn_fs_id_t **new_id_p, apr_file_t *file, @@ -4574,16 +4559,15 @@ write_final_rev(const svn_fs_id_t **new_ const svn_fs_id_t *id, const char *start_node_id, const char *start_copy_id, - apr_hash_t *node_origins, apr_pool_t *pool) { node_revision_t *noderev; apr_off_t my_offset; - char my_node_id[MAX_KEY_SIZE + 2]; - char my_copy_id[MAX_KEY_SIZE + 2]; + char my_node_id_buf[MAX_KEY_SIZE + 2]; + char my_copy_id_buf[MAX_KEY_SIZE + 2]; const svn_fs_id_t *new_id; - const char *node_id, *copy_id; - svn_boolean_t node_id_is_new = FALSE; + const char *node_id, *copy_id, *my_node_id, *my_copy_id; + fs_fs_data_t *ffd = fs->fsap_data; *new_id_p = NULL; @@ -4613,8 +4597,8 @@ write_final_rev(const svn_fs_id_t **new_ apr_hash_this(hi, NULL, NULL, &val); dirent = val; SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id, - start_node_id, start_copy_id, - node_origins, subpool)); + start_node_id, start_copy_id, + subpool)); if (new_id && (svn_fs_fs__id_rev(new_id) == rev)) dirent->id = svn_fs_fs__id_copy(new_id, pool); } @@ -4669,17 +4653,30 @@ write_final_rev(const svn_fs_id_t **new_ node_id = svn_fs_fs__id_node_id(noderev->id); if (*node_id == '_') { - node_id_is_new = TRUE; - svn_fs_fs__add_keys(start_node_id, node_id + 1, my_node_id); + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + my_node_id = apr_psprintf(pool, "%s-%ld", node_id + 1, rev); + else + { + svn_fs_fs__add_keys(start_node_id, node_id + 1, my_node_id_buf); + my_node_id = my_node_id_buf; + } } else - strcpy(my_node_id, node_id); + my_node_id = node_id; copy_id = svn_fs_fs__id_copy_id(noderev->id); if (*copy_id == '_') - svn_fs_fs__add_keys(start_copy_id, copy_id + 1, my_copy_id); + { + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + my_copy_id = apr_psprintf(pool, "%s-%ld", copy_id + 1, rev); + else + { + svn_fs_fs__add_keys(start_copy_id, copy_id + 1, my_copy_id_buf); + my_copy_id = my_copy_id_buf; + } + } else - strcpy(my_copy_id, copy_id); + my_copy_id = copy_id; if (noderev->copyroot_rev == SVN_INVALID_REVNUM) noderev->copyroot_rev = rev; @@ -4689,15 +4686,6 @@ write_final_rev(const svn_fs_id_t **new_ noderev->id = new_id; - if (node_id_is_new) - { - apr_pool_t *hash_pool = apr_hash_pool_get(node_origins); - const char *key = apr_pstrdup(hash_pool, my_node_id); - const svn_fs_id_t *val = svn_fs_fs__id_copy(new_id, hash_pool); - - apr_hash_set(node_origins, key, APR_HASH_KEY_STRING, val); - } - /* Write out our new node-revision. */ SVN_ERR(write_noderev_txn(file, noderev, pool)); @@ -4852,8 +4840,10 @@ svn_fs_fs__move_into_place(const char *o return SVN_NO_ERROR; } -/* Atomically update the current file to hold the specifed REV, NEXT_NODE_ID, - and NEXT_COPY_ID. Perform temporary allocations in POOL. */ +/* Atomically update the current file to hold the specifed REV, + NEXT_NODE_ID, and NEXT_COPY_ID. (The two next-ID parameters are + ignored and may be NULL if the FS format does not use them.) + Perform temporary allocations in POOL. */ static svn_error_t * write_current(svn_fs_t *fs, svn_revnum_t rev, const char *next_node_id, const char *next_copy_id, apr_pool_t *pool) @@ -4861,9 +4851,13 @@ write_current(svn_fs_t *fs, svn_revnum_t char *buf; const char *tmp_name, *name; apr_file_t *file; + fs_fs_data_t *ffd = fs->fsap_data; /* Now we can just write out this line. */ - buf = apr_psprintf(pool, "%ld %s %s\n", rev, next_node_id, next_copy_id); + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + buf = apr_psprintf(pool, "%ld\n", rev); + else + buf = apr_psprintf(pool, "%ld %s %s\n", rev, next_node_id, next_copy_id); name = svn_fs_fs__path_current(fs, pool); SVN_ERR(svn_io_open_unique_file2(&file, &tmp_name, name, ".tmp", @@ -4894,6 +4888,10 @@ write_final_current(svn_fs_t *fs, const char *txn_node_id, *txn_copy_id; char new_node_id[MAX_KEY_SIZE + 2]; char new_copy_id[MAX_KEY_SIZE + 2]; + fs_fs_data_t *ffd = fs->fsap_data; + + if (ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + return write_current(fs, rev, NULL, NULL, pool); /* To find the next available ids, we add the id that used to be in the current file, to the next ids from the transaction file. */ @@ -4989,7 +4987,6 @@ struct commit_baton { svn_revnum_t *new_rev_p; svn_fs_t *fs; svn_fs_txn_t *txn; - apr_hash_t *node_origins; }; /* The work-horse for svn_fs_fs__commit, called with the FS write lock. @@ -5003,7 +5000,7 @@ commit_body(void *baton, apr_pool_t *poo const char *old_rev_filename, *rev_filename, *proto_filename; const char *revprop_filename, *final_revprop; const svn_fs_id_t *root_id, *new_root_id; - const char *start_node_id, *start_copy_id; + const char *start_node_id = NULL, *start_copy_id = NULL; svn_revnum_t old_rev, new_rev; apr_file_t *proto_file; void *proto_file_lockcookie; @@ -5029,8 +5026,9 @@ commit_body(void *baton, apr_pool_t *poo SVN_ERR(verify_locks(cb->fs, cb->txn->id, pool)); /* Get the next node_id and copy_id to use. */ - SVN_ERR(get_next_revision_ids(&start_node_id, &start_copy_id, cb->fs, - pool)); + if (ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + SVN_ERR(get_next_revision_ids(&start_node_id, &start_copy_id, cb->fs, + pool)); /* We are going to be one better than this puny old revision. */ new_rev = old_rev + 1; @@ -5042,7 +5040,7 @@ commit_body(void *baton, apr_pool_t *poo /* Write out all the node-revisions and directory contents. */ root_id = svn_fs_fs__id_txn_create("0", "0", cb->txn->id, pool); SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id, - start_node_id, start_copy_id, cb->node_origins, + start_node_id, start_copy_id, pool)); /* Write the changed-path information. */ @@ -5155,20 +5153,11 @@ svn_fs_fs__commit(svn_revnum_t *new_rev_ apr_pool_t *pool) { struct commit_baton cb; - apr_hash_t *node_origins = apr_hash_make(pool); cb.new_rev_p = new_rev_p; cb.fs = fs; cb.txn = txn; - cb.node_origins = node_origins; - SVN_ERR(svn_fs_fs__with_write_lock(fs, commit_body, &cb, pool)); - - /* Now that we're no longer locked, we can update the node-origins - cache without blocking writers. */ - if (apr_hash_count(node_origins) > 0) - SVN_ERR(svn_fs_fs__set_node_origins(fs, node_origins, pool)); - - return SVN_NO_ERROR; + return svn_fs_fs__with_write_lock(fs, commit_body, &cb, pool); } svn_error_t * @@ -5273,7 +5262,9 @@ svn_fs_fs__create(svn_fs_t *fs, pool), pool)); - SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool), "0 1 1\n", + SVN_ERR(svn_io_file_create(svn_fs_fs__path_current(fs, pool), + (format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT + ? "0\n" : "0 1 1\n"), pool)); SVN_ERR(svn_io_file_create(path_lock(fs, pool), "", pool)); SVN_ERR(svn_fs_fs__set_uuid(fs, svn_uuid_generate(pool), pool)); @@ -5538,48 +5529,59 @@ recover_body(void *baton, apr_pool_t *po { struct recover_baton *b = baton; svn_fs_t *fs = b->fs; - svn_revnum_t rev, max_rev; - apr_pool_t *iterpool; - char max_node_id[MAX_KEY_SIZE] = "0", max_copy_id[MAX_KEY_SIZE] = "0"; - char next_node_id[MAX_KEY_SIZE], next_copy_id[MAX_KEY_SIZE]; - apr_size_t len; + fs_fs_data_t *ffd = fs->fsap_data; + svn_revnum_t max_rev; + char next_node_id_buf[MAX_KEY_SIZE], next_copy_id_buf[MAX_KEY_SIZE]; + char *next_node_id = NULL, *next_copy_id = NULL; /* First, we need to know the largest revision in the filesystem. */ SVN_ERR(recover_get_largest_revision(fs, &max_rev, pool)); - /* Next we need to find the maximum node id and copy id in use across the - filesystem. Unfortunately, the only way we can get this information - is to scan all the noderevs of all the revisions and keep track as - we go along. */ - iterpool = svn_pool_create(pool); - for (rev = 0; rev <= max_rev; rev++) - { - apr_file_t *rev_file; - apr_off_t root_offset; + /* We only need to search for maximum IDs for old FS formats which + use global ID counters. */ + if (ffd->format < SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) + { + /* Next we need to find the maximum node id and copy id in use across the + filesystem. Unfortunately, the only way we can get this information + is to scan all the noderevs of all the revisions and keep track as + we go along. */ + svn_revnum_t rev; + apr_pool_t *iterpool = svn_pool_create(pool); + char max_node_id[MAX_KEY_SIZE] = "0", max_copy_id[MAX_KEY_SIZE] = "0"; + apr_size_t len; - svn_pool_clear(iterpool); + for (rev = 0; rev <= max_rev; rev++) + { + apr_file_t *rev_file; + apr_off_t root_offset; - if (b->cancel_func) - SVN_ERR(b->cancel_func(b->cancel_baton)); + svn_pool_clear(iterpool); - SVN_ERR(svn_io_file_open(&rev_file, - svn_fs_fs__path_rev(fs, rev, iterpool), - APR_READ | APR_BUFFERED, APR_OS_DEFAULT, - iterpool)); - SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file, iterpool)); - SVN_ERR(recover_find_max_ids(fs, rev, rev_file, root_offset, - max_node_id, max_copy_id, iterpool)); - } - svn_pool_destroy(iterpool); + if (b->cancel_func) + SVN_ERR(b->cancel_func(b->cancel_baton)); + + SVN_ERR(svn_io_file_open(&rev_file, + svn_fs_fs__path_rev(fs, rev, iterpool), + APR_READ | APR_BUFFERED, APR_OS_DEFAULT, + iterpool)); + SVN_ERR(get_root_changes_offset(&root_offset, NULL, rev_file, iterpool)); + SVN_ERR(recover_find_max_ids(fs, rev, rev_file, root_offset, + max_node_id, max_copy_id, iterpool)); + } + svn_pool_destroy(iterpool); - /* Now that we finally have the maximum revision, node-id and copy-id, we - can bump the two ids to get the next of each, and store them all in a - new current file. */ - len = strlen(max_node_id); - svn_fs_fs__next_key(max_node_id, &len, next_node_id); - len = strlen(max_copy_id); - svn_fs_fs__next_key(max_copy_id, &len, next_copy_id); + /* Now that we finally have the maximum revision, node-id and copy-id, we + can bump the two ids to get the next of each. */ + len = strlen(max_node_id); + svn_fs_fs__next_key(max_node_id, &len, next_node_id_buf); + next_node_id = next_node_id_buf; + len = strlen(max_copy_id); + svn_fs_fs__next_key(max_copy_id, &len, next_copy_id_buf); + next_copy_id = next_copy_id_buf; + } + /* Now store the discovered youngest revision, and the next IDs if + relevant, in a new current file. */ SVN_ERR(write_current(fs, max_rev, next_node_id, next_copy_id, pool)); return SVN_NO_ERROR; @@ -5646,147 +5648,6 @@ svn_fs_fs__set_uuid(svn_fs_t *fs, return SVN_NO_ERROR; } -/** Node origin lazy cache. */ - -/* If directory PATH does not exist, create it and give it the same - permissions as FS->path.*/ -svn_error_t * -svn_fs_fs__ensure_dir_exists(const char *path, - svn_fs_t *fs, - apr_pool_t *pool) -{ - svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); - if (err && APR_STATUS_IS_EEXIST(err->apr_err)) - { - svn_error_clear(err); - return SVN_NO_ERROR; - } - SVN_ERR(err); - - /* We successfully created a new directory. Dup the permissions - from FS->path. */ - SVN_ERR(svn_fs_fs__dup_perms(path, fs->path, pool)); - - return SVN_NO_ERROR; -} - -svn_error_t * -svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id, - svn_fs_t *fs, - const char *node_id, - apr_pool_t *pool) -{ - apr_file_t *fd; - svn_stringbuf_t *origin_stringbuf; - svn_error_t *err; - - *origin_id = NULL; - err = svn_io_file_open(&fd, path_node_origin(fs, node_id, pool), - APR_READ, APR_OS_DEFAULT, pool); - if (err && APR_STATUS_IS_ENOENT(err->apr_err)) - { - svn_error_clear(err); - return SVN_NO_ERROR; - } - SVN_ERR(err); - - SVN_ERR(svn_stringbuf_from_aprfile(&origin_stringbuf, fd, pool)); - - *origin_id = svn_fs_fs__id_parse(origin_stringbuf->data, - origin_stringbuf->len, pool); - - SVN_ERR(svn_io_file_close(fd, pool)); - - return SVN_NO_ERROR; -} - -/* Helper for svn_fs_fs__set_node_origin[s]. Exactly like - svn_fs_fs__set_node_origin, except that it throws an error if the - file can't be written. */ -static svn_error_t * -set_node_origin(svn_fs_t *fs, - const char *node_id, - const svn_fs_id_t *node_rev_id, - apr_pool_t *pool) -{ - apr_file_t *file; - svn_string_t *node_rev_id_string; - - SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_path_join(fs->path, - PATH_NODE_ORIGINS_DIR, - pool), - fs, pool)); - - node_rev_id_string = svn_fs_fs__id_unparse(node_rev_id, pool); - - SVN_ERR(svn_io_file_open(&file, path_node_origin(fs, node_id, pool), - APR_WRITE | APR_CREATE | APR_TRUNCATE - | APR_BUFFERED, APR_OS_DEFAULT, pool)); - SVN_ERR(svn_io_file_write_full(file, - node_rev_id_string->data, - node_rev_id_string->len, NULL, pool)); - SVN_ERR(svn_io_file_close(file, pool)); - - return SVN_NO_ERROR; -} - - -svn_error_t * -svn_fs_fs__set_node_origins(svn_fs_t *fs, - apr_hash_t *node_origins, - apr_pool_t *pool) -{ - apr_hash_index_t *hi; - apr_pool_t *iterpool = svn_pool_create(pool); - - for (hi = apr_hash_first(pool, node_origins); - hi != NULL; - hi = apr_hash_next(hi)) - { - const void *key; - void *val; - const char *node_id; - const svn_fs_id_t *node_rev_id; - svn_error_t *err; - - svn_pool_clear(iterpool); - - apr_hash_this(hi, &key, NULL, &val); - node_id = key; - node_rev_id = val; - - err = set_node_origin(fs, node_id, node_rev_id, iterpool); - if (err && APR_STATUS_IS_EACCES(err->apr_err)) - { - /* It's just a cache; stop trying if I can't write. */ - svn_error_clear(err); - err = NULL; - goto cleanup; - } - SVN_ERR(err); - } - - cleanup: - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; -} - -svn_error_t * -svn_fs_fs__set_node_origin(svn_fs_t *fs, - const char *node_id, - const svn_fs_id_t *node_rev_id, - apr_pool_t *pool) -{ - svn_error_t *err = set_node_origin(fs, node_id, node_rev_id, pool); - if (err && APR_STATUS_IS_EACCES(err->apr_err)) - { - /* It's just a cache; stop trying if I can't write. */ - svn_error_clear(err); - err = NULL; - } - return err; -} - svn_error_t * svn_fs_fs__list_transactions(apr_array_header_t **names_p, Index: subversion/libsvn_fs_fs/fs_fs.h =================================================================== --- subversion/libsvn_fs_fs/fs_fs.h (revision 29068) +++ subversion/libsvn_fs_fs/fs_fs.h (working copy) @@ -445,47 +445,4 @@ svn_error_t *svn_fs_fs__begin_txn(svn_fs svn_error_t *svn_fs_fs__txn_prop(svn_string_t **value_p, svn_fs_txn_t *txn, const char *propname, apr_pool_t *pool); -/* If directory PATH does not exist, create it and give it the same - permissions as FS->path.*/ -svn_error_t *svn_fs_fs__ensure_dir_exists(const char *path, - svn_fs_t *fs, - apr_pool_t *pool); - -/* Update the node origin index for FS based on the hash - NODE_ORIGIN_FOR_PATHS, which maps from const char * "Node IDs" to - const svn_fs_id_t * node-rev-ids. Returns an error if any cache - entry exists with a different value; pre-existing entries with the - same value are ignored. Use POOL for any temporary allocations. - - Because this is just an "optional" cache, this function does not - return an error if the underlying storage is readonly; it still - returns an error for other error conditions. - */ -svn_error_t * -svn_fs_fs__set_node_origins(svn_fs_t *fs, - apr_hash_t *node_origins, - apr_pool_t *pool); - -/* Shorthand for calling svn_fs_fs__set_node_origins with just one pair. - */ -svn_error_t * -svn_fs_fs__set_node_origin(svn_fs_t *fs, - const char *node_id, - const svn_fs_id_t *node_rev_id, - apr_pool_t *pool); - -/* Set *ORIGIN_ID to the node revision ID from which the history of - all nodes in FS whose "Node ID" is NODE_ID springs, as determined - by a look in the index. ORIGIN_ID needs to be parsed in an - FS-backend-specific way. Use POOL for allocations. - - If there is no entry for NODE_ID in the cache, return NULL - in *ORIGIN_ID. */ -svn_error_t * -svn_fs_fs__get_node_origin(const svn_fs_id_t **origin_id, - svn_fs_t *fs, - const char *node_id, - apr_pool_t *pool); - - #endif Index: subversion/libsvn_fs_fs/lock.c =================================================================== --- subversion/libsvn_fs_fs/lock.c (revision 29068) +++ subversion/libsvn_fs_fs/lock.c (working copy) @@ -135,6 +135,29 @@ digest_path_from_path(svn_fs_t *fs, } +/* If directory PATH does not exist, create it and give it the same + permissions as FS->path.*/ +static svn_error_t * +ensure_dir_exists(const char *path, + svn_fs_t *fs, + apr_pool_t *pool) +{ + svn_error_t *err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); + if (err && APR_STATUS_IS_EEXIST(err->apr_err)) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + SVN_ERR(err); + + /* We successfully created a new directory. Dup the permissions + from FS->path. */ + SVN_ERR(svn_fs_fs__dup_perms(path, fs->path, pool)); + + return SVN_NO_ERROR; +} + + /* Write to DIGEST_PATH a representation of CHILDREN (which may be empty, if the versioned path in FS represented by DIGEST_PATH has no children) and LOCK (which may be NULL if that versioned path is @@ -152,10 +175,9 @@ write_digest_file(apr_hash_t *children, apr_hash_t *hash = apr_hash_make(pool); const char *tmp_path; - SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_path_join(fs->path, PATH_LOCKS_DIR, - pool), fs, pool)); - SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_path_dirname(digest_path, pool), fs, - pool)); + SVN_ERR(ensure_dir_exists(svn_path_join(fs->path, PATH_LOCKS_DIR, pool), + fs, pool)); + SVN_ERR(ensure_dir_exists(svn_path_dirname(digest_path, pool), fs, pool)); SVN_ERR(svn_io_open_unique_file2 (&fd, &tmp_path, digest_path, ".tmp", svn_io_file_del_none, pool)); Index: subversion/libsvn_fs_fs/structure =================================================================== --- subversion/libsvn_fs_fs/structure (revision 29068) +++ subversion/libsvn_fs_fs/structure (working copy) @@ -46,8 +46,6 @@ repository) is: locks/ Subdirectory containing locks / Subdirectory named for first 3 letters of an MD5 digest File containing locks/children for path with - node-origins/ Lazy cache of origin noderevs for nodes - File containing noderev ID of origin of node current File specifying current revision and next node/copy id fs-type File identifying this filesystem as an FSFS filesystem write-lock Empty file, locked to serialise writers @@ -58,10 +56,16 @@ repository) is: Files in the revprops directory are in the hash dump format used by svn_hash_write. -The format of the "current" file is a single line of the form -" \n" giving the -youngest revision, the next unique node-ID, and the next unique -copy-ID for the repository. +The format of the "current" file is: + + * Format 3 and above: a single line of the form + "\n" giving the youngest revision for the + repository. + + * Format 2 and below: a single line of the form " + \n" giving the youngest revision, the + next unique node-ID, and the next unique copy-ID for the + repository. The "write-lock" file is an empty file which is locked before the final stage of a commit and unlocked after the new "current" file has @@ -107,13 +111,14 @@ Format 1: (understood by Subversion 1.1+ data. No format options are permitted. No mechanism is provided to prevent transaction name reuse. Proto-rev and its lock are stored in transactions//rev and transactions//rev-lock. + Node-IDs and copy-IDs use the "current" file. Format 2: (understood by Subversion 1.4+) Delta representations in revision files may contain either svndiff0 or svndiff1 data. No format options are permitted. No mechanism is provided to prevent transaction name reuse. Proto-rev and its lock are stored in transactions//rev and - transactions//rev-lock. + transactions//rev-lock. Node-IDs and copy-IDs use the "current" file. Format 3: (understood by Subversion 1.5+) Delta representations in revision files may contain either svndiff0 @@ -122,6 +127,7 @@ Format 3: (understood by Subversion 1.5+ generated using the transaction sequence number stored in the txn-current file. Proto-rev and its lock are stored in txn-protorevs/.rev and txn-protorevs/.rev-lock. + Node-IDs and copy-IDs do not use the "current" file. Filesystem format options ------------------------- @@ -164,17 +170,28 @@ and to simplify the allocation of fresh we treat the fields of a node-ID in new and interesting ways. Within a revision file, node-revs have a txn-id field of the form -"r/", to support easy lookup. The node-id and copy-id -fields are unique base36 values as in the BDB implementation. +"r/", to support easy lookup. New node-revision IDs +assigned within a transaction have the txn-id field of "t". -New node-revision IDs assigned within a transaction have the txn-id -field of "t". The node-id or copy-id field may be base36 -values if the node-revision is derived from a pre-existing node and/or -copy; if the node-revision must have a freshly-assigned node-id or -copy-id, it uses "_" followed by a base36 unique to the transaction. -During the final phase of a commit, node-revision IDs are rewritten to -have unique node-ID and copy-ID fields and to have "r/" -txn-id fields. +When a new node-id or copy-id is assigned in a transaction, the ID +used is a "_" followed by a base36 number unique to the transaction. +During the final phase of a comment, node-revision IDs are rewritten +to have repository-wide unique node-ID and copy-ID fields, and to have +"r/" txn-id fields. + +In Format 3 and above, this uniqueness is done by changing a temporary +id of "_" to "-". Note that this means that the +originating revision of a line of history or a copy can be determined +by looking at the node ID. + +In Format 2 and below, the "current" file contains global base36 +node-ID and copy-ID counters; during the commit, the counter value is +added to the transaction-specific base36 ID, and the value in +"current" is adjusted. + +(It is legal for Format 3 repositories to contain Format 2-style IDs; +this just prevents the efficient node-origin-rev lookup for those +nodes.) The temporary assignment of node-ID and copy-ID fields has implications for svn_fs_compare_ids and svn_fs_check_related. The IDs @@ -182,11 +199,6 @@ _1.0.t1 is not related to the ID _1.0.t2 same node-ID, because temporary node-IDs are restricted in scope to the transactions they belong to. -There is a lazily created cache mapping from node-IDs to the full -node-revision ID where they are created. This is in the node-origins -directory; the file name is the node-ID and the contents is the -node-revision ID. - Copy-IDs and copy roots ----------------------- Index: subversion/libsvn_fs_fs/tree.c =================================================================== --- subversion/libsvn_fs_fs/tree.c (revision 29068) +++ subversion/libsvn_fs_fs/tree.c (working copy) @@ -3011,21 +3011,19 @@ fs_node_origin_rev(svn_revnum_t *revisio if (node_id[0] != '_') { - const svn_fs_id_t *cached_origin_id; - SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id, - fs, - node_id, - pool)); - if (cached_origin_id != NULL) + /* Maybe this is a new-style node ID that just has the revision + sitting right in it. */ + const char *dash = strchr(node_id, '-'); + if (dash && *(dash+1)) { - *revision = svn_fs_fs__id_rev(cached_origin_id); + *revision = SVN_STR_TO_REV(dash + 1); return SVN_NO_ERROR; } } { - /* Ah well, it's not in the cache. Let's actually calculate it, - then. */ + /* Ah well, it's not in the ID itself. Let's actually calculate + it, then. */ svn_fs_root_t *curroot = root; apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *predidpool = svn_pool_create(pool); @@ -3079,12 +3077,6 @@ fs_node_origin_rev(svn_revnum_t *revisio chain. */ SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool)); - /* Wow, I don't want to have to do all that again. Let's cache - the result. */ - if (node_id[0] != '_') - SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id, - svn_fs_fs__dag_get_id(node), pool)); - svn_pool_destroy(subpool); svn_pool_destroy(predidpool); return SVN_NO_ERROR;