1 /* tree.c : tree-like filesystem, built on DAG filesystem
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
24 /* The job of this layer is to take a filesystem with lots of node
25 sharing going on --- the real DAG filesystem as it appears in the
26 database --- and make it look and act like an ordinary tree
27 filesystem, with no sharing.
29 We do just-in-time cloning: you can walk from some unfinished
30 transaction's root down into directories and files shared with
31 committed revisions; as soon as you try to change something, the
32 appropriate nodes get cloned (and parent directory entries updated)
33 invisibly, behind your back. Any other references you have to
34 nodes that have been cloned by other changes, even made by other
35 processes, are automatically updated to point to the right clones. */
41 #include <apr_pools.h>
45 #include "svn_private_config.h"
46 #include "svn_pools.h"
47 #include "svn_error.h"
49 #include "svn_mergeinfo.h"
51 #include "svn_props.h"
52 #include "svn_sorts.h"
55 #include "cached_data.h"
62 #include "temp_serializer.h"
63 #include "transaction.h"
66 #include "private/svn_mergeinfo_private.h"
67 #include "private/svn_subr_private.h"
68 #include "private/svn_fs_util.h"
69 #include "private/svn_fspath.h"
70 #include "../libsvn_fs/fs-loader.h"
74 /* The root structures.
76 Why do they contain different data? Well, transactions are mutable
77 enough that it isn't safe to cache the DAG node for the root
78 directory or the hash of copyfrom data: somebody else might modify
79 them concurrently on disk! (Why is the DAG node cache safer than
80 the root DAG node? When cloning transaction DAG nodes in and out
81 of the cache, all of the possibly-mutable data from the
82 node_revision_t inside the dag_node_t is dropped.) Additionally,
83 revisions are immutable enough that their DAG node cache can be
84 kept in the FS object and shared among multiple revision root
87 typedef dag_node_t fs_rev_root_data_t;
89 typedef struct fs_txn_root_data_t
91 /* TXN_ID value from the main struct but as a struct instead of a string */
92 svn_fs_fs__id_part_t txn_id;
94 /* Cache of txn DAG nodes (without their nested noderevs, because
95 * it's mutable). Same keys/values as ffd->rev_node_cache. */
96 svn_cache__t *txn_node_cache;
99 /* Declared here to resolve the circular dependencies. */
100 static svn_error_t * get_dag(dag_node_t **dag_node_p,
105 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
106 dag_node_t *root_dir,
109 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
111 const svn_fs_fs__id_part_t *txn,
112 svn_revnum_t base_rev,
116 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
123 /*** Node Caching ***/
125 /* 1st level cache */
127 /* An entry in the first-level cache. REVISION and PATH form the key that
128 will ultimately be matched.
130 typedef struct cache_entry_t
132 /* hash value derived from PATH, REVISION.
133 Used to short-circuit failed lookups. */
134 apr_uint32_t hash_value;
136 /* revision to which the NODE belongs */
137 svn_revnum_t revision;
139 /* path of the NODE */
142 /* cached value of strlen(PATH). */
145 /* the node allocated in the cache's pool. NULL for empty entries. */
149 /* Number of entries in the cache. Keep this low to keep pressure on the
150 CPU caches low as well. A binary value is most efficient. If we walk
151 a directory tree, we want enough entries to store nodes for all files
152 without overwriting the nodes for the parent folder. That way, there
153 will be no unnecessary misses (except for a few random ones caused by
156 The actual number of instances may be higher but entries that got
157 overwritten are no longer visible.
159 enum { BUCKET_COUNT = 256 };
161 /* The actual cache structure. All nodes will be allocated in POOL.
162 When the number of INSERTIONS (i.e. objects created form that pool)
163 exceeds a certain threshold, the pool will be cleared and the cache
166 struct fs_fs_dag_cache_t
168 /* fixed number of (possibly empty) cache entries */
169 cache_entry_t buckets[BUCKET_COUNT];
171 /* pool used for all node allocation */
174 /* number of entries created from POOL since the last cleanup */
175 apr_size_t insertions;
177 /* Property lookups etc. have a very high locality (75% re-hit).
178 Thus, remember the last hit location for optimistic lookup. */
181 /* Position of the last bucket hit that actually had a DAG node in it.
182 LAST_HIT may refer to a bucket that matches path@rev but has not
183 its NODE element set, yet.
184 This value is a mere hint for optimistic lookup and any value is
185 valid (as long as it is < BUCKET_COUNT). */
186 apr_size_t last_non_empty;
190 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
192 fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
193 result->pool = svn_pool_create(pool);
198 /* Clears the CACHE at regular intervals (destroying all cached nodes)
201 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
203 if (cache->insertions > BUCKET_COUNT)
205 svn_pool_clear(cache->pool);
207 memset(cache->buckets, 0, sizeof(cache->buckets));
208 cache->insertions = 0;
212 /* Returns a 32 bit hash value for the given REVISION and PATH of exactly
216 hash_func(svn_revnum_t revision,
221 apr_uint32_t hash_value = (apr_uint32_t)revision;
223 #if SVN_UNALIGNED_ACCESS_IS_OK
224 /* "randomizing" / distributing factor used in our hash function */
225 const apr_uint32_t factor = 0xd1f3da69;
228 /* Calculate the hash value
229 (HASH_VALUE has been initialized to REVISION).
231 Note that the actual hash function is arbitrary as long as its result
232 in HASH_VALUE only depends on REVISION and *PATH. However, we try to
233 make as much of *PATH influence the result as possible to get an "even"
234 spread across the hash buckets (maximizes our cache retention rate and
237 When chunked access is possible (independent of the PATH pointer's
238 value!), we read 4 bytes at once and multiply the hash value with a
239 FACTOR that mirror / pattern / shift all 4 input bytes to various bits
240 of the result. The final result will be taken from the MSBs.
242 When chunked access is not possible (not supported by CPU or odd bytes
243 at the end of *PATH), we use the simple traditional "* 33" hash
244 function that works very well with texts / paths and that e.g. APR uses.
246 Please note that the bytewise and the chunked calculation are *NOT*
247 interchangeable as they will yield different results for the same input.
248 For any given machine and *PATH, we must use a fixed combination of the
252 #if SVN_UNALIGNED_ACCESS_IS_OK
253 /* We relax the dependency chain between iterations by processing
254 two chunks from the input per hash_value self-multiplication.
255 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
256 per 2 chunks instead of 1 chunk.
258 for (; i + 8 <= path_len; i += 8)
259 hash_value = hash_value * factor * factor
260 + ( *(const apr_uint32_t*)(path + i) * factor
261 + *(const apr_uint32_t*)(path + i + 4));
264 for (; i < path_len; ++i)
265 /* Help GCC to minimize the HASH_VALUE update latency by splitting the
266 MUL 33 of the naive implementation: h = h * 33 + path[i]. This
267 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
269 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
274 /* For the given REVISION and PATH, return the respective node found in
275 * CACHE. If there is none, return NULL.
278 cache_lookup( fs_fs_dag_cache_t *cache
279 , svn_revnum_t revision
282 apr_size_t bucket_index;
283 apr_size_t path_len = strlen(path);
284 apr_uint32_t hash_value;
286 /* optimistic lookup: hit the same bucket again? */
287 cache_entry_t *result = &cache->buckets[cache->last_hit];
288 if ( (result->revision == revision)
289 && (result->path_len == path_len)
290 && !memcmp(result->path, path, path_len))
292 /* Remember the position of the last node we found in this cache. */
294 cache->last_non_empty = cache->last_hit;
299 /* need to do a full lookup. */
300 hash_value = hash_func(revision, path, path_len);
302 bucket_index = hash_value + (hash_value >> 16);
303 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
305 /* access the corresponding bucket and remember its location */
306 result = &cache->buckets[bucket_index];
307 cache->last_hit = bucket_index;
309 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
310 in the node and count it as an insertion */
311 if ( (result->hash_value != hash_value)
312 || (result->revision != revision)
313 || (result->path_len != path_len)
314 || memcmp(result->path, path, path_len))
318 else if (result->node)
320 /* This bucket is valid & has a suitable DAG node in it.
321 Remember its location. */
322 cache->last_non_empty = bucket_index;
328 /* Store a copy of NODE in CACHE, taking REVISION and PATH as key.
329 * This function will clean the cache at regular intervals.
332 cache_insert(fs_fs_dag_cache_t *cache,
333 svn_revnum_t revision,
337 apr_size_t bucket_index;
338 apr_size_t path_len = strlen(path);
339 apr_uint32_t hash_value;
340 cache_entry_t *entry;
342 auto_clear_dag_cache(cache);
344 /* calculate the bucket index to use */
345 hash_value = hash_func(revision, path, path_len);
347 bucket_index = hash_value + (hash_value >> 16);
348 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
350 /* access the corresponding bucket and remember its location */
351 entry = &cache->buckets[bucket_index];
352 cache->last_hit = bucket_index;
354 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
355 in the node and count it as an insertion */
356 entry->hash_value = hash_value;
357 entry->revision = revision;
358 if (entry->path_len < path_len)
359 entry->path = apr_palloc(cache->pool, path_len + 1);
360 entry->path_len = path_len;
361 memcpy(entry->path, path, path_len + 1);
363 entry->node = svn_fs_fs__dag_dup(node, cache->pool);
367 /* Optimistic lookup using the last seen non-empty location in CACHE.
368 Return the node of that entry, if it is still in use and matches PATH.
369 Return NULL otherwise. Since the caller usually already knows the path
370 length, provide it in PATH_LEN. */
372 cache_lookup_last_path(fs_fs_dag_cache_t *cache,
376 cache_entry_t *result = &cache->buckets[cache->last_non_empty];
377 assert(strlen(path) == path_len);
380 && (result->path_len == path_len)
381 && !memcmp(result->path, path, path_len))
389 /* 2nd level cache */
391 /* Find and return the DAG node cache for ROOT and the key that
392 should be used for PATH.
394 Pool will only be used for allocating a new keys if necessary */
396 locate_cache(svn_cache__t **cache,
402 if (root->is_txn_root)
404 fs_txn_root_data_t *frd = root->fsap_data;
407 *cache = frd->txn_node_cache;
413 fs_fs_data_t *ffd = root->fs->fsap_data;
416 *cache = ffd->rev_node_cache;
418 *key = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
422 /* In *NODE_P, return the DAG node for PATH from ROOT's node cache, or NULL
423 if the node isn't cached. *NODE_P is allocated in POOL. */
425 dag_node_cache_get(dag_node_t **node_p,
431 dag_node_t *node = NULL;
435 SVN_ERR_ASSERT(*path == '/');
437 if (!root->is_txn_root)
439 /* immutable DAG node. use the global caches for it */
441 fs_fs_data_t *ffd = root->fs->fsap_data;
443 node = cache_lookup(ffd->dag_node_cache, root->rev, path);
446 locate_cache(&cache, &key, root, path, pool);
447 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
450 /* Patch up the FS, since this might have come from an old FS
452 svn_fs_fs__dag_set_fs(node, root->fs);
454 /* Retain the DAG node in L1 cache. */
455 cache_insert(ffd->dag_node_cache, root->rev, path, node);
460 /* Copy the node from L1 cache into the passed-in POOL. */
461 node = svn_fs_fs__dag_dup(node, pool);
466 /* DAG is mutable / may become invalid. Use the TXN-local cache */
468 locate_cache(&cache, &key, root, path, pool);
470 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
473 /* Patch up the FS, since this might have come from an old FS
475 svn_fs_fs__dag_set_fs(node, root->fs);
485 /* Add the NODE for PATH to ROOT's node cache. */
487 dag_node_cache_set(svn_fs_root_t *root,
495 SVN_ERR_ASSERT(*path == '/');
497 locate_cache(&cache, &key, root, path, pool);
498 return svn_cache__set(cache, key, node, pool);
502 /* Baton for find_descendants_in_cache. */
505 apr_array_header_t *list;
509 /* If the given item is a descendant of BATON->PATH, push
510 * it onto BATON->LIST (copying into BATON->POOL). Implements
511 * the svn_iter_apr_hash_cb_t prototype. */
513 find_descendants_in_cache(void *baton,
519 struct fdic_baton *b = baton;
520 const char *item_path = key;
522 if (svn_fspath__skip_ancestor(b->path, item_path))
523 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
528 /* Invalidate cache entries for PATH and any of its children. This
529 should *only* be called on a transaction root! */
531 dag_node_cache_invalidate(svn_fs_root_t *root,
537 apr_pool_t *iterpool;
541 b.pool = svn_pool_create(pool);
542 b.list = apr_array_make(b.pool, 1, sizeof(const char *));
544 SVN_ERR_ASSERT(root->is_txn_root);
545 locate_cache(&cache, NULL, root, NULL, b.pool);
548 SVN_ERR(svn_cache__iter(NULL, cache, find_descendants_in_cache,
551 iterpool = svn_pool_create(b.pool);
553 for (i = 0; i < b.list->nelts; i++)
555 const char *descendant = APR_ARRAY_IDX(b.list, i, const char *);
556 svn_pool_clear(iterpool);
557 SVN_ERR(svn_cache__set(cache, descendant, NULL, iterpool));
560 svn_pool_destroy(iterpool);
561 svn_pool_destroy(b.pool);
567 /* Creating transaction and revision root nodes. */
570 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
574 apr_uint32_t flags = 0;
575 apr_hash_t *txnprops;
577 /* Look for the temporary txn props representing 'flags'. */
578 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
581 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
582 flags |= SVN_FS_TXN_CHECK_OOD;
584 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
585 flags |= SVN_FS_TXN_CHECK_LOCKS;
588 return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
589 txn->base_rev, flags, pool);
594 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
599 dag_node_t *root_dir;
601 SVN_ERR(svn_fs__check_fs(fs, TRUE));
603 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
605 *root_p = make_revision_root(fs, rev, root_dir, pool);
612 /* Getting dag nodes for roots. */
614 /* Return the transaction ID to a given transaction ROOT. */
615 static const svn_fs_fs__id_part_t *
616 root_txn_id(svn_fs_root_t *root)
618 fs_txn_root_data_t *frd = root->fsap_data;
619 assert(root->is_txn_root);
624 /* Set *NODE_P to a freshly opened dag node referring to the root
625 directory of ROOT, allocating from POOL. */
627 root_node(dag_node_t **node_p,
631 if (root->is_txn_root)
633 /* It's a transaction root. Open a fresh copy. */
634 return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
639 /* It's a revision root, so we already have its root directory
641 dag_node_t *root_dir = root->fsap_data;
642 *node_p = svn_fs_fs__dag_dup(root_dir, pool);
648 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
649 necessary, allocating in POOL. ROOT must be a transaction root.
650 Use ERROR_PATH in error messages. */
652 mutable_root_node(dag_node_t **node_p,
654 const char *error_path,
657 if (root->is_txn_root)
659 /* It's a transaction root. Open a fresh copy. */
660 return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
664 /* If it's not a transaction root, we can't change its contents. */
665 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
670 /* Traversing directory paths. */
672 typedef enum copy_id_inherit_t
674 copy_id_inherit_unknown = 0,
675 copy_id_inherit_self,
676 copy_id_inherit_parent,
681 /* A linked list representing the path from a node up to a root
682 directory. We use this for cloning, and for operations that need
683 to deal with both a node and its parent directory. For example, a
684 `delete' operation needs to know that the node actually exists, but
685 also needs to change the parent directory. */
686 typedef struct parent_path_t
689 /* A node along the path. This could be the final node, one of its
690 parents, or the root. Every parent path ends with an element for
691 the root directory. */
694 /* The name NODE has in its parent directory. This is zero for the
695 root directory, which (obviously) has no name in its parent. */
698 /* The parent of NODE, or zero if NODE is the root directory. */
699 struct parent_path_t *parent;
701 /* The copy ID inheritance style. */
702 copy_id_inherit_t copy_inherit;
704 /* If copy ID inheritance style is copy_id_inherit_new, this is the
705 path which should be implicitly copied; otherwise, this is NULL. */
706 const char *copy_src_path;
710 /* Return a text string describing the absolute path of parent_path
711 PARENT_PATH. It will be allocated in POOL. */
713 parent_path_path(parent_path_t *parent_path,
716 const char *path_so_far = "/";
717 if (parent_path->parent)
718 path_so_far = parent_path_path(parent_path->parent, pool);
719 return parent_path->entry
720 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
725 /* Return the FS path for the parent path chain object CHILD relative
726 to its ANCESTOR in the same chain, allocated in POOL. */
728 parent_path_relpath(parent_path_t *child,
729 parent_path_t *ancestor,
732 const char *path_so_far = "";
733 parent_path_t *this_node = child;
734 while (this_node != ancestor)
736 assert(this_node != NULL);
737 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
738 this_node = this_node->parent;
745 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
746 event that immutable node CHILD in FS needs to be made mutable. If
747 the inheritance method is copy_id_inherit_new, also return a
748 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
749 for that path). CHILD must have a parent (it cannot be the root
750 node). Allocations are taken from POOL. */
752 get_copy_inheritance(copy_id_inherit_t *inherit_p,
753 const char **copy_src_path,
755 parent_path_t *child,
758 const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
759 const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
760 const char *id_path = NULL;
761 svn_fs_root_t *copyroot_root;
762 dag_node_t *copyroot_node;
763 svn_revnum_t copyroot_rev;
764 const char *copyroot_path;
766 SVN_ERR_ASSERT(child && child->parent);
768 /* Initialize some convenience variables. */
769 child_id = svn_fs_fs__dag_get_id(child->node);
770 parent_id = svn_fs_fs__dag_get_id(child->parent->node);
771 child_copy_id = svn_fs_fs__id_copy_id(child_id);
772 parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
774 /* If this child is already mutable, we have nothing to do. */
775 if (svn_fs_fs__id_is_txn(child_id))
777 *inherit_p = copy_id_inherit_self;
778 *copy_src_path = NULL;
782 /* From this point on, we'll assume that the child will just take
783 its copy ID from its parent. */
784 *inherit_p = copy_id_inherit_parent;
785 *copy_src_path = NULL;
787 /* Special case: if the child's copy ID is '0', use the parent's
789 if (svn_fs_fs__id_part_is_root(child_copy_id))
792 /* Compare the copy IDs of the child and its parent. If they are
793 the same, then the child is already on the same branch as the
794 parent, and should use the same mutability copy ID that the
796 if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
799 /* If the child is on the same branch that the parent is on, the
800 child should just use the same copy ID that the parent would use.
801 Else, the child needs to generate a new copy ID to use should it
802 need to be made mutable. We will claim that child is on the same
803 branch as its parent if the child itself is not a branch point,
804 or if it is a branch point that we are accessing via its original
805 copy destination path. */
806 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
808 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
809 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
810 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
812 if (svn_fs_fs__id_compare(copyroot_id, child_id) == svn_fs_node_unrelated)
815 /* Determine if we are looking at the child via its original path or
816 as a subtree item of a copied tree. */
817 id_path = svn_fs_fs__dag_get_created_path(child->node);
818 if (strcmp(id_path, parent_path_path(child, pool)) == 0)
820 *inherit_p = copy_id_inherit_self;
824 /* We are pretty sure that the child node is an unedited nested
825 branched node. When it needs to be made mutable, it should claim
827 *inherit_p = copy_id_inherit_new;
828 *copy_src_path = id_path;
832 /* Allocate a new parent_path_t node from POOL, referring to NODE,
833 ENTRY, PARENT, and COPY_ID. */
834 static parent_path_t *
835 make_parent_path(dag_node_t *node,
837 parent_path_t *parent,
840 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
841 parent_path->node = node;
842 parent_path->entry = entry;
843 parent_path->parent = parent;
844 parent_path->copy_inherit = copy_id_inherit_unknown;
845 parent_path->copy_src_path = NULL;
850 /* Flags for open_path. */
851 typedef enum open_path_flags_t {
853 /* The last component of the PATH need not exist. (All parent
854 directories must exist, as usual.) If the last component doesn't
855 exist, simply leave the `node' member of the bottom parent_path
857 open_path_last_optional = 1,
859 /* When this flag is set, don't bother to lookup the DAG node in
860 our caches because we already tried this. Ignoring this flag
861 has no functional impact. */
862 open_path_uncached = 2,
864 /* The caller does not care about the parent node chain but only
865 the final DAG node. */
866 open_path_node_only = 4,
868 /* The caller wants a NULL path object instead of an error if the
869 path cannot be found. */
870 open_path_allow_null = 8
873 /* Try a short-cut for the open_path() function using the last node accessed.
874 * If that ROOT is that nodes's "created rev" and PATH of PATH_LEN chars is
875 * its "created path", return the node in *NODE_P. Set it to NULL otherwise.
877 * This function is used to support ra_serf-style access patterns where we
878 * are first asked for path@rev and then for path@c_rev of the same node.
879 * The shortcut works by ignoring the "rev" part of the cache key and then
880 * checking whether we got lucky. Lookup and verification are both quick
881 * plus there are many early outs for common types of mismatch.
884 try_match_last_node(dag_node_t **node_p,
888 apr_pool_t *scratch_pool)
890 fs_fs_data_t *ffd = root->fs->fsap_data;
892 /* Optimistic lookup: if the last node returned from the cache applied to
893 the same PATH, return it in NODE. */
895 = cache_lookup_last_path(ffd->dag_node_cache, path, path_len);
897 /* Did we get a bucket with a committed node? */
898 if (node && !svn_fs_fs__dag_check_mutable(node))
900 /* Get the path&rev pair at which this node was created.
901 This is repository location for which this node is _known_ to be
902 the right lookup result irrespective of how we found it. */
903 const char *created_path
904 = svn_fs_fs__dag_get_created_path(node);
905 svn_revnum_t revision;
906 SVN_ERR(svn_fs_fs__dag_get_revision(&revision, node, scratch_pool));
908 /* Is it an exact match? */
909 if (revision == root->rev && strcmp(created_path, path) == 0)
911 /* Cache it under its full path@rev access path. */
912 SVN_ERR(dag_node_cache_set(root, path, node, scratch_pool));
923 /* Helper for open_path() that constructs and returns an appropriate
924 SVN_ERR_FS_NOT_DIRECTORY error. */
926 err_not_directory(svn_fs_root_t *root,
928 apr_pool_t *scratch_pool)
932 msg = root->is_txn_root
933 ? apr_psprintf(scratch_pool,
934 _("Failure opening '%s' in transaction '%s'"),
936 : apr_psprintf(scratch_pool,
937 _("Failure opening '%s' in revision %ld"),
940 return svn_error_quick_wrap(SVN_FS__ERR_NOT_DIRECTORY(root->fs, path), msg);
943 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
944 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
945 **PARENT_PATH_P value is guaranteed to contain at least one
946 *element, for the root directory. PATH must be in canonical form.
948 If resulting *PARENT_PATH_P will eventually be made mutable and
949 modified, or if copy ID inheritance information is otherwise needed,
950 IS_TXN_PATH must be set. If IS_TXN_PATH is FALSE, no copy ID
951 inheritance information will be calculated for the *PARENT_PATH_P chain.
953 If FLAGS & open_path_last_optional is zero, return the error
954 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
955 non-zero, require all the parent directories to exist as normal,
956 but if the final path component doesn't exist, simply return a path
957 whose bottom `node' member is zero. This option is useful for
958 callers that create new nodes --- we find the parent directory for
959 them, and tell them whether the entry exists already.
961 The remaining bits in FLAGS are hints that allow this function
962 to take shortcuts based on knowledge that the caller provides,
963 such as the caller is not actually being interested in PARENT_PATH_P,
964 but only in (*PARENT_PATH_P)->NODE.
966 NOTE: Public interfaces which only *read* from the filesystem
967 should not call this function directly, but should instead use
971 open_path(parent_path_t **parent_path_p,
975 svn_boolean_t is_txn_path,
978 svn_fs_t *fs = root->fs;
979 dag_node_t *here = NULL; /* The directory we're currently looking at. */
980 parent_path_t *parent_path; /* The path from HERE up to the root. */
981 const char *rest = NULL; /* The portion of PATH we haven't traversed yet. */
982 apr_pool_t *iterpool = svn_pool_create(pool);
984 /* path to the currently processed entry without trailing '/'.
985 We will reuse this across iterations by simply putting a NUL terminator
986 at the respective position and replacing that with a '/' in the next
987 iteration. This is correct as we assert() PATH to be canonical. */
988 svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
989 apr_size_t path_len = path_so_far->len;
991 /* Callers often traverse the DAG in some path-based order or along the
992 history segments. That allows us to try a few guesses about where to
993 find the next item. This is only useful if the caller didn't request
994 the full parent chain. */
995 assert(svn_fs__is_canonical_abspath(path));
996 path_so_far->len = 0; /* "" */
997 if (flags & open_path_node_only)
999 const char *directory;
1001 /* First attempt: Assume that we access the DAG for the same path as
1002 in the last lookup but for a different revision that happens to be
1003 the last revision that touched the respective node. This is a
1004 common pattern when e.g. checking out over ra_serf. Note that this
1005 will only work for committed data as the revision info for nodes in
1008 This shortcut is quick and will exit this function upon success.
1009 So, try it first. */
1010 if (!root->is_txn_root)
1013 SVN_ERR(try_match_last_node(&node, root, path, path_len, iterpool));
1015 /* Did the shortcut work? */
1018 /* Construct and return the result. */
1019 svn_pool_destroy(iterpool);
1021 parent_path = make_parent_path(node, 0, 0, pool);
1022 parent_path->copy_inherit = copy_id_inherit_self;
1023 *parent_path_p = parent_path;
1025 return SVN_NO_ERROR;
1029 /* Second attempt: Try starting the lookup immediately at the parent
1030 node. We will often have recently accessed either a sibling or
1031 said parent DIRECTORY itself for the same revision. */
1032 directory = svn_dirent_dirname(path, pool);
1033 if (directory[1] != 0) /* root nodes are covered anyway */
1035 SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
1037 /* Did the shortcut work? */
1038 if (here && svn_fs_fs__dag_node_kind(here) == svn_node_dir)
1040 apr_size_t dirname_len = strlen(directory);
1041 path_so_far->len = dirname_len;
1042 rest = path + dirname_len + 1;
1046 /* The parent node is not a directory. We are looking for some
1047 sub-path, so that sub-path will not exist. That will be o.k.
1048 if we are just here to check for the path's existence, but
1049 should result in an error otherwise. */
1050 if (flags & open_path_allow_null)
1052 *parent_path_p = NULL;
1053 return SVN_NO_ERROR;
1056 return svn_error_trace(err_not_directory(root, directory, pool));
1061 /* did the shortcut work? */
1064 /* Make a parent_path item for the root node, using its own current
1066 SVN_ERR(root_node(&here, root, pool));
1067 rest = path + 1; /* skip the leading '/', it saves in iteration */
1070 path_so_far->data[path_so_far->len] = '\0';
1071 parent_path = make_parent_path(here, 0, 0, pool);
1072 parent_path->copy_inherit = copy_id_inherit_self;
1074 /* Whenever we are at the top of this loop:
1075 - HERE is our current directory,
1076 - ID is the node revision ID of HERE,
1077 - REST is the path we're going to find in HERE, and
1078 - PARENT_PATH includes HERE and all its parents. */
1085 svn_pool_clear(iterpool);
1087 /* Parse out the next entry from the path. */
1088 entry = svn_fs__next_entry_name(&next, rest, pool);
1090 /* Update the path traversed thus far. */
1091 path_so_far->data[path_so_far->len] = '/';
1092 path_so_far->len += strlen(entry) + 1;
1093 path_so_far->data[path_so_far->len] = '\0';
1097 /* Given the behavior of svn_fs__next_entry_name(), this
1098 happens when the path either starts or ends with a slash.
1099 In either case, we stay put: the current directory stays
1100 the same, and we add nothing to the parent path. */
1105 copy_id_inherit_t inherit;
1106 const char *copy_path = NULL;
1107 dag_node_t *cached_node = NULL;
1109 /* If we found a directory entry, follow it. First, we
1110 check our node cache, and, failing that, we hit the DAG
1111 layer. Don't bother to contact the cache for the last
1112 element if we already know the lookup to fail for the
1114 if (next || !(flags & open_path_uncached))
1115 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
1118 child = cached_node;
1120 SVN_ERR(svn_fs_fs__dag_open(&child, here, entry, pool, iterpool));
1122 /* "file not found" requires special handling. */
1125 /* If this was the last path component, and the caller
1126 said it was optional, then don't return an error;
1127 just put a NULL node pointer in the path. */
1129 if ((flags & open_path_last_optional)
1130 && (! next || *next == '\0'))
1132 parent_path = make_parent_path(NULL, entry, parent_path,
1136 else if (flags & open_path_allow_null)
1143 /* Build a better error message than svn_fs_fs__dag_open
1144 can provide, giving the root and full path name. */
1145 return SVN_FS__NOT_FOUND(root, path);
1149 if (flags & open_path_node_only)
1151 /* Shortcut: the caller only wants the final DAG node. */
1152 parent_path->node = child;
1156 /* Now, make a parent_path item for CHILD. */
1157 parent_path = make_parent_path(child, entry, parent_path, pool);
1160 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
1161 parent_path, iterpool));
1162 parent_path->copy_inherit = inherit;
1163 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
1167 /* Cache the node we found (if it wasn't already cached). */
1169 SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
1173 /* Are we finished traversing the path? */
1177 /* The path isn't finished yet; we'd better be in a directory. */
1178 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
1180 /* Since this is not a directory and we are looking for some
1181 sub-path, that sub-path will not exist. That will be o.k.,
1182 if we are just here to check for the path's existence. */
1183 if (flags & open_path_allow_null)
1189 /* It's really a problem ... */
1190 return svn_error_trace(
1191 err_not_directory(root, path_so_far->data, iterpool));
1198 svn_pool_destroy(iterpool);
1199 *parent_path_p = parent_path;
1200 return SVN_NO_ERROR;
1204 /* Make the node referred to by PARENT_PATH mutable, if it isn't
1205 already, allocating from POOL. ROOT must be the root from which
1206 PARENT_PATH descends. Clone any parent directories as needed.
1207 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
1208 ERROR_PATH in error messages. */
1209 static svn_error_t *
1210 make_path_mutable(svn_fs_root_t *root,
1211 parent_path_t *parent_path,
1212 const char *error_path,
1216 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
1218 /* Is the node mutable already? */
1219 if (svn_fs_fs__dag_check_mutable(parent_path->node))
1220 return SVN_NO_ERROR;
1222 /* Are we trying to clone the root, or somebody's child node? */
1223 if (parent_path->parent)
1225 const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
1226 svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
1227 svn_fs_fs__id_part_t *copy_id_ptr = ©_id;
1228 copy_id_inherit_t inherit = parent_path->copy_inherit;
1229 const char *clone_path, *copyroot_path;
1230 svn_revnum_t copyroot_rev;
1231 svn_boolean_t is_parent_copyroot = FALSE;
1232 svn_fs_root_t *copyroot_root;
1233 dag_node_t *copyroot_node;
1235 /* We're trying to clone somebody's child. Make sure our parent
1237 SVN_ERR(make_path_mutable(root, parent_path->parent,
1242 case copy_id_inherit_parent:
1243 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1244 copy_id = *svn_fs_fs__id_copy_id(parent_id);
1247 case copy_id_inherit_new:
1248 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
1252 case copy_id_inherit_self:
1256 case copy_id_inherit_unknown:
1258 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1259 inheritance data. */
1262 /* Determine what copyroot our new child node should use. */
1263 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
1264 parent_path->node));
1265 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
1266 copyroot_rev, pool));
1267 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
1269 child_id = svn_fs_fs__dag_get_id(parent_path->node);
1270 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1271 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
1272 svn_fs_fs__id_node_id(copyroot_id)))
1273 is_parent_copyroot = TRUE;
1275 /* Now make this node mutable. */
1276 clone_path = parent_path_path(parent_path->parent, pool);
1277 SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1278 parent_path->parent->node,
1281 copy_id_ptr, txn_id,
1285 /* Update the path cache. */
1286 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1291 /* We're trying to clone the root directory. */
1292 SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1295 /* Update the PARENT_PATH link to refer to the clone. */
1296 parent_path->node = clone;
1298 return SVN_NO_ERROR;
1302 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
1303 node we find, allocated in POOL. Return the error
1304 SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1305 static svn_error_t *
1306 get_dag(dag_node_t **dag_node_p,
1307 svn_fs_root_t *root,
1311 parent_path_t *parent_path;
1312 dag_node_t *node = NULL;
1314 /* First we look for the DAG in our cache
1315 (if the path may be canonical). */
1317 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1321 /* Canonicalize the input PATH. As it turns out, >95% of all paths
1322 * seen here during e.g. svnadmin verify are non-canonical, i.e.
1323 * miss the leading '/'. Check for those quickly.
1325 * For normalized paths, it is much faster to check the path than
1326 * to attempt a second cache lookup (which would fail). */
1327 if (*path != '/' || !svn_fs__is_canonical_abspath(path))
1329 path = svn_fs__canonicalize_abspath(path, pool);
1330 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1335 /* Call open_path with no flags, as we want this to return an
1336 * error if the node for which we are searching doesn't exist. */
1337 SVN_ERR(open_path(&parent_path, root, path,
1338 open_path_uncached | open_path_node_only,
1340 node = parent_path->node;
1342 /* No need to cache our find -- open_path() will do that for us. */
1347 return SVN_NO_ERROR;
1352 /* Populating the `changes' table. */
1354 /* Add a change to the changes table in FS, keyed on transaction id
1355 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1356 PATH (whose node revision id is--or was, in the case of a
1357 deletion--NODEREV_ID), and optionally that TEXT_MODs, PROP_MODs or
1358 MERGEINFO_MODs occurred. If the change resulted from a copy,
1359 COPYFROM_REV and COPYFROM_PATH specify under which revision and path
1360 the node was copied from. If this was not part of a copy, COPYFROM_REV
1361 should be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1362 static svn_error_t *
1363 add_change(svn_fs_t *fs,
1364 const svn_fs_fs__id_part_t *txn_id,
1366 const svn_fs_id_t *noderev_id,
1367 svn_fs_path_change_kind_t change_kind,
1368 svn_boolean_t text_mod,
1369 svn_boolean_t prop_mod,
1370 svn_boolean_t mergeinfo_mod,
1371 svn_node_kind_t node_kind,
1372 svn_revnum_t copyfrom_rev,
1373 const char *copyfrom_path,
1376 return svn_fs_fs__add_change(fs, txn_id,
1377 svn_fs__canonicalize_abspath(path, pool),
1378 noderev_id, change_kind,
1379 text_mod, prop_mod, mergeinfo_mod,
1380 node_kind, copyfrom_rev, copyfrom_path,
1386 /* Generic node operations. */
1388 /* Get the id of a node referenced by path PATH in ROOT. Return the
1389 id in *ID_P allocated in POOL. */
1391 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1392 svn_fs_root_t *root,
1396 if ((! root->is_txn_root)
1397 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1399 /* Optimize the case where we don't need any db access at all.
1400 The root directory ("" or "/") node is stored in the
1401 svn_fs_root_t object, and never changes when it's a revision
1402 root, so we can just reach in and grab it directly. */
1403 dag_node_t *root_dir = root->fsap_data;
1404 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
1410 SVN_ERR(get_dag(&node, root, path, pool));
1411 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1413 return SVN_NO_ERROR;
1416 static svn_error_t *
1417 fs_node_relation(svn_fs_node_relation_t *relation,
1418 svn_fs_root_t *root_a, const char *path_a,
1419 svn_fs_root_t *root_b, const char *path_b,
1423 const svn_fs_id_t *id_a, *id_b;
1424 svn_fs_fs__id_part_t node_id_a, node_id_b;
1426 /* Root paths are a common special case. */
1427 svn_boolean_t a_is_root_dir
1428 = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
1429 svn_boolean_t b_is_root_dir
1430 = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
1432 /* Another useful thing to know: Both are txns but not the same txn. */
1433 svn_boolean_t different_txn
1434 = root_a->is_txn_root && root_b->is_txn_root
1435 && strcmp(root_a->txn, root_b->txn);
1437 /* Path from different repository are always unrelated. */
1438 if (root_a->fs != root_b->fs)
1440 *relation = svn_fs_node_unrelated;
1441 return SVN_NO_ERROR;
1444 /* Are both (!) root paths? Then, they are related and we only test how
1445 * direct the relation is. */
1446 if (a_is_root_dir && b_is_root_dir)
1448 /* For txn roots, root->REV is the base revision of that TXN. */
1449 *relation = ( (root_a->rev == root_b->rev)
1450 && (root_a->is_txn_root == root_b->is_txn_root)
1452 ? svn_fs_node_unchanged
1453 : svn_fs_node_common_ancestor;
1454 return SVN_NO_ERROR;
1457 /* We checked for all separations between ID spaces (repos, txn).
1458 * Now, we can simply test for the ID values themselves. */
1459 SVN_ERR(get_dag(&node, root_a, path_a, pool));
1460 id_a = svn_fs_fs__dag_get_id(node);
1461 node_id_a = *svn_fs_fs__id_node_id(id_a);
1463 SVN_ERR(get_dag(&node, root_b, path_b, pool));
1464 id_b = svn_fs_fs__dag_get_id(node);
1465 node_id_b = *svn_fs_fs__id_node_id(id_b);
1467 /* Noderevs from different nodes are unrelated. */
1468 if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
1470 *relation = svn_fs_node_unrelated;
1471 return SVN_NO_ERROR;
1474 /* Noderevs have the same node-ID now. So, they *seem* to be related.
1476 * Special case: Different txns may create the same (txn-local) node ID.
1477 * These are not related to each other, nor to any other node ID so far. */
1478 if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
1480 *relation = svn_fs_node_unrelated;
1481 return SVN_NO_ERROR;
1484 /* The noderevs are actually related. Are they the same? */
1485 if (svn_fs_fs__id_eq(id_a, id_b))
1486 *relation = svn_fs_node_unchanged;
1488 *relation = svn_fs_node_common_ancestor;
1490 return SVN_NO_ERROR;
1494 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1495 svn_fs_root_t *root,
1501 SVN_ERR(get_dag(&node, root, path, pool));
1502 return svn_fs_fs__dag_get_revision(revision, node, pool);
1506 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1507 Return a string allocated in POOL. */
1508 static svn_error_t *
1509 fs_node_created_path(const char **created_path,
1510 svn_fs_root_t *root,
1516 SVN_ERR(get_dag(&node, root, path, pool));
1517 *created_path = svn_fs_fs__dag_get_created_path(node);
1519 return SVN_NO_ERROR;
1522 /* Set *KIND_P to the type of node present at PATH under ROOT. If
1523 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
1524 POOL for temporary allocation. */
1526 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1527 svn_fs_root_t *root,
1534 err = get_dag(&node, root, path, pool);
1536 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1537 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1539 svn_error_clear(err);
1540 *kind_p = svn_node_none;
1541 return SVN_NO_ERROR;
1544 return svn_error_trace(err);
1546 *kind_p = svn_fs_fs__dag_node_kind(node);
1547 return SVN_NO_ERROR;
1550 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1551 ROOT. If the node has no property by that name, set *VALUE_P to
1552 zero. Allocate the result in POOL. */
1553 static svn_error_t *
1554 fs_node_prop(svn_string_t **value_p,
1555 svn_fs_root_t *root,
1557 const char *propname,
1561 apr_hash_t *proplist;
1563 SVN_ERR(get_dag(&node, root, path, pool));
1564 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1567 *value_p = svn_hash_gets(proplist, propname);
1569 return SVN_NO_ERROR;
1573 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1574 APR hash table allocated in POOL. The resulting property table
1575 maps property names to pointers to svn_string_t objects containing
1576 the property value. */
1577 static svn_error_t *
1578 fs_node_proplist(apr_hash_t **table_p,
1579 svn_fs_root_t *root,
1586 SVN_ERR(get_dag(&node, root, path, pool));
1587 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1588 *table_p = table ? table : apr_hash_make(pool);
1590 return SVN_NO_ERROR;
1593 static svn_error_t *
1594 fs_node_has_props(svn_boolean_t *has_props,
1595 svn_fs_root_t *root,
1597 apr_pool_t *scratch_pool)
1601 SVN_ERR(get_dag(&node, root, path, scratch_pool));
1603 return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
1607 static svn_error_t *
1608 increment_mergeinfo_up_tree(parent_path_t *pp,
1609 apr_int64_t increment,
1612 for (; pp; pp = pp->parent)
1613 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1617 return SVN_NO_ERROR;
1620 /* Change, add, or delete a node's property value. The affected node
1621 is PATH under ROOT, the property value to modify is NAME, and VALUE
1622 points to either a string value to set the new contents to, or NULL
1623 if the property should be deleted. Perform temporary allocations
1625 static svn_error_t *
1626 fs_change_node_prop(svn_fs_root_t *root,
1629 const svn_string_t *value,
1632 parent_path_t *parent_path;
1633 apr_hash_t *proplist;
1634 const svn_fs_fs__id_part_t *txn_id;
1635 svn_boolean_t mergeinfo_mod = FALSE;
1637 if (! root->is_txn_root)
1638 return SVN_FS__NOT_TXN(root);
1639 txn_id = root_txn_id(root);
1641 path = svn_fs__canonicalize_abspath(path, pool);
1642 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
1644 /* Check (non-recursively) to see if path is locked; if so, check
1645 that we can use it. */
1646 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1647 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1650 SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1651 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1653 /* If there's no proplist, but we're just deleting a property, exit now. */
1654 if ((! proplist) && (! value))
1655 return SVN_NO_ERROR;
1657 /* Now, if there's no proplist, we know we need to make one. */
1659 proplist = apr_hash_make(pool);
1661 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1662 && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1664 apr_int64_t increment = 0;
1665 svn_boolean_t had_mergeinfo;
1666 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1668 if (value && !had_mergeinfo)
1670 else if (!value && had_mergeinfo)
1675 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1676 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1677 (value != NULL), pool));
1680 mergeinfo_mod = TRUE;
1683 /* Set the property. */
1684 svn_hash_sets(proplist, name, value);
1686 /* Overwrite the node's proplist. */
1687 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1690 /* Make a record of this modification in the changes table. */
1691 return add_change(root->fs, txn_id, path,
1692 svn_fs_fs__dag_get_id(parent_path->node),
1693 svn_fs_path_change_modify, FALSE, TRUE, mergeinfo_mod,
1694 svn_fs_fs__dag_node_kind(parent_path->node),
1695 SVN_INVALID_REVNUM, NULL, pool);
1699 /* Determine if the properties of two path/root combinations are
1700 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1701 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1702 Both roots must be in the same filesystem. */
1703 static svn_error_t *
1704 fs_props_changed(svn_boolean_t *changed_p,
1705 svn_fs_root_t *root1,
1707 svn_fs_root_t *root2,
1709 svn_boolean_t strict,
1712 dag_node_t *node1, *node2;
1714 /* Check that roots are in the same fs. */
1715 if (root1->fs != root2->fs)
1716 return svn_error_create
1717 (SVN_ERR_FS_GENERAL, NULL,
1718 _("Cannot compare property value between two different filesystems"));
1720 SVN_ERR(get_dag(&node1, root1, path1, pool));
1721 SVN_ERR(get_dag(&node2, root2, path2, pool));
1722 return svn_fs_fs__dag_things_different(changed_p, NULL,
1723 node1, node2, strict, pool);
1728 /* Merges and commits. */
1730 /* Set *NODE to the root node of ROOT. */
1731 static svn_error_t *
1732 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1734 return get_dag(node, root, "/", pool);
1738 /* Set the contents of CONFLICT_PATH to PATH, and return an
1739 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1740 at PATH. Perform all allocations in POOL (except the allocation of
1741 CONFLICT_PATH, which should be handled outside this function). */
1742 static svn_error_t *
1743 conflict_err(svn_stringbuf_t *conflict_path,
1746 svn_stringbuf_set(conflict_path, path);
1747 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1748 _("Conflict at '%s'"), path);
1751 /* Compare the directory representations at nodes LHS and RHS and set
1752 * *CHANGED to TRUE, if at least one entry has been added or removed them.
1753 * Use POOL for temporary allocations.
1755 static svn_error_t *
1756 compare_dir_structure(svn_boolean_t *changed,
1761 apr_array_header_t *lhs_entries;
1762 apr_array_header_t *rhs_entries;
1765 SVN_ERR(svn_fs_fs__dag_dir_entries(&lhs_entries, lhs, pool));
1766 SVN_ERR(svn_fs_fs__dag_dir_entries(&rhs_entries, rhs, pool));
1768 /* different number of entries -> some addition / removal */
1769 if (lhs_entries->nelts != rhs_entries->nelts)
1772 return SVN_NO_ERROR;
1775 /* Since directories are sorted by name, we can simply compare their
1776 entries one-by-one without binary lookup etc. */
1777 for (i = 0; i < lhs_entries->nelts; ++i)
1779 svn_fs_dirent_t *lhs_entry
1780 = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
1781 svn_fs_dirent_t *rhs_entry
1782 = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
1784 if (strcmp(lhs_entry->name, rhs_entry->name)
1785 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(lhs_entry->id),
1786 svn_fs_fs__id_node_id(rhs_entry->id))
1787 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(lhs_entry->id),
1788 svn_fs_fs__id_copy_id(rhs_entry->id)))
1791 return SVN_NO_ERROR;
1796 return SVN_NO_ERROR;
1799 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1800 * and TARGET must be distinct node revisions. TARGET_PATH should
1801 * correspond to TARGET's full path in its filesystem, and is used for
1802 * reporting conflict location.
1804 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1805 * function recursively merges the directories' contents. If any are
1806 * files, this function simply returns an error whenever SOURCE,
1807 * TARGET, and ANCESTOR are all distinct node revisions.
1809 * If there are differences between ANCESTOR and SOURCE that conflict
1810 * with changes between ANCESTOR and TARGET, this function returns an
1811 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1812 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1814 * If there are no conflicting differences, CONFLICT_P is updated to
1817 * CONFLICT_P must point to a valid svn_stringbuf_t.
1819 * Do any necessary temporary allocation in POOL.
1821 static svn_error_t *
1822 merge(svn_stringbuf_t *conflict_p,
1823 const char *target_path,
1826 dag_node_t *ancestor,
1827 const svn_fs_fs__id_part_t *txn_id,
1828 apr_int64_t *mergeinfo_increment_out,
1831 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1832 apr_array_header_t *s_entries, *t_entries, *a_entries;
1833 int i, s_idx = -1, t_idx = -1;
1835 apr_pool_t *iterpool;
1836 apr_int64_t mergeinfo_increment = 0;
1837 svn_boolean_t fs_supports_mergeinfo;
1839 /* Make sure everyone comes from the same filesystem. */
1840 fs = svn_fs_fs__dag_get_fs(ancestor);
1841 if ((fs != svn_fs_fs__dag_get_fs(source))
1842 || (fs != svn_fs_fs__dag_get_fs(target)))
1844 return svn_error_create
1845 (SVN_ERR_FS_CORRUPT, NULL,
1846 _("Bad merge; ancestor, source, and target not all in same fs"));
1849 /* We have the same fs, now check it. */
1850 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1852 source_id = svn_fs_fs__dag_get_id(source);
1853 target_id = svn_fs_fs__dag_get_id(target);
1854 ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1856 /* It's improper to call this function with ancestor == target. */
1857 if (svn_fs_fs__id_eq(ancestor_id, target_id))
1859 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1860 return svn_error_createf
1861 (SVN_ERR_FS_GENERAL, NULL,
1862 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1863 target_path, id_str->data);
1866 svn_stringbuf_setempty(conflict_p);
1869 * Either no change made in source, or same change as made in target.
1870 * Both mean nothing to merge here.
1872 if (svn_fs_fs__id_eq(ancestor_id, source_id)
1873 || (svn_fs_fs__id_eq(source_id, target_id)))
1874 return SVN_NO_ERROR;
1876 /* Else proceed, knowing all three are distinct node revisions.
1878 * How to merge from this point:
1880 * if (not all 3 are directories)
1882 * early exit with conflict;
1885 * // Property changes may only be made to up-to-date
1886 * // directories, because once the client commits the prop
1887 * // change, it bumps the directory's revision, and therefore
1888 * // must be able to depend on there being no other changes to
1889 * // that directory in the repository.
1890 * if (target's property list differs from ancestor's)
1893 * For each entry NAME in the directory ANCESTOR:
1895 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1896 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1897 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1899 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1900 * No changes were made to this entry while the transaction was in
1901 * progress, so do nothing to the target.
1903 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1904 * A change was made to this entry while the transaction was in
1905 * process, but the transaction did not touch this entry. Replace
1906 * TARGET-ENTRY with SOURCE-ENTRY.
1909 * Changes were made to this entry both within the transaction and
1910 * to the repository while the transaction was in progress. They
1911 * must be merged or declared to be in conflict.
1913 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1914 * double delete; flag a conflict.
1916 * If any of the three entries is of type file, declare a conflict.
1918 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1919 * modification of ANCESTOR-ENTRY (determine by comparing the
1920 * node-id fields), declare a conflict. A replacement is
1921 * incompatible with a modification or other replacement--even
1922 * an identical replacement.
1924 * Direct modifications were made to the directory ANCESTOR-ENTRY
1925 * in both SOURCE and TARGET. Recursively merge these
1928 * For each leftover entry NAME in the directory SOURCE:
1930 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1931 * TARGET are adding exactly the same thing, two additions are not
1932 * auto-mergeable with each other.
1934 * Add NAME to TARGET with the entry from SOURCE.
1936 * Now that we are done merging the changes from SOURCE into the
1937 * directory TARGET, update TARGET's predecessor to be SOURCE.
1940 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1941 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1942 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1944 return conflict_err(conflict_p, target_path);
1948 /* Possible early merge failure: if target and ancestor have
1949 different property lists, then the merge should fail.
1950 Propchanges can *only* be committed on an up-to-date directory.
1951 ### TODO: see issue #418 about the inelegance of this.
1953 Another possible, similar, early merge failure: if source and
1954 ancestor have different property lists (meaning someone else
1955 changed directory properties while our commit transaction was
1956 happening), the merge should fail. See issue #2751.
1959 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1961 apr_pool_t *scratch_pool;
1963 /* Get node revisions for our id's. */
1964 scratch_pool = svn_pool_create(pool);
1965 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool,
1967 svn_pool_clear(scratch_pool);
1968 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool,
1970 svn_pool_clear(scratch_pool);
1971 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool,
1973 svn_pool_destroy(scratch_pool);
1975 /* Now compare the prop-keys of the skels. Note that just because
1976 the keys are different -doesn't- mean the proplists have
1977 different contents. */
1978 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, pool));
1980 return conflict_err(conflict_p, target_path);
1982 /* The directory entries got changed in the repository but the directory
1983 properties did not. */
1984 SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, pool));
1987 /* There is an incoming prop change for this directory.
1988 We will accept it only if the directory changes were mere updates
1989 to its entries, i.e. there were no additions or removals.
1990 Those could cause update problems to the working copy. */
1991 svn_boolean_t changed;
1992 SVN_ERR(compare_dir_structure(&changed, source, ancestor, pool));
1995 return conflict_err(conflict_p, target_path);
1999 /* ### todo: it would be more efficient to simply check for a NULL
2000 entries hash where necessary below than to allocate an empty hash
2001 here, but another day, another day... */
2002 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
2003 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
2004 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
2006 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
2008 /* for each entry E in a_entries... */
2009 iterpool = svn_pool_create(pool);
2010 for (i = 0; i < a_entries->nelts; ++i)
2012 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2013 svn_pool_clear(iterpool);
2015 a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
2016 s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
2017 t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
2019 /* No changes were made to this entry while the transaction was
2020 in progress, so do nothing to the target. */
2021 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
2024 /* A change was made to this entry while the transaction was in
2025 process, but the transaction did not touch this entry. */
2026 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
2028 dag_node_t *t_ent_node;
2029 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
2030 t_entry->id, iterpool));
2031 if (fs_supports_mergeinfo)
2033 apr_int64_t mergeinfo_start;
2034 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2036 mergeinfo_increment -= mergeinfo_start;
2041 dag_node_t *s_ent_node;
2042 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2043 s_entry->id, iterpool));
2045 if (fs_supports_mergeinfo)
2047 apr_int64_t mergeinfo_end;
2048 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2050 mergeinfo_increment += mergeinfo_end;
2053 SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
2061 SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
2066 /* Changes were made to this entry both within the transaction
2067 and to the repository while the transaction was in progress.
2068 They must be merged or declared to be in conflict. */
2071 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2072 const char *new_tpath;
2073 apr_int64_t sub_mergeinfo_increment;
2075 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2076 double delete; if one of them is null, that's a delete versus
2077 a modification. In any of these cases, flag a conflict. */
2078 if (s_entry == NULL || t_entry == NULL)
2079 return conflict_err(conflict_p,
2080 svn_fspath__join(target_path,
2084 /* If any of the three entries is of type file, flag a conflict. */
2085 if (s_entry->kind == svn_node_file
2086 || t_entry->kind == svn_node_file
2087 || a_entry->kind == svn_node_file)
2088 return conflict_err(conflict_p,
2089 svn_fspath__join(target_path,
2093 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2094 modification of ANCESTOR-ENTRY, declare a conflict. */
2095 if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
2096 svn_fs_fs__id_node_id(a_entry->id))
2097 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
2098 svn_fs_fs__id_copy_id(a_entry->id))
2099 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
2100 svn_fs_fs__id_node_id(a_entry->id))
2101 || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
2102 svn_fs_fs__id_copy_id(a_entry->id)))
2103 return conflict_err(conflict_p,
2104 svn_fspath__join(target_path,
2108 /* Direct modifications were made to the directory
2109 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2110 merge these modifications. */
2111 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2112 s_entry->id, iterpool));
2113 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
2114 t_entry->id, iterpool));
2115 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
2116 a_entry->id, iterpool));
2117 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2118 SVN_ERR(merge(conflict_p, new_tpath,
2119 t_ent_node, s_ent_node, a_ent_node,
2121 &sub_mergeinfo_increment,
2123 if (fs_supports_mergeinfo)
2124 mergeinfo_increment += sub_mergeinfo_increment;
2128 /* For each entry E in source but not in ancestor */
2129 for (i = 0; i < s_entries->nelts; ++i)
2131 svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
2132 dag_node_t *s_ent_node;
2134 svn_pool_clear(iterpool);
2136 s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
2137 a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
2138 t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
2140 /* Process only entries in source that are NOT in ancestor. */
2144 /* If NAME exists in TARGET, declare a conflict. */
2146 return conflict_err(conflict_p,
2147 svn_fspath__join(target_path,
2151 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
2152 s_entry->id, iterpool));
2153 if (fs_supports_mergeinfo)
2155 apr_int64_t mergeinfo_s;
2156 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
2158 mergeinfo_increment += mergeinfo_s;
2161 SVN_ERR(svn_fs_fs__dag_set_entry
2162 (target, s_entry->name, s_entry->id, s_entry->kind,
2165 svn_pool_destroy(iterpool);
2167 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
2169 if (fs_supports_mergeinfo)
2170 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
2171 mergeinfo_increment,
2174 if (mergeinfo_increment_out)
2175 *mergeinfo_increment_out = mergeinfo_increment;
2177 return SVN_NO_ERROR;
2180 /* Merge changes between an ancestor and SOURCE_NODE into
2181 TXN. The ancestor is either ANCESTOR_NODE, or if
2182 that is null, TXN's base node.
2184 If the merge is successful, TXN's base will become
2185 SOURCE_NODE, and its root node will have a new ID, a
2186 successor of SOURCE_NODE.
2188 If a conflict results, update *CONFLICT to the path in the txn that
2189 conflicted; see the CONFLICT_P parameter of merge() for details. */
2190 static svn_error_t *
2191 merge_changes(dag_node_t *ancestor_node,
2192 dag_node_t *source_node,
2194 svn_stringbuf_t *conflict,
2197 dag_node_t *txn_root_node;
2198 svn_fs_t *fs = txn->fs;
2199 const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
2201 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
2203 if (ancestor_node == NULL)
2205 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
2209 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
2210 svn_fs_fs__dag_get_id(txn_root_node)))
2212 /* If no changes have been made in TXN since its current base,
2213 then it can't conflict with any changes since that base.
2214 The caller isn't supposed to call us in that case. */
2215 SVN_ERR_MALFUNCTION();
2218 SVN_ERR(merge(conflict, "/", txn_root_node,
2219 source_node, ancestor_node, txn_id, NULL, pool));
2221 return SVN_NO_ERROR;
2226 svn_fs_fs__commit_txn(const char **conflict_p,
2227 svn_revnum_t *new_rev,
2231 /* How do commits work in Subversion?
2233 * When you're ready to commit, here's what you have:
2235 * 1. A transaction, with a mutable tree hanging off it.
2236 * 2. A base revision, against which TXN_TREE was made.
2237 * 3. A latest revision, which may be newer than the base rev.
2239 * The problem is that if latest != base, then one can't simply
2240 * attach the txn root as the root of the new revision, because that
2241 * would lose all the changes between base and latest. It is also
2242 * not acceptable to insist that base == latest; in a busy
2243 * repository, commits happen too fast to insist that everyone keep
2244 * their entire tree up-to-date at all times. Non-overlapping
2245 * changes should not interfere with each other.
2247 * The solution is to merge the changes between base and latest into
2248 * the txn tree [see the function merge()]. The txn tree is the
2249 * only one of the three trees that is mutable, so it has to be the
2252 * You might have to adjust it more than once, if a new latest
2253 * revision gets committed while you were merging in the previous
2256 * 1. Jane starts txn T, based at revision 6.
2257 * 2. Someone commits (or already committed) revision 7.
2258 * 3. Jane's starts merging the changes between 6 and 7 into T.
2259 * 4. Meanwhile, someone commits revision 8.
2260 * 5. Jane finishes the 6-->7 merge. T could now be committed
2261 * against a latest revision of 7, if only that were still the
2262 * latest. Unfortunately, 8 is now the latest, so...
2263 * 6. Jane starts merging the changes between 7 and 8 into T.
2264 * 7. Meanwhile, no one commits any new revisions. Whew.
2265 * 8. Jane commits T, creating revision 9, whose tree is exactly
2266 * T's tree, except immutable now.
2268 * Lather, rinse, repeat.
2271 svn_error_t *err = SVN_NO_ERROR;
2272 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2273 svn_fs_t *fs = txn->fs;
2274 fs_fs_data_t *ffd = fs->fsap_data;
2276 /* Limit memory usage when the repository has a high commit rate and
2277 needs to run the following while loop multiple times. The memory
2278 growth without an iteration pool is very noticeable when the
2279 transaction modifies a node that has 20,000 sibling nodes. */
2280 apr_pool_t *iterpool = svn_pool_create(pool);
2282 /* Initialize output params. */
2283 *new_rev = SVN_INVALID_REVNUM;
2289 svn_revnum_t youngish_rev;
2290 svn_fs_root_t *youngish_root;
2291 dag_node_t *youngish_root_node;
2293 svn_pool_clear(iterpool);
2295 /* Get the *current* youngest revision. We call it "youngish"
2296 because new revisions might get committed after we've
2299 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
2300 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
2303 /* Get the dag node for the youngest revision. Later we'll use
2304 it as the SOURCE argument to a merge, and if the merge
2305 succeeds, this youngest root node will become the new base
2306 root for the svn txn that was the target of the merge (but
2307 note that the youngest rev may have changed by then -- that's
2308 why we're careful to get this root in its own bdb txn
2310 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
2312 /* Try to merge. If the merge succeeds, the base root node of
2313 TARGET's txn will become the same as youngish_root_node, so
2314 any future merges will only be between that node and whatever
2315 the root node of the youngest rev is by then. */
2316 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
2319 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2320 *conflict_p = conflict->data;
2323 txn->base_rev = youngish_rev;
2325 /* Try to commit. */
2326 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
2327 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2329 /* Did someone else finish committing a new revision while we
2330 were in mid-merge or mid-commit? If so, we'll need to
2331 loop again to merge the new changes in, then try to
2332 commit again. Or if that's not what happened, then just
2333 return the error. */
2334 svn_revnum_t youngest_rev;
2335 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
2336 if (youngest_rev == youngish_rev)
2339 svn_error_clear(err);
2354 svn_fs_fs__reset_txn_caches(fs);
2356 svn_pool_destroy(iterpool);
2360 if (ffd->pack_after_commit)
2362 SVN_ERR(svn_fs_fs__pack(fs, 0, NULL, NULL, NULL, NULL, pool));
2365 return SVN_NO_ERROR;
2369 /* Merge changes between two nodes into a third node. Given nodes
2370 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
2371 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
2372 changes between the ancestor and source. If there are conflicts,
2373 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
2374 description of the offending changes. Perform any temporary
2375 allocations in POOL. */
2376 static svn_error_t *
2377 fs_merge(const char **conflict_p,
2378 svn_fs_root_t *source_root,
2379 const char *source_path,
2380 svn_fs_root_t *target_root,
2381 const char *target_path,
2382 svn_fs_root_t *ancestor_root,
2383 const char *ancestor_path,
2386 dag_node_t *source, *ancestor;
2389 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2391 if (! target_root->is_txn_root)
2392 return SVN_FS__NOT_TXN(target_root);
2395 if ((source_root->fs != ancestor_root->fs)
2396 || (target_root->fs != ancestor_root->fs))
2398 return svn_error_create
2399 (SVN_ERR_FS_CORRUPT, NULL,
2400 _("Bad merge; ancestor, source, and target not all in same fs"));
2403 /* ### kff todo: is there any compelling reason to get the nodes in
2404 one db transaction? Right now we don't; txn_body_get_root() gets
2405 one node at a time. This will probably need to change:
2407 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2408 > svn_fs_merge needs to be a single transaction, to protect it against
2409 > people deleting parents of nodes it's working on, etc.
2412 /* Get the ancestor node. */
2413 SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2415 /* Get the source node. */
2416 SVN_ERR(get_root(&source, source_root, pool));
2418 /* Open a txn for the txn root into which we're merging. */
2419 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2422 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2423 err = merge_changes(ancestor, source, txn, conflict, pool);
2426 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2427 *conflict_p = conflict->data;
2428 return svn_error_trace(err);
2431 return SVN_NO_ERROR;
2435 svn_fs_fs__deltify(svn_fs_t *fs,
2436 svn_revnum_t revision,
2439 /* Deltify is a no-op for fs_fs. */
2441 return SVN_NO_ERROR;
2448 /* Set *TABLE_P to a newly allocated APR hash table containing the
2449 entries of the directory at PATH in ROOT. The keys of the table
2450 are entry names, as byte strings, excluding the final null
2451 character; the table's values are pointers to svn_fs_dirent_t
2452 structures. Allocate the table and its contents in POOL. */
2453 static svn_error_t *
2454 fs_dir_entries(apr_hash_t **table_p,
2455 svn_fs_root_t *root,
2460 apr_hash_t *hash = svn_hash__make(pool);
2461 apr_array_header_t *table;
2464 /* Get the entries for this path in the caller's pool. */
2465 SVN_ERR(get_dag(&node, root, path, pool));
2466 SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
2468 /* Convert directory array to hash. */
2469 for (i = 0; i < table->nelts; ++i)
2471 svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
2472 svn_hash_sets(hash, entry->name, entry);
2476 return SVN_NO_ERROR;
2479 static svn_error_t *
2480 fs_dir_optimal_order(apr_array_header_t **ordered_p,
2481 svn_fs_root_t *root,
2482 apr_hash_t *entries,
2483 apr_pool_t *result_pool,
2484 apr_pool_t *scratch_pool)
2486 *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
2489 return SVN_NO_ERROR;
2492 /* Raise an error if PATH contains a newline because FSFS cannot handle
2493 * such paths. See issue #4340. */
2494 static svn_error_t *
2495 check_newline(const char *path, apr_pool_t *pool)
2497 char *c = strchr(path, '\n');
2500 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2501 _("Invalid control character '0x%02x' in path '%s'"),
2502 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2504 return SVN_NO_ERROR;
2507 /* Create a new directory named PATH in ROOT. The new directory has
2508 no entries, and no properties. ROOT must be the root of a
2509 transaction, not a revision. Do any necessary temporary allocation
2511 static svn_error_t *
2512 fs_make_dir(svn_fs_root_t *root,
2516 parent_path_t *parent_path;
2517 dag_node_t *sub_dir;
2518 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2520 SVN_ERR(check_newline(path, pool));
2522 path = svn_fs__canonicalize_abspath(path, pool);
2523 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2526 /* Check (recursively) to see if some lock is 'reserving' a path at
2527 that location, or even some child-path; if so, check that we can
2529 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2530 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2533 /* If there's already a sub-directory by that name, complain. This
2534 also catches the case of trying to make a subdirectory named `/'. */
2535 if (parent_path->node)
2536 return SVN_FS__ALREADY_EXISTS(root, path);
2538 /* Create the subdirectory. */
2539 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2540 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2541 parent_path->parent->node,
2542 parent_path_path(parent_path->parent,
2548 /* Add this directory to the path cache. */
2549 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2552 /* Make a record of this modification in the changes table. */
2553 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2554 svn_fs_path_change_add, FALSE, FALSE, FALSE,
2555 svn_node_dir, SVN_INVALID_REVNUM, NULL, pool);
2559 /* Delete the node at PATH under ROOT. ROOT must be a transaction
2560 root. Perform temporary allocations in POOL. */
2561 static svn_error_t *
2562 fs_delete_node(svn_fs_root_t *root,
2566 parent_path_t *parent_path;
2567 const svn_fs_fs__id_part_t *txn_id;
2568 apr_int64_t mergeinfo_count = 0;
2569 svn_node_kind_t kind;
2571 if (! root->is_txn_root)
2572 return SVN_FS__NOT_TXN(root);
2574 txn_id = root_txn_id(root);
2575 path = svn_fs__canonicalize_abspath(path, pool);
2576 SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
2577 kind = svn_fs_fs__dag_node_kind(parent_path->node);
2579 /* We can't remove the root of the filesystem. */
2580 if (! parent_path->parent)
2581 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2582 _("The root directory cannot be deleted"));
2584 /* Check to see if path (or any child thereof) is locked; if so,
2585 check that we can use the existing lock(s). */
2586 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2587 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2590 /* Make the parent directory mutable, and do the deletion. */
2591 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2592 if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2593 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2594 parent_path->node));
2595 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2599 /* Remove this node and any children from the path cache. */
2600 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2603 /* Update mergeinfo counts for parents */
2604 if (mergeinfo_count > 0)
2605 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2609 /* Make a record of this modification in the changes table. */
2610 return add_change(root->fs, txn_id, path,
2611 svn_fs_fs__dag_get_id(parent_path->node),
2612 svn_fs_path_change_delete, FALSE, FALSE, FALSE, kind,
2613 SVN_INVALID_REVNUM, NULL, pool);
2617 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2618 Use POOL for temporary allocation only.
2619 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2620 static svn_error_t *
2621 fs_same_p(svn_boolean_t *same_p,
2626 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2627 return SVN_NO_ERROR;
2630 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2631 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2632 the copies table. Perform temporary allocations in POOL. */
2633 static svn_error_t *
2634 copy_helper(svn_fs_root_t *from_root,
2635 const char *from_path,
2636 svn_fs_root_t *to_root,
2637 const char *to_path,
2638 svn_boolean_t preserve_history,
2641 dag_node_t *from_node;
2642 parent_path_t *to_parent_path;
2643 const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
2644 svn_boolean_t same_p;
2646 /* Use an error check, not an assert, because even the caller cannot
2647 guarantee that a filesystem's UUID has not changed "on the fly". */
2648 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2650 return svn_error_createf
2651 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2652 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2653 from_root->fs->path, to_root->fs->path);
2655 /* more things that we can't do ATM */
2656 if (from_root->is_txn_root)
2657 return svn_error_create
2658 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2659 _("Copy from mutable tree not currently supported"));
2661 if (! to_root->is_txn_root)
2662 return svn_error_create
2663 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2664 _("Copy immutable tree not supported"));
2666 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2667 SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2669 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2670 component does not exist, it's not that big a deal. We'll just
2672 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2673 open_path_last_optional, TRUE, pool));
2675 /* Check to see if path (or any child thereof) is locked; if so,
2676 check that we can use the existing lock(s). */
2677 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2678 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2679 TRUE, FALSE, pool));
2681 /* If the destination node already exists as the same node as the
2682 source (in other words, this operation would result in nothing
2683 happening at all), just do nothing an return successfully,
2684 proud that you saved yourself from a tiresome task. */
2685 if (to_parent_path->node &&
2686 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2687 svn_fs_fs__dag_get_id(to_parent_path->node)))
2688 return SVN_NO_ERROR;
2690 if (! from_root->is_txn_root)
2692 svn_fs_path_change_kind_t kind;
2693 dag_node_t *new_node;
2694 const char *from_canonpath;
2695 apr_int64_t mergeinfo_start;
2696 apr_int64_t mergeinfo_end;
2698 /* If TO_PATH already existed prior to the copy, note that this
2699 operation is a replacement, not an addition. */
2700 if (to_parent_path->node)
2702 kind = svn_fs_path_change_replace;
2703 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2704 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2705 to_parent_path->node));
2709 kind = svn_fs_path_change_add;
2710 mergeinfo_start = 0;
2713 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2714 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2717 /* Make sure the target node's parents are mutable. */
2718 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2721 /* Canonicalize the copyfrom path. */
2722 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2724 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2725 to_parent_path->entry,
2732 if (kind != svn_fs_path_change_add)
2733 SVN_ERR(dag_node_cache_invalidate(to_root,
2734 parent_path_path(to_parent_path,
2737 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2738 && mergeinfo_start != mergeinfo_end)
2739 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2740 mergeinfo_end - mergeinfo_start,
2743 /* Make a record of this modification in the changes table. */
2744 SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2745 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2746 svn_fs_fs__dag_get_id(new_node), kind, FALSE,
2747 FALSE, FALSE, svn_fs_fs__dag_node_kind(from_node),
2748 from_root->rev, from_canonpath, pool));
2752 /* See IZ Issue #436 */
2753 /* Copying from transaction roots not currently available.
2755 ### cmpilato todo someday: make this not so. :-) Note that
2756 when copying from mutable trees, you have to make sure that
2757 you aren't creating a cyclic graph filesystem, and a simple
2758 referencing operation won't cut it. Currently, we should not
2759 be able to reach this clause, and the interface reports that
2760 this only works from immutable trees anyway, but JimB has
2761 stated that this requirement need not be necessary in the
2764 SVN_ERR_MALFUNCTION();
2767 return SVN_NO_ERROR;
2771 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2772 If FROM_PATH is a directory, copy it recursively. Temporary
2773 allocations are from POOL.*/
2774 static svn_error_t *
2775 fs_copy(svn_fs_root_t *from_root,
2776 const char *from_path,
2777 svn_fs_root_t *to_root,
2778 const char *to_path,
2781 SVN_ERR(check_newline(to_path, pool));
2783 return svn_error_trace(copy_helper(from_root,
2784 svn_fs__canonicalize_abspath(from_path,
2787 svn_fs__canonicalize_abspath(to_path,
2793 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2794 If FROM_PATH is a directory, copy it recursively. No history is
2795 preserved. Temporary allocations are from POOL. */
2796 static svn_error_t *
2797 fs_revision_link(svn_fs_root_t *from_root,
2798 svn_fs_root_t *to_root,
2802 if (! to_root->is_txn_root)
2803 return SVN_FS__NOT_TXN(to_root);
2805 path = svn_fs__canonicalize_abspath(path, pool);
2806 return svn_error_trace(copy_helper(from_root, path, to_root, path,
2811 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2812 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2813 allocations are in POOL. */
2814 static svn_error_t *
2815 fs_copied_from(svn_revnum_t *rev_p,
2816 const char **path_p,
2817 svn_fs_root_t *root,
2823 /* There is no cached entry, look it up the old-fashioned
2825 SVN_ERR(get_dag(&node, root, path, pool));
2826 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
2827 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
2829 return SVN_NO_ERROR;
2836 /* Create the empty file PATH under ROOT. Temporary allocations are
2838 static svn_error_t *
2839 fs_make_file(svn_fs_root_t *root,
2843 parent_path_t *parent_path;
2845 const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
2847 SVN_ERR(check_newline(path, pool));
2849 path = svn_fs__canonicalize_abspath(path, pool);
2850 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2853 /* If there's already a file by that name, complain.
2854 This also catches the case of trying to make a file named `/'. */
2855 if (parent_path->node)
2856 return SVN_FS__ALREADY_EXISTS(root, path);
2858 /* Check (non-recursively) to see if path is locked; if so, check
2859 that we can use it. */
2860 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2861 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2864 /* Create the file. */
2865 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2866 SVN_ERR(svn_fs_fs__dag_make_file(&child,
2867 parent_path->parent->node,
2868 parent_path_path(parent_path->parent,
2874 /* Add this file to the path cache. */
2875 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2878 /* Make a record of this modification in the changes table. */
2879 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2880 svn_fs_path_change_add, TRUE, FALSE, FALSE,
2881 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
2885 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2886 allocations are in POOL. */
2887 static svn_error_t *
2888 fs_file_length(svn_filesize_t *length_p,
2889 svn_fs_root_t *root,
2895 /* First create a dag_node_t from the root/path pair. */
2896 SVN_ERR(get_dag(&file, root, path, pool));
2898 /* Now fetch its length */
2899 return svn_fs_fs__dag_file_length(length_p, file, pool);
2903 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2904 NULL if that information isn't available. Temporary allocations
2906 static svn_error_t *
2907 fs_file_checksum(svn_checksum_t **checksum,
2908 svn_checksum_kind_t kind,
2909 svn_fs_root_t *root,
2915 SVN_ERR(get_dag(&file, root, path, pool));
2916 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2920 /* --- Machinery for svn_fs_file_contents() --- */
2922 /* Set *CONTENTS to a readable stream that will return the contents of
2923 PATH under ROOT. The stream is allocated in POOL. */
2924 static svn_error_t *
2925 fs_file_contents(svn_stream_t **contents,
2926 svn_fs_root_t *root,
2931 svn_stream_t *file_stream;
2933 /* First create a dag_node_t from the root/path pair. */
2934 SVN_ERR(get_dag(&node, root, path, pool));
2936 /* Then create a readable stream from the dag_node_t. */
2937 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2939 *contents = file_stream;
2940 return SVN_NO_ERROR;
2943 /* --- End machinery for svn_fs_file_contents() --- */
2946 /* --- Machinery for svn_fs_try_process_file_contents() --- */
2948 static svn_error_t *
2949 fs_try_process_file_contents(svn_boolean_t *success,
2950 svn_fs_root_t *root,
2952 svn_fs_process_contents_func_t processor,
2957 SVN_ERR(get_dag(&node, root, path, pool));
2959 return svn_fs_fs__dag_try_process_file_contents(success, node,
2960 processor, baton, pool);
2963 /* --- End machinery for svn_fs_try_process_file_contents() --- */
2966 /* --- Machinery for svn_fs_apply_textdelta() --- */
2969 /* Local baton type for all the helper functions below. */
2970 typedef struct txdelta_baton_t
2972 /* This is the custom-built window consumer given to us by the delta
2973 library; it uniquely knows how to read data from our designated
2974 "source" stream, interpret the window, and write data to our
2975 designated "target" stream (in this case, our repos file.) */
2976 svn_txdelta_window_handler_t interpreter;
2977 void *interpreter_baton;
2979 /* The original file info */
2980 svn_fs_root_t *root;
2983 /* Derived from the file info */
2986 svn_stream_t *source_stream;
2987 svn_stream_t *target_stream;
2989 /* MD5 digest for the base text against which a delta is to be
2990 applied, and for the resultant fulltext, respectively. Either or
2991 both may be null, in which case ignored. */
2992 svn_checksum_t *base_checksum;
2993 svn_checksum_t *result_checksum;
2995 /* Pool used by db txns */
3001 /* The main window handler returned by svn_fs_apply_textdelta. */
3002 static svn_error_t *
3003 window_consumer(svn_txdelta_window_t *window, void *baton)
3005 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3007 /* Send the window right through to the custom window interpreter.
3008 In theory, the interpreter will then write more data to
3009 cb->target_string. */
3010 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3012 /* Is the window NULL? If so, we're done. The stream has already been
3013 closed by the interpreter. */
3015 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
3018 return SVN_NO_ERROR;
3021 /* Helper function for fs_apply_textdelta. BATON is of type
3023 static svn_error_t *
3024 apply_textdelta(void *baton, apr_pool_t *pool)
3026 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3027 parent_path_t *parent_path;
3028 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
3030 /* Call open_path with no flags, as we want this to return an error
3031 if the node for which we are searching doesn't exist. */
3032 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
3034 /* Check (non-recursively) to see if path is locked; if so, check
3035 that we can use it. */
3036 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3037 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
3038 FALSE, FALSE, pool));
3040 /* Now, make sure this path is mutable. */
3041 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
3042 tb->node = parent_path->node;
3044 if (tb->base_checksum)
3046 svn_checksum_t *checksum;
3048 /* Until we finalize the node, its data_key points to the old
3049 contents, in other words, the base text. */
3050 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
3051 tb->base_checksum->kind, pool));
3052 if (!svn_checksum_match(tb->base_checksum, checksum))
3053 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
3054 _("Base checksum mismatch on '%s'"),
3058 /* Make a readable "source" stream out of the current contents of
3059 ROOT/PATH; obviously, this must done in the context of a db_txn.
3060 The stream is returned in tb->source_stream. */
3061 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
3062 tb->node, tb->pool));
3064 /* Make a writable "target" stream */
3065 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
3068 /* Now, create a custom window handler that uses our two streams. */
3069 svn_txdelta_apply(tb->source_stream,
3075 &(tb->interpreter_baton));
3077 /* Make a record of this modification in the changes table. */
3078 return add_change(tb->root->fs, txn_id, tb->path,
3079 svn_fs_fs__dag_get_id(tb->node),
3080 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3081 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3085 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
3086 that will accept text delta windows to modify the contents of PATH
3087 under ROOT. Allocations are in POOL. */
3088 static svn_error_t *
3089 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3090 void **contents_baton_p,
3091 svn_fs_root_t *root,
3093 svn_checksum_t *base_checksum,
3094 svn_checksum_t *result_checksum,
3097 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3100 tb->path = svn_fs__canonicalize_abspath(path, pool);
3102 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3103 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3105 SVN_ERR(apply_textdelta(tb, pool));
3107 *contents_p = window_consumer;
3108 *contents_baton_p = tb;
3109 return SVN_NO_ERROR;
3112 /* --- End machinery for svn_fs_apply_textdelta() --- */
3114 /* --- Machinery for svn_fs_apply_text() --- */
3116 /* Baton for svn_fs_apply_text(). */
3119 /* The original file info */
3120 svn_fs_root_t *root;
3123 /* Derived from the file info */
3126 /* The returned stream that will accept the file's new contents. */
3127 svn_stream_t *stream;
3129 /* The actual fs stream that the returned stream will write to. */
3130 svn_stream_t *file_stream;
3132 /* MD5 digest for the final fulltext written to the file. May
3133 be null, in which case ignored. */
3134 svn_checksum_t *result_checksum;
3136 /* Pool used by db txns */
3141 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
3142 * fulltext data, not text deltas. Closes BATON->file_stream.
3144 * Note: If you're confused about how this function relates to another
3145 * of similar name, think of it this way:
3147 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3148 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3151 /* Write function for the publically returned stream. */
3152 static svn_error_t *
3153 text_stream_writer(void *baton,
3157 struct text_baton_t *tb = baton;
3159 /* Psst, here's some data. Pass it on to the -real- file stream. */
3160 return svn_stream_write(tb->file_stream, data, len);
3163 /* Close function for the publically returned stream. */
3164 static svn_error_t *
3165 text_stream_closer(void *baton)
3167 struct text_baton_t *tb = baton;
3169 /* Close the internal-use stream. ### This used to be inside of
3170 txn_body_fulltext_finalize_edits(), but that invoked a nested
3171 Berkeley DB transaction -- scandalous! */
3172 SVN_ERR(svn_stream_close(tb->file_stream));
3174 /* Need to tell fs that we're done sending text */
3175 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
3180 /* Helper function for fs_apply_text. BATON is of type
3182 static svn_error_t *
3183 apply_text(void *baton, apr_pool_t *pool)
3185 struct text_baton_t *tb = baton;
3186 parent_path_t *parent_path;
3187 const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
3189 /* Call open_path with no flags, as we want this to return an error
3190 if the node for which we are searching doesn't exist. */
3191 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
3193 /* Check (non-recursively) to see if path is locked; if so, check
3194 that we can use it. */
3195 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3196 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
3197 FALSE, FALSE, pool));
3199 /* Now, make sure this path is mutable. */
3200 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
3201 tb->node = parent_path->node;
3203 /* Make a writable stream for replacing the file's text. */
3204 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
3207 /* Create a 'returnable' stream which writes to the file_stream. */
3208 tb->stream = svn_stream_create(tb, tb->pool);
3209 svn_stream_set_write(tb->stream, text_stream_writer);
3210 svn_stream_set_close(tb->stream, text_stream_closer);
3212 /* Make a record of this modification in the changes table. */
3213 return add_change(tb->root->fs, txn_id, tb->path,
3214 svn_fs_fs__dag_get_id(tb->node),
3215 svn_fs_path_change_modify, TRUE, FALSE, FALSE,
3216 svn_node_file, SVN_INVALID_REVNUM, NULL, pool);
3220 /* Return a writable stream that will set the contents of PATH under
3221 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
3222 Temporary allocations are in POOL. */
3223 static svn_error_t *
3224 fs_apply_text(svn_stream_t **contents_p,
3225 svn_fs_root_t *root,
3227 svn_checksum_t *result_checksum,
3230 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3233 tb->path = svn_fs__canonicalize_abspath(path, pool);
3235 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3237 SVN_ERR(apply_text(tb, pool));
3239 *contents_p = tb->stream;
3240 return SVN_NO_ERROR;
3243 /* --- End machinery for svn_fs_apply_text() --- */
3246 /* Check if the contents of PATH1 under ROOT1 are different from the
3247 contents of PATH2 under ROOT2. If they are different set
3248 *CHANGED_P to TRUE, otherwise set it to FALSE. */
3249 static svn_error_t *
3250 fs_contents_changed(svn_boolean_t *changed_p,
3251 svn_fs_root_t *root1,
3253 svn_fs_root_t *root2,
3255 svn_boolean_t strict,
3258 dag_node_t *node1, *node2;
3260 /* Check that roots are in the same fs. */
3261 if (root1->fs != root2->fs)
3262 return svn_error_create
3263 (SVN_ERR_FS_GENERAL, NULL,
3264 _("Cannot compare file contents between two different filesystems"));
3266 SVN_ERR(get_dag(&node1, root1, path1, pool));
3267 /* Make sure that path is file. */
3268 if (svn_fs_fs__dag_node_kind(node1) != svn_node_file)
3269 return svn_error_createf
3270 (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path1);
3272 SVN_ERR(get_dag(&node2, root2, path2, pool));
3273 /* Make sure that path is file. */
3274 if (svn_fs_fs__dag_node_kind(node2) != svn_node_file)
3275 return svn_error_createf
3276 (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path2);
3278 return svn_fs_fs__dag_things_different(NULL, changed_p,
3279 node1, node2, strict, pool);
3284 /* Public interface to computing file text deltas. */
3286 static svn_error_t *
3287 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
3288 svn_fs_root_t *source_root,
3289 const char *source_path,
3290 svn_fs_root_t *target_root,
3291 const char *target_path,
3294 dag_node_t *source_node, *target_node;
3296 if (source_root && source_path)
3297 SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
3300 SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
3302 /* Create a delta stream that turns the source into the target. */
3303 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
3309 /* Finding Changes */
3311 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
3312 descriptions of the paths changed under ROOT. The hash is keyed
3313 with const char * paths and has svn_fs_path_change2_t * values. Use
3314 POOL for all allocations. */
3315 static svn_error_t *
3316 fs_paths_changed(apr_hash_t **changed_paths_p,
3317 svn_fs_root_t *root,
3320 if (root->is_txn_root)
3321 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
3322 root_txn_id(root), pool);
3324 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3329 /* Copy the contents of ENTRY at PATH with LEN to OUTPUT. */
3331 convert_path_change(svn_fs_path_change3_t *output,
3334 svn_fs_path_change2_t *entry)
3336 output->path.data = path;
3337 output->path.len = path_len;
3338 output->change_kind = entry->change_kind;
3339 output->node_kind = entry->node_kind;
3340 output->text_mod = entry->text_mod;
3341 output->prop_mod = entry->prop_mod;
3342 output->mergeinfo_mod = entry->mergeinfo_mod;
3343 output->copyfrom_known = entry->copyfrom_known;
3344 output->copyfrom_rev = entry->copyfrom_rev;
3345 output->copyfrom_path = entry->copyfrom_path;
3348 /* FSAP data structure for in-txn changes list iterators. */
3349 typedef struct fs_txn_changes_iterator_data_t
3351 /* Current iterator position. */
3352 apr_hash_index_t *hi;
3354 /* For efficiency such that we don't need to dynamically allocate
3355 yet another copy of that data. */
3356 svn_fs_path_change3_t change;
3357 } fs_txn_changes_iterator_data_t;
3359 /* Implement changes_iterator_vtable_t.get for in-txn change lists. */
3360 static svn_error_t *
3361 fs_txn_changes_iterator_get(svn_fs_path_change3_t **change,
3362 svn_fs_path_change_iterator_t *iterator)
3364 fs_txn_changes_iterator_data_t *data = iterator->fsap_data;
3371 apr_hash_this(data->hi, &key, &length, &value);
3373 convert_path_change(&data->change, key, length, value);
3375 *change = &data->change;
3376 data->hi = apr_hash_next(data->hi);
3383 return SVN_NO_ERROR;
3386 static changes_iterator_vtable_t txn_changes_iterator_vtable =
3388 fs_txn_changes_iterator_get
3391 /* FSAP data structure for in-revision changes list iterators. */
3392 typedef struct fs_revision_changes_iterator_data_t
3394 /* Context that tells the lower layers from where to fetch the next
3395 block of changes. */
3396 svn_fs_fs__changes_context_t *context;
3398 /* Changes to send. */
3399 apr_array_header_t *changes;
3401 /* Current indexes within CHANGES. */
3404 /* For efficiency such that we don't need to dynamically allocate
3405 yet another copy of that data. */
3406 svn_fs_path_change3_t change;
3408 /* A cleanable scratch pool in case we need one.
3409 No further sub-pool creation necessary. */
3410 apr_pool_t *scratch_pool;
3411 } fs_revision_changes_iterator_data_t;
3413 /* Implement changes_iterator_vtable_t.get for in-revision change lists. */
3414 static svn_error_t *
3415 fs_revision_changes_iterator_get(svn_fs_path_change3_t **change,
3416 svn_fs_path_change_iterator_t *iterator)
3418 fs_revision_changes_iterator_data_t *data = iterator->fsap_data;
3420 /* If we exhausted our block of changes and did not reach the end of the
3421 list, yet, fetch the next block. Note that that block may be empty. */
3422 if ((data->idx >= data->changes->nelts) && !data->context->eol)
3424 apr_pool_t *changes_pool = data->changes->pool;
3426 /* Drop old changes block, read new block. */
3427 svn_pool_clear(changes_pool);
3428 SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
3429 changes_pool, data->scratch_pool));
3432 /* Immediately release any temporary data. */
3433 svn_pool_clear(data->scratch_pool);
3436 if (data->idx < data->changes->nelts)
3438 change_t *entry = APR_ARRAY_IDX(data->changes, data->idx, change_t *);
3439 convert_path_change(&data->change, entry->path.data, entry->path.len,
3442 *change = &data->change;
3450 return SVN_NO_ERROR;
3453 static changes_iterator_vtable_t rev_changes_iterator_vtable =
3455 fs_revision_changes_iterator_get
3458 static svn_error_t *
3459 fs_report_changes(svn_fs_path_change_iterator_t **iterator,
3460 svn_fs_root_t *root,
3461 apr_pool_t *result_pool,
3462 apr_pool_t *scratch_pool)
3464 svn_fs_path_change_iterator_t *result = apr_pcalloc(result_pool,
3466 if (root->is_txn_root)
3468 fs_txn_changes_iterator_data_t *data = apr_pcalloc(result_pool,
3470 apr_hash_t *changed_paths;
3471 SVN_ERR(svn_fs_fs__txn_changes_fetch(&changed_paths, root->fs,
3472 root_txn_id(root), result_pool));
3474 data->hi = apr_hash_first(result_pool, changed_paths);
3475 result->fsap_data = data;
3476 result->vtable = &txn_changes_iterator_vtable;
3480 /* The block of changes that we retrieve need to live in a separately
3482 apr_pool_t *changes_pool = svn_pool_create(result_pool);
3484 /* Our iteration context info. */
3485 fs_revision_changes_iterator_data_t *data = apr_pcalloc(result_pool,
3488 /* This pool must remain valid as long as ITERATOR lives but will
3489 be used only for temporary allocations and will be cleaned up
3490 frequently. So, this must be a sub-pool of RESULT_POOL. */
3491 data->scratch_pool = svn_pool_create(result_pool);
3493 /* Fetch the first block of data. */
3494 SVN_ERR(svn_fs_fs__create_changes_context(&data->context,
3495 root->fs, root->rev,
3497 SVN_ERR(svn_fs_fs__get_changes(&data->changes, data->context,
3498 changes_pool, scratch_pool));
3500 /* Return the fully initialized object. */
3501 result->fsap_data = data;
3502 result->vtable = &rev_changes_iterator_vtable;
3507 return SVN_NO_ERROR;
3511 /* Our coolio opaque history object. */
3512 typedef struct fs_history_data_t
3514 /* filesystem object */
3517 /* path and revision of historical location */
3519 svn_revnum_t revision;
3521 /* internal-use hints about where to resume the history search. */
3522 const char *path_hint;
3523 svn_revnum_t rev_hint;
3525 /* FALSE until the first call to svn_fs_history_prev(). */
3526 svn_boolean_t is_interesting;
3528 /* If not SVN_INVALID_REVISION, we know that the next copy operation
3529 is at this revision. */
3530 svn_revnum_t next_copy;
3532 /* If not NULL, this is the noderev ID of PATH@REVISION. */
3533 const svn_fs_id_t *current_id;
3535 } fs_history_data_t;
3537 static svn_fs_history_t *
3538 assemble_history(svn_fs_t *fs,
3540 svn_revnum_t revision,
3541 svn_boolean_t is_interesting,
3542 const char *path_hint,
3543 svn_revnum_t rev_hint,
3544 svn_revnum_t next_copy,
3545 const svn_fs_id_t *current_id,
3549 /* Set *HISTORY_P to an opaque node history object which represents
3550 PATH under ROOT. ROOT must be a revision root. Use POOL for all
3552 static svn_error_t *
3553 fs_node_history(svn_fs_history_t **history_p,
3554 svn_fs_root_t *root,
3556 apr_pool_t *result_pool,
3557 apr_pool_t *scratch_pool)
3559 svn_node_kind_t kind;
3561 /* We require a revision root. */
3562 if (root->is_txn_root)
3563 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3565 /* And we require that the path exist in the root. */
3566 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, scratch_pool));
3567 if (kind == svn_node_none)
3568 return SVN_FS__NOT_FOUND(root, path);
3570 /* Okay, all seems well. Build our history object and return it. */
3571 *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
3572 SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
3574 return SVN_NO_ERROR;
3577 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3578 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3579 Perform all allocations in POOL. */
3580 static svn_error_t *
3581 find_youngest_copyroot(svn_revnum_t *rev_p,
3582 const char **path_p,
3584 parent_path_t *parent_path,
3587 svn_revnum_t rev_mine;
3588 svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3589 const char *path_mine;
3590 const char *path_parent = NULL;
3592 /* First find our parent's youngest copyroot. */
3593 if (parent_path->parent)
3594 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3595 parent_path->parent, pool));
3597 /* Find our copyroot. */
3598 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3599 parent_path->node));
3601 /* If a parent and child were copied to in the same revision, prefer
3602 the child copy target, since it is the copy relevant to the
3603 history of the child. */
3604 if (rev_mine >= rev_parent)
3607 *path_p = path_mine;
3611 *rev_p = rev_parent;
3612 *path_p = path_parent;
3615 return SVN_NO_ERROR;
3619 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3620 const char **path_p,
3621 svn_fs_root_t *root,
3625 svn_fs_t *fs = root->fs;
3626 parent_path_t *parent_path, *copy_dst_parent_path;
3627 svn_revnum_t copy_dst_rev, created_rev;
3628 const char *copy_dst_path;
3629 svn_fs_root_t *copy_dst_root;
3630 dag_node_t *copy_dst_node;
3632 /* Initialize return values. */
3636 path = svn_fs__canonicalize_abspath(path, pool);
3637 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
3639 /* Find the youngest copyroot in the path of this node-rev, which
3640 will indicate the target of the innermost copy affecting the
3642 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
3643 fs, parent_path, pool));
3644 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
3645 return SVN_NO_ERROR;
3647 /* It is possible that this node was created from scratch at some
3648 revision between COPY_DST_REV and REV. Make sure that PATH
3649 exists as of COPY_DST_REV and is related to this node-rev. */
3650 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3651 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3652 open_path_node_only | open_path_allow_null, FALSE, pool));
3653 if (copy_dst_parent_path == NULL)
3654 return SVN_NO_ERROR;
3656 copy_dst_node = copy_dst_parent_path->node;
3657 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3658 svn_fs_fs__dag_get_id(parent_path->node)))
3659 return SVN_NO_ERROR;
3661 /* One final check must be done here. If you copy a directory and
3662 create a new entity somewhere beneath that directory in the same
3663 txn, then we can't claim that the copy affected the new entity.
3664 For example, if you do:
3667 create dir2/new-thing
3670 then dir2/new-thing was not affected by the copy of dir1 to dir2.
3671 We detect this situation by asking if PATH@COPY_DST_REV's
3672 created-rev is COPY_DST_REV, and that node-revision has no
3673 predecessors, then there is no relevant closest copy.
3675 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3676 if (created_rev == copy_dst_rev)
3678 const svn_fs_id_t *pred;
3679 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3681 return SVN_NO_ERROR;
3684 /* The copy destination checks out. Return it. */
3685 *root_p = copy_dst_root;
3686 *path_p = copy_dst_path;
3687 return SVN_NO_ERROR;
3691 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3692 represent the location at which PATH in FS was located immediately
3693 prior to REVISION iff there was a copy operation (to PATH or one of
3694 its parent directories) between that previous location and
3697 If there was no such copy operation in that portion of PATH's
3698 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
3699 static svn_error_t *
3700 prev_location(const char **prev_path,
3701 svn_revnum_t *prev_rev,
3703 svn_fs_root_t *root,
3707 const char *copy_path, *copy_src_path, *remainder_path;
3708 svn_fs_root_t *copy_root;
3709 svn_revnum_t copy_src_rev;
3711 /* Ask about the most recent copy which affected PATH@REVISION. If
3712 there was no such copy, we're done. */
3713 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
3716 *prev_rev = SVN_INVALID_REVNUM;
3718 return SVN_NO_ERROR;
3721 /* Ultimately, it's not the path of the closest copy's source that
3722 we care about -- it's our own path's location in the copy source
3723 revision. So we'll tack the relative path that expresses the
3724 difference between the copy destination and our path in the copy
3725 revision onto the copy source path to determine this information.
3727 In other words, if our path is "/branches/my-branch/foo/bar", and
3728 we know that the closest relevant copy was a copy of "/trunk" to
3729 "/branches/my-branch", then that relative path under the copy
3730 destination is "/foo/bar". Tacking that onto the copy source
3731 path tells us that our path was located at "/trunk/foo/bar"
3734 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
3735 copy_root, copy_path, pool));
3736 remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3737 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3738 *prev_rev = copy_src_rev;
3739 return SVN_NO_ERROR;
3743 static svn_error_t *
3744 fs_node_origin_rev(svn_revnum_t *revision,
3745 svn_fs_root_t *root,
3749 svn_fs_t *fs = root->fs;
3750 const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3751 const svn_fs_fs__id_part_t *node_id;
3753 path = svn_fs__canonicalize_abspath(path, pool);
3755 /* Check the cache first. */
3756 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3757 node_id = svn_fs_fs__id_node_id(given_noderev_id);
3759 /* Is it a brand new uncommitted node or a new-style node ID?
3760 * (committed old-style nodes will have a 0 revision value;
3761 * rev 0, number 0 is rev 0 root node). Note that != 0 includes
3762 * SVN_INVALID_REVNUM for uncommitted nodes. */
3763 if (node_id->revision != 0 || node_id->number == 0)
3765 *revision = node_id->revision;
3766 return SVN_NO_ERROR;
3769 /* OK, it's an old-style ID? Maybe it's cached. */
3770 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3774 if (cached_origin_id != NULL)
3776 *revision = svn_fs_fs__id_rev(cached_origin_id);
3777 return SVN_NO_ERROR;
3781 /* Ah well, the answer isn't in the ID itself or in the cache.
3782 Let's actually calculate it, then. */
3783 svn_fs_root_t *curroot = root;
3784 apr_pool_t *subpool = svn_pool_create(pool);
3785 apr_pool_t *predidpool = svn_pool_create(pool);
3786 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3787 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3789 const svn_fs_id_t *pred_id;
3791 /* Walk the closest-copy chain back to the first copy in our history.
3793 NOTE: We merely *assume* that this is faster than walking the
3794 predecessor chain, because we *assume* that copies of parent
3795 directories happen less often than modifications to a given item. */
3798 svn_revnum_t currev;
3799 const char *curpath = lastpath->data;
3801 svn_pool_clear(subpool);
3803 /* Get a root pointing to LASTREV. (The first time around,
3804 LASTREV is invalid, but that's cool because CURROOT is
3805 already initialized.) */
3806 if (SVN_IS_VALID_REVNUM(lastrev))
3807 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3809 /* Find the previous location using the closest-copy shortcut. */
3810 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3815 /* Update our LASTPATH and LASTREV variables (which survive
3817 svn_stringbuf_set(lastpath, curpath);
3821 /* Walk the predecessor links back to origin. */
3822 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3825 svn_pool_clear(subpool);
3826 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3828 /* Why not just fetch the predecessor ID in PREDIDPOOL?
3829 Because svn_fs_fs__dag_get_predecessor_id() doesn't
3830 necessarily honor the passed-in pool, and might return a
3831 value cached in the node (which is allocated in
3832 SUBPOOL... maybe). */
3833 svn_pool_clear(predidpool);
3834 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3835 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3839 /* When we get here, NODE should be the first node-revision in our
3841 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3843 /* Wow, I don't want to have to do all that again. Let's cache
3845 if (node_id->revision != SVN_INVALID_REVNUM)
3846 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3847 svn_fs_fs__dag_get_id(node), pool));
3849 svn_pool_destroy(subpool);
3850 svn_pool_destroy(predidpool);
3851 return SVN_NO_ERROR;
3856 static svn_error_t *
3857 history_prev(svn_fs_history_t **prev_history,
3858 svn_fs_history_t *history,
3859 svn_boolean_t cross_copies,
3860 apr_pool_t *result_pool,
3861 apr_pool_t *scratch_pool)
3863 fs_history_data_t *fhd = history->fsap_data;
3864 const char *commit_path, *src_path, *path = fhd->path;
3865 svn_revnum_t commit_rev, src_rev, dst_rev;
3866 svn_revnum_t revision = fhd->revision;
3867 svn_fs_t *fs = fhd->fs;
3868 parent_path_t *parent_path;
3870 svn_fs_root_t *root;
3871 svn_boolean_t reported = fhd->is_interesting;
3872 svn_revnum_t copyroot_rev;
3873 const char *copyroot_path;
3874 const svn_fs_id_t *pred_id = NULL;
3876 /* Initialize our return value. */
3877 *prev_history = NULL;
3879 /* When following history, there tend to be long sections of linear
3880 history where there are no copies at PATH or its parents. Within
3881 these sections, we only need to follow the node history. */
3882 if ( SVN_IS_VALID_REVNUM(fhd->next_copy)
3883 && revision > fhd->next_copy
3886 /* We know the last reported node (CURRENT_ID) and the NEXT_COPY
3887 revision is somewhat further in the past. */
3888 node_revision_t *noderev;
3891 /* Get the previous node change. If there is none, then we already
3892 reported the initial addition and this history traversal is done. */
3893 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, fhd->current_id,
3894 scratch_pool, scratch_pool));
3895 if (! noderev->predecessor_id)
3896 return SVN_NO_ERROR;
3898 /* If the previous node change is younger than the next copy, it is
3899 part of the linear history section. */
3900 commit_rev = svn_fs_fs__id_rev(noderev->predecessor_id);
3901 if (commit_rev > fhd->next_copy)
3903 /* Within the linear history, simply report all node changes and
3904 continue with the respective predecessor. */
3905 *prev_history = assemble_history(fs, noderev->created_path,
3906 commit_rev, TRUE, NULL,
3909 noderev->predecessor_id,
3912 return SVN_NO_ERROR;
3915 /* We hit a copy. Fall back to the standard code path. */
3918 /* If our last history report left us hints about where to pickup
3919 the chase, then our last report was on the destination of a
3920 copy. If we are crossing copies, start from those locations,
3921 otherwise, we're all done here. */
3922 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3926 return SVN_NO_ERROR;
3927 path = fhd->path_hint;
3928 revision = fhd->rev_hint;
3931 /* Construct a ROOT for the current revision. */
3932 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
3934 /* Open PATH/REVISION, and get its node and a bunch of other
3936 SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
3937 node = parent_path->node;
3938 commit_path = svn_fs_fs__dag_get_created_path(node);
3939 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3941 /* The Subversion filesystem is written in such a way that a given
3942 line of history may have at most one interesting history point
3943 per filesystem revision. Either that node was edited (and
3944 possibly copied), or it was copied but not edited. And a copy
3945 source cannot be from the same revision as its destination. So,
3946 if our history revision matches its node's commit revision, we
3948 if (revision == commit_rev)
3952 /* ... we either have not yet reported on this revision (and
3953 need now to do so) ... */
3954 *prev_history = assemble_history(fs, commit_path,
3955 commit_rev, TRUE, NULL,
3957 SVN_INVALID_REVNUM, NULL,
3959 return SVN_NO_ERROR;
3963 /* ... or we *have* reported on this revision, and must now
3964 progress toward this node's predecessor (unless there is
3965 no predecessor, in which case we're all done!). */
3966 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3968 return SVN_NO_ERROR;
3970 /* Replace NODE and friends with the information from its
3972 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
3973 commit_path = svn_fs_fs__dag_get_created_path(node);
3974 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
3978 /* Find the youngest copyroot in the path of this node, including
3980 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3981 parent_path, scratch_pool));
3983 /* Initialize some state variables. */
3985 src_rev = SVN_INVALID_REVNUM;
3986 dst_rev = SVN_INVALID_REVNUM;
3988 if (copyroot_rev > commit_rev)
3990 const char *remainder_path;
3991 const char *copy_dst, *copy_src;
3992 svn_fs_root_t *copyroot_root;
3994 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3996 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, scratch_pool));
3997 copy_dst = svn_fs_fs__dag_get_created_path(node);
3999 /* If our current path was the very destination of the copy,
4000 then our new current path will be the copy source. If our
4001 current path was instead the *child* of the destination of
4002 the copy, then figure out its previous location by taking its
4003 path relative to the copy destination and appending that to
4004 the copy source. Finally, if our current path doesn't meet
4005 one of these other criteria ... ### for now just fallback to
4006 the old copy hunt algorithm. */
4007 remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
4011 /* If we get here, then our current path is the destination
4012 of, or the child of the destination of, a copy. Fill
4013 in the return values and get outta here. */
4014 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
4015 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
4017 dst_rev = copyroot_rev;
4018 src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
4022 /* If we calculated a copy source path and revision, we'll make a
4023 'copy-style' history object. */
4024 if (src_path && SVN_IS_VALID_REVNUM(src_rev))
4026 svn_boolean_t retry = FALSE;
4028 /* It's possible for us to find a copy location that is the same
4029 as the history point we've just reported. If that happens,
4030 we simply need to take another trip through this history
4032 if ((dst_rev == revision) && reported)
4035 *prev_history = assemble_history(fs, path, dst_rev, ! retry,
4037 SVN_INVALID_REVNUM, NULL,
4042 /* We know the next copy revision. If we are not at the copy rev
4043 itself, we will also know the predecessor node ID and the next
4044 invocation will use the optimized "linear history" code path. */
4045 *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
4046 NULL, SVN_INVALID_REVNUM,
4047 copyroot_rev, pred_id, result_pool);
4050 return SVN_NO_ERROR;
4054 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
4055 svn_fs_history_t object that represents the predecessory of
4056 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
4057 only through a copy operation. Perform all allocations in POOL. */
4058 static svn_error_t *
4059 fs_history_prev(svn_fs_history_t **prev_history_p,
4060 svn_fs_history_t *history,
4061 svn_boolean_t cross_copies,
4062 apr_pool_t *result_pool,
4063 apr_pool_t *scratch_pool)
4065 svn_fs_history_t *prev_history = NULL;
4066 fs_history_data_t *fhd = history->fsap_data;
4067 svn_fs_t *fs = fhd->fs;
4069 /* Special case: the root directory changes in every single
4070 revision, no exceptions. And, the root can't be the target (or
4071 child of a target -- duh) of a copy. So, if that's our path,
4072 then we need only decrement our revision by 1, and there you go. */
4073 if (strcmp(fhd->path, "/") == 0)
4075 if (! fhd->is_interesting)
4076 prev_history = assemble_history(fs, "/", fhd->revision,
4077 1, NULL, SVN_INVALID_REVNUM,
4078 SVN_INVALID_REVNUM, NULL,
4080 else if (fhd->revision > 0)
4081 prev_history = assemble_history(fs, "/", fhd->revision - 1,
4082 1, NULL, SVN_INVALID_REVNUM,
4083 SVN_INVALID_REVNUM, NULL,
4088 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4089 prev_history = history;
4093 svn_pool_clear(iterpool);
4094 SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
4095 result_pool, iterpool));
4099 fhd = prev_history->fsap_data;
4100 if (fhd->is_interesting)
4104 svn_pool_destroy(iterpool);
4107 *prev_history_p = prev_history;
4108 return SVN_NO_ERROR;
4112 /* Set *PATH and *REVISION to the path and revision for the HISTORY
4113 object. Use POOL for all allocations. */
4114 static svn_error_t *
4115 fs_history_location(const char **path,
4116 svn_revnum_t *revision,
4117 svn_fs_history_t *history,
4120 fs_history_data_t *fhd = history->fsap_data;
4122 *path = apr_pstrdup(pool, fhd->path);
4123 *revision = fhd->revision;
4124 return SVN_NO_ERROR;
4127 static history_vtable_t history_vtable = {
4132 /* Return a new history object (marked as "interesting") for PATH and
4133 REVISION, allocated in POOL, and with its members set to the values
4134 of the parameters provided. Note that PATH and PATH_HINT get
4135 normalized and duplicated in POOL. */
4136 static svn_fs_history_t *
4137 assemble_history(svn_fs_t *fs,
4139 svn_revnum_t revision,
4140 svn_boolean_t is_interesting,
4141 const char *path_hint,
4142 svn_revnum_t rev_hint,
4143 svn_revnum_t next_copy,
4144 const svn_fs_id_t *current_id,
4147 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4148 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
4149 fhd->path = svn_fs__canonicalize_abspath(path, pool);
4150 fhd->revision = revision;
4151 fhd->is_interesting = is_interesting;
4152 fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
4154 fhd->rev_hint = rev_hint;
4155 fhd->next_copy = next_copy;
4156 fhd->current_id = current_id ? svn_fs_fs__id_copy(current_id, pool) : NULL;
4159 history->vtable = &history_vtable;
4160 history->fsap_data = fhd;
4165 /* mergeinfo queries */
4168 /* DIR_DAG is a directory DAG node which has mergeinfo in its
4169 descendants. This function iterates over its children. For each
4170 child with immediate mergeinfo, call RECEIVER with it and BATON.
4171 For each child with descendants with mergeinfo, it recurses. Note
4172 that it does *not* call the action on the path for DIR_DAG itself.
4174 SCRATCH_POOL is used for temporary allocations, including the mergeinfo
4175 hashes passed to actions.
4177 static svn_error_t *
4178 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
4179 const char *this_path,
4180 dag_node_t *dir_dag,
4181 svn_fs_mergeinfo_receiver_t receiver,
4183 apr_pool_t *scratch_pool)
4185 apr_array_header_t *entries;
4187 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4189 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
4190 for (i = 0; i < entries->nelts; ++i)
4192 svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4193 const char *kid_path;
4194 dag_node_t *kid_dag;
4195 svn_boolean_t has_mergeinfo, go_down;
4197 svn_pool_clear(iterpool);
4199 kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
4200 SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
4202 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
4203 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
4207 /* Save this particular node's mergeinfo. */
4208 apr_hash_t *proplist;
4209 svn_mergeinfo_t kid_mergeinfo;
4210 svn_string_t *mergeinfo_string;
4213 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
4214 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
4215 if (!mergeinfo_string)
4217 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
4218 return svn_error_createf
4219 (SVN_ERR_FS_CORRUPT, NULL,
4220 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
4224 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
4225 treat it as if no mergeinfo is present rather than raising a parse
4227 err = svn_mergeinfo_parse(&kid_mergeinfo,
4228 mergeinfo_string->data,
4232 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4233 svn_error_clear(err);
4235 return svn_error_trace(err);
4239 SVN_ERR(receiver(kid_path, kid_mergeinfo, baton, iterpool));
4244 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4252 svn_pool_destroy(iterpool);
4253 return SVN_NO_ERROR;
4256 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
4257 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
4258 will be allocated in POOL..
4261 mergeinfo_cache_key(const char *path,
4262 svn_fs_root_t *rev_root,
4263 svn_mergeinfo_inheritance_t inherit,
4264 svn_boolean_t adjust_inherited_mergeinfo,
4267 apr_int64_t number = rev_root->rev;
4269 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
4270 + (adjust_inherited_mergeinfo ? 1 : 0);
4272 return svn_fs_fs__combine_number_and_string(number, path, pool);
4275 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
4276 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
4277 The result is allocated in RESULT_POOL; SCRATCH_POOL is
4278 used for temporary allocations.
4280 static svn_error_t *
4281 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
4282 svn_fs_root_t *rev_root,
4284 svn_mergeinfo_inheritance_t inherit,
4285 svn_boolean_t adjust_inherited_mergeinfo,
4286 apr_pool_t *result_pool,
4287 apr_pool_t *scratch_pool)
4289 parent_path_t *parent_path, *nearest_ancestor;
4290 apr_hash_t *proplist;
4291 svn_string_t *mergeinfo_string;
4293 path = svn_fs__canonicalize_abspath(path, scratch_pool);
4295 SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
4297 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
4298 return SVN_NO_ERROR;
4300 if (inherit == svn_mergeinfo_nearest_ancestor)
4301 nearest_ancestor = parent_path->parent;
4303 nearest_ancestor = parent_path;
4307 svn_boolean_t has_mergeinfo;
4309 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
4310 nearest_ancestor->node));
4314 /* No need to loop if we're looking for explicit mergeinfo. */
4315 if (inherit == svn_mergeinfo_explicit)
4317 return SVN_NO_ERROR;
4320 nearest_ancestor = nearest_ancestor->parent;
4322 /* Run out? There's no mergeinfo. */
4323 if (!nearest_ancestor)
4325 return SVN_NO_ERROR;
4329 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
4331 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
4332 if (!mergeinfo_string)
4333 return svn_error_createf
4334 (SVN_ERR_FS_CORRUPT, NULL,
4335 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
4336 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
4338 /* Parse the mergeinfo; store the result in *MERGEINFO. */
4340 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
4341 treat it as if no mergeinfo is present rather than raising a parse
4343 svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
4344 mergeinfo_string->data,
4348 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4350 svn_error_clear(err);
4354 return svn_error_trace(err);
4358 /* If our nearest ancestor is the very path we inquired about, we
4359 can return the mergeinfo results directly. Otherwise, we're
4360 inheriting the mergeinfo, so we need to a) remove non-inheritable
4361 ranges and b) telescope the merged-from paths. */
4362 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
4364 svn_mergeinfo_t tmp_mergeinfo;
4366 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
4367 NULL, SVN_INVALID_REVNUM,
4368 SVN_INVALID_REVNUM, TRUE,
4369 scratch_pool, scratch_pool));
4370 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
4371 parent_path_relpath(
4372 parent_path, nearest_ancestor,
4377 return SVN_NO_ERROR;
4380 /* Caching wrapper around get_mergeinfo_for_path_internal().
4382 static svn_error_t *
4383 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
4384 svn_fs_root_t *rev_root,
4386 svn_mergeinfo_inheritance_t inherit,
4387 svn_boolean_t adjust_inherited_mergeinfo,
4388 apr_pool_t *result_pool,
4389 apr_pool_t *scratch_pool)
4391 fs_fs_data_t *ffd = rev_root->fs->fsap_data;
4392 const char *cache_key;
4393 svn_boolean_t found = FALSE;
4394 svn_stringbuf_t *mergeinfo_exists;
4398 cache_key = mergeinfo_cache_key(path, rev_root, inherit,
4399 adjust_inherited_mergeinfo, scratch_pool);
4400 if (ffd->mergeinfo_existence_cache)
4402 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
4403 ffd->mergeinfo_existence_cache,
4404 cache_key, result_pool));
4405 if (found && mergeinfo_exists->data[0] == '1')
4406 SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
4407 ffd->mergeinfo_cache,
4408 cache_key, result_pool));
4413 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
4415 adjust_inherited_mergeinfo,
4416 result_pool, scratch_pool));
4417 if (ffd->mergeinfo_existence_cache)
4419 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
4421 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
4422 cache_key, mergeinfo_exists, scratch_pool));
4424 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
4425 cache_key, *mergeinfo, scratch_pool));
4429 return SVN_NO_ERROR;
4432 /* Invoke RECEIVER with BATON for each mergeinfo found on descendants of
4433 PATH (but not PATH itself). Use SCRATCH_POOL for temporary values. */
4434 static svn_error_t *
4435 add_descendant_mergeinfo(svn_fs_root_t *root,
4437 svn_fs_mergeinfo_receiver_t receiver,
4439 apr_pool_t *scratch_pool)
4441 dag_node_t *this_dag;
4442 svn_boolean_t go_down;
4444 SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
4445 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
4448 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
4454 return SVN_NO_ERROR;
4458 /* Find all the mergeinfo for a set of PATHS under ROOT and report it
4459 through RECEIVER with BATON. INHERITED, INCLUDE_DESCENDANTS and
4460 ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
4462 Allocate temporary values are allocated in SCRATCH_POOL. */
4463 static svn_error_t *
4464 get_mergeinfos_for_paths(svn_fs_root_t *root,
4465 const apr_array_header_t *paths,
4466 svn_mergeinfo_inheritance_t inherit,
4467 svn_boolean_t include_descendants,
4468 svn_boolean_t adjust_inherited_mergeinfo,
4469 svn_fs_mergeinfo_receiver_t receiver,
4471 apr_pool_t *scratch_pool)
4473 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4476 for (i = 0; i < paths->nelts; i++)
4479 svn_mergeinfo_t path_mergeinfo;
4480 const char *path = APR_ARRAY_IDX(paths, i, const char *);
4482 svn_pool_clear(iterpool);
4484 err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
4485 inherit, adjust_inherited_mergeinfo,
4486 iterpool, iterpool);
4489 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
4491 svn_error_clear(err);
4493 path_mergeinfo = NULL;
4497 return svn_error_trace(err);
4502 SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
4503 if (include_descendants)
4504 SVN_ERR(add_descendant_mergeinfo(root, path, receiver, baton,
4507 svn_pool_destroy(iterpool);
4509 return SVN_NO_ERROR;
4513 /* Implements svn_fs_get_mergeinfo. */
4514 static svn_error_t *
4515 fs_get_mergeinfo(svn_fs_root_t *root,
4516 const apr_array_header_t *paths,
4517 svn_mergeinfo_inheritance_t inherit,
4518 svn_boolean_t include_descendants,
4519 svn_boolean_t adjust_inherited_mergeinfo,
4520 svn_fs_mergeinfo_receiver_t receiver,
4522 apr_pool_t *scratch_pool)
4524 fs_fs_data_t *ffd = root->fs->fsap_data;
4526 /* We require a revision root. */
4527 if (root->is_txn_root)
4528 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4530 /* We have to actually be able to find the mergeinfo metadata! */
4531 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
4532 return svn_error_createf
4533 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4534 _("Querying mergeinfo requires version %d of the FSFS filesystem "
4535 "schema; filesystem '%s' uses only version %d"),
4536 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4538 /* Retrieve a path -> mergeinfo hash mapping. */
4539 return get_mergeinfos_for_paths(root, paths, inherit,
4540 include_descendants,
4541 adjust_inherited_mergeinfo,
4547 /* The vtable associated with root objects. */
4548 static root_vtable_t root_vtable = {
4551 svn_fs_fs__check_path,
4555 svn_fs_fs__node_created_rev,
4557 fs_node_created_path,
4566 fs_change_node_prop,
4569 fs_dir_optimal_order,
4574 fs_try_process_file_contents,
4578 fs_contents_changed,
4579 fs_get_file_delta_stream,
4584 /* Construct a new root object in FS, allocated from POOL. */
4585 static svn_fs_root_t *
4586 make_root(svn_fs_t *fs,
4589 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4593 root->vtable = &root_vtable;
4599 /* Construct a root object referring to the root of REVISION in FS,
4600 whose root directory is ROOT_DIR. Create the new root in POOL. */
4601 static svn_fs_root_t *
4602 make_revision_root(svn_fs_t *fs,
4604 dag_node_t *root_dir,
4607 svn_fs_root_t *root = make_root(fs, pool);
4609 root->is_txn_root = FALSE;
4611 root->fsap_data = root_dir;
4617 /* Construct a root object referring to the root of the transaction
4618 named TXN and based on revision BASE_REV in FS, with FLAGS to
4619 describe transaction's behavior. Create the new root in POOL. */
4620 static svn_error_t *
4621 make_txn_root(svn_fs_root_t **root_p,
4623 const svn_fs_fs__id_part_t *txn,
4624 svn_revnum_t base_rev,
4628 svn_fs_root_t *root = make_root(fs, pool);
4629 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4632 root->is_txn_root = TRUE;
4633 root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
4634 root->txn_flags = flags;
4635 root->rev = base_rev;
4637 /* Because this cache actually tries to invalidate elements, keep
4638 the number of elements per page down.
4640 Note that since dag_node_cache_invalidate uses svn_cache__iter,
4641 this *cannot* be a memcache-based cache. */
4642 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4643 svn_fs_fs__dag_serialize,
4644 svn_fs_fs__dag_deserialize,
4645 APR_HASH_KEY_STRING,
4647 apr_pstrcat(pool, root->txn, ":TXN",
4651 /* Initialize transaction-local caches in FS.
4653 Note that we cannot put those caches in frd because that content
4654 fs root object is not available where we would need it. */
4655 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
4657 root->fsap_data = frd;
4660 return SVN_NO_ERROR;
4667 stringify_node(dag_node_t *node,
4670 /* ### TODO: print some PATH@REV to it, too. */
4671 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4674 /* Check metadata sanity on NODE, and on its children. Manually verify
4675 information for DAG nodes in revision REV, and trust the metadata
4676 accuracy for nodes belonging to older revisions. To detect cycles,
4677 provide all parent dag_node_t * in PARENT_NODES. */
4678 static svn_error_t *
4679 verify_node(dag_node_t *node,
4681 apr_array_header_t *parent_nodes,
4684 svn_boolean_t has_mergeinfo;
4685 apr_int64_t mergeinfo_count;
4686 const svn_fs_id_t *pred_id;
4687 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4689 svn_node_kind_t kind;
4690 apr_pool_t *iterpool = svn_pool_create(pool);
4693 /* Detect (non-)DAG cycles. */
4694 for (i = 0; i < parent_nodes->nelts; ++i)
4696 dag_node_t *parent = APR_ARRAY_IDX(parent_nodes, i, dag_node_t *);
4697 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(parent),
4698 svn_fs_fs__dag_get_id(node)))
4699 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4700 "Node is its own direct or indirect parent '%s'",
4701 stringify_node(node, iterpool));
4704 /* Fetch some data. */
4705 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4706 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4707 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4708 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4709 kind = svn_fs_fs__dag_node_kind(node);
4712 if (mergeinfo_count < 0)
4713 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4714 "Negative mergeinfo-count %" APR_INT64_T_FMT
4716 mergeinfo_count, stringify_node(node, iterpool));
4718 /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4722 int pred_pred_count;
4723 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4724 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4725 if (pred_pred_count+1 != pred_count)
4726 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4727 "Predecessor count mismatch: "
4728 "%s has %d, but %s has %d",
4729 stringify_node(node, iterpool), pred_count,
4730 stringify_node(pred, iterpool),
4734 /* Kind-dependent verifications. */
4735 if (kind == svn_node_none)
4737 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4738 "Node '%s' has kind 'none'",
4739 stringify_node(node, iterpool));
4741 if (kind == svn_node_file)
4743 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4744 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4745 "File node '%s' has inconsistent mergeinfo: "
4746 "has_mergeinfo=%d, "
4747 "mergeinfo_count=%" APR_INT64_T_FMT,
4748 stringify_node(node, iterpool),
4749 has_mergeinfo, mergeinfo_count);
4751 if (kind == svn_node_dir)
4753 apr_array_header_t *entries;
4754 apr_int64_t children_mergeinfo = 0;
4755 APR_ARRAY_PUSH(parent_nodes, dag_node_t*) = node;
4757 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4759 /* Compute CHILDREN_MERGEINFO. */
4760 for (i = 0; i < entries->nelts; ++i)
4762 svn_fs_dirent_t *dirent
4763 = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
4765 apr_int64_t child_mergeinfo;
4767 svn_pool_clear(iterpool);
4769 /* Compute CHILD_REV. */
4770 if (svn_fs_fs__id_rev(dirent->id) == rev)
4772 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
4774 SVN_ERR(verify_node(child, rev, parent_nodes, iterpool));
4775 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
4780 /* access mergeinfo counter with minimal overhead */
4781 node_revision_t *noderev;
4782 SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
4783 iterpool, iterpool));
4784 child_mergeinfo = noderev->mergeinfo_count;
4787 children_mergeinfo += child_mergeinfo;
4790 /* Side-effect of issue #4129. */
4791 if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4792 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4793 "Mergeinfo-count discrepancy on '%s': "
4794 "expected %" APR_INT64_T_FMT "+%d, "
4795 "counted %" APR_INT64_T_FMT,
4796 stringify_node(node, iterpool),
4797 mergeinfo_count, has_mergeinfo,
4798 children_mergeinfo);
4800 /* If we don't make it here, there was an error / corruption.
4801 * In that case, nobody will need PARENT_NODES anymore. */
4802 apr_array_pop(parent_nodes);
4805 svn_pool_destroy(iterpool);
4806 return SVN_NO_ERROR;
4810 svn_fs_fs__verify_root(svn_fs_root_t *root,
4813 svn_fs_t *fs = root->fs;
4814 dag_node_t *root_dir;
4815 apr_array_header_t *parent_nodes;
4817 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4818 (and elsewhere). This code makes more thorough checks than the
4819 commit-time checks in validate_root_noderev(). */
4821 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4824 When this code is called in the library, we want to ensure we
4825 use the on-disk data --- rather than some data that was read
4826 in the possibly-distance past and cached since. */
4828 if (root->is_txn_root)
4830 fs_txn_root_data_t *frd = root->fsap_data;
4831 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
4835 root_dir = root->fsap_data;
4838 /* Recursively verify ROOT_DIR. */
4839 parent_nodes = apr_array_make(pool, 16, sizeof(dag_node_t *));
4840 SVN_ERR(verify_node(root_dir, root->rev, parent_nodes, pool));
4842 /* Verify explicitly the predecessor of the root. */
4844 const svn_fs_id_t *pred_id;
4846 /* Only r0 should have no predecessor. */
4847 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4848 if (! root->is_txn_root && !!pred_id != !!root->rev)
4849 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4850 "r%ld's root node's predecessor is "
4851 "unexpectedly '%s'",
4854 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4856 if (root->is_txn_root && !pred_id)
4857 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4858 "Transaction '%s''s root node's predecessor is "
4859 "unexpectedly NULL",
4862 /* Check the predecessor's revision. */
4865 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4866 if (! root->is_txn_root && pred_rev+1 != root->rev)
4868 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4869 "r%ld's root node's predecessor is r%ld"
4870 " but should be r%ld",
4871 root->rev, pred_rev, root->rev - 1);
4872 if (root->is_txn_root && pred_rev != root->rev)
4873 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4874 "Transaction '%s''s root node's predecessor"
4876 " but should be r%ld",
4877 root->txn, pred_rev, root->rev);
4881 return SVN_NO_ERROR;