You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/11/30 11:24:23 UTC
svn commit: r1717223 [25/50] - in /subversion/branches/ra-git: ./ build/
build/ac-macros/ build/generator/ build/generator/templates/
contrib/hook-scripts/ notes/ notes/api-errata/1.9/ notes/move-tracking/
subversion/ subversion/bindings/ctypes-python/...
Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c Mon Nov 30 10:24:16 2015
@@ -53,6 +53,7 @@
#include "fs.h"
#include "dag.h"
+#include "dag_cache.h"
#include "lock.h"
#include "tree.h"
#include "fs_x.h"
@@ -90,19 +91,8 @@ typedef struct fs_txn_root_data_t
{
/* TXN_ID value from the main struct but as a struct instead of a string */
svn_fs_x__txn_id_t txn_id;
-
- /* Cache of txn DAG nodes (without their nested noderevs, because
- * it's mutable). Same keys/values as ffd->rev_node_cache. */
- svn_cache__t *txn_node_cache;
} fs_txn_root_data_t;
-/* Declared here to resolve the circular dependencies. */
-static svn_error_t *
-get_dag(dag_node_t **dag_node_p,
- svn_fs_root_t *root,
- const char *path,
- apr_pool_t *pool);
-
static svn_fs_root_t *
make_revision_root(svn_fs_t *fs,
svn_revnum_t rev,
@@ -124,391 +114,6 @@ x_closest_copy(svn_fs_root_t **root_p,
apr_pool_t *pool);
-/*** Node Caching ***/
-
-/* 1st level cache */
-
-/* An entry in the first-level cache. REVISION and PATH form the key that
- will ultimately be matched.
- */
-typedef struct cache_entry_t
-{
- /* hash value derived from PATH, REVISION.
- Used to short-circuit failed lookups. */
- apr_uint32_t hash_value;
-
- /* revision to which the NODE belongs */
- svn_revnum_t revision;
-
- /* path of the NODE */
- char *path;
-
- /* cached value of strlen(PATH). */
- apr_size_t path_len;
-
- /* the node allocated in the cache's pool. NULL for empty entries. */
- dag_node_t *node;
-} cache_entry_t;
-
-/* Number of entries in the cache. Keep this low to keep pressure on the
- CPU caches low as well. A binary value is most efficient. If we walk
- a directory tree, we want enough entries to store nodes for all files
- without overwriting the nodes for the parent folder. That way, there
- will be no unnecessary misses (except for a few random ones caused by
- hash collision).
-
- The actual number of instances may be higher but entries that got
- overwritten are no longer visible.
- */
-enum { BUCKET_COUNT = 256 };
-
-/* The actual cache structure. All nodes will be allocated in POOL.
- When the number of INSERTIONS (i.e. objects created form that pool)
- exceeds a certain threshold, the pool will be cleared and the cache
- with it.
- */
-struct svn_fs_x__dag_cache_t
-{
- /* fixed number of (possibly empty) cache entries */
- cache_entry_t buckets[BUCKET_COUNT];
-
- /* pool used for all node allocation */
- apr_pool_t *pool;
-
- /* number of entries created from POOL since the last cleanup */
- apr_size_t insertions;
-
- /* Property lookups etc. have a very high locality (75% re-hit).
- Thus, remember the last hit location for optimistic lookup. */
- apr_size_t last_hit;
-
- /* Position of the last bucket hit that actually had a DAG node in it.
- LAST_HIT may refer to a bucket that matches path@rev but has not
- its NODE element set, yet.
- This value is a mere hint for optimistic lookup and any value is
- valid (as long as it is < BUCKET_COUNT). */
- apr_size_t last_non_empty;
-};
-
-svn_fs_x__dag_cache_t*
-svn_fs_x__create_dag_cache(apr_pool_t *result_pool)
-{
- svn_fs_x__dag_cache_t *result = apr_pcalloc(result_pool, sizeof(*result));
- result->pool = svn_pool_create(result_pool);
-
- return result;
-}
-
-/* Clears the CACHE at regular intervals (destroying all cached nodes)
- */
-static void
-auto_clear_dag_cache(svn_fs_x__dag_cache_t* cache)
-{
- if (cache->insertions > BUCKET_COUNT)
- {
- svn_pool_clear(cache->pool);
-
- memset(cache->buckets, 0, sizeof(cache->buckets));
- cache->insertions = 0;
- }
-}
-
-/* For the given REVISION and PATH, return the respective entry in CACHE.
- If the entry is empty, its NODE member will be NULL and the caller
- may then set it to the corresponding DAG node allocated in CACHE->POOL.
- */
-static cache_entry_t *
-cache_lookup( svn_fs_x__dag_cache_t *cache
- , svn_revnum_t revision
- , const char *path)
-{
- apr_size_t i, bucket_index;
- apr_size_t path_len = strlen(path);
- apr_uint32_t hash_value = (apr_uint32_t)revision;
-
-#if SVN_UNALIGNED_ACCESS_IS_OK
- /* "randomizing" / distributing factor used in our hash function */
- const apr_uint32_t factor = 0xd1f3da69;
-#endif
-
- /* optimistic lookup: hit the same bucket again? */
- cache_entry_t *result = &cache->buckets[cache->last_hit];
- if ( (result->revision == revision)
- && (result->path_len == path_len)
- && !memcmp(result->path, path, path_len))
- {
- /* Remember the position of the last node we found in this cache. */
- if (result->node)
- cache->last_non_empty = cache->last_hit;
-
- return result;
- }
-
- /* need to do a full lookup. Calculate the hash value
- (HASH_VALUE has been initialized to REVISION). */
- i = 0;
-#if SVN_UNALIGNED_ACCESS_IS_OK
- /* We relax the dependency chain between iterations by processing
- two chunks from the input per hash_value self-multiplication.
- The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
- per 2 chunks instead of 1 chunk.
- */
- for (; i + 8 <= path_len; i += 8)
- hash_value = hash_value * factor * factor
- + ( *(const apr_uint32_t*)(path + i) * factor
- + *(const apr_uint32_t*)(path + i + 4));
-#endif
-
- for (; i < path_len; ++i)
- /* Help GCC to minimize the HASH_VALUE update latency by splitting the
- MUL 33 of the naive implementation: h = h * 33 + path[i]. This
- shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
- */
- hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
-
- bucket_index = hash_value + (hash_value >> 16);
- bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
-
- /* access the corresponding bucket and remember its location */
- result = &cache->buckets[bucket_index];
- cache->last_hit = bucket_index;
-
- /* if it is *NOT* a match, clear the bucket, expect the caller to fill
- in the node and count it as an insertion */
- if ( (result->hash_value != hash_value)
- || (result->revision != revision)
- || (result->path_len != path_len)
- || memcmp(result->path, path, path_len))
- {
- result->hash_value = hash_value;
- result->revision = revision;
- if (result->path_len < path_len)
- result->path = apr_palloc(cache->pool, path_len + 1);
- result->path_len = path_len;
- memcpy(result->path, path, path_len + 1);
-
- result->node = NULL;
-
- cache->insertions++;
- }
- else if (result->node)
- {
- /* This bucket is valid & has a suitable DAG node in it.
- Remember its location. */
- cache->last_non_empty = bucket_index;
- }
-
- return result;
-}
-
-/* Optimistic lookup using the last seen non-empty location in CACHE.
- Return the node of that entry, if it is still in use and matches PATH.
- Return NULL otherwise. Since the caller usually already knows the path
- length, provide it in PATH_LEN. */
-static dag_node_t *
-cache_lookup_last_path(svn_fs_x__dag_cache_t *cache,
- const char *path,
- apr_size_t path_len)
-{
- cache_entry_t *result = &cache->buckets[cache->last_non_empty];
- assert(strlen(path) == path_len);
-
- if ( result->node
- && (result->path_len == path_len)
- && !memcmp(result->path, path, path_len))
- {
- return result->node;
- }
-
- return NULL;
-}
-
-/* 2nd level cache */
-
-/* Find and return the DAG node cache for ROOT and the key that
- should be used for PATH.
-
- RESULT_POOL will only be used for allocating a new keys if necessary. */
-static void
-locate_cache(svn_cache__t **cache,
- const char **key,
- svn_fs_root_t *root,
- const char *path,
- apr_pool_t *result_pool)
-{
- if (root->is_txn_root)
- {
- fs_txn_root_data_t *frd = root->fsap_data;
-
- if (cache)
- *cache = frd->txn_node_cache;
- if (key && path)
- *key = path;
- }
- else
- {
- svn_fs_x__data_t *ffd = root->fs->fsap_data;
-
- if (cache)
- *cache = ffd->rev_node_cache;
- if (key && path)
- *key = svn_fs_x__combine_number_and_string(root->rev, path,
- result_pool);
- }
-}
-
-/* Return NODE for PATH from ROOT's node cache, or NULL if the node
- isn't cached; read it from the FS. *NODE remains valid until either
- POOL or the FS gets cleared or destroyed (whichever comes first).
- */
-static svn_error_t *
-dag_node_cache_get(dag_node_t **node_p,
- svn_fs_root_t *root,
- const char *path,
- apr_pool_t *pool)
-{
- svn_boolean_t found;
- dag_node_t *node = NULL;
- svn_cache__t *cache;
- const char *key;
-
- SVN_ERR_ASSERT(*path == '/');
-
- if (!root->is_txn_root)
- {
- /* immutable DAG node. use the global caches for it */
-
- svn_fs_x__data_t *ffd = root->fs->fsap_data;
- cache_entry_t *bucket;
-
- auto_clear_dag_cache(ffd->dag_node_cache);
- bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
- if (bucket->node == NULL)
- {
- locate_cache(&cache, &key, root, path, pool);
- SVN_ERR(svn_cache__get((void **)&node, &found, cache, key,
- ffd->dag_node_cache->pool));
- if (found && node)
- {
- /* Patch up the FS, since this might have come from an old FS
- * object. */
- svn_fs_x__dag_set_fs(node, root->fs);
- bucket->node = node;
- }
- }
- else
- {
- node = bucket->node;
- }
- }
- else
- {
- /* DAG is mutable / may become invalid. Use the TXN-local cache */
-
- locate_cache(&cache, &key, root, path, pool);
-
- SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
- if (found && node)
- {
- /* Patch up the FS, since this might have come from an old FS
- * object. */
- svn_fs_x__dag_set_fs(node, root->fs);
- }
- }
-
- *node_p = node;
-
- return SVN_NO_ERROR;
-}
-
-
-/* Add the NODE for PATH to ROOT's node cache. */
-static svn_error_t *
-dag_node_cache_set(svn_fs_root_t *root,
- const char *path,
- dag_node_t *node,
- apr_pool_t *scratch_pool)
-{
- svn_cache__t *cache;
- const char *key;
-
- SVN_ERR_ASSERT(*path == '/');
-
- /* Do *not* attempt to dup and put the node into L1.
- * dup() is twice as expensive as an L2 lookup (which will set also L1).
- */
- locate_cache(&cache, &key, root, path, scratch_pool);
-
- return svn_cache__set(cache, key, node, scratch_pool);
-}
-
-
-/* Baton for find_descendants_in_cache. */
-typedef struct fdic_baton_t
-{
- const char *path;
- apr_array_header_t *list;
- apr_pool_t *pool;
-} fdic_baton_t;
-
-/* If the given item is a descendant of BATON->PATH, push
- * it onto BATON->LIST (copying into BATON->POOL). Implements
- * the svn_iter_apr_hash_cb_t prototype. */
-static svn_error_t *
-find_descendants_in_cache(void *baton,
- const void *key,
- apr_ssize_t klen,
- void *val,
- apr_pool_t *pool)
-{
- fdic_baton_t *b = baton;
- const char *item_path = key;
-
- if (svn_fspath__skip_ancestor(b->path, item_path))
- APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
-
- return SVN_NO_ERROR;
-}
-
-/* Invalidate cache entries for PATH and any of its children. This
- should *only* be called on a transaction root! */
-static svn_error_t *
-dag_node_cache_invalidate(svn_fs_root_t *root,
- const char *path,
- apr_pool_t *scratch_pool)
-{
- fdic_baton_t b;
- svn_cache__t *cache;
- apr_pool_t *iterpool;
- int i;
-
- b.path = path;
- b.pool = svn_pool_create(scratch_pool);
- b.list = apr_array_make(b.pool, 1, sizeof(const char *));
-
- SVN_ERR_ASSERT(root->is_txn_root);
- locate_cache(&cache, NULL, root, NULL, b.pool);
-
-
- SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
- &b, b.pool));
-
- iterpool = svn_pool_create(b.pool);
-
- for (i = 0; i < b.list->nelts; i++)
- {
- const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
- svn_pool_clear(iterpool);
- SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
- }
-
- svn_pool_destroy(iterpool);
- svn_pool_destroy(b.pool);
- return SVN_NO_ERROR;
-}
-
-
-
/* Creating transaction and revision root nodes. */
svn_error_t *
@@ -554,8 +159,8 @@ svn_fs_x__revision_root(svn_fs_root_t **
/* Getting dag nodes for roots. */
/* Return the transaction ID to a given transaction ROOT. */
-static svn_fs_x__txn_id_t
-root_txn_id(svn_fs_root_t *root)
+svn_fs_x__txn_id_t
+svn_fs_x__root_txn_id(svn_fs_root_t *root)
{
fs_txn_root_data_t *frd = root->fsap_data;
assert(root->is_txn_root);
@@ -563,105 +168,32 @@ root_txn_id(svn_fs_root_t *root)
return frd->txn_id;
}
-/* Set *NODE_P to a freshly opened dag node referring to the root
- directory of ROOT, allocating from RESULT_POOL. Use SCRATCH_POOL
- for temporary allocations. */
-static svn_error_t *
-root_node(dag_node_t **node_p,
- svn_fs_root_t *root,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+/* Return the change set to a given ROOT. */
+svn_fs_x__change_set_t
+svn_fs_x__root_change_set(svn_fs_root_t *root)
{
if (root->is_txn_root)
- {
- /* It's a transaction root. Open a fresh copy. */
- return svn_fs_x__dag_txn_root(node_p, root->fs, root_txn_id(root),
- result_pool, scratch_pool);
- }
- else
- {
- /* It's a revision root, so we already have its root directory
- opened. */
- return svn_fs_x__dag_revision_root(node_p, root->fs, root->rev,
- result_pool, scratch_pool);
- }
-}
-
+ return svn_fs_x__change_set_by_txn(svn_fs_x__root_txn_id(root));
-/* Set *NODE_P to a mutable root directory for ROOT, cloning if
- necessary, allocating in RESULT_POOL. ROOT must be a transaction root.
- Use ERROR_PATH in error messages. Use SCRATCH_POOL for temporaries.*/
-static svn_error_t *
-mutable_root_node(dag_node_t **node_p,
- svn_fs_root_t *root,
- const char *error_path,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- if (root->is_txn_root)
- {
- /* It's a transaction root. Open a fresh copy. */
- return svn_fs_x__dag_txn_root(node_p, root->fs, root_txn_id(root),
- result_pool, scratch_pool);
- }
- else
- /* If it's not a transaction root, we can't change its contents. */
- return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
+ return svn_fs_x__change_set_by_rev(root->rev);
}
+
/* Traversing directory paths. */
-typedef enum copy_id_inherit_t
-{
- copy_id_inherit_unknown = 0,
- copy_id_inherit_self,
- copy_id_inherit_parent,
- copy_id_inherit_new
-
-} copy_id_inherit_t;
-
-/* A linked list representing the path from a node up to a root
- directory. We use this for cloning, and for operations that need
- to deal with both a node and its parent directory. For example, a
- `delete' operation needs to know that the node actually exists, but
- also needs to change the parent directory. */
-typedef struct parent_path_t
-{
-
- /* A node along the path. This could be the final node, one of its
- parents, or the root. Every parent path ends with an element for
- the root directory. */
- dag_node_t *node;
-
- /* The name NODE has in its parent directory. This is zero for the
- root directory, which (obviously) has no name in its parent. */
- char *entry;
-
- /* The parent of NODE, or zero if NODE is the root directory. */
- struct parent_path_t *parent;
-
- /* The copy ID inheritance style. */
- copy_id_inherit_t copy_inherit;
-
- /* If copy ID inheritance style is copy_id_inherit_new, this is the
- path which should be implicitly copied; otherwise, this is NULL. */
- const char *copy_src_path;
-
-} parent_path_t;
-
-/* Return a text string describing the absolute path of parent_path
- PARENT_PATH. It will be allocated in POOL. */
+/* Return a text string describing the absolute path of parent path
+ DAG_PATH. It will be allocated in POOL. */
static const char *
-parent_path_path(parent_path_t *parent_path,
+parent_path_path(svn_fs_x__dag_path_t *dag_path,
apr_pool_t *pool)
{
const char *path_so_far = "/";
- if (parent_path->parent)
- path_so_far = parent_path_path(parent_path->parent, pool);
- return parent_path->entry
- ? svn_fspath__join(path_so_far, parent_path->entry, pool)
+ if (dag_path->parent)
+ path_so_far = parent_path_path(dag_path->parent, pool);
+ return dag_path->entry
+ ? svn_fspath__join(path_so_far, dag_path->entry, pool)
: path_so_far;
}
@@ -669,12 +201,12 @@ parent_path_path(parent_path_t *parent_p
/* Return the FS path for the parent path chain object CHILD relative
to its ANCESTOR in the same chain, allocated in POOL. */
static const char *
-parent_path_relpath(parent_path_t *child,
- parent_path_t *ancestor,
+parent_path_relpath(svn_fs_x__dag_path_t *child,
+ svn_fs_x__dag_path_t *ancestor,
apr_pool_t *pool)
{
const char *path_so_far = "";
- parent_path_t *this_node = child;
+ svn_fs_x__dag_path_t *this_node = child;
while (this_node != ancestor)
{
assert(this_node != NULL);
@@ -686,565 +218,6 @@ parent_path_relpath(parent_path_t *child
-/* Choose a copy ID inheritance method *INHERIT_P to be used in the
- event that immutable node CHILD in FS needs to be made mutable. If
- the inheritance method is copy_id_inherit_new, also return a
- *COPY_SRC_PATH on which to base the new copy ID (else return NULL
- for that path). CHILD must have a parent (it cannot be the root
- node). Allocations are taken from POOL. */
-static svn_error_t *
-get_copy_inheritance(copy_id_inherit_t *inherit_p,
- const char **copy_src_path,
- svn_fs_t *fs,
- parent_path_t *child,
- apr_pool_t *pool)
-{
- svn_fs_x__id_t child_copy_id, parent_copy_id;
- svn_boolean_t related;
- const char *id_path = NULL;
- svn_fs_root_t *copyroot_root;
- dag_node_t *copyroot_node;
- svn_revnum_t copyroot_rev;
- const char *copyroot_path;
-
- SVN_ERR_ASSERT(child && child->parent);
-
- /* Initialize some convenience variables. */
- SVN_ERR(svn_fs_x__dag_get_copy_id(&child_copy_id, child->node));
- SVN_ERR(svn_fs_x__dag_get_copy_id(&parent_copy_id, child->parent->node));
-
- /* If this child is already mutable, we have nothing to do. */
- if (svn_fs_x__dag_check_mutable(child->node))
- {
- *inherit_p = copy_id_inherit_self;
- *copy_src_path = NULL;
- return SVN_NO_ERROR;
- }
-
- /* From this point on, we'll assume that the child will just take
- its copy ID from its parent. */
- *inherit_p = copy_id_inherit_parent;
- *copy_src_path = NULL;
-
- /* Special case: if the child's copy ID is '0', use the parent's
- copy ID. */
- if (svn_fs_x__id_is_root(&child_copy_id))
- return SVN_NO_ERROR;
-
- /* Compare the copy IDs of the child and its parent. If they are
- the same, then the child is already on the same branch as the
- parent, and should use the same mutability copy ID that the
- parent will use. */
- if (svn_fs_x__id_eq(&child_copy_id, &parent_copy_id))
- return SVN_NO_ERROR;
-
- /* If the child is on the same branch that the parent is on, the
- child should just use the same copy ID that the parent would use.
- Else, the child needs to generate a new copy ID to use should it
- need to be made mutable. We will claim that child is on the same
- branch as its parent if the child itself is not a branch point,
- or if it is a branch point that we are accessing via its original
- copy destination path. */
- SVN_ERR(svn_fs_x__dag_get_copyroot(©root_rev, ©root_path,
- child->node));
- SVN_ERR(svn_fs_x__revision_root(©root_root, fs, copyroot_rev, pool));
- SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
-
- SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node, child->node));
- if (!related)
- return SVN_NO_ERROR;
-
- /* Determine if we are looking at the child via its original path or
- as a subtree item of a copied tree. */
- id_path = svn_fs_x__dag_get_created_path(child->node);
- if (strcmp(id_path, parent_path_path(child, pool)) == 0)
- {
- *inherit_p = copy_id_inherit_self;
- return SVN_NO_ERROR;
- }
-
- /* We are pretty sure that the child node is an unedited nested
- branched node. When it needs to be made mutable, it should claim
- a new copy ID. */
- *inherit_p = copy_id_inherit_new;
- *copy_src_path = id_path;
- return SVN_NO_ERROR;
-}
-
-/* Allocate a new parent_path_t node from RESULT_POOL, referring to NODE,
- ENTRY, PARENT, and COPY_ID. */
-static parent_path_t *
-make_parent_path(dag_node_t *node,
- char *entry,
- parent_path_t *parent,
- apr_pool_t *result_pool)
-{
- parent_path_t *parent_path = apr_pcalloc(result_pool, sizeof(*parent_path));
- if (node)
- parent_path->node = svn_fs_x__dag_copy_into_pool(node, result_pool);
- parent_path->entry = entry;
- parent_path->parent = parent;
- parent_path->copy_inherit = copy_id_inherit_unknown;
- parent_path->copy_src_path = NULL;
- return parent_path;
-}
-
-
-/* Flags for open_path. */
-typedef enum open_path_flags_t {
-
- /* The last component of the PATH need not exist. (All parent
- directories must exist, as usual.) If the last component doesn't
- exist, simply leave the `node' member of the bottom parent_path
- component zero. */
- open_path_last_optional = 1,
-
- /* When this flag is set, don't bother to lookup the DAG node in
- our caches because we already tried this. Ignoring this flag
- has no functional impact. */
- open_path_uncached = 2,
-
- /* The caller does not care about the parent node chain but only
- the final DAG node. */
- open_path_node_only = 4,
-
- /* The caller wants a NULL path object instead of an error if the
- path cannot be found. */
- open_path_allow_null = 8
-} open_path_flags_t;
-
-/* Try a short-cut for the open_path() function using the last node accessed.
- * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
- * its "created path", return the node in *NODE_P. Set it to NULL otherwise.
- *
- * This function is used to support ra_serf-style access patterns where we
- * are first asked for path@rev and then for path@c_rev of the same node.
- * The shortcut works by ignoring the "rev" part of the cache key and then
- * checking whether we got lucky. Lookup and verification are both quick
- * plus there are many early outs for common types of mismatch.
- */
-static svn_error_t *
-try_match_last_node(dag_node_t **node_p,
- svn_fs_root_t *root,
- const char *path,
- apr_size_t path_len,
- apr_pool_t *scratch_pool)
-{
- svn_fs_x__data_t *ffd = root->fs->fsap_data;
-
- /* Optimistic lookup: if the last node returned from the cache applied to
- the same PATH, return it in NODE. */
- dag_node_t *node
- = cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
-
- /* Did we get a bucket with a committed node? */
- if (node && !svn_fs_x__dag_check_mutable(node))
- {
- /* Get the path&rev pair at which this node was created.
- This is repository location for which this node is _known_ to be
- the right lookup result irrespective of how we found it. */
- const char *created_path
- = svn_fs_x__dag_get_created_path(node);
- svn_revnum_t revision = svn_fs_x__dag_get_revision(node);
-
- /* Is it an exact match? */
- if (revision == root->rev && strcmp(created_path, path) == 0)
- {
- /* Cache it under its full path@rev access path. */
- SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
-
- *node_p = node;
- return SVN_NO_ERROR;
- }
- }
-
- *node_p = NULL;
- return SVN_NO_ERROR;
-}
-
-
-/* Open the node identified by PATH in ROOT, allocating in POOL. Set
- *PARENT_PATH_P to a path from the node up to ROOT. The resulting
- **PARENT_PATH_P value is guaranteed to contain at least one
- *element, for the root directory. PATH must be in canonical form.
-
- If resulting *PARENT_PATH_P will eventually be made mutable and
- modified, or if copy ID inheritance information is otherwise needed,
- IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, no copy ID
- inheritance information will be calculated for the *PARENT_PATH_P chain.
-
- If FLAGS & open_path_last_optional is zero, return the error
- SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
- non-zero, require all the parent directories to exist as normal,
- but if the final path component doesn't exist, simply return a path
- whose bottom `node' member is zero. This option is useful for
- callers that create new nodes --- we find the parent directory for
- them, and tell them whether the entry exists already.
-
- The remaining bits in FLAGS are hints that allow this function
- to take shortcuts based on knowledge that the caller provides,
- such as the caller is not actually being interested in PARENT_PATH_P,
- but only in (*PARENT_PATH_P)->NODE.
-
- NOTE: Public interfaces which only *read* from the filesystem
- should not call this function directly, but should instead use
- get_dag().
-*/
-static svn_error_t *
-open_path(parent_path_t **parent_path_p,
- svn_fs_root_t *root,
- const char *path,
- int flags,
- svn_boolean_t is_txn_path,
- apr_pool_t *pool)
-{
- svn_fs_t *fs = root->fs;
- dag_node_t *here = NULL; /* The directory we're currently looking at. */
- parent_path_t *parent_path; /* The path from HERE up to the root. */
- const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
- apr_pool_t *iterpool = svn_pool_create(pool);
-
- /* path to the currently processed entry without trailing '/'.
- We will reuse this across iterations by simply putting a NUL terminator
- at the respective position and replacing that with a '/' in the next
- iteration. This is correct as we assert() PATH to be canonical. */
- svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
- apr_size_t path_len = path_so_far->len;
-
- /* Callers often traverse the DAG in some path-based order or along the
- history segments. That allows us to try a few guesses about where to
- find the next item. This is only useful if the caller didn't request
- the full parent chain. */
- assert(svn_fs__is_canonical_abspath(path));
- path_so_far->len = 0; /* "" */
- if (flags & open_path_node_only)
- {
- const char *directory;
-
- /* First attempt: Assume that we access the DAG for the same path as
- in the last lookup but for a different revision that happens to be
- the last revision that touched the respective node. This is a
- common pattern when e.g. checking out over ra_serf. Note that this
- will only work for committed data as the revision info for nodes in
- txns is bogus.
-
- This shortcut is quick and will exit this function upon success.
- So, try it first. */
- if (!root->is_txn_root)
- {
- dag_node_t *node;
- SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
-
- /* Did the shortcut work? */
- if (node)
- {
- /* Construct and return the result. */
- svn_pool_destroy(iterpool);
-
- parent_path = make_parent_path(node, 0, 0, pool);
- parent_path->copy_inherit = copy_id_inherit_self;
- *parent_path_p = parent_path;
-
- return SVN_NO_ERROR;
- }
- }
-
- /* Second attempt: Try starting the lookup immediately at the parent
- node. We will often have recently accessed either a sibling or
- said parent DIRECTORY itself for the same revision. */
- directory = svn_dirent_dirname(path, pool);
- if (directory[1] != 0) /* root nodes are covered anyway */
- {
- SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
-
- /* Did the shortcut work? */
- if (here)
- {
- apr_size_t dirname_len = strlen(directory);
- path_so_far->len = dirname_len;
- rest = path + dirname_len + 1;
- }
- }
- }
-
- /* did the shortcut work? */
- if (!here)
- {
- /* Make a parent_path item for the root node, using its own current
- copy id. */
- SVN_ERR(root_node(&here, root, pool, iterpool));
- rest = path + 1; /* skip the leading '/', it saves in iteration */
- }
-
- path_so_far->data[path_so_far->len] = '\0';
- parent_path = make_parent_path(here, 0, 0, pool);
- parent_path->copy_inherit = copy_id_inherit_self;
-
- /* Whenever we are at the top of this loop:
- - HERE is our current directory,
- - ID is the node revision ID of HERE,
- - REST is the path we're going to find in HERE, and
- - PARENT_PATH includes HERE and all its parents. */
- for (;;)
- {
- const char *next;
- char *entry;
- dag_node_t *child;
-
- svn_pool_clear(iterpool);
-
- /* The NODE in PARENT_PATH always lives in POOL, i.e. it will
- * survive the cleanup of ITERPOOL and the DAG cache.*/
- here = parent_path->node;
-
- /* Parse out the next entry from the path. */
- entry = svn_fs__next_entry_name(&next, rest, pool);
-
- /* Update the path traversed thus far. */
- path_so_far->data[path_so_far->len] = '/';
- path_so_far->len += strlen(entry) + 1;
- path_so_far->data[path_so_far->len] = '\0';
-
- /* Given the behavior of svn_fs__next_entry_name(), ENTRY may be an
- empty string when the path either starts or ends with a slash.
- In either case, we stay put: the current directory stays the
- same, and we add nothing to the parent path. We only need to
- process non-empty path segments. */
- if (*entry != '\0')
- {
- copy_id_inherit_t inherit;
- const char *copy_path = NULL;
- dag_node_t *cached_node = NULL;
-
- /* If we found a directory entry, follow it. First, we
- check our node cache, and, failing that, we hit the DAG
- layer. Don't bother to contact the cache for the last
- element if we already know the lookup to fail for the
- complete path. */
- if (next || !(flags & open_path_uncached))
- SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
- pool));
- if (cached_node)
- child = cached_node;
- else
- SVN_ERR(svn_fs_x__dag_open(&child, here, entry, pool, iterpool));
-
- /* "file not found" requires special handling. */
- if (child == NULL)
- {
- /* If this was the last path component, and the caller
- said it was optional, then don't return an error;
- just put a NULL node pointer in the path. */
-
- if ((flags & open_path_last_optional)
- && (! next || *next == '\0'))
- {
- parent_path = make_parent_path(NULL, entry, parent_path,
- pool);
- break;
- }
- else if (flags & open_path_allow_null)
- {
- parent_path = NULL;
- break;
- }
- else
- {
- /* Build a better error message than svn_fs_x__dag_open
- can provide, giving the root and full path name. */
- return SVN_FS__NOT_FOUND(root, path);
- }
- }
-
- if (flags & open_path_node_only)
- {
- /* Shortcut: the caller only wants the final DAG node. */
- parent_path->node = svn_fs_x__dag_copy_into_pool(child, pool);
- }
- else
- {
- /* Now, make a parent_path item for CHILD. */
- parent_path = make_parent_path(child, entry, parent_path, pool);
- if (is_txn_path)
- {
- SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
- parent_path, iterpool));
- parent_path->copy_inherit = inherit;
- parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
- }
- }
-
- /* Cache the node we found (if it wasn't already cached). */
- if (! cached_node)
- SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
- iterpool));
- }
-
- /* Are we finished traversing the path? */
- if (! next)
- break;
-
- /* The path isn't finished yet; we'd better be in a directory. */
- if (svn_fs_x__dag_node_kind(child) != svn_node_dir)
- SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
- apr_psprintf(iterpool, _("Failure opening '%s'"), path));
-
- rest = next;
- }
-
- svn_pool_destroy(iterpool);
- *parent_path_p = parent_path;
- return SVN_NO_ERROR;
-}
-
-
-/* Make the node referred to by PARENT_PATH mutable, if it isn't already,
- allocating from RESULT_POOL. ROOT must be the root from which
- PARENT_PATH descends. Clone any parent directories as needed.
- Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
- ERROR_PATH in error messages. Use SCRATCH_POOL for temporaries. */
-static svn_error_t *
-make_path_mutable(svn_fs_root_t *root,
- parent_path_t *parent_path,
- const char *error_path,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- dag_node_t *clone;
- svn_fs_x__txn_id_t txn_id = root_txn_id(root);
-
- /* Is the node mutable already? */
- if (svn_fs_x__dag_check_mutable(parent_path->node))
- return SVN_NO_ERROR;
-
- /* Are we trying to clone the root, or somebody's child node? */
- if (parent_path->parent)
- {
- svn_fs_x__id_t copy_id = { SVN_INVALID_REVNUM, 0 };
- svn_fs_x__id_t *copy_id_ptr = ©_id;
- copy_id_inherit_t inherit = parent_path->copy_inherit;
- const char *clone_path, *copyroot_path;
- svn_revnum_t copyroot_rev;
- svn_boolean_t is_parent_copyroot = FALSE;
- svn_fs_root_t *copyroot_root;
- dag_node_t *copyroot_node;
- svn_boolean_t related;
-
- /* We're trying to clone somebody's child. Make sure our parent
- is mutable. */
- SVN_ERR(make_path_mutable(root, parent_path->parent,
- error_path, result_pool, scratch_pool));
-
- switch (inherit)
- {
- case copy_id_inherit_parent:
- SVN_ERR(svn_fs_x__dag_get_copy_id(©_id,
- parent_path->parent->node));
- break;
-
- case copy_id_inherit_new:
- SVN_ERR(svn_fs_x__reserve_copy_id(©_id, root->fs, txn_id,
- scratch_pool));
- break;
-
- case copy_id_inherit_self:
- copy_id_ptr = NULL;
- break;
-
- case copy_id_inherit_unknown:
- default:
- SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
- inheritance data. */
- }
-
- /* Determine what copyroot our new child node should use. */
- SVN_ERR(svn_fs_x__dag_get_copyroot(©root_rev, ©root_path,
- parent_path->node));
- SVN_ERR(svn_fs_x__revision_root(©root_root, root->fs,
- copyroot_rev, scratch_pool));
- SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path,
- result_pool));
-
- SVN_ERR(svn_fs_x__dag_related_node(&related, copyroot_node,
- parent_path->node));
- if (!related)
- is_parent_copyroot = TRUE;
-
- /* Now make this node mutable. */
- clone_path = parent_path_path(parent_path->parent, scratch_pool);
- SVN_ERR(svn_fs_x__dag_clone_child(&clone,
- parent_path->parent->node,
- clone_path,
- parent_path->entry,
- copy_id_ptr, txn_id,
- is_parent_copyroot,
- result_pool,
- scratch_pool));
-
- /* Update the path cache. */
- SVN_ERR(dag_node_cache_set(root,
- parent_path_path(parent_path, scratch_pool),
- clone, scratch_pool));
- }
- else
- {
- /* We're trying to clone the root directory. */
- SVN_ERR(mutable_root_node(&clone, root, error_path, result_pool,
- scratch_pool));
- }
-
- /* Update the PARENT_PATH link to refer to the clone. */
- parent_path->node = clone;
-
- return SVN_NO_ERROR;
-}
-
-
-/* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
- node we find, allocated in POOL. Return the error
- SVN_ERR_FS_NOT_FOUND if this node doesn't exist.
- */
-static svn_error_t *
-get_dag(dag_node_t **dag_node_p,
- svn_fs_root_t *root,
- const char *path,
- apr_pool_t *pool)
-{
- parent_path_t *parent_path;
- dag_node_t *node = NULL;
-
- /* First we look for the DAG in our cache
- (if the path may be canonical). */
- if (*path == '/')
- SVN_ERR(dag_node_cache_get(&node, root, path, pool));
-
- if (! node)
- {
- /* Canonicalize the input PATH. As it turns out, >95% of all paths
- * seen here during e.g. svnadmin verify are non-canonical, i.e.
- * miss the leading '/'. Unconditional canonicalization has a net
- * performance benefit over previously checking path for being
- * canonical. */
- path = svn_fs__canonicalize_abspath(path, pool);
- SVN_ERR(dag_node_cache_get(&node, root, path, pool));
-
- if (! node)
- {
- /* Call open_path with no flags, as we want this to return an
- * error if the node for which we are searching doesn't exist. */
- SVN_ERR(open_path(&parent_path, root, path,
- open_path_uncached | open_path_node_only,
- FALSE, pool));
- node = parent_path->node;
-
- /* No need to cache our find -- open_path() will do that for us. */
- }
- }
-
- *dag_node_p = svn_fs_x__dag_copy_into_pool(node, pool);
- return SVN_NO_ERROR;
-}
-
/* Populating the `changes' table. */
@@ -1306,10 +279,12 @@ x_node_id(const svn_fs_id_t **id_p,
}
else
{
+ apr_pool_t *scratch_pool = svn_pool_create(pool);
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
noderev_id = *svn_fs_x__dag_get_id(node);
+ svn_pool_destroy(scratch_pool);
}
*id_p = svn_fs_x__id_create(svn_fs_x__id_create_context(root->fs, pool),
@@ -1342,36 +317,37 @@ x_node_relation(svn_fs_node_relation_t *
return SVN_NO_ERROR;
}
- /* Nodes from different transactions are never related. */
- if (root_a->is_txn_root && root_b->is_txn_root
- && strcmp(root_a->txn, root_b->txn))
- {
- *relation = svn_fs_node_unrelated;
- return SVN_NO_ERROR;
- }
-
/* Are both (!) root paths? Then, they are related and we only test how
* direct the relation is. */
if (a_is_root_dir && b_is_root_dir)
{
- *relation = root_a->rev == root_b->rev
- ? svn_fs_node_same
+ svn_boolean_t different_txn
+ = root_a->is_txn_root && root_b->is_txn_root
+ && strcmp(root_a->txn, root_b->txn);
+
+ /* For txn roots, root->REV is the base revision of that TXN. */
+ *relation = ( (root_a->rev == root_b->rev)
+ && (root_a->is_txn_root == root_b->is_txn_root)
+ && !different_txn)
+ ? svn_fs_node_unchanged
: svn_fs_node_common_ancestor;
return SVN_NO_ERROR;
}
/* We checked for all separations between ID spaces (repos, txn).
* Now, we can simply test for the ID values themselves. */
- SVN_ERR(get_dag(&node, root_a, path_a, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root_a, path_a, scratch_pool));
noderev_id_a = *svn_fs_x__dag_get_id(node);
- SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_a, node));
+ node_id_a = *svn_fs_x__dag_get_node_id(node);
- SVN_ERR(get_dag(&node, root_b, path_b, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root_b, path_b, scratch_pool));
noderev_id_b = *svn_fs_x__dag_get_id(node);
- SVN_ERR(svn_fs_x__dag_get_node_id(&node_id_b, node));
+ node_id_b = *svn_fs_x__dag_get_node_id(node);
+ /* In FSX, even in-txn IDs are globally unique.
+ * So, we can simply compare them. */
if (svn_fs_x__id_eq(&noderev_id_a, &noderev_id_b))
- *relation = svn_fs_node_same;
+ *relation = svn_fs_node_unchanged;
else if (svn_fs_x__id_eq(&node_id_a, &node_id_b))
*relation = svn_fs_node_common_ancestor;
else
@@ -1388,7 +364,7 @@ svn_fs_x__node_created_rev(svn_revnum_t
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
*revision = svn_fs_x__dag_get_revision(node);
return SVN_NO_ERROR;
@@ -1405,8 +381,8 @@ x_node_created_path(const char **created
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, pool));
- *created_path = svn_fs_x__dag_get_created_path(node);
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, pool));
+ *created_path = apr_pstrdup(pool, svn_fs_x__dag_get_created_path(node));
return SVN_NO_ERROR;
}
@@ -1423,7 +399,7 @@ node_kind(svn_node_kind_t *kind_p,
dag_node_t *node;
/* Get the node id. */
- SVN_ERR(get_dag(&node, root, path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
/* Use the node id to get the real kind. */
*kind_p = svn_fs_x__dag_node_kind(node);
@@ -1468,11 +444,12 @@ x_node_prop(svn_string_t **value_p,
apr_hash_t *proplist;
apr_pool_t *scratch_pool = svn_pool_create(pool);
- SVN_ERR(get_dag(&node, root, path, pool));
- SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, node, pool, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
+ SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, node, scratch_pool,
+ scratch_pool));
*value_p = NULL;
if (proplist)
- *value_p = svn_hash_gets(proplist, propname);
+ *value_p = svn_string_dup(svn_hash_gets(proplist, propname), pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
@@ -1492,16 +469,30 @@ x_node_proplist(apr_hash_t **table_p,
dag_node_t *node;
apr_pool_t *scratch_pool = svn_pool_create(pool);
- SVN_ERR(get_dag(&node, root, path, pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
SVN_ERR(svn_fs_x__dag_get_proplist(table_p, node, pool, scratch_pool));
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
+static svn_error_t *
+x_node_has_props(svn_boolean_t *has_props,
+ svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *props;
+
+ SVN_ERR(x_node_proplist(&props, root, path, scratch_pool));
+
+ *has_props = (0 < apr_hash_count(props));
+
+ return SVN_NO_ERROR;
+}
static svn_error_t *
-increment_mergeinfo_up_tree(parent_path_t *pp,
+increment_mergeinfo_up_tree(svn_fs_x__dag_path_t *pp,
apr_int64_t increment,
apr_pool_t *scratch_pool)
{
@@ -1531,7 +522,7 @@ x_change_node_prop(svn_fs_root_t *root,
const svn_string_t *value,
apr_pool_t *scratch_pool)
{
- parent_path_t *parent_path;
+ svn_fs_x__dag_path_t *dag_path;
apr_hash_t *proplist;
svn_fs_x__txn_id_t txn_id;
svn_boolean_t mergeinfo_mod = FALSE;
@@ -1539,10 +530,10 @@ x_change_node_prop(svn_fs_root_t *root,
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
- txn_id = root_txn_id(root);
+ txn_id = svn_fs_x__root_txn_id(root);
- path = svn_fs__canonicalize_abspath(path, subpool);
- SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path, 0, TRUE, subpool,
+ subpool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
@@ -1550,8 +541,9 @@ x_change_node_prop(svn_fs_root_t *root,
SVN_ERR(svn_fs_x__allow_locked_operation(path, root->fs, FALSE, FALSE,
subpool));
- SVN_ERR(make_path_mutable(root, parent_path, path, subpool, subpool));
- SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, parent_path->node, subpool,
+ SVN_ERR(svn_fs_x__make_path_mutable(root, dag_path, path, subpool,
+ subpool));
+ SVN_ERR(svn_fs_x__dag_get_proplist(&proplist, dag_path->node, subpool,
subpool));
/* If there's no proplist, but we're just deleting a property, exit now. */
@@ -1565,8 +557,8 @@ x_change_node_prop(svn_fs_root_t *root,
if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
{
apr_int64_t increment = 0;
- svn_boolean_t had_mergeinfo;
- SVN_ERR(svn_fs_x__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
+ svn_boolean_t had_mergeinfo
+ = svn_fs_x__dag_has_mergeinfo(dag_path->node);
if (value && !had_mergeinfo)
increment = 1;
@@ -1575,8 +567,8 @@ x_change_node_prop(svn_fs_root_t *root,
if (increment != 0)
{
- SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, subpool));
- SVN_ERR(svn_fs_x__dag_set_has_mergeinfo(parent_path->node,
+ SVN_ERR(increment_mergeinfo_up_tree(dag_path, increment, subpool));
+ SVN_ERR(svn_fs_x__dag_set_has_mergeinfo(dag_path->node,
(value != NULL), subpool));
}
@@ -1587,14 +579,14 @@ x_change_node_prop(svn_fs_root_t *root,
svn_hash_sets(proplist, name, value);
/* Overwrite the node's proplist. */
- SVN_ERR(svn_fs_x__dag_set_proplist(parent_path->node, proplist,
+ SVN_ERR(svn_fs_x__dag_set_proplist(dag_path->node, proplist,
subpool));
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path,
- svn_fs_x__dag_get_id(parent_path->node),
+ svn_fs_x__dag_get_id(dag_path->node),
svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
- svn_fs_x__dag_node_kind(parent_path->node),
+ svn_fs_x__dag_node_kind(dag_path->node),
SVN_INVALID_REVNUM, NULL, subpool));
svn_pool_destroy(subpool);
@@ -1624,8 +616,8 @@ x_props_changed(svn_boolean_t *changed_p
(SVN_ERR_FS_GENERAL, NULL,
_("Cannot compare property value between two different filesystems"));
- SVN_ERR(get_dag(&node1, root1, path1, subpool));
- SVN_ERR(get_dag(&node2, root2, path2, subpool));
+ SVN_ERR(svn_fs_x__get_dag_node(&node1, root1, path1, subpool, subpool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node2, root2, path2, subpool));
SVN_ERR(svn_fs_x__dag_things_different(changed_p, NULL, node1, node2,
strict, subpool));
svn_pool_destroy(subpool);
@@ -1639,9 +631,12 @@ x_props_changed(svn_boolean_t *changed_p
/* Set *NODE to the root node of ROOT. */
static svn_error_t *
-get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
+get_root(dag_node_t **node,
+ svn_fs_root_t *root,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- return get_dag(node, root, "/", pool);
+ return svn_fs_x__get_dag_node(node, root, "/", result_pool, scratch_pool);
}
@@ -1679,6 +674,15 @@ compare_dir_structure(svn_boolean_t *cha
SVN_ERR(svn_fs_x__dag_dir_entries(&rhs_entries, rhs, scratch_pool,
iterpool));
+ /* different number of entries -> some addition / removal */
+ if (lhs_entries->nelts != rhs_entries->nelts)
+ {
+ svn_pool_destroy(iterpool);
+ *changed = TRUE;
+
+ return SVN_NO_ERROR;
+ }
+
/* Since directories are sorted by name, we can simply compare their
entries one-by-one without binary lookup etc. */
for (i = 0; i < lhs_entries->nelts; ++i)
@@ -1690,7 +694,6 @@ compare_dir_structure(svn_boolean_t *cha
if (strcmp(lhs_entry->name, rhs_entry->name) == 0)
{
- svn_boolean_t same_history;
dag_node_t *lhs_node, *rhs_node;
/* Unchanged entry? */
@@ -1705,15 +708,15 @@ compare_dir_structure(svn_boolean_t *cha
iterpool, iterpool));
SVN_ERR(svn_fs_x__dag_get_node(&rhs_node, fs, &rhs_entry->id,
iterpool, iterpool));
- SVN_ERR(svn_fs_x__dag_same_line_of_history(&same_history,
- lhs_node, rhs_node));
- if (same_history)
+ if (svn_fs_x__dag_same_line_of_history(lhs_node, rhs_node))
continue;
}
/* This is a different entry. */
*changed = TRUE;
- break;
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
}
svn_pool_destroy(iterpool);
@@ -1948,15 +951,11 @@ merge(svn_stringbuf_t *conflict_p,
process, but the transaction did not touch this entry. */
else if (t_entry && svn_fs_x__id_eq(&a_entry->id, &t_entry->id))
{
- apr_int64_t mergeinfo_start;
- apr_int64_t mergeinfo_end;
-
dag_node_t *t_ent_node;
SVN_ERR(svn_fs_x__dag_get_node(&t_ent_node, fs, &t_entry->id,
iterpool, iterpool));
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_start,
- t_ent_node));
- mergeinfo_increment -= mergeinfo_start;
+ mergeinfo_increment
+ -= svn_fs_x__dag_get_mergeinfo_count(t_ent_node);
if (s_entry)
{
@@ -1964,9 +963,8 @@ merge(svn_stringbuf_t *conflict_p,
SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id,
iterpool, iterpool));
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_end,
- s_ent_node));
- mergeinfo_increment += mergeinfo_end;
+ mergeinfo_increment
+ += svn_fs_x__dag_get_mergeinfo_count(s_ent_node);
SVN_ERR(svn_fs_x__dag_set_entry(target, a_entry->name,
&s_entry->id,
@@ -1989,7 +987,6 @@ merge(svn_stringbuf_t *conflict_p,
dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
const char *new_tpath;
apr_int64_t sub_mergeinfo_increment;
- svn_boolean_t s_a_same, t_a_same;
/* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
double delete; if one of them is null, that's a delete versus
@@ -2019,11 +1016,8 @@ merge(svn_stringbuf_t *conflict_p,
/* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
modification of ANCESTOR-ENTRY, declare a conflict. */
- SVN_ERR(svn_fs_x__dag_same_line_of_history(&s_a_same, s_ent_node,
- a_ent_node));
- SVN_ERR(svn_fs_x__dag_same_line_of_history(&t_a_same, t_ent_node,
- a_ent_node));
- if (!s_a_same || !t_a_same)
+ if ( !svn_fs_x__dag_same_line_of_history(s_ent_node, a_ent_node)
+ || !svn_fs_x__dag_same_line_of_history(t_ent_node, a_ent_node))
return conflict_err(conflict_p,
svn_fspath__join(target_path,
a_entry->name,
@@ -2047,7 +1041,6 @@ merge(svn_stringbuf_t *conflict_p,
{
svn_fs_x__dirent_t *a_entry, *s_entry, *t_entry;
dag_node_t *s_ent_node;
- apr_int64_t mergeinfo_s;
svn_pool_clear(iterpool);
@@ -2068,8 +1061,7 @@ merge(svn_stringbuf_t *conflict_p,
SVN_ERR(svn_fs_x__dag_get_node(&s_ent_node, fs, &s_entry->id,
iterpool, iterpool));
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_s, s_ent_node));
- mergeinfo_increment += mergeinfo_s;
+ mergeinfo_increment += svn_fs_x__dag_get_mergeinfo_count(s_ent_node);
SVN_ERR(svn_fs_x__dag_set_entry
(target, s_entry->name, &s_entry->id, s_entry->kind,
@@ -2109,21 +1101,21 @@ merge_changes(dag_node_t *ancestor_node,
dag_node_t *txn_root_node;
svn_fs_t *fs = txn->fs;
svn_fs_x__txn_id_t txn_id = svn_fs_x__txn_get_id(txn);
- svn_boolean_t related;
- SVN_ERR(svn_fs_x__dag_txn_root(&txn_root_node, fs, txn_id, scratch_pool,
- scratch_pool));
+ SVN_ERR(svn_fs_x__dag_root(&txn_root_node, fs,
+ svn_fs_x__change_set_by_txn(txn_id),
+ scratch_pool, scratch_pool));
if (ancestor_node == NULL)
{
svn_revnum_t base_rev;
SVN_ERR(svn_fs_x__get_base_rev(&base_rev, fs, txn_id, scratch_pool));
- SVN_ERR(svn_fs_x__dag_revision_root(&ancestor_node, fs, base_rev,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__dag_root(&ancestor_node, fs,
+ svn_fs_x__change_set_by_rev(base_rev),
+ scratch_pool, scratch_pool));
}
- SVN_ERR(svn_fs_x__dag_related_node(&related, ancestor_node, txn_root_node));
- if (!related)
+ if (!svn_fs_x__dag_related_node(ancestor_node, txn_root_node))
{
/* If no changes have been made in TXN since its current base,
then it can't conflict with any changes since that base.
@@ -2223,7 +1215,8 @@ svn_fs_x__commit_txn(const char **confli
note that the youngest rev may have changed by then -- that's
why we're careful to get this root in its own bdb txn
here). */
- SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
+ SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool,
+ iterpool));
/* Try to merge. If the merge succeeds, the base root node of
TARGET's txn will become the same as youngish_root_node, so
@@ -2324,10 +1317,10 @@ x_merge(const char **conflict_p,
*/
/* Get the ancestor node. */
- SVN_ERR(get_root(&ancestor, ancestor_root, pool));
+ SVN_ERR(get_root(&ancestor, ancestor_root, pool, pool));
/* Get the source node. */
- SVN_ERR(get_root(&source, source_root, pool));
+ SVN_ERR(get_root(&source, source_root, pool, pool));
/* Open a txn for the txn root into which we're merging. */
SVN_ERR(svn_fs_x__open_txn(&txn, ancestor_root->fs, target_root->txn,
@@ -2378,7 +1371,7 @@ x_dir_entries(apr_hash_t **table_p,
apr_pool_t *scratch_pool = svn_pool_create(pool);
/* Get the entries for this path in the caller's pool. */
- SVN_ERR(get_dag(&node, root, path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
SVN_ERR(svn_fs_x__dag_dir_entries(&table, node, scratch_pool,
scratch_pool));
@@ -2410,9 +1403,11 @@ static svn_error_t *
x_dir_optimal_order(apr_array_header_t **ordered_p,
svn_fs_root_t *root,
apr_hash_t *entries,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- *ordered_p = svn_fs_x__order_dir_entries(root->fs, entries, pool);
+ *ordered_p = svn_fs_x__order_dir_entries(root->fs, entries, result_pool,
+ scratch_pool);
return SVN_NO_ERROR;
}
@@ -2426,14 +1421,14 @@ x_make_dir(svn_fs_root_t *root,
const char *path,
apr_pool_t *scratch_pool)
{
- parent_path_t *parent_path;
+ svn_fs_x__dag_path_t *dag_path;
dag_node_t *sub_dir;
- svn_fs_x__txn_id_t txn_id = root_txn_id(root);
+ svn_fs_x__txn_id_t txn_id = svn_fs_x__root_txn_id(root);
apr_pool_t *subpool = svn_pool_create(scratch_pool);
- path = svn_fs__canonicalize_abspath(path, subpool);
- SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
- TRUE, subpool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path,
+ svn_fs_x__dag_path_last_optional,
+ TRUE, subpool, subpool));
/* Check (recursively) to see if some lock is 'reserving' a path at
that location, or even some child-path; if so, check that we can
@@ -2444,23 +1439,22 @@ x_make_dir(svn_fs_root_t *root,
/* If there's already a sub-directory by that name, complain. This
also catches the case of trying to make a subdirectory named `/'. */
- if (parent_path->node)
+ if (dag_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
/* Create the subdirectory. */
- SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool,
- subpool));
+ SVN_ERR(svn_fs_x__make_path_mutable(root, dag_path->parent, path, subpool,
+ subpool));
SVN_ERR(svn_fs_x__dag_make_dir(&sub_dir,
- parent_path->parent->node,
- parent_path_path(parent_path->parent,
+ dag_path->parent->node,
+ parent_path_path(dag_path->parent,
subpool),
- parent_path->entry,
+ dag_path->entry,
txn_id,
subpool, subpool));
/* Add this directory to the path cache. */
- SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool),
- sub_dir, subpool));
+ svn_fs_x__update_dag_cache(sub_dir);
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(sub_dir),
@@ -2479,7 +1473,7 @@ x_delete_node(svn_fs_root_t *root,
const char *path,
apr_pool_t *scratch_pool)
{
- parent_path_t *parent_path;
+ svn_fs_x__dag_path_t *dag_path;
svn_fs_x__txn_id_t txn_id;
apr_int64_t mergeinfo_count = 0;
svn_node_kind_t kind;
@@ -2488,13 +1482,13 @@ x_delete_node(svn_fs_root_t *root,
if (! root->is_txn_root)
return SVN_FS__NOT_TXN(root);
- txn_id = root_txn_id(root);
- path = svn_fs__canonicalize_abspath(path, subpool);
- SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, subpool));
- kind = svn_fs_x__dag_node_kind(parent_path->node);
+ txn_id = svn_fs_x__root_txn_id(root);
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path, 0, TRUE, subpool,
+ subpool));
+ kind = svn_fs_x__dag_node_kind(dag_path->node);
/* We can't remove the root of the filesystem. */
- if (! parent_path->parent)
+ if (! dag_path->parent)
return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
_("The root directory cannot be deleted"));
@@ -2505,28 +1499,25 @@ x_delete_node(svn_fs_root_t *root,
subpool));
/* Make the parent directory mutable, and do the deletion. */
- SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool,
- subpool));
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_count,
- parent_path->node));
- SVN_ERR(svn_fs_x__dag_delete(parent_path->parent->node,
- parent_path->entry,
+ SVN_ERR(svn_fs_x__make_path_mutable(root, dag_path->parent, path, subpool,
+ subpool));
+ mergeinfo_count = svn_fs_x__dag_get_mergeinfo_count(dag_path->node);
+ SVN_ERR(svn_fs_x__dag_delete(dag_path->parent->node,
+ dag_path->entry,
txn_id, subpool));
/* Remove this node and any children from the path cache. */
- SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path,
- subpool),
- subpool));
+ svn_fs_x__invalidate_dag_cache(root, parent_path_path(dag_path, subpool));
/* Update mergeinfo counts for parents */
if (mergeinfo_count > 0)
- SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
+ SVN_ERR(increment_mergeinfo_up_tree(dag_path->parent,
-mergeinfo_count,
subpool));
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path,
- svn_fs_x__dag_get_id(parent_path->node),
+ svn_fs_x__dag_get_id(dag_path->node),
svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
SVN_INVALID_REVNUM, NULL, subpool));
@@ -2560,8 +1551,8 @@ copy_helper(svn_fs_root_t *from_root,
apr_pool_t *scratch_pool)
{
dag_node_t *from_node;
- parent_path_t *to_parent_path;
- svn_fs_x__txn_id_t txn_id = root_txn_id(to_root);
+ svn_fs_x__dag_path_t *to_dag_path;
+ svn_fs_x__txn_id_t txn_id = svn_fs_x__root_txn_id(to_root);
svn_boolean_t same_p;
/* Use an error check, not an assert, because even the caller cannot
@@ -2585,13 +1576,15 @@ copy_helper(svn_fs_root_t *from_root,
_("Copy immutable tree not supported"));
/* Get the NODE for FROM_PATH in FROM_ROOT.*/
- SVN_ERR(get_dag(&from_node, from_root, from_path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_node(&from_node, from_root, from_path,
+ scratch_pool, scratch_pool));
/* Build up the parent path from TO_PATH in TO_ROOT. If the last
component does not exist, it's not that big a deal. We'll just
make one there. */
- SVN_ERR(open_path(&to_parent_path, to_root, to_path,
- open_path_last_optional, TRUE, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_path(&to_dag_path, to_root, to_path,
+ svn_fs_x__dag_path_last_optional, TRUE,
+ scratch_pool, scratch_pool));
/* Check to see if path (or any child thereof) is locked; if so,
check that we can use the existing lock(s). */
@@ -2603,9 +1596,9 @@ copy_helper(svn_fs_root_t *from_root,
source (in other words, this operation would result in nothing
happening at all), just do nothing an return successfully,
proud that you saved yourself from a tiresome task. */
- if (to_parent_path->node &&
+ if (to_dag_path->node &&
svn_fs_x__id_eq(svn_fs_x__dag_get_id(from_node),
- svn_fs_x__dag_get_id(to_parent_path->node)))
+ svn_fs_x__dag_get_id(to_dag_path->node)))
return SVN_NO_ERROR;
if (! from_root->is_txn_root)
@@ -2618,11 +1611,11 @@ copy_helper(svn_fs_root_t *from_root,
/* If TO_PATH already existed prior to the copy, note that this
operation is a replacement, not an addition. */
- if (to_parent_path->node)
+ if (to_dag_path->node)
{
kind = svn_fs_path_change_replace;
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_start,
- to_parent_path->node));
+ mergeinfo_start
+ = svn_fs_x__dag_get_mergeinfo_count(to_dag_path->node);
}
else
{
@@ -2630,17 +1623,18 @@ copy_helper(svn_fs_root_t *from_root,
mergeinfo_start = 0;
}
- SVN_ERR(svn_fs_x__dag_get_mergeinfo_count(&mergeinfo_end, from_node));
+ mergeinfo_end = svn_fs_x__dag_get_mergeinfo_count(from_node);
/* Make sure the target node's parents are mutable. */
- SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
- to_path, scratch_pool, scratch_pool));
+ SVN_ERR(svn_fs_x__make_path_mutable(to_root, to_dag_path->parent,
+ to_path, scratch_pool,
+ scratch_pool));
/* Canonicalize the copyfrom path. */
from_canonpath = svn_fs__canonicalize_abspath(from_path, scratch_pool);
- SVN_ERR(svn_fs_x__dag_copy(to_parent_path->parent->node,
- to_parent_path->entry,
+ SVN_ERR(svn_fs_x__dag_copy(to_dag_path->parent->node,
+ to_dag_path->entry,
from_node,
preserve_history,
from_root->rev,
@@ -2648,18 +1642,18 @@ copy_helper(svn_fs_root_t *from_root,
txn_id, scratch_pool));
if (kind != svn_fs_path_change_add)
- SVN_ERR(dag_node_cache_invalidate(to_root,
- parent_path_path(to_parent_path,
- scratch_pool),
- scratch_pool));
+ svn_fs_x__invalidate_dag_cache(to_root,
+ parent_path_path(to_dag_path,
+ scratch_pool));
if (mergeinfo_start != mergeinfo_end)
- SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
+ SVN_ERR(increment_mergeinfo_up_tree(to_dag_path->parent,
mergeinfo_end - mergeinfo_start,
scratch_pool));
/* Make a record of this modification in the changes table. */
- SVN_ERR(get_dag(&new_node, to_root, to_path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_node(&new_node, to_root, to_path,
+ scratch_pool, scratch_pool));
SVN_ERR(add_change(to_root->fs, txn_id, to_path,
svn_fs_x__dag_get_id(new_node), kind, FALSE,
FALSE, FALSE, svn_fs_x__dag_node_kind(from_node),
@@ -2747,11 +1741,10 @@ x_copied_from(svn_revnum_t *rev_p,
{
dag_node_t *node;
- /* There is no cached entry, look it up the old-fashioned
- way. */
- SVN_ERR(get_dag(&node, root, path, pool));
- SVN_ERR(svn_fs_x__dag_get_copyfrom_rev(rev_p, node));
- SVN_ERR(svn_fs_x__dag_get_copyfrom_path(path_p, node));
+ /* There is no cached entry, look it up the old-fashioned way. */
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, pool));
+ *rev_p = svn_fs_x__dag_get_copyfrom_rev(node);
+ *path_p = svn_fs_x__dag_get_copyfrom_path(node);
return SVN_NO_ERROR;
}
@@ -2767,18 +1760,18 @@ x_make_file(svn_fs_root_t *root,
const char *path,
apr_pool_t *scratch_pool)
{
- parent_path_t *parent_path;
+ svn_fs_x__dag_path_t *dag_path;
dag_node_t *child;
- svn_fs_x__txn_id_t txn_id = root_txn_id(root);
+ svn_fs_x__txn_id_t txn_id = svn_fs_x__root_txn_id(root);
apr_pool_t *subpool = svn_pool_create(scratch_pool);
- path = svn_fs__canonicalize_abspath(path, subpool);
- SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
- TRUE, subpool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path,
+ svn_fs_x__dag_path_last_optional,
+ TRUE, subpool, subpool));
/* If there's already a file by that name, complain.
This also catches the case of trying to make a file named `/'. */
- if (parent_path->node)
+ if (dag_path->node)
return SVN_FS__ALREADY_EXISTS(root, path);
/* Check (non-recursively) to see if path is locked; if so, check
@@ -2788,19 +1781,18 @@ x_make_file(svn_fs_root_t *root,
subpool));
/* Create the file. */
- SVN_ERR(make_path_mutable(root, parent_path->parent, path, subpool,
- subpool));
+ SVN_ERR(svn_fs_x__make_path_mutable(root, dag_path->parent, path, subpool,
+ subpool));
SVN_ERR(svn_fs_x__dag_make_file(&child,
- parent_path->parent->node,
- parent_path_path(parent_path->parent,
+ dag_path->parent->node,
+ parent_path_path(dag_path->parent,
subpool),
- parent_path->entry,
+ dag_path->entry,
txn_id,
subpool, subpool));
/* Add this file to the path cache. */
- SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, subpool),
- child, subpool));
+ svn_fs_x__update_dag_cache(child);
/* Make a record of this modification in the changes table. */
SVN_ERR(add_change(root->fs, txn_id, path, svn_fs_x__dag_get_id(child),
@@ -2823,7 +1815,7 @@ x_file_length(svn_filesize_t *length_p,
dag_node_t *file;
/* First create a dag_node_t from the root/path pair. */
- SVN_ERR(get_dag(&file, root, path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&file, root, path, scratch_pool));
/* Now fetch its length */
return svn_fs_x__dag_file_length(length_p, file);
@@ -2842,7 +1834,7 @@ x_file_checksum(svn_checksum_t **checksu
{
dag_node_t *file;
- SVN_ERR(get_dag(&file, root, path, pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&file, root, path, pool));
return svn_fs_x__dag_file_checksum(checksum, file, kind, pool);
}
@@ -2861,7 +1853,7 @@ x_file_contents(svn_stream_t **contents,
svn_stream_t *file_stream;
/* First create a dag_node_t from the root/path pair. */
- SVN_ERR(get_dag(&node, root, path, pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, pool));
/* Then create a readable stream from the dag_node_t. */
SVN_ERR(svn_fs_x__dag_get_contents(&file_stream, node, pool));
@@ -2884,7 +1876,7 @@ x_try_process_file_contents(svn_boolean_
apr_pool_t *pool)
{
dag_node_t *node;
- SVN_ERR(get_dag(&node, root, path, pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, pool));
return svn_fs_x__dag_try_process_file_contents(success, node,
processor, baton, pool);
@@ -2955,12 +1947,13 @@ apply_textdelta(void *baton,
apr_pool_t *scratch_pool)
{
txdelta_baton_t *tb = (txdelta_baton_t *) baton;
- parent_path_t *parent_path;
- svn_fs_x__txn_id_t txn_id = root_txn_id(tb->root);
+ svn_fs_x__dag_path_t *dag_path;
+ svn_fs_x__txn_id_t txn_id = svn_fs_x__root_txn_id(tb->root);
/* Call open_path with no flags, as we want this to return an error
if the node for which we are searching doesn't exist. */
- SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, tb->root, tb->path, 0, TRUE,
+ scratch_pool, scratch_pool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
@@ -2969,9 +1962,9 @@ apply_textdelta(void *baton,
FALSE, FALSE, scratch_pool));
/* Now, make sure this path is mutable. */
- SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, scratch_pool,
- scratch_pool));
- tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool);
+ SVN_ERR(svn_fs_x__make_path_mutable(tb->root, dag_path, tb->path,
+ scratch_pool, scratch_pool));
+ tb->node = svn_fs_x__dag_dup(dag_path->node, tb->pool);
if (tb->base_checksum)
{
@@ -3121,12 +2114,13 @@ apply_text(void *baton,
apr_pool_t *scratch_pool)
{
text_baton_t *tb = baton;
- parent_path_t *parent_path;
- svn_fs_x__txn_id_t txn_id = root_txn_id(tb->root);
+ svn_fs_x__dag_path_t *dag_path;
+ svn_fs_x__txn_id_t txn_id = svn_fs_x__root_txn_id(tb->root);
/* Call open_path with no flags, as we want this to return an error
if the node for which we are searching doesn't exist. */
- SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, tb->root, tb->path, 0, TRUE,
+ scratch_pool, scratch_pool));
/* Check (non-recursively) to see if path is locked; if so, check
that we can use it. */
@@ -3135,9 +2129,9 @@ apply_text(void *baton,
FALSE, FALSE, scratch_pool));
/* Now, make sure this path is mutable. */
- SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, scratch_pool,
- scratch_pool));
- tb->node = svn_fs_x__dag_dup(parent_path->node, tb->pool);
+ SVN_ERR(svn_fs_x__make_path_mutable(tb->root, dag_path, tb->path,
+ scratch_pool, scratch_pool));
+ tb->node = svn_fs_x__dag_dup(dag_path->node, tb->pool);
/* Make a writable stream for replacing the file's text. */
SVN_ERR(svn_fs_x__dag_get_edit_stream(&(tb->file_stream), tb->node,
@@ -3221,8 +2215,8 @@ x_contents_changed(svn_boolean_t *change
(SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
}
- SVN_ERR(get_dag(&node1, root1, path1, subpool));
- SVN_ERR(get_dag(&node2, root2, path2, subpool));
+ SVN_ERR(svn_fs_x__get_dag_node(&node1, root1, path1, subpool, subpool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node2, root2, path2, subpool));
SVN_ERR(svn_fs_x__dag_things_different(NULL, changed_p, node1, node2,
strict, subpool));
@@ -3246,10 +2240,12 @@ x_get_file_delta_stream(svn_txdelta_stre
apr_pool_t *scratch_pool = svn_pool_create(pool);
if (source_root && source_path)
- SVN_ERR(get_dag(&source_node, source_root, source_path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_node(&source_node, source_root, source_path,
+ scratch_pool, scratch_pool));
else
source_node = NULL;
- SVN_ERR(get_dag(&target_node, target_root, target_path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&target_node, target_root, target_path,
+ scratch_pool));
/* Create a delta stream that turns the source into the target. */
SVN_ERR(svn_fs_x__dag_get_file_delta_stream(stream_p, source_node,
@@ -3311,7 +2307,8 @@ x_paths_changed(apr_hash_t **changed_pat
{
apr_hash_index_t *hi;
SVN_ERR(svn_fs_x__txn_changes_fetch(&changed_paths, root->fs,
- root_txn_id(root), pool));
+ svn_fs_x__root_txn_id(root),
+ pool));
for (hi = apr_hash_first(pool, changed_paths);
hi;
hi = apr_hash_next(hi))
@@ -3366,6 +2363,15 @@ typedef struct fs_history_data_t
/* FALSE until the first call to svn_fs_history_prev(). */
svn_boolean_t is_interesting;
+
+ /* If not SVN_INVALID_REVISION, we know that the next copy operation
+ is at this revision. */
+ svn_revnum_t next_copy;
+
+ /* If used, see svn_fs_x__id_used, this is the noderev ID of
+ PATH@REVISION. */
+ svn_fs_x__id_t current_id;
+
} fs_history_data_t;
static svn_fs_history_t *
@@ -3375,6 +2381,8 @@ assemble_history(svn_fs_t *fs,
svn_boolean_t is_interesting,
const char *path_hint,
svn_revnum_t rev_hint,
+ svn_revnum_t next_copy,
+ const svn_fs_x__id_t *current_id,
apr_pool_t *result_pool);
@@ -3401,17 +2409,18 @@ x_node_history(svn_fs_history_t **histor
/* Okay, all seems well. Build our history object and return it. */
*history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
- SVN_INVALID_REVNUM, result_pool);
+ SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
+ NULL, result_pool);
return SVN_NO_ERROR;
}
-/* Find the youngest copyroot for path PARENT_PATH or its parents in
+/* Find the youngest copyroot for path DAG_PATH or its parents in
filesystem FS, and store the copyroot in *REV_P and *PATH_P. */
static svn_error_t *
find_youngest_copyroot(svn_revnum_t *rev_p,
const char **path_p,
svn_fs_t *fs,
- parent_path_t *parent_path)
+ svn_fs_x__dag_path_t *dag_path)
{
svn_revnum_t rev_mine;
svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
@@ -3419,13 +2428,12 @@ find_youngest_copyroot(svn_revnum_t *rev
const char *path_parent = NULL;
/* First find our parent's youngest copyroot. */
- if (parent_path->parent)
+ if (dag_path->parent)
SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
- parent_path->parent));
+ dag_path->parent));
/* Find our copyroot. */
- SVN_ERR(svn_fs_x__dag_get_copyroot(&rev_mine, &path_mine,
- parent_path->node));
+ svn_fs_x__dag_get_copyroot(&rev_mine, &path_mine, dag_path->node);
/* If a parent and child were copied to in the same revision, prefer
the child copy target, since it is the copy relevant to the
@@ -3453,26 +2461,25 @@ x_closest_copy(svn_fs_root_t **root_p,
apr_pool_t *pool)
{
svn_fs_t *fs = root->fs;
- parent_path_t *parent_path, *copy_dst_parent_path;
+ svn_fs_x__dag_path_t *dag_path, *copy_dst_dag_path;
svn_revnum_t copy_dst_rev, created_rev;
const char *copy_dst_path;
svn_fs_root_t *copy_dst_root;
dag_node_t *copy_dst_node;
- svn_boolean_t related;
apr_pool_t *scratch_pool = svn_pool_create(pool);
/* Initialize return values. */
*root_p = NULL;
*path_p = NULL;
- path = svn_fs__canonicalize_abspath(path, scratch_pool);
- SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path, 0, FALSE,
+ scratch_pool, scratch_pool));
/* Find the youngest copyroot in the path of this node-rev, which
will indicate the target of the innermost copy affecting the
node-rev. */
SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
- fs, parent_path));
+ fs, dag_path));
if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
{
svn_pool_destroy(scratch_pool);
@@ -3483,19 +2490,17 @@ x_closest_copy(svn_fs_root_t **root_p,
revision between COPY_DST_REV and REV. Make sure that PATH
exists as of COPY_DST_REV and is related to this node-rev. */
SVN_ERR(svn_fs_x__revision_root(©_dst_root, fs, copy_dst_rev, pool));
- SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
- open_path_node_only | open_path_allow_null, FALSE,
- scratch_pool));
- if (copy_dst_parent_path == NULL)
+ SVN_ERR(svn_fs_x__get_dag_path(©_dst_dag_path, copy_dst_root, path,
+ svn_fs_x__dag_path_allow_null, FALSE,
+ scratch_pool, scratch_pool));
+ if (copy_dst_dag_path == NULL)
{
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
- copy_dst_node = copy_dst_parent_path->node;
- SVN_ERR(svn_fs_x__dag_related_node(&related, copy_dst_node,
- parent_path->node));
- if (!related)
+ copy_dst_node = copy_dst_dag_path->node;
+ if (!svn_fs_x__dag_related_node(copy_dst_node, dag_path->node))
{
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
@@ -3517,15 +2522,11 @@ x_closest_copy(svn_fs_root_t **root_p,
*/
created_rev = svn_fs_x__dag_get_revision(copy_dst_node);
if (created_rev == copy_dst_rev)
- {
- svn_fs_x__id_t pred;
- SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred, copy_dst_node));
- if (!svn_fs_x__id_used(&pred))
- {
- svn_pool_destroy(scratch_pool);
- return SVN_NO_ERROR;
- }
- }
+ if (!svn_fs_x__id_used(svn_fs_x__dag_get_predecessor_id(copy_dst_node)))
+ {
+ svn_pool_destroy(scratch_pool);
+ return SVN_NO_ERROR;
+ }
/* The copy destination checks out. Return it. */
*root_p = copy_dst_root;
@@ -3545,10 +2546,8 @@ x_node_origin_rev(svn_revnum_t *revision
svn_fs_x__id_t node_id;
dag_node_t *node;
- path = svn_fs__canonicalize_abspath(path, scratch_pool);
-
- SVN_ERR(get_dag(&node, root, path, scratch_pool));
- SVN_ERR(svn_fs_x__dag_get_node_id(&node_id, node));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, root, path, scratch_pool));
+ node_id = *svn_fs_x__dag_get_node_id(node);
*revision = svn_fs_x__get_revnum(node_id.change_set);
@@ -3568,16 +2567,56 @@ history_prev(svn_fs_history_t **prev_his
svn_revnum_t commit_rev, src_rev, dst_rev;
svn_revnum_t revision = fhd->revision;
svn_fs_t *fs = fhd->fs;
- parent_path_t *parent_path;
+ svn_fs_x__dag_path_t *dag_path;
dag_node_t *node;
svn_fs_root_t *root;
svn_boolean_t reported = fhd->is_interesting;
svn_revnum_t copyroot_rev;
const char *copyroot_path;
+ svn_fs_x__id_t pred_id;
/* Initialize our return value. */
*prev_history = NULL;
+ /* When following history, there tend to be long sections of linear
+ history where there are no copies at PATH or its parents. Within
+ these sections, we only need to follow the node history. */
+ if ( SVN_IS_VALID_REVNUM(fhd->next_copy)
+ && revision > fhd->next_copy
+ && svn_fs_x__id_used(&fhd->current_id))
+ {
+ /* We know the last reported node (CURRENT_ID) and the NEXT_COPY
+ revision is somewhat further in the past. */
+ svn_fs_x__noderev_t *noderev;
+ assert(reported);
+
+ /* Get the previous node change. If there is none, then we already
+ reported the initial addition and this history traversal is done. */
+ SVN_ERR(svn_fs_x__get_node_revision(&noderev, fs, &fhd->current_id,
+ scratch_pool, scratch_pool));
+ if (! svn_fs_x__id_used(&noderev->predecessor_id))
+ return SVN_NO_ERROR;
+
+ /* If the previous node change is younger than the next copy, it is
+ part of the linear history section. */
+ commit_rev = svn_fs_x__get_revnum(noderev->predecessor_id.change_set);
+ if (commit_rev > fhd->next_copy)
+ {
+ /* Within the linear history, simply report all node changes and
+ continue with the respective predecessor. */
+ *prev_history = assemble_history(fs, noderev->created_path,
+ commit_rev, TRUE, NULL,
+ SVN_INVALID_REVNUM,
+ fhd->next_copy,
+ &noderev->predecessor_id,
+ result_pool);
+
+ return SVN_NO_ERROR;
+ }
+
+ /* We hit a copy. Fall back to the standard code path. */
+ }
+
/* If our last history report left us hints about where to pickup
the chase, then our last report was on the destination of a
copy. If we are crossing copies, start from those locations,
@@ -3596,10 +2635,12 @@ history_prev(svn_fs_history_t **prev_his
/* Open PATH/REVISION, and get its node and a bunch of other
goodies. */
- SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
- node = parent_path->node;
+ SVN_ERR(svn_fs_x__get_dag_path(&dag_path, root, path, 0, FALSE,
+ scratch_pool, scratch_pool));
+ node = dag_path->node;
commit_path = svn_fs_x__dag_get_created_path(node);
commit_rev = svn_fs_x__dag_get_revision(node);
+ svn_fs_x__id_reset(&pred_id);
/* The Subversion filesystem is written in such a way that a given
line of history may have at most one interesting history point
@@ -3616,7 +2657,9 @@ history_prev(svn_fs_history_t **prev_his
need now to do so) ... */
*prev_history = assemble_history(fs, commit_path,
commit_rev, TRUE, NULL,
- SVN_INVALID_REVNUM, result_pool);
+ SVN_INVALID_REVNUM,
+ SVN_INVALID_REVNUM, NULL,
+ result_pool);
return SVN_NO_ERROR;
}
else
@@ -3624,9 +2667,7 @@ history_prev(svn_fs_history_t **prev_his
/* ... or we *have* reported on this revision, and must now
progress toward this node's predecessor (unless there is
no predecessor, in which case we're all done!). */
- svn_fs_x__id_t pred_id;
-
- SVN_ERR(svn_fs_x__dag_get_predecessor_id(&pred_id, node));
+ pred_id = *svn_fs_x__dag_get_predecessor_id(node);
if (!svn_fs_x__id_used(&pred_id))
return SVN_NO_ERROR;
@@ -3642,7 +2683,7 @@ history_prev(svn_fs_history_t **prev_his
/* Find the youngest copyroot in the path of this node, including
itself. */
SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
- parent_path));
+ dag_path));
/* Initialize some state variables. */
src_path = NULL;
@@ -3657,7 +2698,8 @@ history_prev(svn_fs_history_t **prev_his
SVN_ERR(svn_fs_x__revision_root(©root_root, fs, copyroot_rev,
scratch_pool));
- SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
+ SVN_ERR(svn_fs_x__get_temp_dag_node(&node, copyroot_root,
+ copyroot_path, scratch_pool));
copy_dst = svn_fs_x__dag_get_created_path(node);
/* If our current path was the very destination of the copy,
@@ -3675,8 +2717,8 @@ history_prev(svn_fs_history_t **prev_his
/* If we get here, then our current path is the destination
of, or the child of the destination of, a copy. Fill
in the return values and get outta here. */
- SVN_ERR(svn_fs_x__dag_get_copyfrom_rev(&src_rev, node));
- SVN_ERR(svn_fs_x__dag_get_copyfrom_path(©_src, node));
+ src_rev = svn_fs_x__dag_get_copyfrom_rev(node);
+ copy_src = svn_fs_x__dag_get_copyfrom_path(node);
dst_rev = copyroot_rev;
src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
@@ -3697,12 +2739,18 @@ history_prev(svn_fs_history_t **prev_his
retry = TRUE;
*prev_history = assemble_history(fs, path, dst_rev, ! retry,
- src_path, src_rev, result_pool);
+ src_path, src_rev,
+ SVN_INVALID_REVNUM, NULL,
+ result_pool);
}
else
[... 337 lines stripped ...]