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"
60 #include "temp_serializer.h"
62 #include "private/svn_mergeinfo_private.h"
63 #include "private/svn_subr_private.h"
64 #include "private/svn_fs_util.h"
65 #include "private/svn_fspath.h"
66 #include "../libsvn_fs/fs-loader.h"
69 /* ### I believe this constant will become internal to reps-strings.c.
70 ### see the comment in window_consumer() for more information. */
72 /* ### the comment also seems to need tweaking: the log file stuff
73 ### is no longer an issue... */
74 /* Data written to the filesystem through the svn_fs_apply_textdelta()
75 interface is cached in memory until the end of the data stream, or
76 until a size trigger is hit. Define that trigger here (in bytes).
77 Setting the value to 0 will result in no filesystem buffering at
78 all. The value only really matters when dealing with file contents
79 bigger than the value itself. Above that point, large values here
80 allow the filesystem to buffer more data in memory before flushing
81 to the database, which increases memory usage but greatly decreases
82 the amount of disk access (and log-file generation) in database.
83 Smaller values will limit your overall memory consumption, but can
84 drastically hurt throughput by necessitating more write operations
85 to the database (which also generates more log-files). */
86 #define WRITE_BUFFER_SIZE 512000
90 /* The root structures.
92 Why do they contain different data? Well, transactions are mutable
93 enough that it isn't safe to cache the DAG node for the root
94 directory or the hash of copyfrom data: somebody else might modify
95 them concurrently on disk! (Why is the DAG node cache safer than
96 the root DAG node? When cloning transaction DAG nodes in and out
97 of the cache, all of the possibly-mutable data from the
98 node_revision_t inside the dag_node_t is dropped.) Additionally,
99 revisions are immutable enough that their DAG node cache can be
100 kept in the FS object and shared among multiple revision root
103 typedef struct fs_rev_root_data_t
105 /* A dag node for the revision's root directory. */
106 dag_node_t *root_dir;
108 /* Cache structure for mapping const char * PATH to const char
109 *COPYFROM_STRING, so that paths_changed can remember all the
110 copyfrom information in the changes file.
111 COPYFROM_STRING has the format "REV PATH", or is the empty string if
112 the path was added without history. */
113 apr_hash_t *copyfrom_cache;
115 } fs_rev_root_data_t;
117 typedef struct fs_txn_root_data_t
121 /* Cache of txn DAG nodes (without their nested noderevs, because
122 * it's mutable). Same keys/values as ffd->rev_node_cache. */
123 svn_cache__t *txn_node_cache;
124 } fs_txn_root_data_t;
126 /* Declared here to resolve the circular dependencies. */
127 static svn_error_t * get_dag(dag_node_t **dag_node_p,
132 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
133 dag_node_t *root_dir,
136 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
137 svn_fs_t *fs, const char *txn,
138 svn_revnum_t base_rev, apr_uint32_t flags,
142 /*** Node Caching ***/
144 /* 1st level cache */
146 /* An entry in the first-level cache. REVISION and PATH form the key that
147 will ultimately be matched.
149 typedef struct cache_entry_t
151 /* hash value derived from PATH, REVISION.
152 Used to short-circuit failed lookups. */
153 apr_uint32_t hash_value;
155 /* revision to which the NODE belongs */
156 svn_revnum_t revision;
158 /* path of the NODE */
161 /* cached value of strlen(PATH). */
164 /* the node allocated in the cache's pool. NULL for empty entries. */
168 /* Number of entries in the cache. Keep this low to keep pressure on the
169 CPU caches low as well. A binary value is most efficient. If we walk
170 a directory tree, we want enough entries to store nodes for all files
171 without overwriting the nodes for the parent folder. That way, there
172 will be no unnecessary misses (except for a few random ones caused by
175 The actual number of instances may be higher but entries that got
176 overwritten are no longer visible.
178 enum { BUCKET_COUNT = 256 };
180 /* The actual cache structure. All nodes will be allocated in POOL.
181 When the number of INSERTIONS (i.e. objects created form that pool)
182 exceeds a certain threshold, the pool will be cleared and the cache
185 struct fs_fs_dag_cache_t
187 /* fixed number of (possibly empty) cache entries */
188 cache_entry_t buckets[BUCKET_COUNT];
190 /* pool used for all node allocation */
193 /* number of entries created from POOL since the last cleanup */
194 apr_size_t insertions;
196 /* Property lookups etc. have a very high locality (75% re-hit).
197 Thus, remember the last hit location for optimistic lookup. */
202 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
204 fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
205 result->pool = svn_pool_create(pool);
210 /* Clears the CACHE at regular intervals (destroying all cached nodes)
213 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
215 if (cache->insertions > BUCKET_COUNT)
217 svn_pool_clear(cache->pool);
219 memset(cache->buckets, 0, sizeof(cache->buckets));
220 cache->insertions = 0;
224 /* For the given REVISION and PATH, return the respective entry in CACHE.
225 If the entry is empty, its NODE member will be NULL and the caller
226 may then set it to the corresponding DAG node allocated in CACHE->POOL.
228 static cache_entry_t *
229 cache_lookup( fs_fs_dag_cache_t *cache
230 , svn_revnum_t revision
233 apr_size_t i, bucket_index;
234 apr_size_t path_len = strlen(path);
235 apr_uint32_t hash_value = (apr_uint32_t)revision;
237 #if SVN_UNALIGNED_ACCESS_IS_OK
238 /* "randomizing" / distributing factor used in our hash function */
239 const apr_uint32_t factor = 0xd1f3da69;
242 /* optimistic lookup: hit the same bucket again? */
243 cache_entry_t *result = &cache->buckets[cache->last_hit];
244 if ( (result->revision == revision)
245 && (result->path_len == path_len)
246 && !memcmp(result->path, path, path_len))
251 /* need to do a full lookup. Calculate the hash value
252 (HASH_VALUE has been initialized to REVISION). */
254 #if SVN_UNALIGNED_ACCESS_IS_OK
255 /* We relax the dependency chain between iterations by processing
256 two chunks from the input per hash_value self-multiplication.
257 The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
258 per 2 chunks instead of 1 chunk.
260 for (; i + 8 <= path_len; i += 8)
261 hash_value = hash_value * factor * factor
262 + ( *(const apr_uint32_t*)(path + i) * factor
263 + *(const apr_uint32_t*)(path + i + 4));
266 for (; i < path_len; ++i)
267 /* Help GCC to minimize the HASH_VALUE update latency by splitting the
268 MUL 33 of the naive implementation: h = h * 33 + path[i]. This
269 shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
271 hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
273 bucket_index = hash_value + (hash_value >> 16);
274 bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
276 /* access the corresponding bucket and remember its location */
277 result = &cache->buckets[bucket_index];
278 cache->last_hit = bucket_index;
280 /* if it is *NOT* a match, clear the bucket, expect the caller to fill
281 in the node and count it as an insertion */
282 if ( (result->hash_value != hash_value)
283 || (result->revision != revision)
284 || (result->path_len != path_len)
285 || memcmp(result->path, path, path_len))
287 result->hash_value = hash_value;
288 result->revision = revision;
289 if (result->path_len < path_len)
290 result->path = apr_palloc(cache->pool, path_len + 1);
291 result->path_len = path_len;
292 memcpy(result->path, path, path_len + 1);
302 /* 2nd level cache */
304 /* Find and return the DAG node cache for ROOT and the key that
305 should be used for PATH. */
307 locate_cache(svn_cache__t **cache,
313 if (root->is_txn_root)
315 fs_txn_root_data_t *frd = root->fsap_data;
316 if (cache) *cache = frd->txn_node_cache;
317 if (key && path) *key = path;
321 fs_fs_data_t *ffd = root->fs->fsap_data;
322 if (cache) *cache = ffd->rev_node_cache;
323 if (key && path) *key
324 = svn_fs_fs__combine_number_and_string(root->rev, path, pool);
328 /* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
329 isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
331 dag_node_cache_get(dag_node_t **node_p,
337 dag_node_t *node = NULL;
341 SVN_ERR_ASSERT(*path == '/');
343 if (!root->is_txn_root)
345 /* immutable DAG node. use the global caches for it */
347 fs_fs_data_t *ffd = root->fs->fsap_data;
348 cache_entry_t *bucket;
350 auto_clear_dag_cache(ffd->dag_node_cache);
351 bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
352 if (bucket->node == NULL)
354 locate_cache(&cache, &key, root, path, pool);
355 SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
358 /* Patch up the FS, since this might have come from an old FS
360 svn_fs_fs__dag_set_fs(node, root->fs);
362 /* Retain the DAG node in L1 cache. */
363 bucket->node = svn_fs_fs__dag_dup(node,
364 ffd->dag_node_cache->pool);
369 /* Copy the node from L1 cache into the passed-in POOL. */
370 node = svn_fs_fs__dag_dup(bucket->node, pool);
375 /* DAG is mutable / may become invalid. Use the TXN-local cache */
377 locate_cache(&cache, &key, root, path, pool);
379 SVN_ERR(svn_cache__get((void **) &node, &found, cache, key, pool));
382 /* Patch up the FS, since this might have come from an old FS
384 svn_fs_fs__dag_set_fs(node, root->fs);
394 /* Add the NODE for PATH to ROOT's node cache. */
396 dag_node_cache_set(svn_fs_root_t *root,
404 SVN_ERR_ASSERT(*path == '/');
406 /* Do *not* attempt to dup and put the node into L1.
407 * dup() is twice as expensive as an L2 lookup (which will set also L1).
409 locate_cache(&cache, &key, root, path, pool);
411 return svn_cache__set(cache, key, node, pool);
415 /* Baton for find_descendents_in_cache. */
418 apr_array_header_t *list;
422 /* If the given item is a descendent of BATON->PATH, push
423 * it onto BATON->LIST (copying into BATON->POOL). Implements
424 * the svn_iter_apr_hash_cb_t prototype. */
426 find_descendents_in_cache(void *baton,
432 struct fdic_baton *b = baton;
433 const char *item_path = key;
435 if (svn_fspath__skip_ancestor(b->path, item_path))
436 APR_ARRAY_PUSH(b->list, const char *) = apr_pstrdup(b->pool, item_path);
441 /* Invalidate cache entries for PATH and any of its children. This
442 should *only* be called on a transaction root! */
444 dag_node_cache_invalidate(svn_fs_root_t *root,
450 apr_pool_t *iterpool;
454 b.pool = svn_pool_create(pool);
455 b.list = apr_array_make(b.pool, 1, sizeof(const char *));
457 SVN_ERR_ASSERT(root->is_txn_root);
458 locate_cache(&cache, NULL, root, NULL, b.pool);
461 SVN_ERR(svn_cache__iter(NULL, cache, find_descendents_in_cache,
464 iterpool = svn_pool_create(b.pool);
466 for (i = 0; i < b.list->nelts; i++)
468 const char *descendent = APR_ARRAY_IDX(b.list, i, const char *);
469 svn_pool_clear(iterpool);
470 SVN_ERR(svn_cache__set(cache, descendent, NULL, iterpool));
473 svn_pool_destroy(iterpool);
474 svn_pool_destroy(b.pool);
480 /* Creating transaction and revision root nodes. */
483 svn_fs_fs__txn_root(svn_fs_root_t **root_p,
487 apr_uint32_t flags = 0;
488 apr_hash_t *txnprops;
490 /* Look for the temporary txn props representing 'flags'. */
491 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
494 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
495 flags |= SVN_FS_TXN_CHECK_OOD;
497 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
498 flags |= SVN_FS_TXN_CHECK_LOCKS;
501 return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
506 svn_fs_fs__revision_root(svn_fs_root_t **root_p,
511 dag_node_t *root_dir;
513 SVN_ERR(svn_fs__check_fs(fs, TRUE));
515 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir, fs, rev, pool));
517 *root_p = make_revision_root(fs, rev, root_dir, pool);
524 /* Getting dag nodes for roots. */
527 /* Set *NODE_P to a freshly opened dag node referring to the root
528 directory of ROOT, allocating from POOL. */
530 root_node(dag_node_t **node_p,
534 if (root->is_txn_root)
536 /* It's a transaction root. Open a fresh copy. */
537 return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
541 /* It's a revision root, so we already have its root directory
543 fs_rev_root_data_t *frd = root->fsap_data;
544 *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
550 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
551 necessary, allocating in POOL. ROOT must be a transaction root.
552 Use ERROR_PATH in error messages. */
554 mutable_root_node(dag_node_t **node_p,
556 const char *error_path,
559 if (root->is_txn_root)
560 return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
562 /* If it's not a transaction root, we can't change its contents. */
563 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
568 /* Traversing directory paths. */
570 typedef enum copy_id_inherit_t
572 copy_id_inherit_unknown = 0,
573 copy_id_inherit_self,
574 copy_id_inherit_parent,
579 /* A linked list representing the path from a node up to a root
580 directory. We use this for cloning, and for operations that need
581 to deal with both a node and its parent directory. For example, a
582 `delete' operation needs to know that the node actually exists, but
583 also needs to change the parent directory. */
584 typedef struct parent_path_t
587 /* A node along the path. This could be the final node, one of its
588 parents, or the root. Every parent path ends with an element for
589 the root directory. */
592 /* The name NODE has in its parent directory. This is zero for the
593 root directory, which (obviously) has no name in its parent. */
596 /* The parent of NODE, or zero if NODE is the root directory. */
597 struct parent_path_t *parent;
599 /* The copy ID inheritance style. */
600 copy_id_inherit_t copy_inherit;
602 /* If copy ID inheritance style is copy_id_inherit_new, this is the
603 path which should be implicitly copied; otherwise, this is NULL. */
604 const char *copy_src_path;
608 /* Return a text string describing the absolute path of parent_path
609 PARENT_PATH. It will be allocated in POOL. */
611 parent_path_path(parent_path_t *parent_path,
614 const char *path_so_far = "/";
615 if (parent_path->parent)
616 path_so_far = parent_path_path(parent_path->parent, pool);
617 return parent_path->entry
618 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
623 /* Return the FS path for the parent path chain object CHILD relative
624 to its ANCESTOR in the same chain, allocated in POOL. */
626 parent_path_relpath(parent_path_t *child,
627 parent_path_t *ancestor,
630 const char *path_so_far = "";
631 parent_path_t *this_node = child;
632 while (this_node != ancestor)
634 assert(this_node != NULL);
635 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
636 this_node = this_node->parent;
643 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
644 event that immutable node CHILD in FS needs to be made mutable. If
645 the inheritance method is copy_id_inherit_new, also return a
646 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
647 for that path). CHILD must have a parent (it cannot be the root
648 node). TXN_ID is the transaction in which these items might be
649 mutable. Allocations are taken from POOL. */
651 get_copy_inheritance(copy_id_inherit_t *inherit_p,
652 const char **copy_src_path,
654 parent_path_t *child,
658 const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
659 const char *child_copy_id, *parent_copy_id;
660 const char *id_path = NULL;
661 svn_fs_root_t *copyroot_root;
662 dag_node_t *copyroot_node;
663 svn_revnum_t copyroot_rev;
664 const char *copyroot_path;
666 SVN_ERR_ASSERT(child && child->parent && txn_id);
668 /* Initialize some convenience variables. */
669 child_id = svn_fs_fs__dag_get_id(child->node);
670 parent_id = svn_fs_fs__dag_get_id(child->parent->node);
671 child_copy_id = svn_fs_fs__id_copy_id(child_id);
672 parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
674 /* If this child is already mutable, we have nothing to do. */
675 if (svn_fs_fs__id_txn_id(child_id))
677 *inherit_p = copy_id_inherit_self;
678 *copy_src_path = NULL;
682 /* From this point on, we'll assume that the child will just take
683 its copy ID from its parent. */
684 *inherit_p = copy_id_inherit_parent;
685 *copy_src_path = NULL;
687 /* Special case: if the child's copy ID is '0', use the parent's
689 if (strcmp(child_copy_id, "0") == 0)
692 /* Compare the copy IDs of the child and its parent. If they are
693 the same, then the child is already on the same branch as the
694 parent, and should use the same mutability copy ID that the
696 if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
699 /* If the child is on the same branch that the parent is on, the
700 child should just use the same copy ID that the parent would use.
701 Else, the child needs to generate a new copy ID to use should it
702 need to be made mutable. We will claim that child is on the same
703 branch as its parent if the child itself is not a branch point,
704 or if it is a branch point that we are accessing via its original
705 copy destination path. */
706 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
708 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev, pool));
709 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
710 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
712 if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
715 /* Determine if we are looking at the child via its original path or
716 as a subtree item of a copied tree. */
717 id_path = svn_fs_fs__dag_get_created_path(child->node);
718 if (strcmp(id_path, parent_path_path(child, pool)) == 0)
720 *inherit_p = copy_id_inherit_self;
724 /* We are pretty sure that the child node is an unedited nested
725 branched node. When it needs to be made mutable, it should claim
727 *inherit_p = copy_id_inherit_new;
728 *copy_src_path = id_path;
732 /* Allocate a new parent_path_t node from POOL, referring to NODE,
733 ENTRY, PARENT, and COPY_ID. */
734 static parent_path_t *
735 make_parent_path(dag_node_t *node,
737 parent_path_t *parent,
740 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
741 parent_path->node = node;
742 parent_path->entry = entry;
743 parent_path->parent = parent;
744 parent_path->copy_inherit = copy_id_inherit_unknown;
745 parent_path->copy_src_path = NULL;
750 /* Flags for open_path. */
751 typedef enum open_path_flags_t {
753 /* The last component of the PATH need not exist. (All parent
754 directories must exist, as usual.) If the last component doesn't
755 exist, simply leave the `node' member of the bottom parent_path
757 open_path_last_optional = 1,
759 /* When this flag is set, don't bother to lookup the DAG node in
760 our caches because we already tried this. Ignoring this flag
761 has no functional impact. */
762 open_path_uncached = 2,
764 /* The caller does not care about the parent node chain but only
765 the final DAG node. */
766 open_path_node_only = 4
770 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
771 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
772 **PARENT_PATH_P value is guaranteed to contain at least one
773 *element, for the root directory. PATH must be in canonical form.
775 If resulting *PARENT_PATH_P will eventually be made mutable and
776 modified, or if copy ID inheritance information is otherwise
777 needed, TXN_ID should be the ID of the mutability transaction. If
778 TXN_ID is NULL, no copy ID inheritance information will be
779 calculated for the *PARENT_PATH_P chain.
781 If FLAGS & open_path_last_optional is zero, return the error
782 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
783 non-zero, require all the parent directories to exist as normal,
784 but if the final path component doesn't exist, simply return a path
785 whose bottom `node' member is zero. This option is useful for
786 callers that create new nodes --- we find the parent directory for
787 them, and tell them whether the entry exists already.
789 The remaining bits in FLAGS are hints that allow this function
790 to take shortcuts based on knowledge that the caller provides,
791 such as the caller is not actually being interested in PARENT_PATH_P,
792 but only in (*PARENT_PATH_P)->NODE.
794 NOTE: Public interfaces which only *read* from the filesystem
795 should not call this function directly, but should instead use
799 open_path(parent_path_t **parent_path_p,
806 svn_fs_t *fs = root->fs;
807 dag_node_t *here = NULL; /* The directory we're currently looking at. */
808 parent_path_t *parent_path; /* The path from HERE up to the root. */
809 const char *rest; /* The portion of PATH we haven't traversed yet. */
811 /* ensure a canonical path representation */
812 const char *path_so_far = "/";
813 apr_pool_t *iterpool = svn_pool_create(pool);
815 /* callers often traverse the tree in some path-based order. That means
816 a sibling of PATH has been presently accessed. Try to start the lookup
817 directly at the parent node, if the caller did not requested the full
819 const char *directory;
820 assert(svn_fs__is_canonical_abspath(path));
821 if (flags & open_path_node_only)
823 directory = svn_dirent_dirname(path, pool);
824 if (directory[1] != 0) /* root nodes are covered anyway */
825 SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
828 /* did the shortcut work? */
831 path_so_far = directory;
832 rest = path + strlen(directory) + 1;
836 /* Make a parent_path item for the root node, using its own current
838 SVN_ERR(root_node(&here, root, pool));
839 rest = path + 1; /* skip the leading '/', it saves in iteration */
842 parent_path = make_parent_path(here, 0, 0, pool);
843 parent_path->copy_inherit = copy_id_inherit_self;
845 /* Whenever we are at the top of this loop:
846 - HERE is our current directory,
847 - ID is the node revision ID of HERE,
848 - REST is the path we're going to find in HERE, and
849 - PARENT_PATH includes HERE and all its parents. */
856 svn_pool_clear(iterpool);
858 /* Parse out the next entry from the path. */
859 entry = svn_fs__next_entry_name(&next, rest, pool);
861 /* Calculate the path traversed thus far. */
862 path_so_far = svn_fspath__join(path_so_far, entry, pool);
866 /* Given the behavior of svn_fs__next_entry_name(), this
867 happens when the path either starts or ends with a slash.
868 In either case, we stay put: the current directory stays
869 the same, and we add nothing to the parent path. */
874 copy_id_inherit_t inherit;
875 const char *copy_path = NULL;
876 svn_error_t *err = SVN_NO_ERROR;
877 dag_node_t *cached_node = NULL;
879 /* If we found a directory entry, follow it. First, we
880 check our node cache, and, failing that, we hit the DAG
881 layer. Don't bother to contact the cache for the last
882 element if we already know the lookup to fail for the
884 if (next || !(flags & open_path_uncached))
885 SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
890 err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
892 /* "file not found" requires special handling. */
893 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
895 /* If this was the last path component, and the caller
896 said it was optional, then don't return an error;
897 just put a NULL node pointer in the path. */
899 svn_error_clear(err);
901 if ((flags & open_path_last_optional)
902 && (! next || *next == '\0'))
904 parent_path = make_parent_path(NULL, entry, parent_path,
910 /* Build a better error message than svn_fs_fs__dag_open
911 can provide, giving the root and full path name. */
912 return SVN_FS__NOT_FOUND(root, path);
916 /* Other errors we return normally. */
919 if (flags & open_path_node_only)
921 /* Shortcut: the caller only wan'ts the final DAG node. */
922 parent_path->node = child;
926 /* Now, make a parent_path item for CHILD. */
927 parent_path = make_parent_path(child, entry, parent_path, pool);
930 SVN_ERR(get_copy_inheritance(&inherit, ©_path, fs,
931 parent_path, txn_id, iterpool));
932 parent_path->copy_inherit = inherit;
933 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
937 /* Cache the node we found (if it wasn't already cached). */
939 SVN_ERR(dag_node_cache_set(root, path_so_far, child, iterpool));
942 /* Are we finished traversing the path? */
946 /* The path isn't finished yet; we'd better be in a directory. */
947 if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
948 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
949 apr_psprintf(iterpool, _("Failure opening '%s'"), path));
955 svn_pool_destroy(iterpool);
956 *parent_path_p = parent_path;
961 /* Make the node referred to by PARENT_PATH mutable, if it isn't
962 already, allocating from POOL. ROOT must be the root from which
963 PARENT_PATH descends. Clone any parent directories as needed.
964 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
965 ERROR_PATH in error messages. */
967 make_path_mutable(svn_fs_root_t *root,
968 parent_path_t *parent_path,
969 const char *error_path,
973 const char *txn_id = root->txn;
975 /* Is the node mutable already? */
976 if (svn_fs_fs__dag_check_mutable(parent_path->node))
979 /* Are we trying to clone the root, or somebody's child node? */
980 if (parent_path->parent)
982 const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
983 const char *copy_id = NULL;
984 copy_id_inherit_t inherit = parent_path->copy_inherit;
985 const char *clone_path, *copyroot_path;
986 svn_revnum_t copyroot_rev;
987 svn_boolean_t is_parent_copyroot = FALSE;
988 svn_fs_root_t *copyroot_root;
989 dag_node_t *copyroot_node;
991 /* We're trying to clone somebody's child. Make sure our parent
993 SVN_ERR(make_path_mutable(root, parent_path->parent,
998 case copy_id_inherit_parent:
999 parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
1000 copy_id = svn_fs_fs__id_copy_id(parent_id);
1003 case copy_id_inherit_new:
1004 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id, root->fs, txn_id,
1008 case copy_id_inherit_self:
1012 case copy_id_inherit_unknown:
1014 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
1015 inheritance data. */
1018 /* Determine what copyroot our new child node should use. */
1019 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev, ©root_path,
1020 parent_path->node));
1021 SVN_ERR(svn_fs_fs__revision_root(©root_root, root->fs,
1022 copyroot_rev, pool));
1023 SVN_ERR(get_dag(©root_node, copyroot_root, copyroot_path, pool));
1025 child_id = svn_fs_fs__dag_get_id(parent_path->node);
1026 copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
1027 if (strcmp(svn_fs_fs__id_node_id(child_id),
1028 svn_fs_fs__id_node_id(copyroot_id)) != 0)
1029 is_parent_copyroot = TRUE;
1031 /* Now make this node mutable. */
1032 clone_path = parent_path_path(parent_path->parent, pool);
1033 SVN_ERR(svn_fs_fs__dag_clone_child(&clone,
1034 parent_path->parent->node,
1041 /* Update the path cache. */
1042 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
1047 /* We're trying to clone the root directory. */
1048 SVN_ERR(mutable_root_node(&clone, root, error_path, pool));
1051 /* Update the PARENT_PATH link to refer to the clone. */
1052 parent_path->node = clone;
1054 return SVN_NO_ERROR;
1058 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
1059 node we find, allocated in POOL. Return the error
1060 SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
1061 static svn_error_t *
1062 get_dag(dag_node_t **dag_node_p,
1063 svn_fs_root_t *root,
1067 parent_path_t *parent_path;
1068 dag_node_t *node = NULL;
1070 /* First we look for the DAG in our cache
1071 (if the path may be canonical). */
1073 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1077 /* Canonicalize the input PATH. */
1078 if (! svn_fs__is_canonical_abspath(path))
1080 path = svn_fs__canonicalize_abspath(path, pool);
1082 /* Try again with the corrected path. */
1083 SVN_ERR(dag_node_cache_get(&node, root, path, pool));
1088 /* Call open_path with no flags, as we want this to return an
1089 * error if the node for which we are searching doesn't exist. */
1090 SVN_ERR(open_path(&parent_path, root, path,
1091 open_path_uncached | open_path_node_only,
1093 node = parent_path->node;
1095 /* No need to cache our find -- open_path() will do that for us. */
1100 return SVN_NO_ERROR;
1105 /* Populating the `changes' table. */
1107 /* Add a change to the changes table in FS, keyed on transaction id
1108 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
1109 PATH (whose node revision id is--or was, in the case of a
1110 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
1111 occurred. If the change resulted from a copy, COPYFROM_REV and
1112 COPYFROM_PATH specify under which revision and path the node was
1113 copied from. If this was not part of a copy, COPYFROM_REV should
1114 be SVN_INVALID_REVNUM. Do all this as part of POOL. */
1115 static svn_error_t *
1116 add_change(svn_fs_t *fs,
1119 const svn_fs_id_t *noderev_id,
1120 svn_fs_path_change_kind_t change_kind,
1121 svn_boolean_t text_mod,
1122 svn_boolean_t prop_mod,
1123 svn_node_kind_t node_kind,
1124 svn_revnum_t copyfrom_rev,
1125 const char *copyfrom_path,
1128 return svn_fs_fs__add_change(fs, txn_id,
1129 svn_fs__canonicalize_abspath(path, pool),
1130 noderev_id, change_kind, text_mod, prop_mod,
1131 node_kind, copyfrom_rev, copyfrom_path,
1137 /* Generic node operations. */
1139 /* Get the id of a node referenced by path PATH in ROOT. Return the
1140 id in *ID_P allocated in POOL. */
1142 svn_fs_fs__node_id(const svn_fs_id_t **id_p,
1143 svn_fs_root_t *root,
1147 if ((! root->is_txn_root)
1148 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1150 /* Optimize the case where we don't need any db access at all.
1151 The root directory ("" or "/") node is stored in the
1152 svn_fs_root_t object, and never changes when it's a revision
1153 root, so we can just reach in and grab it directly. */
1154 fs_rev_root_data_t *frd = root->fsap_data;
1155 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
1161 SVN_ERR(get_dag(&node, root, path, pool));
1162 *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
1164 return SVN_NO_ERROR;
1169 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
1170 svn_fs_root_t *root,
1176 SVN_ERR(get_dag(&node, root, path, pool));
1177 return svn_fs_fs__dag_get_revision(revision, node, pool);
1181 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
1182 Return a string allocated in POOL. */
1183 static svn_error_t *
1184 fs_node_created_path(const char **created_path,
1185 svn_fs_root_t *root,
1191 SVN_ERR(get_dag(&node, root, path, pool));
1192 *created_path = svn_fs_fs__dag_get_created_path(node);
1194 return SVN_NO_ERROR;
1198 /* Set *KIND_P to the type of node located at PATH under ROOT.
1199 Perform temporary allocations in POOL. */
1200 static svn_error_t *
1201 node_kind(svn_node_kind_t *kind_p,
1202 svn_fs_root_t *root,
1206 const svn_fs_id_t *node_id;
1209 /* Get the node id. */
1210 SVN_ERR(svn_fs_fs__node_id(&node_id, root, path, pool));
1212 /* Use the node id to get the real kind. */
1213 SVN_ERR(svn_fs_fs__dag_get_node(&node, root->fs, node_id, pool));
1214 *kind_p = svn_fs_fs__dag_node_kind(node);
1216 return SVN_NO_ERROR;
1220 /* Set *KIND_P to the type of node present at PATH under ROOT. If
1221 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
1222 POOL for temporary allocation. */
1224 svn_fs_fs__check_path(svn_node_kind_t *kind_p,
1225 svn_fs_root_t *root,
1229 svn_error_t *err = node_kind(kind_p, root, path, pool);
1231 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1232 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1234 svn_error_clear(err);
1236 *kind_p = svn_node_none;
1239 return svn_error_trace(err);
1242 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1243 ROOT. If the node has no property by that name, set *VALUE_P to
1244 zero. Allocate the result in POOL. */
1245 static svn_error_t *
1246 fs_node_prop(svn_string_t **value_p,
1247 svn_fs_root_t *root,
1249 const char *propname,
1253 apr_hash_t *proplist;
1255 SVN_ERR(get_dag(&node, root, path, pool));
1256 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
1259 *value_p = svn_hash_gets(proplist, propname);
1261 return SVN_NO_ERROR;
1265 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1266 APR hash table allocated in POOL. The resulting property table
1267 maps property names to pointers to svn_string_t objects containing
1268 the property value. */
1269 static svn_error_t *
1270 fs_node_proplist(apr_hash_t **table_p,
1271 svn_fs_root_t *root,
1278 SVN_ERR(get_dag(&node, root, path, pool));
1279 SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
1280 *table_p = table ? table : apr_hash_make(pool);
1282 return SVN_NO_ERROR;
1286 static svn_error_t *
1287 increment_mergeinfo_up_tree(parent_path_t *pp,
1288 apr_int64_t increment,
1291 for (; pp; pp = pp->parent)
1292 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp->node,
1296 return SVN_NO_ERROR;
1299 /* Change, add, or delete a node's property value. The affected node
1300 is PATH under ROOT, the property value to modify is NAME, and VALUE
1301 points to either a string value to set the new contents to, or NULL
1302 if the property should be deleted. Perform temporary allocations
1304 static svn_error_t *
1305 fs_change_node_prop(svn_fs_root_t *root,
1308 const svn_string_t *value,
1311 parent_path_t *parent_path;
1312 apr_hash_t *proplist;
1315 if (! root->is_txn_root)
1316 return SVN_FS__NOT_TXN(root);
1319 path = svn_fs__canonicalize_abspath(path, pool);
1320 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
1322 /* Check (non-recursively) to see if path is locked; if so, check
1323 that we can use it. */
1324 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1325 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
1328 SVN_ERR(make_path_mutable(root, parent_path, path, pool));
1329 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, parent_path->node, pool));
1331 /* If there's no proplist, but we're just deleting a property, exit now. */
1332 if ((! proplist) && (! value))
1333 return SVN_NO_ERROR;
1335 /* Now, if there's no proplist, we know we need to make one. */
1337 proplist = apr_hash_make(pool);
1339 if (svn_fs_fs__fs_supports_mergeinfo(root->fs)
1340 && strcmp (name, SVN_PROP_MERGEINFO) == 0)
1342 apr_int64_t increment = 0;
1343 svn_boolean_t had_mergeinfo;
1344 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo, parent_path->node));
1346 if (value && !had_mergeinfo)
1348 else if (!value && had_mergeinfo)
1353 SVN_ERR(increment_mergeinfo_up_tree(parent_path, increment, pool));
1354 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path->node,
1355 (value != NULL), pool));
1359 /* Set the property. */
1360 svn_hash_sets(proplist, name, value);
1362 /* Overwrite the node's proplist. */
1363 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path->node, proplist,
1366 /* Make a record of this modification in the changes table. */
1367 return add_change(root->fs, txn_id, path,
1368 svn_fs_fs__dag_get_id(parent_path->node),
1369 svn_fs_path_change_modify, FALSE, TRUE,
1370 svn_fs_fs__dag_node_kind(parent_path->node),
1371 SVN_INVALID_REVNUM, NULL, pool);
1375 /* Determine if the properties of two path/root combinations are
1376 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1377 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1378 Both roots must be in the same filesystem. */
1379 static svn_error_t *
1380 fs_props_changed(svn_boolean_t *changed_p,
1381 svn_fs_root_t *root1,
1383 svn_fs_root_t *root2,
1387 dag_node_t *node1, *node2;
1389 /* Check that roots are in the same fs. */
1390 if (root1->fs != root2->fs)
1391 return svn_error_create
1392 (SVN_ERR_FS_GENERAL, NULL,
1393 _("Cannot compare property value between two different filesystems"));
1395 SVN_ERR(get_dag(&node1, root1, path1, pool));
1396 SVN_ERR(get_dag(&node2, root2, path2, pool));
1397 return svn_fs_fs__dag_things_different(changed_p, NULL,
1403 /* Merges and commits. */
1405 /* Set *NODE to the root node of ROOT. */
1406 static svn_error_t *
1407 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
1409 return get_dag(node, root, "/", pool);
1413 /* Set the contents of CONFLICT_PATH to PATH, and return an
1414 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1415 at PATH. Perform all allocations in POOL (except the allocation of
1416 CONFLICT_PATH, which should be handled outside this function). */
1417 static svn_error_t *
1418 conflict_err(svn_stringbuf_t *conflict_path,
1421 svn_stringbuf_set(conflict_path, path);
1422 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1423 _("Conflict at '%s'"), path);
1427 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1428 * and TARGET must be distinct node revisions. TARGET_PATH should
1429 * correspond to TARGET's full path in its filesystem, and is used for
1430 * reporting conflict location.
1432 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1433 * function recursively merges the directories' contents. If any are
1434 * files, this function simply returns an error whenever SOURCE,
1435 * TARGET, and ANCESTOR are all distinct node revisions.
1437 * If there are differences between ANCESTOR and SOURCE that conflict
1438 * with changes between ANCESTOR and TARGET, this function returns an
1439 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1440 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1442 * If there are no conflicting differences, CONFLICT_P is updated to
1445 * CONFLICT_P must point to a valid svn_stringbuf_t.
1447 * Do any necessary temporary allocation in POOL.
1449 static svn_error_t *
1450 merge(svn_stringbuf_t *conflict_p,
1451 const char *target_path,
1454 dag_node_t *ancestor,
1456 apr_int64_t *mergeinfo_increment_out,
1459 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1460 apr_hash_t *s_entries, *t_entries, *a_entries;
1461 apr_hash_index_t *hi;
1463 apr_pool_t *iterpool;
1464 apr_int64_t mergeinfo_increment = 0;
1465 svn_boolean_t fs_supports_mergeinfo;
1467 /* Make sure everyone comes from the same filesystem. */
1468 fs = svn_fs_fs__dag_get_fs(ancestor);
1469 if ((fs != svn_fs_fs__dag_get_fs(source))
1470 || (fs != svn_fs_fs__dag_get_fs(target)))
1472 return svn_error_create
1473 (SVN_ERR_FS_CORRUPT, NULL,
1474 _("Bad merge; ancestor, source, and target not all in same fs"));
1477 /* We have the same fs, now check it. */
1478 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1480 source_id = svn_fs_fs__dag_get_id(source);
1481 target_id = svn_fs_fs__dag_get_id(target);
1482 ancestor_id = svn_fs_fs__dag_get_id(ancestor);
1484 /* It's improper to call this function with ancestor == target. */
1485 if (svn_fs_fs__id_eq(ancestor_id, target_id))
1487 svn_string_t *id_str = svn_fs_fs__id_unparse(target_id, pool);
1488 return svn_error_createf
1489 (SVN_ERR_FS_GENERAL, NULL,
1490 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1491 target_path, id_str->data);
1494 svn_stringbuf_setempty(conflict_p);
1497 * Either no change made in source, or same change as made in target.
1498 * Both mean nothing to merge here.
1500 if (svn_fs_fs__id_eq(ancestor_id, source_id)
1501 || (svn_fs_fs__id_eq(source_id, target_id)))
1502 return SVN_NO_ERROR;
1504 /* Else proceed, knowing all three are distinct node revisions.
1506 * How to merge from this point:
1508 * if (not all 3 are directories)
1510 * early exit with conflict;
1513 * // Property changes may only be made to up-to-date
1514 * // directories, because once the client commits the prop
1515 * // change, it bumps the directory's revision, and therefore
1516 * // must be able to depend on there being no other changes to
1517 * // that directory in the repository.
1518 * if (target's property list differs from ancestor's)
1521 * For each entry NAME in the directory ANCESTOR:
1523 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1524 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1525 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1527 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1528 * No changes were made to this entry while the transaction was in
1529 * progress, so do nothing to the target.
1531 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1532 * A change was made to this entry while the transaction was in
1533 * process, but the transaction did not touch this entry. Replace
1534 * TARGET-ENTRY with SOURCE-ENTRY.
1537 * Changes were made to this entry both within the transaction and
1538 * to the repository while the transaction was in progress. They
1539 * must be merged or declared to be in conflict.
1541 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1542 * double delete; flag a conflict.
1544 * If any of the three entries is of type file, declare a conflict.
1546 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1547 * modification of ANCESTOR-ENTRY (determine by comparing the
1548 * node-id fields), declare a conflict. A replacement is
1549 * incompatible with a modification or other replacement--even
1550 * an identical replacement.
1552 * Direct modifications were made to the directory ANCESTOR-ENTRY
1553 * in both SOURCE and TARGET. Recursively merge these
1556 * For each leftover entry NAME in the directory SOURCE:
1558 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1559 * TARGET are adding exactly the same thing, two additions are not
1560 * auto-mergeable with each other.
1562 * Add NAME to TARGET with the entry from SOURCE.
1564 * Now that we are done merging the changes from SOURCE into the
1565 * directory TARGET, update TARGET's predecessor to be SOURCE.
1568 if ((svn_fs_fs__dag_node_kind(source) != svn_node_dir)
1569 || (svn_fs_fs__dag_node_kind(target) != svn_node_dir)
1570 || (svn_fs_fs__dag_node_kind(ancestor) != svn_node_dir))
1572 return conflict_err(conflict_p, target_path);
1576 /* Possible early merge failure: if target and ancestor have
1577 different property lists, then the merge should fail.
1578 Propchanges can *only* be committed on an up-to-date directory.
1579 ### TODO: see issue #418 about the inelegance of this.
1581 Another possible, similar, early merge failure: if source and
1582 ancestor have different property lists (meaning someone else
1583 changed directory properties while our commit transaction was
1584 happening), the merge should fail. See issue #2751.
1587 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1589 /* Get node revisions for our id's. */
1590 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr, fs, target_id, pool));
1591 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr, fs, ancestor_id, pool));
1592 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr, fs, source_id, pool));
1594 /* Now compare the prop-keys of the skels. Note that just because
1595 the keys are different -doesn't- mean the proplists have
1596 different contents. But merge() isn't concerned with contents;
1597 it doesn't do a brute-force comparison on textual contents, so
1598 it won't do that here either. Checking to see if the propkey
1599 atoms are `equal' is enough. */
1600 if (! svn_fs_fs__noderev_same_rep_key(tgt_nr->prop_rep, anc_nr->prop_rep))
1601 return conflict_err(conflict_p, target_path);
1602 if (! svn_fs_fs__noderev_same_rep_key(src_nr->prop_rep, anc_nr->prop_rep))
1603 return conflict_err(conflict_p, target_path);
1606 /* ### todo: it would be more efficient to simply check for a NULL
1607 entries hash where necessary below than to allocate an empty hash
1608 here, but another day, another day... */
1609 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries, source, pool));
1610 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries, target, pool));
1611 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries, ancestor, pool));
1613 fs_supports_mergeinfo = svn_fs_fs__fs_supports_mergeinfo(fs);
1615 /* for each entry E in a_entries... */
1616 iterpool = svn_pool_create(pool);
1617 for (hi = apr_hash_first(pool, a_entries);
1619 hi = apr_hash_next(hi))
1621 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
1625 svn_pool_clear(iterpool);
1627 name = svn__apr_hash_index_key(hi);
1628 klen = svn__apr_hash_index_klen(hi);
1629 a_entry = svn__apr_hash_index_val(hi);
1631 s_entry = apr_hash_get(s_entries, name, klen);
1632 t_entry = apr_hash_get(t_entries, name, klen);
1634 /* No changes were made to this entry while the transaction was
1635 in progress, so do nothing to the target. */
1636 if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
1639 /* A change was made to this entry while the transaction was in
1640 process, but the transaction did not touch this entry. */
1641 else if (t_entry && svn_fs_fs__id_eq(a_entry->id, t_entry->id))
1643 dag_node_t *t_ent_node;
1644 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1645 t_entry->id, iterpool));
1646 if (fs_supports_mergeinfo)
1648 apr_int64_t mergeinfo_start;
1649 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
1651 mergeinfo_increment -= mergeinfo_start;
1656 dag_node_t *s_ent_node;
1657 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1658 s_entry->id, iterpool));
1660 if (fs_supports_mergeinfo)
1662 apr_int64_t mergeinfo_end;
1663 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
1665 mergeinfo_increment += mergeinfo_end;
1668 SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
1676 SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
1680 /* Changes were made to this entry both within the transaction
1681 and to the repository while the transaction was in progress.
1682 They must be merged or declared to be in conflict. */
1685 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
1686 const char *new_tpath;
1687 apr_int64_t sub_mergeinfo_increment;
1689 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1690 double delete; if one of them is null, that's a delete versus
1691 a modification. In any of these cases, flag a conflict. */
1692 if (s_entry == NULL || t_entry == NULL)
1693 return conflict_err(conflict_p,
1694 svn_fspath__join(target_path,
1698 /* If any of the three entries is of type file, flag a conflict. */
1699 if (s_entry->kind == svn_node_file
1700 || t_entry->kind == svn_node_file
1701 || a_entry->kind == svn_node_file)
1702 return conflict_err(conflict_p,
1703 svn_fspath__join(target_path,
1707 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1708 modification of ANCESTOR-ENTRY, declare a conflict. */
1709 if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
1710 svn_fs_fs__id_node_id(a_entry->id)) != 0
1711 || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
1712 svn_fs_fs__id_copy_id(a_entry->id)) != 0
1713 || strcmp(svn_fs_fs__id_node_id(t_entry->id),
1714 svn_fs_fs__id_node_id(a_entry->id)) != 0
1715 || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
1716 svn_fs_fs__id_copy_id(a_entry->id)) != 0)
1717 return conflict_err(conflict_p,
1718 svn_fspath__join(target_path,
1722 /* Direct modifications were made to the directory
1723 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
1724 merge these modifications. */
1725 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1726 s_entry->id, iterpool));
1727 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node, fs,
1728 t_entry->id, iterpool));
1729 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node, fs,
1730 a_entry->id, iterpool));
1731 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
1732 SVN_ERR(merge(conflict_p, new_tpath,
1733 t_ent_node, s_ent_node, a_ent_node,
1735 &sub_mergeinfo_increment,
1737 if (fs_supports_mergeinfo)
1738 mergeinfo_increment += sub_mergeinfo_increment;
1741 /* We've taken care of any possible implications E could have.
1742 Remove it from source_entries, so it's easy later to loop
1743 over all the source entries that didn't exist in
1744 ancestor_entries. */
1746 apr_hash_set(s_entries, name, klen, NULL);
1749 /* For each entry E in source but not in ancestor */
1750 for (hi = apr_hash_first(pool, s_entries);
1752 hi = apr_hash_next(hi))
1754 svn_fs_dirent_t *s_entry, *t_entry;
1755 const char *name = svn__apr_hash_index_key(hi);
1756 apr_ssize_t klen = svn__apr_hash_index_klen(hi);
1757 dag_node_t *s_ent_node;
1759 svn_pool_clear(iterpool);
1761 s_entry = svn__apr_hash_index_val(hi);
1762 t_entry = apr_hash_get(t_entries, name, klen);
1764 /* If NAME exists in TARGET, declare a conflict. */
1766 return conflict_err(conflict_p,
1767 svn_fspath__join(target_path,
1771 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node, fs,
1772 s_entry->id, iterpool));
1773 if (fs_supports_mergeinfo)
1775 apr_int64_t mergeinfo_s;
1776 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s,
1778 mergeinfo_increment += mergeinfo_s;
1781 SVN_ERR(svn_fs_fs__dag_set_entry
1782 (target, s_entry->name, s_entry->id, s_entry->kind,
1785 svn_pool_destroy(iterpool);
1787 SVN_ERR(svn_fs_fs__dag_update_ancestry(target, source, pool));
1789 if (fs_supports_mergeinfo)
1790 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target,
1791 mergeinfo_increment,
1794 if (mergeinfo_increment_out)
1795 *mergeinfo_increment_out = mergeinfo_increment;
1797 return SVN_NO_ERROR;
1800 /* Merge changes between an ancestor and SOURCE_NODE into
1801 TXN. The ancestor is either ANCESTOR_NODE, or if
1802 that is null, TXN's base node.
1804 If the merge is successful, TXN's base will become
1805 SOURCE_NODE, and its root node will have a new ID, a
1806 successor of SOURCE_NODE.
1808 If a conflict results, update *CONFLICT to the path in the txn that
1809 conflicted; see the CONFLICT_P parameter of merge() for details. */
1810 static svn_error_t *
1811 merge_changes(dag_node_t *ancestor_node,
1812 dag_node_t *source_node,
1814 svn_stringbuf_t *conflict,
1817 dag_node_t *txn_root_node;
1818 svn_fs_t *fs = txn->fs;
1819 const char *txn_id = txn->id;
1821 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
1823 if (ancestor_node == NULL)
1825 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node, fs,
1829 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node),
1830 svn_fs_fs__dag_get_id(txn_root_node)))
1832 /* If no changes have been made in TXN since its current base,
1833 then it can't conflict with any changes since that base.
1834 The caller isn't supposed to call us in that case. */
1835 SVN_ERR_MALFUNCTION();
1838 SVN_ERR(merge(conflict, "/", txn_root_node,
1839 source_node, ancestor_node, txn_id, NULL, pool));
1841 return SVN_NO_ERROR;
1846 svn_fs_fs__commit_txn(const char **conflict_p,
1847 svn_revnum_t *new_rev,
1851 /* How do commits work in Subversion?
1853 * When you're ready to commit, here's what you have:
1855 * 1. A transaction, with a mutable tree hanging off it.
1856 * 2. A base revision, against which TXN_TREE was made.
1857 * 3. A latest revision, which may be newer than the base rev.
1859 * The problem is that if latest != base, then one can't simply
1860 * attach the txn root as the root of the new revision, because that
1861 * would lose all the changes between base and latest. It is also
1862 * not acceptable to insist that base == latest; in a busy
1863 * repository, commits happen too fast to insist that everyone keep
1864 * their entire tree up-to-date at all times. Non-overlapping
1865 * changes should not interfere with each other.
1867 * The solution is to merge the changes between base and latest into
1868 * the txn tree [see the function merge()]. The txn tree is the
1869 * only one of the three trees that is mutable, so it has to be the
1872 * You might have to adjust it more than once, if a new latest
1873 * revision gets committed while you were merging in the previous
1876 * 1. Jane starts txn T, based at revision 6.
1877 * 2. Someone commits (or already committed) revision 7.
1878 * 3. Jane's starts merging the changes between 6 and 7 into T.
1879 * 4. Meanwhile, someone commits revision 8.
1880 * 5. Jane finishes the 6-->7 merge. T could now be committed
1881 * against a latest revision of 7, if only that were still the
1882 * latest. Unfortunately, 8 is now the latest, so...
1883 * 6. Jane starts merging the changes between 7 and 8 into T.
1884 * 7. Meanwhile, no one commits any new revisions. Whew.
1885 * 8. Jane commits T, creating revision 9, whose tree is exactly
1886 * T's tree, except immutable now.
1888 * Lather, rinse, repeat.
1891 svn_error_t *err = SVN_NO_ERROR;
1892 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
1893 svn_fs_t *fs = txn->fs;
1895 /* Limit memory usage when the repository has a high commit rate and
1896 needs to run the following while loop multiple times. The memory
1897 growth without an iteration pool is very noticeable when the
1898 transaction modifies a node that has 20,000 sibling nodes. */
1899 apr_pool_t *iterpool = svn_pool_create(pool);
1901 /* Initialize output params. */
1902 *new_rev = SVN_INVALID_REVNUM;
1908 svn_revnum_t youngish_rev;
1909 svn_fs_root_t *youngish_root;
1910 dag_node_t *youngish_root_node;
1912 svn_pool_clear(iterpool);
1914 /* Get the *current* youngest revision. We call it "youngish"
1915 because new revisions might get committed after we've
1918 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev, fs, iterpool));
1919 SVN_ERR(svn_fs_fs__revision_root(&youngish_root, fs, youngish_rev,
1922 /* Get the dag node for the youngest revision. Later we'll use
1923 it as the SOURCE argument to a merge, and if the merge
1924 succeeds, this youngest root node will become the new base
1925 root for the svn txn that was the target of the merge (but
1926 note that the youngest rev may have changed by then -- that's
1927 why we're careful to get this root in its own bdb txn
1929 SVN_ERR(get_root(&youngish_root_node, youngish_root, iterpool));
1931 /* Try to merge. If the merge succeeds, the base root node of
1932 TARGET's txn will become the same as youngish_root_node, so
1933 any future merges will only be between that node and whatever
1934 the root node of the youngest rev is by then. */
1935 err = merge_changes(NULL, youngish_root_node, txn, conflict, iterpool);
1938 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
1939 *conflict_p = conflict->data;
1942 txn->base_rev = youngish_rev;
1944 /* Try to commit. */
1945 err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
1946 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
1948 /* Did someone else finish committing a new revision while we
1949 were in mid-merge or mid-commit? If so, we'll need to
1950 loop again to merge the new changes in, then try to
1951 commit again. Or if that's not what happened, then just
1952 return the error. */
1953 svn_revnum_t youngest_rev;
1954 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev, fs, iterpool));
1955 if (youngest_rev == youngish_rev)
1958 svn_error_clear(err);
1973 svn_fs_fs__reset_txn_caches(fs);
1975 svn_pool_destroy(iterpool);
1976 return svn_error_trace(err);
1980 /* Merge changes between two nodes into a third node. Given nodes
1981 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
1982 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
1983 changes between the ancestor and source. If there are conflicts,
1984 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
1985 description of the offending changes. Perform any temporary
1986 allocations in POOL. */
1987 static svn_error_t *
1988 fs_merge(const char **conflict_p,
1989 svn_fs_root_t *source_root,
1990 const char *source_path,
1991 svn_fs_root_t *target_root,
1992 const char *target_path,
1993 svn_fs_root_t *ancestor_root,
1994 const char *ancestor_path,
1997 dag_node_t *source, *ancestor;
2000 svn_stringbuf_t *conflict = svn_stringbuf_create_empty(pool);
2002 if (! target_root->is_txn_root)
2003 return SVN_FS__NOT_TXN(target_root);
2006 if ((source_root->fs != ancestor_root->fs)
2007 || (target_root->fs != ancestor_root->fs))
2009 return svn_error_create
2010 (SVN_ERR_FS_CORRUPT, NULL,
2011 _("Bad merge; ancestor, source, and target not all in same fs"));
2014 /* ### kff todo: is there any compelling reason to get the nodes in
2015 one db transaction? Right now we don't; txn_body_get_root() gets
2016 one node at a time. This will probably need to change:
2018 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2019 > svn_fs_merge needs to be a single transaction, to protect it against
2020 > people deleting parents of nodes it's working on, etc.
2023 /* Get the ancestor node. */
2024 SVN_ERR(get_root(&ancestor, ancestor_root, pool));
2026 /* Get the source node. */
2027 SVN_ERR(get_root(&source, source_root, pool));
2029 /* Open a txn for the txn root into which we're merging. */
2030 SVN_ERR(svn_fs_fs__open_txn(&txn, ancestor_root->fs, target_root->txn,
2033 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2034 err = merge_changes(ancestor, source, txn, conflict, pool);
2037 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2038 *conflict_p = conflict->data;
2039 return svn_error_trace(err);
2042 return SVN_NO_ERROR;
2046 svn_fs_fs__deltify(svn_fs_t *fs,
2047 svn_revnum_t revision,
2050 /* Deltify is a no-op for fs_fs. */
2052 return SVN_NO_ERROR;
2059 /* Set *TABLE_P to a newly allocated APR hash table containing the
2060 entries of the directory at PATH in ROOT. The keys of the table
2061 are entry names, as byte strings, excluding the final null
2062 character; the table's values are pointers to svn_fs_dirent_t
2063 structures. Allocate the table and its contents in POOL. */
2064 static svn_error_t *
2065 fs_dir_entries(apr_hash_t **table_p,
2066 svn_fs_root_t *root,
2072 /* Get the entries for this path in the caller's pool. */
2073 SVN_ERR(get_dag(&node, root, path, pool));
2074 return svn_fs_fs__dag_dir_entries(table_p, node, pool);
2077 /* Raise an error if PATH contains a newline because FSFS cannot handle
2078 * such paths. See issue #4340. */
2079 static svn_error_t *
2080 check_newline(const char *path, apr_pool_t *pool)
2082 char *c = strchr(path, '\n');
2085 return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
2086 _("Invalid control character '0x%02x' in path '%s'"),
2087 (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
2089 return SVN_NO_ERROR;
2092 /* Create a new directory named PATH in ROOT. The new directory has
2093 no entries, and no properties. ROOT must be the root of a
2094 transaction, not a revision. Do any necessary temporary allocation
2096 static svn_error_t *
2097 fs_make_dir(svn_fs_root_t *root,
2101 parent_path_t *parent_path;
2102 dag_node_t *sub_dir;
2103 const char *txn_id = root->txn;
2105 SVN_ERR(check_newline(path, pool));
2107 path = svn_fs__canonicalize_abspath(path, pool);
2108 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2111 /* Check (recursively) to see if some lock is 'reserving' a path at
2112 that location, or even some child-path; if so, check that we can
2114 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2115 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2118 /* If there's already a sub-directory by that name, complain. This
2119 also catches the case of trying to make a subdirectory named `/'. */
2120 if (parent_path->node)
2121 return SVN_FS__ALREADY_EXISTS(root, path);
2123 /* Create the subdirectory. */
2124 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2125 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir,
2126 parent_path->parent->node,
2127 parent_path_path(parent_path->parent,
2133 /* Add this directory to the path cache. */
2134 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool),
2137 /* Make a record of this modification in the changes table. */
2138 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(sub_dir),
2139 svn_fs_path_change_add, FALSE, FALSE, svn_node_dir,
2140 SVN_INVALID_REVNUM, NULL, pool);
2144 /* Delete the node at PATH under ROOT. ROOT must be a transaction
2145 root. Perform temporary allocations in POOL. */
2146 static svn_error_t *
2147 fs_delete_node(svn_fs_root_t *root,
2151 parent_path_t *parent_path;
2152 const char *txn_id = root->txn;
2153 apr_int64_t mergeinfo_count = 0;
2154 svn_node_kind_t kind;
2156 if (! root->is_txn_root)
2157 return SVN_FS__NOT_TXN(root);
2159 path = svn_fs__canonicalize_abspath(path, pool);
2160 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
2161 kind = svn_fs_fs__dag_node_kind(parent_path->node);
2163 /* We can't remove the root of the filesystem. */
2164 if (! parent_path->parent)
2165 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2166 _("The root directory cannot be deleted"));
2168 /* Check to see if path (or any child thereof) is locked; if so,
2169 check that we can use the existing lock(s). */
2170 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2171 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, TRUE, FALSE,
2174 /* Make the parent directory mutable, and do the deletion. */
2175 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2176 if (svn_fs_fs__fs_supports_mergeinfo(root->fs))
2177 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count,
2178 parent_path->node));
2179 SVN_ERR(svn_fs_fs__dag_delete(parent_path->parent->node,
2183 /* Remove this node and any children from the path cache. */
2184 SVN_ERR(dag_node_cache_invalidate(root, parent_path_path(parent_path, pool),
2187 /* Update mergeinfo counts for parents */
2188 if (mergeinfo_count > 0)
2189 SVN_ERR(increment_mergeinfo_up_tree(parent_path->parent,
2193 /* Make a record of this modification in the changes table. */
2194 return add_change(root->fs, txn_id, path,
2195 svn_fs_fs__dag_get_id(parent_path->node),
2196 svn_fs_path_change_delete, FALSE, FALSE, kind,
2197 SVN_INVALID_REVNUM, NULL, pool);
2201 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2202 Use POOL for temporary allocation only.
2203 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2204 static svn_error_t *
2205 fs_same_p(svn_boolean_t *same_p,
2210 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
2211 return SVN_NO_ERROR;
2214 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2215 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2216 the copies table. Perform temporary allocations in POOL. */
2217 static svn_error_t *
2218 copy_helper(svn_fs_root_t *from_root,
2219 const char *from_path,
2220 svn_fs_root_t *to_root,
2221 const char *to_path,
2222 svn_boolean_t preserve_history,
2225 dag_node_t *from_node;
2226 parent_path_t *to_parent_path;
2227 const char *txn_id = to_root->txn;
2228 svn_boolean_t same_p;
2230 /* Use an error check, not an assert, because even the caller cannot
2231 guarantee that a filesystem's UUID has not changed "on the fly". */
2232 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
2234 return svn_error_createf
2235 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2236 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2237 from_root->fs->path, to_root->fs->path);
2239 if (from_root->is_txn_root)
2240 return svn_error_create
2241 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2242 _("Copy from mutable tree not currently supported"));
2244 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2245 SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
2247 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2248 component does not exist, it's not that big a deal. We'll just
2250 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2251 open_path_last_optional, txn_id, pool));
2253 /* Check to see if path (or any child thereof) is locked; if so,
2254 check that we can use the existing lock(s). */
2255 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2256 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path, to_root->fs,
2257 TRUE, FALSE, pool));
2259 /* If the destination node already exists as the same node as the
2260 source (in other words, this operation would result in nothing
2261 happening at all), just do nothing an return successfully,
2262 proud that you saved yourself from a tiresome task. */
2263 if (to_parent_path->node &&
2264 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node),
2265 svn_fs_fs__dag_get_id(to_parent_path->node)))
2266 return SVN_NO_ERROR;
2268 if (! from_root->is_txn_root)
2270 svn_fs_path_change_kind_t kind;
2271 dag_node_t *new_node;
2272 const char *from_canonpath;
2273 apr_int64_t mergeinfo_start;
2274 apr_int64_t mergeinfo_end;
2276 /* If TO_PATH already existed prior to the copy, note that this
2277 operation is a replacement, not an addition. */
2278 if (to_parent_path->node)
2280 kind = svn_fs_path_change_replace;
2281 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2282 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
2283 to_parent_path->node));
2287 kind = svn_fs_path_change_add;
2288 mergeinfo_start = 0;
2291 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
2292 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end,
2295 /* Make sure the target node's parents are mutable. */
2296 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2299 /* Canonicalize the copyfrom path. */
2300 from_canonpath = svn_fs__canonicalize_abspath(from_path, pool);
2302 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
2303 to_parent_path->entry,
2310 if (kind == svn_fs_path_change_replace)
2311 SVN_ERR(dag_node_cache_invalidate(to_root,
2312 parent_path_path(to_parent_path,
2315 if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs)
2316 && mergeinfo_start != mergeinfo_end)
2317 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path->parent,
2318 mergeinfo_end - mergeinfo_start,
2321 /* Make a record of this modification in the changes table. */
2322 SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
2323 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2324 svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
2325 svn_fs_fs__dag_node_kind(from_node),
2326 from_root->rev, from_canonpath, pool));
2330 /* See IZ Issue #436 */
2331 /* Copying from transaction roots not currently available.
2333 ### cmpilato todo someday: make this not so. :-) Note that
2334 when copying from mutable trees, you have to make sure that
2335 you aren't creating a cyclic graph filesystem, and a simple
2336 referencing operation won't cut it. Currently, we should not
2337 be able to reach this clause, and the interface reports that
2338 this only works from immutable trees anyway, but JimB has
2339 stated that this requirement need not be necessary in the
2342 SVN_ERR_MALFUNCTION();
2345 return SVN_NO_ERROR;
2349 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2350 If FROM_PATH is a directory, copy it recursively. Temporary
2351 allocations are from POOL.*/
2352 static svn_error_t *
2353 fs_copy(svn_fs_root_t *from_root,
2354 const char *from_path,
2355 svn_fs_root_t *to_root,
2356 const char *to_path,
2359 SVN_ERR(check_newline(to_path, pool));
2361 return svn_error_trace(copy_helper(from_root,
2362 svn_fs__canonicalize_abspath(from_path,
2365 svn_fs__canonicalize_abspath(to_path,
2371 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2372 If FROM_PATH is a directory, copy it recursively. No history is
2373 preserved. Temporary allocations are from POOL. */
2374 static svn_error_t *
2375 fs_revision_link(svn_fs_root_t *from_root,
2376 svn_fs_root_t *to_root,
2380 if (! to_root->is_txn_root)
2381 return SVN_FS__NOT_TXN(to_root);
2383 path = svn_fs__canonicalize_abspath(path, pool);
2384 return svn_error_trace(copy_helper(from_root, path, to_root, path,
2389 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2390 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2391 allocations are in POOL. */
2392 static svn_error_t *
2393 fs_copied_from(svn_revnum_t *rev_p,
2394 const char **path_p,
2395 svn_fs_root_t *root,
2400 const char *copyfrom_path, *copyfrom_str = NULL;
2401 svn_revnum_t copyfrom_rev;
2404 /* Check to see if there is a cached version of this copyfrom
2406 if (! root->is_txn_root) {
2407 fs_rev_root_data_t *frd = root->fsap_data;
2408 copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
2413 if (*copyfrom_str == 0)
2415 /* We have a cached entry that says there is no copyfrom
2417 copyfrom_rev = SVN_INVALID_REVNUM;
2418 copyfrom_path = NULL;
2422 /* Parse the copyfrom string for our cached entry. */
2423 buf = apr_pstrdup(pool, copyfrom_str);
2424 str = svn_cstring_tokenize(" ", &buf);
2425 copyfrom_rev = SVN_STR_TO_REV(str);
2426 copyfrom_path = buf;
2431 /* There is no cached entry, look it up the old-fashioned
2433 SVN_ERR(get_dag(&node, root, path, pool));
2434 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev, node));
2435 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path, node));
2438 *rev_p = copyfrom_rev;
2439 *path_p = copyfrom_path;
2441 return SVN_NO_ERROR;
2448 /* Create the empty file PATH under ROOT. Temporary allocations are
2450 static svn_error_t *
2451 fs_make_file(svn_fs_root_t *root,
2455 parent_path_t *parent_path;
2457 const char *txn_id = root->txn;
2459 SVN_ERR(check_newline(path, pool));
2461 path = svn_fs__canonicalize_abspath(path, pool);
2462 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2465 /* If there's already a file by that name, complain.
2466 This also catches the case of trying to make a file named `/'. */
2467 if (parent_path->node)
2468 return SVN_FS__ALREADY_EXISTS(root, path);
2470 /* Check (non-recursively) to see if path is locked; if so, check
2471 that we can use it. */
2472 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2473 SVN_ERR(svn_fs_fs__allow_locked_operation(path, root->fs, FALSE, FALSE,
2476 /* Create the file. */
2477 SVN_ERR(make_path_mutable(root, parent_path->parent, path, pool));
2478 SVN_ERR(svn_fs_fs__dag_make_file(&child,
2479 parent_path->parent->node,
2480 parent_path_path(parent_path->parent,
2486 /* Add this file to the path cache. */
2487 SVN_ERR(dag_node_cache_set(root, parent_path_path(parent_path, pool), child,
2490 /* Make a record of this modification in the changes table. */
2491 return add_change(root->fs, txn_id, path, svn_fs_fs__dag_get_id(child),
2492 svn_fs_path_change_add, TRUE, FALSE, svn_node_file,
2493 SVN_INVALID_REVNUM, NULL, pool);
2497 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2498 allocations are in POOL. */
2499 static svn_error_t *
2500 fs_file_length(svn_filesize_t *length_p,
2501 svn_fs_root_t *root,
2507 /* First create a dag_node_t from the root/path pair. */
2508 SVN_ERR(get_dag(&file, root, path, pool));
2510 /* Now fetch its length */
2511 return svn_fs_fs__dag_file_length(length_p, file, pool);
2515 /* Set *CHECKSUM to the checksum of type KIND for PATH under ROOT, or
2516 NULL if that information isn't available. Temporary allocations
2518 static svn_error_t *
2519 fs_file_checksum(svn_checksum_t **checksum,
2520 svn_checksum_kind_t kind,
2521 svn_fs_root_t *root,
2527 SVN_ERR(get_dag(&file, root, path, pool));
2528 return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
2532 /* --- Machinery for svn_fs_file_contents() --- */
2534 /* Set *CONTENTS to a readable stream that will return the contents of
2535 PATH under ROOT. The stream is allocated in POOL. */
2536 static svn_error_t *
2537 fs_file_contents(svn_stream_t **contents,
2538 svn_fs_root_t *root,
2543 svn_stream_t *file_stream;
2545 /* First create a dag_node_t from the root/path pair. */
2546 SVN_ERR(get_dag(&node, root, path, pool));
2548 /* Then create a readable stream from the dag_node_t. */
2549 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
2551 *contents = file_stream;
2552 return SVN_NO_ERROR;
2555 /* --- End machinery for svn_fs_file_contents() --- */
2558 /* --- Machinery for svn_fs_try_process_file_contents() --- */
2560 static svn_error_t *
2561 fs_try_process_file_contents(svn_boolean_t *success,
2562 svn_fs_root_t *root,
2564 svn_fs_process_contents_func_t processor,
2569 SVN_ERR(get_dag(&node, root, path, pool));
2571 return svn_fs_fs__dag_try_process_file_contents(success, node,
2572 processor, baton, pool);
2575 /* --- End machinery for svn_fs_try_process_file_contents() --- */
2578 /* --- Machinery for svn_fs_apply_textdelta() --- */
2581 /* Local baton type for all the helper functions below. */
2582 typedef struct txdelta_baton_t
2584 /* This is the custom-built window consumer given to us by the delta
2585 library; it uniquely knows how to read data from our designated
2586 "source" stream, interpret the window, and write data to our
2587 designated "target" stream (in this case, our repos file.) */
2588 svn_txdelta_window_handler_t interpreter;
2589 void *interpreter_baton;
2591 /* The original file info */
2592 svn_fs_root_t *root;
2595 /* Derived from the file info */
2598 svn_stream_t *source_stream;
2599 svn_stream_t *target_stream;
2600 svn_stream_t *string_stream;
2601 svn_stringbuf_t *target_string;
2603 /* MD5 digest for the base text against which a delta is to be
2604 applied, and for the resultant fulltext, respectively. Either or
2605 both may be null, in which case ignored. */
2606 svn_checksum_t *base_checksum;
2607 svn_checksum_t *result_checksum;
2609 /* Pool used by db txns */
2615 /* ### see comment in window_consumer() regarding this function. */
2617 /* Helper function of generic type `svn_write_fn_t'. Implements a
2618 writable stream which appends to an svn_stringbuf_t. */
2619 static svn_error_t *
2620 write_to_string(void *baton, const char *data, apr_size_t *len)
2622 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2623 svn_stringbuf_appendbytes(tb->target_string, data, *len);
2624 return SVN_NO_ERROR;
2629 /* The main window handler returned by svn_fs_apply_textdelta. */
2630 static svn_error_t *
2631 window_consumer(svn_txdelta_window_t *window, void *baton)
2633 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2635 /* Send the window right through to the custom window interpreter.
2636 In theory, the interpreter will then write more data to
2637 cb->target_string. */
2638 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
2640 /* ### the write_to_string() callback for the txdelta's output stream
2641 ### should be doing all the flush determination logic, not here.
2642 ### in a drastic case, a window could generate a LOT more than the
2643 ### maximum buffer size. we want to flush to the underlying target
2644 ### stream much sooner (e.g. also in a streamy fashion). also, by
2645 ### moving this logic inside the stream, the stream becomes nice
2646 ### and encapsulated: it holds all the logic about buffering and
2649 ### further: I believe the buffering should be removed from tree.c
2650 ### the buffering should go into the target_stream itself, which
2651 ### is defined by reps-string.c. Specifically, I think the
2652 ### rep_write_contents() function will handle the buffering and
2653 ### the spill to the underlying DB. by locating it there, then
2654 ### anybody who gets a writable stream for FS content can take
2655 ### advantage of the buffering capability. this will be important
2656 ### when we export an FS API function for writing a fulltext into
2657 ### the FS, rather than forcing that fulltext thru apply_textdelta.
2660 /* Check to see if we need to purge the portion of the contents that
2661 have been written thus far. */
2662 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
2664 apr_size_t len = tb->target_string->len;
2665 SVN_ERR(svn_stream_write(tb->target_stream,
2666 tb->target_string->data,
2668 svn_stringbuf_setempty(tb->target_string);
2671 /* Is the window NULL? If so, we're done. */
2674 /* Close the internal-use stream. ### This used to be inside of
2675 txn_body_fulltext_finalize_edits(), but that invoked a nested
2676 Berkeley DB transaction -- scandalous! */
2677 SVN_ERR(svn_stream_close(tb->target_stream));
2679 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2683 return SVN_NO_ERROR;
2686 /* Helper function for fs_apply_textdelta. BATON is of type
2688 static svn_error_t *
2689 apply_textdelta(void *baton, apr_pool_t *pool)
2691 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
2692 parent_path_t *parent_path;
2693 const char *txn_id = tb->root->txn;
2695 /* Call open_path with no flags, as we want this to return an error
2696 if the node for which we are searching doesn't exist. */
2697 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2699 /* Check (non-recursively) to see if path is locked; if so, check
2700 that we can use it. */
2701 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2702 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2703 FALSE, FALSE, pool));
2705 /* Now, make sure this path is mutable. */
2706 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2707 tb->node = parent_path->node;
2709 if (tb->base_checksum)
2711 svn_checksum_t *checksum;
2713 /* Until we finalize the node, its data_key points to the old
2714 contents, in other words, the base text. */
2715 SVN_ERR(svn_fs_fs__dag_file_checksum(&checksum, tb->node,
2716 tb->base_checksum->kind, pool));
2717 if (!svn_checksum_match(tb->base_checksum, checksum))
2718 return svn_checksum_mismatch_err(tb->base_checksum, checksum, pool,
2719 _("Base checksum mismatch on '%s'"),
2723 /* Make a readable "source" stream out of the current contents of
2724 ROOT/PATH; obviously, this must done in the context of a db_txn.
2725 The stream is returned in tb->source_stream. */
2726 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb->source_stream),
2727 tb->node, tb->pool));
2729 /* Make a writable "target" stream */
2730 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->target_stream), tb->node,
2733 /* Make a writable "string" stream which writes data to
2734 tb->target_string. */
2735 tb->target_string = svn_stringbuf_create_empty(tb->pool);
2736 tb->string_stream = svn_stream_create(tb, tb->pool);
2737 svn_stream_set_write(tb->string_stream, write_to_string);
2739 /* Now, create a custom window handler that uses our two streams. */
2740 svn_txdelta_apply(tb->source_stream,
2746 &(tb->interpreter_baton));
2748 /* Make a record of this modification in the changes table. */
2749 return add_change(tb->root->fs, txn_id, tb->path,
2750 svn_fs_fs__dag_get_id(tb->node),
2751 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2752 SVN_INVALID_REVNUM, NULL, pool);
2756 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
2757 that will accept text delta windows to modify the contents of PATH
2758 under ROOT. Allocations are in POOL. */
2759 static svn_error_t *
2760 fs_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
2761 void **contents_baton_p,
2762 svn_fs_root_t *root,
2764 svn_checksum_t *base_checksum,
2765 svn_checksum_t *result_checksum,
2768 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2771 tb->path = svn_fs__canonicalize_abspath(path, pool);
2773 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
2774 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2776 SVN_ERR(apply_textdelta(tb, pool));
2778 *contents_p = window_consumer;
2779 *contents_baton_p = tb;
2780 return SVN_NO_ERROR;
2783 /* --- End machinery for svn_fs_apply_textdelta() --- */
2785 /* --- Machinery for svn_fs_apply_text() --- */
2787 /* Baton for svn_fs_apply_text(). */
2790 /* The original file info */
2791 svn_fs_root_t *root;
2794 /* Derived from the file info */
2797 /* The returned stream that will accept the file's new contents. */
2798 svn_stream_t *stream;
2800 /* The actual fs stream that the returned stream will write to. */
2801 svn_stream_t *file_stream;
2803 /* MD5 digest for the final fulltext written to the file. May
2804 be null, in which case ignored. */
2805 svn_checksum_t *result_checksum;
2807 /* Pool used by db txns */
2812 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2813 * fulltext data, not text deltas. Closes BATON->file_stream.
2815 * Note: If you're confused about how this function relates to another
2816 * of similar name, think of it this way:
2818 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2819 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
2822 /* Write function for the publically returned stream. */
2823 static svn_error_t *
2824 text_stream_writer(void *baton,
2828 struct text_baton_t *tb = baton;
2830 /* Psst, here's some data. Pass it on to the -real- file stream. */
2831 return svn_stream_write(tb->file_stream, data, len);
2834 /* Close function for the publically returned stream. */
2835 static svn_error_t *
2836 text_stream_closer(void *baton)
2838 struct text_baton_t *tb = baton;
2840 /* Close the internal-use stream. ### This used to be inside of
2841 txn_body_fulltext_finalize_edits(), but that invoked a nested
2842 Berkeley DB transaction -- scandalous! */
2843 SVN_ERR(svn_stream_close(tb->file_stream));
2845 /* Need to tell fs that we're done sending text */
2846 return svn_fs_fs__dag_finalize_edits(tb->node, tb->result_checksum,
2851 /* Helper function for fs_apply_text. BATON is of type
2853 static svn_error_t *
2854 apply_text(void *baton, apr_pool_t *pool)
2856 struct text_baton_t *tb = baton;
2857 parent_path_t *parent_path;
2858 const char *txn_id = tb->root->txn;
2860 /* Call open_path with no flags, as we want this to return an error
2861 if the node for which we are searching doesn't exist. */
2862 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
2864 /* Check (non-recursively) to see if path is locked; if so, check
2865 that we can use it. */
2866 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2867 SVN_ERR(svn_fs_fs__allow_locked_operation(tb->path, tb->root->fs,
2868 FALSE, FALSE, pool));
2870 /* Now, make sure this path is mutable. */
2871 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path, pool));
2872 tb->node = parent_path->node;
2874 /* Make a writable stream for replacing the file's text. */
2875 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb->file_stream), tb->node,
2878 /* Create a 'returnable' stream which writes to the file_stream. */
2879 tb->stream = svn_stream_create(tb, tb->pool);
2880 svn_stream_set_write(tb->stream, text_stream_writer);
2881 svn_stream_set_close(tb->stream, text_stream_closer);
2883 /* Make a record of this modification in the changes table. */
2884 return add_change(tb->root->fs, txn_id, tb->path,
2885 svn_fs_fs__dag_get_id(tb->node),
2886 svn_fs_path_change_modify, TRUE, FALSE, svn_node_file,
2887 SVN_INVALID_REVNUM, NULL, pool);
2891 /* Return a writable stream that will set the contents of PATH under
2892 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
2893 Temporary allocations are in POOL. */
2894 static svn_error_t *
2895 fs_apply_text(svn_stream_t **contents_p,
2896 svn_fs_root_t *root,
2898 svn_checksum_t *result_checksum,
2901 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
2904 tb->path = svn_fs__canonicalize_abspath(path, pool);
2906 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
2908 SVN_ERR(apply_text(tb, pool));
2910 *contents_p = tb->stream;
2911 return SVN_NO_ERROR;
2914 /* --- End machinery for svn_fs_apply_text() --- */
2917 /* Check if the contents of PATH1 under ROOT1 are different from the
2918 contents of PATH2 under ROOT2. If they are different set
2919 *CHANGED_P to TRUE, otherwise set it to FALSE. */
2920 static svn_error_t *
2921 fs_contents_changed(svn_boolean_t *changed_p,
2922 svn_fs_root_t *root1,
2924 svn_fs_root_t *root2,
2928 dag_node_t *node1, *node2;
2930 /* Check that roots are in the same fs. */
2931 if (root1->fs != root2->fs)
2932 return svn_error_create
2933 (SVN_ERR_FS_GENERAL, NULL,
2934 _("Cannot compare file contents between two different filesystems"));
2936 /* Check that both paths are files. */
2938 svn_node_kind_t kind;
2940 SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
2941 if (kind != svn_node_file)
2942 return svn_error_createf
2943 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
2945 SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
2946 if (kind != svn_node_file)
2947 return svn_error_createf
2948 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
2951 SVN_ERR(get_dag(&node1, root1, path1, pool));
2952 SVN_ERR(get_dag(&node2, root2, path2, pool));
2953 return svn_fs_fs__dag_things_different(NULL, changed_p,
2959 /* Public interface to computing file text deltas. */
2961 static svn_error_t *
2962 fs_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
2963 svn_fs_root_t *source_root,
2964 const char *source_path,
2965 svn_fs_root_t *target_root,
2966 const char *target_path,
2969 dag_node_t *source_node, *target_node;
2971 if (source_root && source_path)
2972 SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
2975 SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
2977 /* Create a delta stream that turns the source into the target. */
2978 return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
2984 /* Finding Changes */
2986 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
2987 descriptions of the paths changed under ROOT. The hash is keyed
2988 with const char * paths and has svn_fs_path_change2_t * values. Use
2989 POOL for all allocations. */
2990 static svn_error_t *
2991 fs_paths_changed(apr_hash_t **changed_paths_p,
2992 svn_fs_root_t *root,
2995 if (root->is_txn_root)
2996 return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
3000 fs_rev_root_data_t *frd = root->fsap_data;
3001 return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
3002 frd->copyfrom_cache, pool);
3008 /* Our coolio opaque history object. */
3009 typedef struct fs_history_data_t
3011 /* filesystem object */
3014 /* path and revision of historical location */
3016 svn_revnum_t revision;
3018 /* internal-use hints about where to resume the history search. */
3019 const char *path_hint;
3020 svn_revnum_t rev_hint;
3022 /* FALSE until the first call to svn_fs_history_prev(). */
3023 svn_boolean_t is_interesting;
3024 } fs_history_data_t;
3026 static svn_fs_history_t *
3027 assemble_history(svn_fs_t *fs,
3029 svn_revnum_t revision,
3030 svn_boolean_t is_interesting,
3031 const char *path_hint,
3032 svn_revnum_t rev_hint,
3036 /* Set *HISTORY_P to an opaque node history object which represents
3037 PATH under ROOT. ROOT must be a revision root. Use POOL for all
3039 static svn_error_t *
3040 fs_node_history(svn_fs_history_t **history_p,
3041 svn_fs_root_t *root,
3045 svn_node_kind_t kind;
3047 /* We require a revision root. */
3048 if (root->is_txn_root)
3049 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3051 /* And we require that the path exist in the root. */
3052 SVN_ERR(svn_fs_fs__check_path(&kind, root, path, pool));
3053 if (kind == svn_node_none)
3054 return SVN_FS__NOT_FOUND(root, path);
3056 /* Okay, all seems well. Build our history object and return it. */
3057 *history_p = assemble_history(root->fs,
3058 svn_fs__canonicalize_abspath(path, pool),
3059 root->rev, FALSE, NULL,
3060 SVN_INVALID_REVNUM, pool);
3061 return SVN_NO_ERROR;
3064 /* Find the youngest copyroot for path PARENT_PATH or its parents in
3065 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
3066 Perform all allocations in POOL. */
3067 static svn_error_t *
3068 find_youngest_copyroot(svn_revnum_t *rev_p,
3069 const char **path_p,
3071 parent_path_t *parent_path,
3074 svn_revnum_t rev_mine;
3075 svn_revnum_t rev_parent = SVN_INVALID_REVNUM;
3076 const char *path_mine;
3077 const char *path_parent = NULL;
3079 /* First find our parent's youngest copyroot. */
3080 if (parent_path->parent)
3081 SVN_ERR(find_youngest_copyroot(&rev_parent, &path_parent, fs,
3082 parent_path->parent, pool));
3084 /* Find our copyroot. */
3085 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine, &path_mine,
3086 parent_path->node));
3088 /* If a parent and child were copied to in the same revision, prefer
3089 the child copy target, since it is the copy relevant to the
3090 history of the child. */
3091 if (rev_mine >= rev_parent)
3094 *path_p = path_mine;
3098 *rev_p = rev_parent;
3099 *path_p = path_parent;
3102 return SVN_NO_ERROR;
3106 static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
3107 const char **path_p,
3108 svn_fs_root_t *root,
3112 svn_fs_t *fs = root->fs;
3113 parent_path_t *parent_path, *copy_dst_parent_path;
3114 svn_revnum_t copy_dst_rev, created_rev;
3115 const char *copy_dst_path;
3116 svn_fs_root_t *copy_dst_root;
3117 dag_node_t *copy_dst_node;
3118 svn_node_kind_t kind;
3120 /* Initialize return values. */
3124 path = svn_fs__canonicalize_abspath(path, pool);
3125 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3127 /* Find the youngest copyroot in the path of this node-rev, which
3128 will indicate the target of the innermost copy affecting the
3130 SVN_ERR(find_youngest_copyroot(©_dst_rev, ©_dst_path,
3131 fs, parent_path, pool));
3132 if (copy_dst_rev == 0) /* There are no copies affecting this node-rev. */
3133 return SVN_NO_ERROR;
3135 /* It is possible that this node was created from scratch at some
3136 revision between COPY_DST_REV and REV. Make sure that PATH
3137 exists as of COPY_DST_REV and is related to this node-rev. */
3138 SVN_ERR(svn_fs_fs__revision_root(©_dst_root, fs, copy_dst_rev, pool));
3139 SVN_ERR(svn_fs_fs__check_path(&kind, copy_dst_root, path, pool));
3140 if (kind == svn_node_none)
3141 return SVN_NO_ERROR;
3142 SVN_ERR(open_path(©_dst_parent_path, copy_dst_root, path,
3143 open_path_node_only, NULL, pool));
3144 copy_dst_node = copy_dst_parent_path->node;
3145 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
3146 svn_fs_fs__dag_get_id(parent_path->node)))
3147 return SVN_NO_ERROR;
3149 /* One final check must be done here. If you copy a directory and
3150 create a new entity somewhere beneath that directory in the same
3151 txn, then we can't claim that the copy affected the new entity.
3152 For example, if you do:
3155 create dir2/new-thing
3158 then dir2/new-thing was not affected by the copy of dir1 to dir2.
3159 We detect this situation by asking if PATH@COPY_DST_REV's
3160 created-rev is COPY_DST_REV, and that node-revision has no
3161 predecessors, then there is no relevant closest copy.
3163 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev, copy_dst_node, pool));
3164 if (created_rev == copy_dst_rev)
3166 const svn_fs_id_t *pred;
3167 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred, copy_dst_node));
3169 return SVN_NO_ERROR;
3172 /* The copy destination checks out. Return it. */
3173 *root_p = copy_dst_root;
3174 *path_p = copy_dst_path;
3175 return SVN_NO_ERROR;
3179 /* Set *PREV_PATH and *PREV_REV to the path and revision which
3180 represent the location at which PATH in FS was located immediately
3181 prior to REVISION iff there was a copy operation (to PATH or one of
3182 its parent directories) between that previous location and
3185 If there was no such copy operation in that portion of PATH's
3186 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
3187 static svn_error_t *
3188 prev_location(const char **prev_path,
3189 svn_revnum_t *prev_rev,
3191 svn_fs_root_t *root,
3195 const char *copy_path, *copy_src_path, *remainder_path;
3196 svn_fs_root_t *copy_root;
3197 svn_revnum_t copy_src_rev;
3199 /* Ask about the most recent copy which affected PATH@REVISION. If
3200 there was no such copy, we're done. */
3201 SVN_ERR(fs_closest_copy(©_root, ©_path, root, path, pool));
3204 *prev_rev = SVN_INVALID_REVNUM;
3206 return SVN_NO_ERROR;
3209 /* Ultimately, it's not the path of the closest copy's source that
3210 we care about -- it's our own path's location in the copy source
3211 revision. So we'll tack the relative path that expresses the
3212 difference between the copy destination and our path in the copy
3213 revision onto the copy source path to determine this information.
3215 In other words, if our path is "/branches/my-branch/foo/bar", and
3216 we know that the closest relevant copy was a copy of "/trunk" to
3217 "/branches/my-branch", then that relative path under the copy
3218 destination is "/foo/bar". Tacking that onto the copy source
3219 path tells us that our path was located at "/trunk/foo/bar"
3222 SVN_ERR(fs_copied_from(©_src_rev, ©_src_path,
3223 copy_root, copy_path, pool));
3224 remainder_path = svn_fspath__skip_ancestor(copy_path, path);
3225 *prev_path = svn_fspath__join(copy_src_path, remainder_path, pool);
3226 *prev_rev = copy_src_rev;
3227 return SVN_NO_ERROR;
3231 static svn_error_t *
3232 fs_node_origin_rev(svn_revnum_t *revision,
3233 svn_fs_root_t *root,
3237 svn_fs_t *fs = root->fs;
3238 const svn_fs_id_t *given_noderev_id, *cached_origin_id;
3239 const char *node_id, *dash;
3241 path = svn_fs__canonicalize_abspath(path, pool);
3243 /* Check the cache first. */
3244 SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
3245 node_id = svn_fs_fs__id_node_id(given_noderev_id);
3247 /* Is it a brand new uncommitted node? */
3248 if (node_id[0] == '_')
3250 *revision = SVN_INVALID_REVNUM;
3251 return SVN_NO_ERROR;
3254 /* Maybe this is a new-style node ID that just has the revision
3255 sitting right in it. */
3256 dash = strchr(node_id, '-');
3257 if (dash && *(dash+1))
3259 *revision = SVN_STR_TO_REV(dash + 1);
3260 return SVN_NO_ERROR;
3263 /* The root node always has ID 0, created in revision 0 and will never
3264 use the new-style ID format. */
3265 if (strcmp(node_id, "0") == 0)
3268 return SVN_NO_ERROR;
3271 /* OK, it's an old-style ID? Maybe it's cached. */
3272 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id,
3276 if (cached_origin_id != NULL)
3278 *revision = svn_fs_fs__id_rev(cached_origin_id);
3279 return SVN_NO_ERROR;
3283 /* Ah well, the answer isn't in the ID itself or in the cache.
3284 Let's actually calculate it, then. */
3285 svn_fs_root_t *curroot = root;
3286 apr_pool_t *subpool = svn_pool_create(pool);
3287 apr_pool_t *predidpool = svn_pool_create(pool);
3288 svn_stringbuf_t *lastpath = svn_stringbuf_create(path, pool);
3289 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
3291 const svn_fs_id_t *pred_id;
3293 /* Walk the closest-copy chain back to the first copy in our history.
3295 NOTE: We merely *assume* that this is faster than walking the
3296 predecessor chain, because we *assume* that copies of parent
3297 directories happen less often than modifications to a given item. */
3300 svn_revnum_t currev;
3301 const char *curpath = lastpath->data;
3303 svn_pool_clear(subpool);
3305 /* Get a root pointing to LASTREV. (The first time around,
3306 LASTREV is invalid, but that's cool because CURROOT is
3307 already initialized.) */
3308 if (SVN_IS_VALID_REVNUM(lastrev))
3309 SVN_ERR(svn_fs_fs__revision_root(&curroot, fs, lastrev, subpool));
3311 /* Find the previous location using the closest-copy shortcut. */
3312 SVN_ERR(prev_location(&curpath, &currev, fs, curroot, curpath,
3317 /* Update our LASTPATH and LASTREV variables (which survive
3319 svn_stringbuf_set(lastpath, curpath);
3323 /* Walk the predecessor links back to origin. */
3324 SVN_ERR(svn_fs_fs__node_id(&pred_id, curroot, lastpath->data, predidpool));
3327 svn_pool_clear(subpool);
3328 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, subpool));
3330 /* Why not just fetch the predecessor ID in PREDIDPOOL?
3331 Because svn_fs_fs__dag_get_predecessor_id() doesn't
3332 necessarily honor the passed-in pool, and might return a
3333 value cached in the node (which is allocated in
3334 SUBPOOL... maybe). */
3335 svn_pool_clear(predidpool);
3336 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3337 pred_id = pred_id ? svn_fs_fs__id_copy(pred_id, predidpool) : NULL;
3341 /* When we get here, NODE should be the first node-revision in our
3343 SVN_ERR(svn_fs_fs__dag_get_revision(revision, node, pool));
3345 /* Wow, I don't want to have to do all that again. Let's cache
3347 if (node_id[0] != '_')
3348 SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
3349 svn_fs_fs__dag_get_id(node), pool));
3351 svn_pool_destroy(subpool);
3352 svn_pool_destroy(predidpool);
3353 return SVN_NO_ERROR;
3358 struct history_prev_args
3360 svn_fs_history_t **prev_history_p;
3361 svn_fs_history_t *history;
3362 svn_boolean_t cross_copies;
3367 static svn_error_t *
3368 history_prev(void *baton, apr_pool_t *pool)
3370 struct history_prev_args *args = baton;
3371 svn_fs_history_t **prev_history = args->prev_history_p;
3372 svn_fs_history_t *history = args->history;
3373 fs_history_data_t *fhd = history->fsap_data;
3374 const char *commit_path, *src_path, *path = fhd->path;
3375 svn_revnum_t commit_rev, src_rev, dst_rev;
3376 svn_revnum_t revision = fhd->revision;
3377 apr_pool_t *retpool = args->pool;
3378 svn_fs_t *fs = fhd->fs;
3379 parent_path_t *parent_path;
3381 svn_fs_root_t *root;
3382 svn_boolean_t reported = fhd->is_interesting;
3383 svn_revnum_t copyroot_rev;
3384 const char *copyroot_path;
3386 /* Initialize our return value. */
3387 *prev_history = NULL;
3389 /* If our last history report left us hints about where to pickup
3390 the chase, then our last report was on the destination of a
3391 copy. If we are crossing copies, start from those locations,
3392 otherwise, we're all done here. */
3393 if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
3396 if (! args->cross_copies)
3397 return SVN_NO_ERROR;
3398 path = fhd->path_hint;
3399 revision = fhd->rev_hint;
3402 /* Construct a ROOT for the current revision. */
3403 SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
3405 /* Open PATH/REVISION, and get its node and a bunch of other
3407 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
3408 node = parent_path->node;
3409 commit_path = svn_fs_fs__dag_get_created_path(node);
3410 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3412 /* The Subversion filesystem is written in such a way that a given
3413 line of history may have at most one interesting history point
3414 per filesystem revision. Either that node was edited (and
3415 possibly copied), or it was copied but not edited. And a copy
3416 source cannot be from the same revision as its destination. So,
3417 if our history revision matches its node's commit revision, we
3419 if (revision == commit_rev)
3423 /* ... we either have not yet reported on this revision (and
3424 need now to do so) ... */
3425 *prev_history = assemble_history(fs,
3426 apr_pstrdup(retpool, commit_path),
3427 commit_rev, TRUE, NULL,
3428 SVN_INVALID_REVNUM, retpool);
3429 return SVN_NO_ERROR;
3433 /* ... or we *have* reported on this revision, and must now
3434 progress toward this node's predecessor (unless there is
3435 no predecessor, in which case we're all done!). */
3436 const svn_fs_id_t *pred_id;
3438 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
3440 return SVN_NO_ERROR;
3442 /* Replace NODE and friends with the information from its
3444 SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
3445 commit_path = svn_fs_fs__dag_get_created_path(node);
3446 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
3450 /* Find the youngest copyroot in the path of this node, including
3452 SVN_ERR(find_youngest_copyroot(©root_rev, ©root_path, fs,
3453 parent_path, pool));
3455 /* Initialize some state variables. */
3457 src_rev = SVN_INVALID_REVNUM;
3458 dst_rev = SVN_INVALID_REVNUM;
3460 if (copyroot_rev > commit_rev)
3462 const char *remainder_path;
3463 const char *copy_dst, *copy_src;
3464 svn_fs_root_t *copyroot_root;
3466 SVN_ERR(svn_fs_fs__revision_root(©root_root, fs, copyroot_rev,
3468 SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
3469 copy_dst = svn_fs_fs__dag_get_created_path(node);
3471 /* If our current path was the very destination of the copy,
3472 then our new current path will be the copy source. If our
3473 current path was instead the *child* of the destination of
3474 the copy, then figure out its previous location by taking its
3475 path relative to the copy destination and appending that to
3476 the copy source. Finally, if our current path doesn't meet
3477 one of these other criteria ... ### for now just fallback to
3478 the old copy hunt algorithm. */
3479 remainder_path = svn_fspath__skip_ancestor(copy_dst, path);
3483 /* If we get here, then our current path is the destination
3484 of, or the child of the destination of, a copy. Fill
3485 in the return values and get outta here. */
3486 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev, node));
3487 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src, node));
3489 dst_rev = copyroot_rev;
3490 src_path = svn_fspath__join(copy_src, remainder_path, pool);
3494 /* If we calculated a copy source path and revision, we'll make a
3495 'copy-style' history object. */
3496 if (src_path && SVN_IS_VALID_REVNUM(src_rev))
3498 svn_boolean_t retry = FALSE;
3500 /* It's possible for us to find a copy location that is the same
3501 as the history point we've just reported. If that happens,
3502 we simply need to take another trip through this history
3504 if ((dst_rev == revision) && reported)
3507 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
3509 src_path, src_rev, retpool);
3513 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
3514 commit_rev, TRUE, NULL,
3515 SVN_INVALID_REVNUM, retpool);
3518 return SVN_NO_ERROR;
3522 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3523 svn_fs_history_t object that represents the predecessory of
3524 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3525 only through a copy operation. Perform all allocations in POOL. */
3526 static svn_error_t *
3527 fs_history_prev(svn_fs_history_t **prev_history_p,
3528 svn_fs_history_t *history,
3529 svn_boolean_t cross_copies,
3532 svn_fs_history_t *prev_history = NULL;
3533 fs_history_data_t *fhd = history->fsap_data;
3534 svn_fs_t *fs = fhd->fs;
3536 /* Special case: the root directory changes in every single
3537 revision, no exceptions. And, the root can't be the target (or
3538 child of a target -- duh) of a copy. So, if that's our path,
3539 then we need only decrement our revision by 1, and there you go. */
3540 if (strcmp(fhd->path, "/") == 0)
3542 if (! fhd->is_interesting)
3543 prev_history = assemble_history(fs, "/", fhd->revision,
3544 1, NULL, SVN_INVALID_REVNUM, pool);
3545 else if (fhd->revision > 0)
3546 prev_history = assemble_history(fs, "/", fhd->revision - 1,
3547 1, NULL, SVN_INVALID_REVNUM, pool);
3551 struct history_prev_args args;
3552 prev_history = history;
3556 args.prev_history_p = &prev_history;
3557 args.history = prev_history;
3558 args.cross_copies = cross_copies;
3560 SVN_ERR(history_prev(&args, pool));
3564 fhd = prev_history->fsap_data;
3565 if (fhd->is_interesting)
3570 *prev_history_p = prev_history;
3571 return SVN_NO_ERROR;
3575 /* Set *PATH and *REVISION to the path and revision for the HISTORY
3576 object. Use POOL for all allocations. */
3577 static svn_error_t *
3578 fs_history_location(const char **path,
3579 svn_revnum_t *revision,
3580 svn_fs_history_t *history,
3583 fs_history_data_t *fhd = history->fsap_data;
3585 *path = apr_pstrdup(pool, fhd->path);
3586 *revision = fhd->revision;
3587 return SVN_NO_ERROR;
3590 static history_vtable_t history_vtable = {
3595 /* Return a new history object (marked as "interesting") for PATH and
3596 REVISION, allocated in POOL, and with its members set to the values
3597 of the parameters provided. Note that PATH and PATH_HINT are not
3598 duped into POOL -- it is the responsibility of the caller to ensure
3599 that this happens. */
3600 static svn_fs_history_t *
3601 assemble_history(svn_fs_t *fs,
3603 svn_revnum_t revision,
3604 svn_boolean_t is_interesting,
3605 const char *path_hint,
3606 svn_revnum_t rev_hint,
3609 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
3610 fs_history_data_t *fhd = apr_pcalloc(pool, sizeof(*fhd));
3611 fhd->path = svn_fs__canonicalize_abspath(path, pool);
3612 fhd->revision = revision;
3613 fhd->is_interesting = is_interesting;
3614 fhd->path_hint = path_hint;
3615 fhd->rev_hint = rev_hint;
3618 history->vtable = &history_vtable;
3619 history->fsap_data = fhd;
3624 /* mergeinfo queries */
3627 /* DIR_DAG is a directory DAG node which has mergeinfo in its
3628 descendants. This function iterates over its children. For each
3629 child with immediate mergeinfo, it adds its mergeinfo to
3630 RESULT_CATALOG. appropriate arguments. For each child with
3631 descendants with mergeinfo, it recurses. Note that it does *not*
3632 call the action on the path for DIR_DAG itself.
3634 POOL is used for temporary allocations, including the mergeinfo
3635 hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3638 static svn_error_t *
3639 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
3640 const char *this_path,
3641 dag_node_t *dir_dag,
3642 svn_mergeinfo_catalog_t result_catalog,
3643 apr_pool_t *result_pool,
3644 apr_pool_t *scratch_pool)
3646 apr_hash_t *entries;
3647 apr_hash_index_t *hi;
3648 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3650 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
3653 for (hi = apr_hash_first(scratch_pool, entries);
3655 hi = apr_hash_next(hi))
3657 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
3658 const char *kid_path;
3659 dag_node_t *kid_dag;
3660 svn_boolean_t has_mergeinfo, go_down;
3662 svn_pool_clear(iterpool);
3664 kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
3665 SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
3667 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
3668 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
3672 /* Save this particular node's mergeinfo. */
3673 apr_hash_t *proplist;
3674 svn_mergeinfo_t kid_mergeinfo;
3675 svn_string_t *mergeinfo_string;
3678 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, kid_dag, iterpool));
3679 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3680 if (!mergeinfo_string)
3682 svn_string_t *idstr = svn_fs_fs__id_unparse(dirent->id, iterpool);
3683 return svn_error_createf
3684 (SVN_ERR_FS_CORRUPT, NULL,
3685 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3689 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3690 treat it as if no mergeinfo is present rather than raising a parse
3692 err = svn_mergeinfo_parse(&kid_mergeinfo,
3693 mergeinfo_string->data,
3697 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3698 svn_error_clear(err);
3700 return svn_error_trace(err);
3704 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
3710 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3718 svn_pool_destroy(iterpool);
3719 return SVN_NO_ERROR;
3722 /* Return the cache key as a combination of REV_ROOT->REV, the inheritance
3723 flags INHERIT and ADJUST_INHERITED_MERGEINFO, and the PATH. The result
3724 will be allocated in POOL..
3727 mergeinfo_cache_key(const char *path,
3728 svn_fs_root_t *rev_root,
3729 svn_mergeinfo_inheritance_t inherit,
3730 svn_boolean_t adjust_inherited_mergeinfo,
3733 apr_int64_t number = rev_root->rev;
3735 + (inherit == svn_mergeinfo_nearest_ancestor ? 2 : 0)
3736 + (adjust_inherited_mergeinfo ? 1 : 0);
3738 return svn_fs_fs__combine_number_and_string(number, path, pool);
3741 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3742 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
3743 The result is allocated in RESULT_POOL; SCRATCH_POOL is
3744 used for temporary allocations.
3746 static svn_error_t *
3747 get_mergeinfo_for_path_internal(svn_mergeinfo_t *mergeinfo,
3748 svn_fs_root_t *rev_root,
3750 svn_mergeinfo_inheritance_t inherit,
3751 svn_boolean_t adjust_inherited_mergeinfo,
3752 apr_pool_t *result_pool,
3753 apr_pool_t *scratch_pool)
3755 parent_path_t *parent_path, *nearest_ancestor;
3756 apr_hash_t *proplist;
3757 svn_string_t *mergeinfo_string;
3759 path = svn_fs__canonicalize_abspath(path, scratch_pool);
3761 SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
3763 if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
3764 return SVN_NO_ERROR;
3766 if (inherit == svn_mergeinfo_nearest_ancestor)
3767 nearest_ancestor = parent_path->parent;
3769 nearest_ancestor = parent_path;
3773 svn_boolean_t has_mergeinfo;
3775 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo,
3776 nearest_ancestor->node));
3780 /* No need to loop if we're looking for explicit mergeinfo. */
3781 if (inherit == svn_mergeinfo_explicit)
3783 return SVN_NO_ERROR;
3786 nearest_ancestor = nearest_ancestor->parent;
3788 /* Run out? There's no mergeinfo. */
3789 if (!nearest_ancestor)
3791 return SVN_NO_ERROR;
3795 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, nearest_ancestor->node,
3797 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
3798 if (!mergeinfo_string)
3799 return svn_error_createf
3800 (SVN_ERR_FS_CORRUPT, NULL,
3801 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
3802 parent_path_path(nearest_ancestor, scratch_pool), rev_root->rev);
3804 /* Parse the mergeinfo; store the result in *MERGEINFO. */
3806 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
3807 treat it as if no mergeinfo is present rather than raising a parse
3809 svn_error_t *err = svn_mergeinfo_parse(mergeinfo,
3810 mergeinfo_string->data,
3814 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3816 svn_error_clear(err);
3820 return svn_error_trace(err);
3824 /* If our nearest ancestor is the very path we inquired about, we
3825 can return the mergeinfo results directly. Otherwise, we're
3826 inheriting the mergeinfo, so we need to a) remove non-inheritable
3827 ranges and b) telescope the merged-from paths. */
3828 if (adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
3830 svn_mergeinfo_t tmp_mergeinfo;
3832 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *mergeinfo,
3833 NULL, SVN_INVALID_REVNUM,
3834 SVN_INVALID_REVNUM, TRUE,
3835 scratch_pool, scratch_pool));
3836 SVN_ERR(svn_fs__append_to_merged_froms(mergeinfo, tmp_mergeinfo,
3837 parent_path_relpath(
3838 parent_path, nearest_ancestor,
3843 return SVN_NO_ERROR;
3846 /* Caching wrapper around get_mergeinfo_for_path_internal().
3848 static svn_error_t *
3849 get_mergeinfo_for_path(svn_mergeinfo_t *mergeinfo,
3850 svn_fs_root_t *rev_root,
3852 svn_mergeinfo_inheritance_t inherit,
3853 svn_boolean_t adjust_inherited_mergeinfo,
3854 apr_pool_t *result_pool,
3855 apr_pool_t *scratch_pool)
3857 fs_fs_data_t *ffd = rev_root->fs->fsap_data;
3858 const char *cache_key;
3859 svn_boolean_t found = FALSE;
3860 svn_stringbuf_t *mergeinfo_exists;
3864 cache_key = mergeinfo_cache_key(path, rev_root, inherit,
3865 adjust_inherited_mergeinfo, scratch_pool);
3866 if (ffd->mergeinfo_existence_cache)
3868 SVN_ERR(svn_cache__get((void **)&mergeinfo_exists, &found,
3869 ffd->mergeinfo_existence_cache,
3870 cache_key, result_pool));
3871 if (found && mergeinfo_exists->data[0] == '1')
3872 SVN_ERR(svn_cache__get((void **)mergeinfo, &found,
3873 ffd->mergeinfo_cache,
3874 cache_key, result_pool));
3879 SVN_ERR(get_mergeinfo_for_path_internal(mergeinfo, rev_root, path,
3881 adjust_inherited_mergeinfo,
3882 result_pool, scratch_pool));
3883 if (ffd->mergeinfo_existence_cache)
3885 mergeinfo_exists = svn_stringbuf_create(*mergeinfo ? "1" : "0",
3887 SVN_ERR(svn_cache__set(ffd->mergeinfo_existence_cache,
3888 cache_key, mergeinfo_exists, scratch_pool));
3890 SVN_ERR(svn_cache__set(ffd->mergeinfo_cache,
3891 cache_key, *mergeinfo, scratch_pool));
3895 return SVN_NO_ERROR;
3898 /* Adds mergeinfo for each descendant of PATH (but not PATH itself)
3899 under ROOT to RESULT_CATALOG. Returned values are allocated in
3900 RESULT_POOL; temporary values in POOL. */
3901 static svn_error_t *
3902 add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
3903 svn_fs_root_t *root,
3905 apr_pool_t *result_pool,
3906 apr_pool_t *scratch_pool)
3908 dag_node_t *this_dag;
3909 svn_boolean_t go_down;
3911 SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
3912 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
3915 SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
3921 return SVN_NO_ERROR;
3925 /* Get the mergeinfo for a set of paths, returned in
3926 *MERGEINFO_CATALOG. Returned values are allocated in
3927 POOL, while temporary values are allocated in a sub-pool. */
3928 static svn_error_t *
3929 get_mergeinfos_for_paths(svn_fs_root_t *root,
3930 svn_mergeinfo_catalog_t *mergeinfo_catalog,
3931 const apr_array_header_t *paths,
3932 svn_mergeinfo_inheritance_t inherit,
3933 svn_boolean_t include_descendants,
3934 svn_boolean_t adjust_inherited_mergeinfo,
3935 apr_pool_t *result_pool,
3936 apr_pool_t *scratch_pool)
3938 svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
3939 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3942 for (i = 0; i < paths->nelts; i++)
3945 svn_mergeinfo_t path_mergeinfo;
3946 const char *path = APR_ARRAY_IDX(paths, i, const char *);
3948 svn_pool_clear(iterpool);
3950 err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
3951 inherit, adjust_inherited_mergeinfo,
3952 result_pool, iterpool);
3955 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
3957 svn_error_clear(err);
3959 path_mergeinfo = NULL;
3963 return svn_error_trace(err);
3968 svn_hash_sets(result_catalog, path, path_mergeinfo);
3969 if (include_descendants)
3970 SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
3971 result_pool, scratch_pool));
3973 svn_pool_destroy(iterpool);
3975 *mergeinfo_catalog = result_catalog;
3976 return SVN_NO_ERROR;
3980 /* Implements svn_fs_get_mergeinfo. */
3981 static svn_error_t *
3982 fs_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
3983 svn_fs_root_t *root,
3984 const apr_array_header_t *paths,
3985 svn_mergeinfo_inheritance_t inherit,
3986 svn_boolean_t include_descendants,
3987 svn_boolean_t adjust_inherited_mergeinfo,
3988 apr_pool_t *result_pool,
3989 apr_pool_t *scratch_pool)
3991 fs_fs_data_t *ffd = root->fs->fsap_data;
3993 /* We require a revision root. */
3994 if (root->is_txn_root)
3995 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3997 /* We have to actually be able to find the mergeinfo metadata! */
3998 if (! svn_fs_fs__fs_supports_mergeinfo(root->fs))
3999 return svn_error_createf
4000 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
4001 _("Querying mergeinfo requires version %d of the FSFS filesystem "
4002 "schema; filesystem '%s' uses only version %d"),
4003 SVN_FS_FS__MIN_MERGEINFO_FORMAT, root->fs->path, ffd->format);
4005 /* Retrieve a path -> mergeinfo hash mapping. */
4006 return get_mergeinfos_for_paths(root, catalog, paths,
4008 include_descendants,
4009 adjust_inherited_mergeinfo,
4010 result_pool, scratch_pool);
4014 /* The vtable associated with root objects. */
4015 static root_vtable_t root_vtable = {
4017 svn_fs_fs__check_path,
4020 svn_fs_fs__node_created_rev,
4022 fs_node_created_path,
4028 fs_change_node_prop,
4037 fs_try_process_file_contents,
4041 fs_contents_changed,
4042 fs_get_file_delta_stream,
4047 /* Construct a new root object in FS, allocated from POOL. */
4048 static svn_fs_root_t *
4049 make_root(svn_fs_t *fs,
4052 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
4056 root->vtable = &root_vtable;
4062 /* Construct a root object referring to the root of REVISION in FS,
4063 whose root directory is ROOT_DIR. Create the new root in POOL. */
4064 static svn_fs_root_t *
4065 make_revision_root(svn_fs_t *fs,
4067 dag_node_t *root_dir,
4070 svn_fs_root_t *root = make_root(fs, pool);
4071 fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4073 root->is_txn_root = FALSE;
4076 frd->root_dir = root_dir;
4077 frd->copyfrom_cache = svn_hash__make(root->pool);
4079 root->fsap_data = frd;
4085 /* Construct a root object referring to the root of the transaction
4086 named TXN and based on revision BASE_REV in FS, with FLAGS to
4087 describe transaction's behavior. Create the new root in POOL. */
4088 static svn_error_t *
4089 make_txn_root(svn_fs_root_t **root_p,
4092 svn_revnum_t base_rev,
4096 svn_fs_root_t *root = make_root(fs, pool);
4097 fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
4099 root->is_txn_root = TRUE;
4100 root->txn = apr_pstrdup(root->pool, txn);
4101 root->txn_flags = flags;
4102 root->rev = base_rev;
4106 /* Because this cache actually tries to invalidate elements, keep
4107 the number of elements per page down.
4109 Note that since dag_node_cache_invalidate uses svn_cache__iter,
4110 this *cannot* be a memcache-based cache. */
4111 SVN_ERR(svn_cache__create_inprocess(&(frd->txn_node_cache),
4112 svn_fs_fs__dag_serialize,
4113 svn_fs_fs__dag_deserialize,
4114 APR_HASH_KEY_STRING,
4116 apr_pstrcat(pool, txn, ":TXN",
4120 /* Initialize transaction-local caches in FS.
4122 Note that we cannot put those caches in frd because that content
4123 fs root object is not available where we would need it. */
4124 SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, txn, pool));
4126 root->fsap_data = frd;
4129 return SVN_NO_ERROR;
4135 static APR_INLINE const char *
4136 stringify_node(dag_node_t *node,
4139 /* ### TODO: print some PATH@REV to it, too. */
4140 return svn_fs_fs__id_unparse(svn_fs_fs__dag_get_id(node), pool)->data;
4143 /* Check metadata sanity on NODE, and on its children. Manually verify
4144 information for DAG nodes in revision REV, and trust the metadata
4145 accuracy for nodes belonging to older revisions. */
4146 static svn_error_t *
4147 verify_node(dag_node_t *node,
4151 svn_boolean_t has_mergeinfo;
4152 apr_int64_t mergeinfo_count;
4153 const svn_fs_id_t *pred_id;
4154 svn_fs_t *fs = svn_fs_fs__dag_get_fs(node);
4156 svn_node_kind_t kind;
4157 apr_pool_t *iterpool = svn_pool_create(pool);
4159 /* Fetch some data. */
4160 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, node));
4161 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count, node));
4162 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
4163 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count, node));
4164 kind = svn_fs_fs__dag_node_kind(node);
4167 if (mergeinfo_count < 0)
4168 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4169 "Negative mergeinfo-count %" APR_INT64_T_FMT
4171 mergeinfo_count, stringify_node(node, iterpool));
4173 /* Issue #4129. (This check will explicitly catch non-root instances too.) */
4177 int pred_pred_count;
4178 SVN_ERR(svn_fs_fs__dag_get_node(&pred, fs, pred_id, iterpool));
4179 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_pred_count, pred));
4180 if (pred_pred_count+1 != pred_count)
4181 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4182 "Predecessor count mismatch: "
4183 "%s has %d, but %s has %d",
4184 stringify_node(node, iterpool), pred_count,
4185 stringify_node(pred, iterpool),
4189 /* Kind-dependent verifications. */
4190 if (kind == svn_node_none)
4192 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4193 "Node '%s' has kind 'none'",
4194 stringify_node(node, iterpool));
4196 if (kind == svn_node_file)
4198 if (has_mergeinfo != mergeinfo_count) /* comparing int to bool */
4199 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4200 "File node '%s' has inconsistent mergeinfo: "
4201 "has_mergeinfo=%d, "
4202 "mergeinfo_count=%" APR_INT64_T_FMT,
4203 stringify_node(node, iterpool),
4204 has_mergeinfo, mergeinfo_count);
4206 if (kind == svn_node_dir)
4208 apr_hash_t *entries;
4209 apr_hash_index_t *hi;
4210 apr_int64_t children_mergeinfo = 0;
4212 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
4214 /* Compute CHILDREN_MERGEINFO. */
4215 for (hi = apr_hash_first(pool, entries);
4217 hi = apr_hash_next(hi))
4219 svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
4221 svn_revnum_t child_rev;
4222 apr_int64_t child_mergeinfo;
4224 svn_pool_clear(iterpool);
4226 /* Compute CHILD_REV. */
4227 SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
4228 SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
4230 if (child_rev == rev)
4231 SVN_ERR(verify_node(child, rev, iterpool));
4233 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
4234 children_mergeinfo += child_mergeinfo;
4237 /* Side-effect of issue #4129. */
4238 if (children_mergeinfo+has_mergeinfo != mergeinfo_count)
4239 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4240 "Mergeinfo-count discrepancy on '%s': "
4241 "expected %" APR_INT64_T_FMT "+%d, "
4242 "counted %" APR_INT64_T_FMT,
4243 stringify_node(node, iterpool),
4244 mergeinfo_count, has_mergeinfo,
4245 children_mergeinfo);
4248 svn_pool_destroy(iterpool);
4249 return SVN_NO_ERROR;
4253 svn_fs_fs__verify_root(svn_fs_root_t *root,
4256 svn_fs_t *fs = root->fs;
4257 dag_node_t *root_dir;
4259 /* Issue #4129: bogus pred-counts and minfo-cnt's on the root node-rev
4260 (and elsewhere). This code makes more thorough checks than the
4261 commit-time checks in validate_root_noderev(). */
4263 /* Callers should disable caches by setting SVN_FS_CONFIG_FSFS_CACHE_NS;
4266 When this code is called in the library, we want to ensure we
4267 use the on-disk data --- rather than some data that was read
4268 in the possibly-distance past and cached since. */
4270 if (root->is_txn_root)
4272 fs_txn_root_data_t *frd = root->fsap_data;
4273 SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
4277 fs_rev_root_data_t *frd = root->fsap_data;
4278 root_dir = frd->root_dir;
4281 /* Recursively verify ROOT_DIR. */
4282 SVN_ERR(verify_node(root_dir, root->rev, pool));
4284 /* Verify explicitly the predecessor of the root. */
4286 const svn_fs_id_t *pred_id;
4288 /* Only r0 should have no predecessor. */
4289 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, root_dir));
4290 if (! root->is_txn_root && !!pred_id != !!root->rev)
4291 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4292 "r%ld's root node's predecessor is "
4293 "unexpectedly '%s'",
4296 ? svn_fs_fs__id_unparse(pred_id, pool)->data
4298 if (root->is_txn_root && !pred_id)
4299 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4300 "Transaction '%s''s root node's predecessor is "
4301 "unexpectedly NULL",
4304 /* Check the predecessor's revision. */
4307 svn_revnum_t pred_rev = svn_fs_fs__id_rev(pred_id);
4308 if (! root->is_txn_root && pred_rev+1 != root->rev)
4310 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4311 "r%ld's root node's predecessor is r%ld"
4312 " but should be r%ld",
4313 root->rev, pred_rev, root->rev - 1);
4314 if (root->is_txn_root && pred_rev != root->rev)
4315 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4316 "Transaction '%s''s root node's predecessor"
4318 " but should be r%ld",
4319 root->txn, pred_rev, root->rev);
4323 return SVN_NO_ERROR;