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 "svn_private_config.h"
43 #include "svn_pools.h"
44 #include "svn_error.h"
46 #include "svn_mergeinfo.h"
48 #include "svn_sorts.h"
49 #include "svn_checksum.h"
58 #include "revs-txns.h"
60 #include "bdb/txn-table.h"
61 #include "bdb/rev-table.h"
62 #include "bdb/nodes-table.h"
63 #include "bdb/changes-table.h"
64 #include "bdb/copies-table.h"
65 #include "bdb/node-origins-table.h"
66 #include "bdb/miscellaneous-table.h"
67 #include "../libsvn_fs/fs-loader.h"
68 #include "private/svn_fspath.h"
69 #include "private/svn_fs_util.h"
70 #include "private/svn_mergeinfo_private.h"
73 /* ### I believe this constant will become internal to reps-strings.c.
74 ### see the comment in window_consumer() for more information. */
76 /* ### the comment also seems to need tweaking: the log file stuff
77 ### is no longer an issue... */
78 /* Data written to the filesystem through the svn_fs_apply_textdelta()
79 interface is cached in memory until the end of the data stream, or
80 until a size trigger is hit. Define that trigger here (in bytes).
81 Setting the value to 0 will result in no filesystem buffering at
82 all. The value only really matters when dealing with file contents
83 bigger than the value itself. Above that point, large values here
84 allow the filesystem to buffer more data in memory before flushing
85 to the database, which increases memory usage but greatly decreases
86 the amount of disk access (and log-file generation) in database.
87 Smaller values will limit your overall memory consumption, but can
88 drastically hurt throughput by necessitating more write operations
89 to the database (which also generates more log-files). */
90 #define WRITE_BUFFER_SIZE 512000
92 /* The maximum number of cache items to maintain in the node cache. */
93 #define NODE_CACHE_MAX_KEYS 32
97 /* The root structure. */
99 /* Structure for svn_fs_root_t's node_cache hash values. */
100 struct dag_node_cache_t
102 dag_node_t *node; /* NODE to be cached. */
103 int idx; /* Index into the keys array for this cache item's key. */
104 apr_pool_t *pool; /* Pool in which NODE is allocated. */
108 typedef struct base_root_data_t
111 /* For revision roots, this is a dag node for the revision's root
112 directory. For transaction roots, we open the root directory
113 afresh every time, since the root may have been cloned, or
114 the transaction may have disappeared altogether. */
115 dag_node_t *root_dir;
117 /* Cache structures, for mapping const char * PATH to const
118 struct dag_node_cache_t * structures.
120 ### Currently this is only used for revision roots. To be safe
121 for transaction roots, you must have the guarantee that there is
122 never more than a single transaction root per Subversion
123 transaction ever open at a given time -- having two roots open to
124 the same Subversion transaction would be a request for pain.
125 Also, you have to ensure that if a 'make_path_mutable()' fails for
126 any reason, you don't leave cached nodes for the portion of that
127 function that succeeded. In other words, this cache must never,
129 apr_hash_t *node_cache;
130 const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
135 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
136 dag_node_t *root_dir,
139 static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
140 svn_revnum_t base_rev, apr_uint32_t flags,
144 /*** Node Caching in the Roots. ***/
146 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
149 dag_node_cache_get(svn_fs_root_t *root,
153 base_root_data_t *brd = root->fsap_data;
154 struct dag_node_cache_t *cache_item;
156 /* Assert valid input. */
157 assert(*path == '/');
159 /* Only allow revision roots. */
160 if (root->is_txn_root)
163 /* Look in the cache for our desired item. */
164 cache_item = svn_hash_gets(brd->node_cache, path);
166 return svn_fs_base__dag_dup(cache_item->node, pool);
172 /* Add the NODE for PATH to ROOT's node cache. Callers should *NOT*
173 call this unless they are adding a currently un-cached item to the
174 cache, or are replacing the NODE for PATH with a new (different)
177 dag_node_cache_set(svn_fs_root_t *root,
181 base_root_data_t *brd = root->fsap_data;
182 const char *cache_path;
183 apr_pool_t *cache_pool;
184 struct dag_node_cache_t *cache_item;
185 int num_keys = apr_hash_count(brd->node_cache);
187 /* What? No POOL passed to this function?
189 To ensure that our cache values live as long as the svn_fs_root_t
190 in which they are ultimately stored, and to allow us to free()
191 them individually without harming the rest, they are each
192 allocated from a subpool of ROOT's pool. We'll keep one subpool
193 around for each cache slot -- as we start expiring stuff
194 to make room for more entries, we'll re-use the expired thing's
197 /* Assert valid input and state. */
198 assert(*path == '/');
199 assert((brd->node_cache_idx <= num_keys)
200 && (num_keys <= NODE_CACHE_MAX_KEYS));
202 /* Only allow revision roots. */
203 if (root->is_txn_root)
206 /* Special case: the caller wants us to replace an existing cached
207 node with a new one. If the callers aren't mindless, this should
208 only happen when a node is made mutable under a transaction
209 root, and that only happens once under that root. So, we'll be a
210 little bit sloppy here, and count on callers doing the right
212 cache_item = svn_hash_gets(brd->node_cache, path);
215 /* ### This section is somehow broken. I don't know how, but it
216 ### is. And I don't want to spend any more time on it. So,
217 ### callers, use only revision root and don't try to update
218 ### an already-cached thing. -- cmpilato */
219 SVN_ERR_MALFUNCTION_NO_RETURN();
222 int cache_index = cache_item->idx;
223 cache_path = brd->node_cache_keys[cache_index];
224 cache_pool = cache_item->pool;
225 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
227 /* Now, move the cache key reference to the end of the keys in
228 the keys array (unless it's already at the end). ### Yes,
229 it's a memmove(), but we're not talking about pages of memory
231 if (cache_index != (num_keys - 1))
233 int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
234 memmove(brd->node_cache_keys + cache_index,
235 brd->node_cache_keys + cache_index + 1,
236 move_num * sizeof(const char *));
237 cache_index = num_keys - 1;
238 brd->node_cache_keys[cache_index] = cache_path;
241 /* Advance the cache pointers. */
242 cache_item->idx = cache_index;
243 brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
248 /* We're adding a new cache item. First, see if we have room for it
249 (otherwise, make some room). */
250 if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
252 /* No room. Expire the oldest thing. */
253 cache_path = brd->node_cache_keys[brd->node_cache_idx];
254 cache_item = svn_hash_gets(brd->node_cache, cache_path);
255 svn_hash_sets(brd->node_cache, cache_path, NULL);
256 cache_pool = cache_item->pool;
257 svn_pool_clear(cache_pool);
261 cache_pool = svn_pool_create(root->pool);
264 /* Make the cache item, allocated in its own pool. */
265 cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
266 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
267 cache_item->idx = brd->node_cache_idx;
268 cache_item->pool = cache_pool;
270 /* Now add it to the cache. */
271 cache_path = apr_pstrdup(cache_pool, path);
272 svn_hash_sets(brd->node_cache, cache_path, cache_item);
273 brd->node_cache_keys[brd->node_cache_idx] = cache_path;
275 /* Advance the cache pointer. */
276 brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
282 /* Creating transaction and revision root nodes. */
286 svn_fs_root_t **root_p;
292 txn_body_txn_root(void *baton,
295 struct txn_root_args *args = baton;
296 svn_fs_root_t **root_p = args->root_p;
297 svn_fs_txn_t *txn = args->txn;
298 svn_fs_t *fs = txn->fs;
299 const char *svn_txn_id = txn->id;
300 const svn_fs_id_t *root_id, *base_root_id;
302 apr_hash_t *txnprops;
303 apr_uint32_t flags = 0;
305 /* Verify that the transaction actually exists. */
306 SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
307 svn_txn_id, trail, trail->pool));
309 /* Look for special txn props that represent the 'flags' behavior of
311 SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
312 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
313 flags |= SVN_FS_TXN_CHECK_OOD;
315 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
316 flags |= SVN_FS_TXN_CHECK_LOCKS;
318 root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
326 svn_fs_base__txn_root(svn_fs_root_t **root_p,
331 struct txn_root_args args;
335 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args,
343 struct revision_root_args
345 svn_fs_root_t **root_p;
351 txn_body_revision_root(void *baton,
354 struct revision_root_args *args = baton;
355 dag_node_t *root_dir;
358 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
359 trail, trail->pool));
360 root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
362 *args->root_p = root;
368 svn_fs_base__revision_root(svn_fs_root_t **root_p,
373 struct revision_root_args args;
376 SVN_ERR(svn_fs__check_fs(fs, TRUE));
380 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args,
389 /* Getting dag nodes for roots. */
392 /* Set *NODE_P to a freshly opened dag node referring to the root
393 directory of ROOT, as part of TRAIL. */
395 root_node(dag_node_t **node_p,
400 base_root_data_t *brd = root->fsap_data;
402 if (! root->is_txn_root)
404 /* It's a revision root, so we already have its root directory
406 *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
411 /* It's a transaction root. Open a fresh copy. */
412 return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
418 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
419 necessary, as part of TRAIL. ROOT must be a transaction root. Use
420 ERROR_PATH in error messages. */
422 mutable_root_node(dag_node_t **node_p,
424 const char *error_path,
428 if (root->is_txn_root)
429 return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
432 /* If it's not a transaction root, we can't change its contents. */
433 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
438 /* Traversing directory paths. */
440 typedef enum copy_id_inherit_t
442 copy_id_inherit_unknown = 0,
443 copy_id_inherit_self,
444 copy_id_inherit_parent,
449 /* A linked list representing the path from a node up to a root
450 directory. We use this for cloning, and for operations that need
451 to deal with both a node and its parent directory. For example, a
452 `delete' operation needs to know that the node actually exists, but
453 also needs to change the parent directory. */
454 typedef struct parent_path_t
457 /* A node along the path. This could be the final node, one of its
458 parents, or the root. Every parent path ends with an element for
459 the root directory. */
462 /* The name NODE has in its parent directory. This is zero for the
463 root directory, which (obviously) has no name in its parent. */
466 /* The parent of NODE, or zero if NODE is the root directory. */
467 struct parent_path_t *parent;
469 /* The copy ID inheritance style. */
470 copy_id_inherit_t copy_inherit;
472 /* If copy ID inheritance style is copy_id_inherit_new, this is the
473 path which should be implicitly copied; otherwise, this is NULL. */
474 const char *copy_src_path;
479 /* Return the FS path for the parent path chain object PARENT_PATH,
480 allocated in POOL. */
482 parent_path_path(parent_path_t *parent_path,
485 const char *path_so_far = "/";
486 if (parent_path->parent)
487 path_so_far = parent_path_path(parent_path->parent, pool);
488 return parent_path->entry
489 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
494 /* Return the FS path for the parent path chain object CHILD relative
495 to its ANCESTOR in the same chain, allocated in POOL. */
497 parent_path_relpath(parent_path_t *child,
498 parent_path_t *ancestor,
501 const char *path_so_far = "";
502 parent_path_t *this_node = child;
503 while (this_node != ancestor)
505 assert(this_node != NULL);
506 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
507 this_node = this_node->parent;
513 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
514 event that immutable node CHILD in FS needs to be made mutable. If
515 the inheritance method is copy_id_inherit_new, also return a
516 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
517 for that path). CHILD must have a parent (it cannot be the root
518 node). TXN_ID is the transaction in which these items might be
521 get_copy_inheritance(copy_id_inherit_t *inherit_p,
522 const char **copy_src_path,
524 parent_path_t *child,
529 const svn_fs_id_t *child_id, *parent_id;
530 const char *child_copy_id, *parent_copy_id;
531 const char *id_path = NULL;
533 SVN_ERR_ASSERT(child && child->parent && txn_id);
535 /* Initialize our return variables (default: self-inheritance). */
536 *inherit_p = copy_id_inherit_self;
537 *copy_src_path = NULL;
539 /* Initialize some convenience variables. */
540 child_id = svn_fs_base__dag_get_id(child->node);
541 parent_id = svn_fs_base__dag_get_id(child->parent->node);
542 child_copy_id = svn_fs_base__id_copy_id(child_id);
543 parent_copy_id = svn_fs_base__id_copy_id(parent_id);
545 /* Easy out: if this child is already mutable, we have nothing to do. */
546 if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
549 /* If the child and its parent are on the same branch, then the
550 child will inherit the copy ID of its parent when made mutable.
551 This is trivially detectable when the child and its parent have
552 the same copy ID. But that's not the sole indicator of
553 same-branchness. It might be the case that the parent was the
554 result of a copy, but the child has not yet been cloned for
555 mutability since that copy. Detection of this latter case
556 basically means making sure the copy IDs don't differ for some
557 other reason, such as that the child was the direct target of the
558 copy whose ID it has. There is a special case here, too -- if
559 the child's copy ID is the special ID "0", it can't have been the
560 target of any copy, and therefore must be on the same branch as
562 if ((strcmp(child_copy_id, "0") == 0)
563 || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0))
565 *inherit_p = copy_id_inherit_parent;
571 SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool));
572 if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1)
574 *inherit_p = copy_id_inherit_parent;
579 /* If we get here, the child and its parent are not on speaking
580 terms -- there will be no parental inheritance handed down in
581 *this* generation. */
583 /* If the child was created at a different path than the one we are
584 expecting its clone to live, one of its parents must have been
585 created via a copy since the child was created. The child isn't
586 on the same branch as its parent (we caught those cases early);
587 it can't keep its current copy ID because there's been an
588 affecting copy (its clone won't be on the same branch as the
589 child is). That leaves only one course of action -- to assign
590 the child a brand new "soft" copy ID. */
591 id_path = svn_fs_base__dag_get_created_path(child->node);
592 if (strcmp(id_path, parent_path_path(child, pool)) != 0)
594 *inherit_p = copy_id_inherit_new;
595 *copy_src_path = id_path;
599 /* The node gets to keep its own ID. */
604 /* Allocate a new parent_path_t node from POOL, referring to NODE,
605 ENTRY, PARENT, and COPY_ID. */
606 static parent_path_t *
607 make_parent_path(dag_node_t *node,
609 parent_path_t *parent,
612 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
613 parent_path->node = node;
614 parent_path->entry = entry;
615 parent_path->parent = parent;
616 parent_path->copy_inherit = copy_id_inherit_unknown;
617 parent_path->copy_src_path = NULL;
622 /* Flags for open_path. */
623 typedef enum open_path_flags_t {
625 /* The last component of the PATH need not exist. (All parent
626 directories must exist, as usual.) If the last component doesn't
627 exist, simply leave the `node' member of the bottom parent_path
629 open_path_last_optional = 1
634 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
635 *PARENT_PATH_P to a path from the node up to ROOT, allocated in
636 TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to
637 contain at least one element, for the root directory.
639 If resulting *PARENT_PATH_P will eventually be made mutable and
640 modified, or if copy ID inheritance information is otherwise
641 needed, TXN_ID should be the ID of the mutability transaction. If
642 TXN_ID is NULL, no copy ID in heritance information will be
643 calculated for the *PARENT_PATH_P chain.
645 If FLAGS & open_path_last_optional is zero, return the error
646 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
647 non-zero, require all the parent directories to exist as normal,
648 but if the final path component doesn't exist, simply return a path
649 whose bottom `node' member is zero. This option is useful for
650 callers that create new nodes --- we find the parent directory for
651 them, and tell them whether the entry exists already.
653 NOTE: Public interfaces which only *read* from the filesystem
654 should not call this function directly, but should instead use
658 open_path(parent_path_t **parent_path_p,
666 svn_fs_t *fs = root->fs;
667 dag_node_t *here; /* The directory we're currently looking at. */
668 parent_path_t *parent_path; /* The path from HERE up to the root. */
669 const char *rest; /* The portion of PATH we haven't traversed yet. */
670 const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
671 const char *path_so_far = "/";
673 /* Make a parent_path item for the root node, using its own current
675 SVN_ERR(root_node(&here, root, trail, pool));
676 parent_path = make_parent_path(here, 0, 0, pool);
677 parent_path->copy_inherit = copy_id_inherit_self;
679 rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
681 /* Whenever we are at the top of this loop:
682 - HERE is our current directory,
683 - ID is the node revision ID of HERE,
684 - REST is the path we're going to find in HERE, and
685 - PARENT_PATH includes HERE and all its parents. */
692 /* Parse out the next entry from the path. */
693 entry = svn_fs__next_entry_name(&next, rest, pool);
695 /* Calculate the path traversed thus far. */
696 path_so_far = svn_fspath__join(path_so_far, entry, pool);
700 /* Given the behavior of svn_fs__next_entry_name(), this
701 happens when the path either starts or ends with a slash.
702 In either case, we stay put: the current directory stays
703 the same, and we add nothing to the parent path. */
708 copy_id_inherit_t inherit;
709 const char *copy_path = NULL;
710 svn_error_t *err = SVN_NO_ERROR;
711 dag_node_t *cached_node;
713 /* If we found a directory entry, follow it. First, we
714 check our node cache, and, failing that, we hit the DAG
716 cached_node = dag_node_cache_get(root, path_so_far, pool);
720 err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
722 /* "file not found" requires special handling. */
723 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
725 /* If this was the last path component, and the caller
726 said it was optional, then don't return an error;
727 just put a NULL node pointer in the path. */
729 svn_error_clear(err);
731 if ((flags & open_path_last_optional)
732 && (! next || *next == '\0'))
734 parent_path = make_parent_path(NULL, entry, parent_path,
740 /* Build a better error message than svn_fs_base__dag_open
741 can provide, giving the root and full path name. */
742 return SVN_FS__NOT_FOUND(root, path);
746 /* Other errors we return normally. */
749 /* Now, make a parent_path item for CHILD. */
750 parent_path = make_parent_path(child, entry, parent_path, pool);
753 SVN_ERR(get_copy_inheritance(&inherit, ©_path,
754 fs, parent_path, txn_id,
756 parent_path->copy_inherit = inherit;
757 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
760 /* Cache the node we found (if it wasn't already cached). */
762 dag_node_cache_set(root, path_so_far, child);
765 /* Are we finished traversing the path? */
769 /* The path isn't finished yet; we'd better be in a directory. */
770 if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
771 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
772 apr_psprintf(pool, _("Failure opening '%s'"), path));
778 *parent_path_p = parent_path;
783 /* Make the node referred to by PARENT_PATH mutable, if it isn't
784 already, as part of TRAIL. ROOT must be the root from which
785 PARENT_PATH descends. Clone any parent directories as needed.
786 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
787 ERROR_PATH in error messages. */
789 make_path_mutable(svn_fs_root_t *root,
790 parent_path_t *parent_path,
791 const char *error_path,
795 dag_node_t *cloned_node;
796 const char *txn_id = root->txn;
797 svn_fs_t *fs = root->fs;
799 /* Is the node mutable already? */
800 if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
803 /* Are we trying to clone the root, or somebody's child node? */
804 if (parent_path->parent)
806 const svn_fs_id_t *parent_id;
807 const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
808 const char *copy_id = NULL;
809 const char *copy_src_path = parent_path->copy_src_path;
810 copy_id_inherit_t inherit = parent_path->copy_inherit;
811 const char *clone_path;
813 /* We're trying to clone somebody's child. Make sure our parent
815 SVN_ERR(make_path_mutable(root, parent_path->parent,
816 error_path, trail, pool));
820 case copy_id_inherit_parent:
821 parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
822 copy_id = svn_fs_base__id_copy_id(parent_id);
825 case copy_id_inherit_new:
826 SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool));
829 case copy_id_inherit_self:
833 case copy_id_inherit_unknown:
835 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
839 /* Now make this node mutable. */
840 clone_path = parent_path_path(parent_path->parent, pool);
841 SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
842 parent_path->parent->node,
848 /* If we just created a brand new copy ID, we need to store a
849 `copies' table entry for it, as well as a notation in the
850 transaction that should this transaction be terminated, our
851 new copy needs to be removed. */
852 if (inherit == copy_id_inherit_new)
854 const svn_fs_id_t *new_node_id =
855 svn_fs_base__dag_get_id(cloned_node);
856 SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
857 svn_fs_base__id_txn_id(node_id),
859 copy_kind_soft, trail, pool));
860 SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
866 /* We're trying to clone the root directory. */
867 SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
870 /* Update the PARENT_PATH link to refer to the clone. */
871 parent_path->node = cloned_node;
877 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
878 mergeinfo count by COUNT_DELTA as part of Subversion transaction
879 TXN_ID and TRAIL. Use POOL for allocations. */
881 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
882 apr_int64_t count_delta,
887 apr_pool_t *iterpool;
888 parent_path_t *pp = parent_path;
890 if (count_delta == 0)
893 iterpool = svn_pool_create(pool);
897 svn_pool_clear(iterpool);
898 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
903 svn_pool_destroy(iterpool);
909 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
910 *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return
911 the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
913 get_dag(dag_node_t **dag_node_p,
919 parent_path_t *parent_path;
920 dag_node_t *node = NULL;
922 /* Canonicalize the input PATH. */
923 path = svn_fs__canonicalize_abspath(path, pool);
925 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
926 node = dag_node_cache_get(root, path, pool);
929 /* Call open_path with no flags, as we want this to return an error
930 if the node for which we are searching doesn't exist. */
931 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
932 node = parent_path->node;
934 /* No need to cache our find -- open_path() will do that for us. */
943 /* Populating the `changes' table. */
945 /* Add a change to the changes table in FS, keyed on transaction id
946 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
947 PATH (whose node revision id is--or was, in the case of a
948 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
949 occurred. Do all this as part of TRAIL. */
951 add_change(svn_fs_t *fs,
954 const svn_fs_id_t *noderev_id,
955 svn_fs_path_change_kind_t change_kind,
956 svn_boolean_t text_mod,
957 svn_boolean_t prop_mod,
962 change.path = svn_fs__canonicalize_abspath(path, pool);
963 change.noderev_id = noderev_id;
964 change.kind = change_kind;
965 change.text_mod = text_mod;
966 change.prop_mod = prop_mod;
967 return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
972 /* Generic node operations. */
975 struct node_id_args {
976 const svn_fs_id_t **id_p;
983 txn_body_node_id(void *baton, trail_t *trail)
985 struct node_id_args *args = baton;
988 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
989 *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
997 base_node_id(const svn_fs_id_t **id_p,
1002 base_root_data_t *brd = root->fsap_data;
1004 if (! root->is_txn_root
1005 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1007 /* Optimize the case where we don't need any db access at all.
1008 The root directory ("" or "/") node is stored in the
1009 svn_fs_root_t object, and never changes when it's a revision
1010 root, so we can just reach in and grab it directly. */
1011 *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1016 const svn_fs_id_t *id;
1017 struct node_id_args args;
1023 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1027 return SVN_NO_ERROR;
1031 struct node_created_rev_args {
1032 svn_revnum_t revision;
1033 svn_fs_root_t *root;
1038 static svn_error_t *
1039 txn_body_node_created_rev(void *baton, trail_t *trail)
1041 struct node_created_rev_args *args = baton;
1044 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1045 return svn_fs_base__dag_get_revision(&(args->revision), node,
1046 trail, trail->pool);
1050 static svn_error_t *
1051 base_node_created_rev(svn_revnum_t *revision,
1052 svn_fs_root_t *root,
1056 struct node_created_rev_args args;
1058 args.revision = SVN_INVALID_REVNUM;
1061 SVN_ERR(svn_fs_base__retry_txn
1062 (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1063 *revision = args.revision;
1064 return SVN_NO_ERROR;
1068 struct node_created_path_args {
1069 const char **created_path;
1070 svn_fs_root_t *root;
1075 static svn_error_t *
1076 txn_body_node_created_path(void *baton, trail_t *trail)
1078 struct node_created_path_args *args = baton;
1081 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1082 *args->created_path = svn_fs_base__dag_get_created_path(node);
1083 return SVN_NO_ERROR;
1087 static svn_error_t *
1088 base_node_created_path(const char **created_path,
1089 svn_fs_root_t *root,
1093 struct node_created_path_args args;
1094 apr_pool_t *scratch_pool = svn_pool_create(pool);
1096 args.created_path = created_path;
1100 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1101 FALSE, scratch_pool));
1103 *created_path = apr_pstrdup(pool, *created_path);
1104 svn_pool_destroy(scratch_pool);
1105 return SVN_NO_ERROR;
1109 struct node_kind_args {
1110 const svn_fs_id_t *id;
1111 svn_node_kind_t kind; /* OUT parameter */
1115 static svn_error_t *
1116 txn_body_node_kind(void *baton, trail_t *trail)
1118 struct node_kind_args *args = baton;
1121 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1122 trail, trail->pool));
1123 args->kind = svn_fs_base__dag_node_kind(node);
1125 return SVN_NO_ERROR;
1129 static svn_error_t *
1130 node_kind(svn_node_kind_t *kind_p,
1131 svn_fs_root_t *root,
1135 struct node_kind_args args;
1136 const svn_fs_id_t *node_id;
1138 /* Get the node id. */
1139 SVN_ERR(base_node_id(&node_id, root, path, pool));
1141 /* Use the node id to get the real kind. */
1143 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1146 *kind_p = args.kind;
1147 return SVN_NO_ERROR;
1151 static svn_error_t *
1152 base_check_path(svn_node_kind_t *kind_p,
1153 svn_fs_root_t *root,
1157 svn_error_t *err = node_kind(kind_p, root, path, pool);
1159 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1160 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1162 svn_error_clear(err);
1164 *kind_p = svn_node_none;
1167 return svn_error_trace(err);
1171 struct node_prop_args
1173 svn_string_t **value_p;
1174 svn_fs_root_t *root;
1176 const char *propname;
1180 static svn_error_t *
1181 txn_body_node_prop(void *baton,
1184 struct node_prop_args *args = baton;
1186 apr_hash_t *proplist;
1188 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1189 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1190 trail, trail->pool));
1191 *(args->value_p) = NULL;
1193 *(args->value_p) = svn_hash_gets(proplist, args->propname);
1194 return SVN_NO_ERROR;
1198 static svn_error_t *
1199 base_node_prop(svn_string_t **value_p,
1200 svn_fs_root_t *root,
1202 const char *propname,
1205 struct node_prop_args args;
1206 svn_string_t *value;
1207 apr_pool_t *scratch_pool = svn_pool_create(pool);
1209 args.value_p = &value;
1212 args.propname = propname;
1213 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1214 FALSE, scratch_pool));
1215 *value_p = value ? svn_string_dup(value, pool) : NULL;
1216 svn_pool_destroy(scratch_pool);
1217 return SVN_NO_ERROR;
1221 struct node_proplist_args {
1222 apr_hash_t **table_p;
1223 svn_fs_root_t *root;
1228 static svn_error_t *
1229 txn_body_node_proplist(void *baton, trail_t *trail)
1231 struct node_proplist_args *args = baton;
1233 apr_hash_t *proplist;
1235 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1236 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1237 trail, trail->pool));
1238 *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1239 return SVN_NO_ERROR;
1243 static svn_error_t *
1244 base_node_proplist(apr_hash_t **table_p,
1245 svn_fs_root_t *root,
1250 struct node_proplist_args args;
1252 args.table_p = &table;
1256 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1260 return SVN_NO_ERROR;
1264 struct change_node_prop_args {
1265 svn_fs_root_t *root;
1268 const svn_string_t *value;
1272 static svn_error_t *
1273 txn_body_change_node_prop(void *baton,
1276 struct change_node_prop_args *args = baton;
1277 parent_path_t *parent_path;
1278 apr_hash_t *proplist;
1279 const char *txn_id = args->root->txn;
1280 base_fs_data_t *bfd = trail->fs->fsap_data;
1282 SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1283 trail, trail->pool));
1285 /* Check to see if path is locked; if so, check that we can use it.
1286 Notice that we're doing this non-recursively, regardless of node kind. */
1287 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1288 SVN_ERR(svn_fs_base__allow_locked_operation
1289 (args->path, FALSE, trail, trail->pool));
1291 SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1292 trail, trail->pool));
1293 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1294 trail, trail->pool));
1296 /* If there's no proplist, but we're just deleting a property, exit now. */
1297 if ((! proplist) && (! args->value))
1298 return SVN_NO_ERROR;
1300 /* Now, if there's no proplist, we know we need to make one. */
1302 proplist = apr_hash_make(trail->pool);
1304 /* Set the property. */
1305 svn_hash_sets(proplist, args->name, args->value);
1307 /* Overwrite the node's proplist. */
1308 SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1309 txn_id, trail, trail->pool));
1311 /* If this was a change to the mergeinfo property, and our version
1312 of the filesystem cares, we have some extra recording to do.
1314 ### If the format *doesn't* support mergeinfo recording, should
1315 ### we fuss about attempts to change the svn:mergeinfo property
1316 ### in any way save to delete it? */
1317 if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1318 && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1320 svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1322 /* First, note on our node that it has mergeinfo. */
1323 SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1325 &had_mergeinfo, txn_id,
1326 trail, trail->pool));
1328 /* If this is a change from the old state, we need to update our
1329 node's parents' mergeinfo counts by a factor of 1. */
1330 if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1331 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1332 has_mergeinfo ? 1 : -1,
1333 txn_id, trail, trail->pool));
1336 /* Make a record of this modification in the changes table. */
1337 return add_change(args->root->fs, txn_id,
1338 args->path, svn_fs_base__dag_get_id(parent_path->node),
1339 svn_fs_path_change_modify, FALSE, TRUE, trail,
1344 static svn_error_t *
1345 base_change_node_prop(svn_fs_root_t *root,
1348 const svn_string_t *value,
1351 struct change_node_prop_args args;
1353 if (! root->is_txn_root)
1354 return SVN_FS__NOT_TXN(root);
1360 return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1365 struct things_changed_args
1367 svn_boolean_t *changed_p;
1368 svn_fs_root_t *root1;
1369 svn_fs_root_t *root2;
1376 static svn_error_t *
1377 txn_body_props_changed(void *baton, trail_t *trail)
1379 struct things_changed_args *args = baton;
1380 dag_node_t *node1, *node2;
1382 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1383 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1384 return svn_fs_base__things_different(args->changed_p, NULL,
1385 node1, node2, trail, trail->pool);
1389 static svn_error_t *
1390 base_props_changed(svn_boolean_t *changed_p,
1391 svn_fs_root_t *root1,
1393 svn_fs_root_t *root2,
1397 struct things_changed_args args;
1399 /* Check that roots are in the same fs. */
1400 if (root1->fs != root2->fs)
1401 return svn_error_create
1402 (SVN_ERR_FS_GENERAL, NULL,
1403 _("Cannot compare property value between two different filesystems"));
1409 args.changed_p = changed_p;
1412 return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1418 /* Miscellaneous table handling */
1420 struct miscellaneous_set_args
1426 static svn_error_t *
1427 txn_body_miscellaneous_set(void *baton, trail_t *trail)
1429 struct miscellaneous_set_args *msa = baton;
1431 return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1436 svn_fs_base__miscellaneous_set(svn_fs_t *fs,
1441 struct miscellaneous_set_args msa;
1445 return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1449 struct miscellaneous_get_args
1455 static svn_error_t *
1456 txn_body_miscellaneous_get(void *baton, trail_t *trail)
1458 struct miscellaneous_get_args *mga = baton;
1459 return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1464 svn_fs_base__miscellaneous_get(const char **val,
1469 struct miscellaneous_get_args mga;
1470 apr_pool_t *scratch_pool = svn_pool_create(pool);
1474 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1475 FALSE, scratch_pool));
1477 *val = apr_pstrdup(pool, *val);
1478 svn_pool_destroy(scratch_pool);
1479 return SVN_NO_ERROR;
1484 /* Getting a directory's entries */
1487 struct dir_entries_args
1489 apr_hash_t **table_p;
1490 svn_fs_root_t *root;
1495 /* *(BATON->table_p) will never be NULL on successful return */
1496 static svn_error_t *
1497 txn_body_dir_entries(void *baton,
1500 struct dir_entries_args *args = baton;
1502 apr_hash_t *entries;
1504 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1506 /* Get the entries for PARENT_PATH. */
1507 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1509 /* Potentially initialize the return value to an empty hash. */
1510 *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1511 return SVN_NO_ERROR;
1515 static svn_error_t *
1516 base_dir_entries(apr_hash_t **table_p,
1517 svn_fs_root_t *root,
1521 struct dir_entries_args args;
1522 apr_pool_t *iterpool;
1524 svn_fs_t *fs = root->fs;
1525 apr_hash_index_t *hi;
1527 args.table_p = &table;
1530 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1533 iterpool = svn_pool_create(pool);
1535 /* Add in the kind data. */
1536 for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1538 svn_fs_dirent_t *entry;
1539 struct node_kind_args nk_args;
1542 svn_pool_clear(iterpool);
1544 /* KEY will be the entry name in ancestor (about which we
1545 simply don't care), VAL the dirent. */
1546 apr_hash_this(hi, NULL, NULL, &val);
1548 nk_args.id = entry->id;
1550 /* We don't need to have the retry function destroy the trail
1551 pool because we're already doing that via the use of an
1553 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1555 entry->kind = nk_args.kind;
1558 svn_pool_destroy(iterpool);
1561 return SVN_NO_ERROR;
1566 /* Merges and commits. */
1569 struct deltify_committed_args
1571 svn_fs_t *fs; /* the filesystem */
1572 svn_revnum_t rev; /* revision just committed */
1573 const char *txn_id; /* transaction just committed */
1577 struct txn_deltify_args
1579 /* The transaction ID whose nodes are being deltified. */
1582 /* The target is what we're deltifying. */
1583 const svn_fs_id_t *tgt_id;
1585 /* The base is what we're deltifying against. It's not necessarily
1586 the "next" revision of the node; skip deltas mean we sometimes
1587 deltify against a successor many generations away. This may be
1588 NULL, in which case we'll avoid deltification and simply index
1589 TGT_ID's data checksum. */
1590 const svn_fs_id_t *base_id;
1592 /* We only deltify props for directories.
1593 ### Didn't we try removing this horrid little optimization once?
1594 ### What was the result? I would have thought that skip deltas
1595 ### mean directory undeltification is cheap enough now. */
1596 svn_boolean_t is_dir;
1600 static svn_error_t *
1601 txn_body_txn_deltify(void *baton, trail_t *trail)
1603 struct txn_deltify_args *args = baton;
1604 dag_node_t *tgt_node, *base_node;
1605 base_fs_data_t *bfd = trail->fs->fsap_data;
1607 SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1608 trail, trail->pool));
1609 /* If we have something to deltify against, do so. */
1612 SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1613 trail, trail->pool));
1614 SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1615 args->txn_id, trail, trail->pool));
1618 /* If we support rep sharing, and this isn't a directory, record a
1619 mapping of TGT_NODE's data checksum to its representation key. */
1620 if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1621 SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1623 return SVN_NO_ERROR;
1627 struct txn_pred_count_args
1629 const svn_fs_id_t *id;
1634 static svn_error_t *
1635 txn_body_pred_count(void *baton, trail_t *trail)
1637 node_revision_t *noderev;
1638 struct txn_pred_count_args *args = baton;
1640 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1641 args->id, trail, trail->pool));
1642 args->pred_count = noderev->predecessor_count;
1643 return SVN_NO_ERROR;
1647 struct txn_pred_id_args
1649 const svn_fs_id_t *id; /* The node id whose predecessor we want. */
1650 const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1651 apr_pool_t *pool; /* The pool in which to allocate pred_id. */
1655 static svn_error_t *
1656 txn_body_pred_id(void *baton, trail_t *trail)
1658 node_revision_t *nr;
1659 struct txn_pred_id_args *args = baton;
1661 SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1662 trail, trail->pool));
1663 if (nr->predecessor_id)
1664 args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1666 args->pred_id = NULL;
1668 return SVN_NO_ERROR;
1672 /* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1673 in FS. If PATH is a mutable directory, recurse.
1675 NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1676 value isn't known. KIND is the node kind for PATH in ROOT, or
1677 svn_node_unknown is the kind isn't known.
1679 Use POOL for necessary allocations. */
1680 static svn_error_t *
1681 deltify_mutable(svn_fs_t *fs,
1682 svn_fs_root_t *root,
1684 const svn_fs_id_t *node_id,
1685 svn_node_kind_t kind,
1689 const svn_fs_id_t *id = node_id;
1690 apr_hash_t *entries = NULL;
1691 struct txn_deltify_args td_args;
1692 base_fs_data_t *bfd = fs->fsap_data;
1694 /* Get the ID for PATH under ROOT if it wasn't provided. */
1696 SVN_ERR(base_node_id(&id, root, path, pool));
1698 /* Check for mutability. Not mutable? Go no further. This is safe
1699 to do because for items in the tree to be mutable, their parent
1700 dirs must also be mutable. Therefore, if a directory is not
1701 mutable under TXN_ID, its children cannot be. */
1702 if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1703 return SVN_NO_ERROR;
1705 /* Is this a directory? */
1706 if (kind == svn_node_unknown)
1707 SVN_ERR(base_check_path(&kind, root, path, pool));
1709 /* If this is a directory, read its entries. */
1710 if (kind == svn_node_dir)
1711 SVN_ERR(base_dir_entries(&entries, root, path, pool));
1713 /* If there are entries, recurse on 'em. */
1716 apr_pool_t *subpool = svn_pool_create(pool);
1717 apr_hash_index_t *hi;
1719 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1721 /* KEY will be the entry name, VAL the dirent */
1724 svn_fs_dirent_t *entry;
1725 svn_pool_clear(subpool);
1726 apr_hash_this(hi, &key, NULL, &val);
1728 SVN_ERR(deltify_mutable(fs, root,
1729 svn_fspath__join(path, key, subpool),
1730 entry->id, entry->kind, txn_id, subpool));
1733 svn_pool_destroy(subpool);
1736 /* Index ID's data checksum. */
1737 td_args.txn_id = txn_id;
1738 td_args.tgt_id = id;
1739 td_args.base_id = NULL;
1740 td_args.is_dir = (kind == svn_node_dir);
1741 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1744 /* Finally, deltify old data against this node. */
1746 /* Prior to 1.6, we use the following algorithm to deltify nodes:
1748 Redeltify predecessor node-revisions of the one we added. The
1749 idea is to require at most 2*lg(N) deltas to be applied to get
1750 to any node-revision in a chain of N predecessors. We do this
1751 using a technique derived from skip lists:
1753 - Always redeltify the immediate parent
1755 - If the number of predecessors is divisible by 2,
1756 redeltify the revision two predecessors back
1758 - If the number of predecessors is divisible by 4,
1759 redeltify the revision four predecessors back
1763 That's the theory, anyway. Unfortunately, if we strictly
1764 follow that theory we get a bunch of overhead up front and no
1765 great benefit until the number of predecessors gets large. So,
1766 stop at redeltifying the parent if the number of predecessors
1767 is less than 32, and also skip the second level (redeltifying
1768 two predecessors back), since that doesn't help much. Also,
1769 don't redeltify the oldest node-revision; it's potentially
1770 expensive and doesn't help retrieve any other revision.
1771 (Retrieving the oldest node-revision will still be fast, just
1772 not as blindingly so.)
1774 For 1.6 and beyond, we just deltify the current node against its
1775 predecessors, using skip deltas similar to the way FSFS does. */
1778 const svn_fs_id_t *pred_id;
1779 struct txn_pred_count_args tpc_args;
1780 apr_pool_t *subpools[2];
1781 int active_subpool = 0;
1782 svn_revnum_t forward_delta_rev = 0;
1785 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1787 pred_count = tpc_args.pred_count;
1789 /* If nothing to deltify, then we're done. */
1790 if (pred_count == 0)
1791 return SVN_NO_ERROR;
1793 subpools[0] = svn_pool_create(pool);
1794 subpools[1] = svn_pool_create(pool);
1796 /* If we support the 'miscellaneous' table, check it to see if
1797 there is a point in time before which we don't want to do
1799 /* ### FIXME: I think this is an unnecessary restriction. We
1800 ### should be able to do something meaningful for most
1801 ### deltification requests -- what that is depends on the
1802 ### directory of the deltas for that revision, though. */
1803 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1806 SVN_ERR(svn_fs_base__miscellaneous_get
1807 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1809 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1812 if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1813 && forward_delta_rev <= root->rev)
1815 /**** FORWARD DELTA STORAGE ****/
1817 /* Decide which predecessor to deltify against. Flip the rightmost '1'
1818 bit of the predecessor count to determine which file rev (counting
1819 from 0) we want to use. (To see why count & (count - 1) unsets the
1820 rightmost set bit, think about how you decrement a binary number. */
1821 pred_count = pred_count & (pred_count - 1);
1823 /* Walk back a number of predecessors equal to the difference between
1824 pred_count and the original predecessor count. (For example, if
1825 the node has ten predecessors and we want the eighth node, walk back
1826 two predecessors. */
1829 /* We need to use two alternating pools because the id used in the
1830 call to txn_body_pred_id is allocated by the previous inner
1831 loop iteration. If we would clear the pool each iteration we
1832 would free the previous result. */
1833 while ((pred_count++) < tpc_args.pred_count)
1835 struct txn_pred_id_args tpi_args;
1837 active_subpool = !active_subpool;
1838 svn_pool_clear(subpools[active_subpool]);
1840 tpi_args.id = pred_id;
1841 tpi_args.pool = subpools[active_subpool];
1842 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1843 FALSE, subpools[active_subpool]));
1844 pred_id = tpi_args.pred_id;
1846 if (pred_id == NULL)
1847 return svn_error_create
1848 (SVN_ERR_FS_CORRUPT, 0,
1849 _("Corrupt DB: faulty predecessor count"));
1853 /* Finally, do the deltification. */
1854 td_args.txn_id = txn_id;
1855 td_args.tgt_id = id;
1856 td_args.base_id = pred_id;
1857 td_args.is_dir = (kind == svn_node_dir);
1858 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1859 TRUE, subpools[active_subpool]));
1863 int nlevels, lev, count;
1865 /**** REVERSE DELTA STORAGE ****/
1867 /* Decide how many predecessors to redeltify. To save overhead,
1868 don't redeltify anything but the immediate predecessor if there
1869 are less than 32 predecessors. */
1871 if (pred_count >= 32)
1873 while (pred_count % 2 == 0)
1879 /* Don't redeltify the oldest revision. */
1880 if (1 << (nlevels - 1) == pred_count)
1884 /* Redeltify the desired number of predecessors. */
1888 /* We need to use two alternating pools because the id used in the
1889 call to txn_body_pred_id is allocated by the previous inner
1890 loop iteration. If we would clear the pool each iteration we
1891 would free the previous result. */
1892 for (lev = 0; lev < nlevels; lev++)
1894 /* To save overhead, skip the second level (that is, never
1895 redeltify the node-revision two predecessors back). */
1899 /* Note that COUNT is not reset between levels, and neither is
1900 PREDNODE; we just keep counting from where we were up to
1901 where we're supposed to get. */
1902 while (count < (1 << lev))
1904 struct txn_pred_id_args tpi_args;
1906 active_subpool = !active_subpool;
1907 svn_pool_clear(subpools[active_subpool]);
1909 tpi_args.id = pred_id;
1910 tpi_args.pool = subpools[active_subpool];
1911 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1913 subpools[active_subpool]));
1914 pred_id = tpi_args.pred_id;
1916 if (pred_id == NULL)
1917 return svn_error_create
1918 (SVN_ERR_FS_CORRUPT, 0,
1919 _("Corrupt DB: faulty predecessor count"));
1924 /* Finally, do the deltification. */
1925 td_args.txn_id = NULL; /* Don't require mutable reps */
1926 td_args.tgt_id = pred_id;
1927 td_args.base_id = id;
1928 td_args.is_dir = (kind == svn_node_dir);
1929 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1930 TRUE, subpools[active_subpool]));
1935 svn_pool_destroy(subpools[0]);
1936 svn_pool_destroy(subpools[1]);
1939 return SVN_NO_ERROR;
1943 struct get_root_args
1945 svn_fs_root_t *root;
1950 /* Set ARGS->node to the root node of ARGS->root. */
1951 static svn_error_t *
1952 txn_body_get_root(void *baton, trail_t *trail)
1954 struct get_root_args *args = baton;
1955 return get_dag(&(args->node), args->root, "", trail, trail->pool);
1960 static svn_error_t *
1961 update_ancestry(svn_fs_t *fs,
1962 const svn_fs_id_t *source_id,
1963 const svn_fs_id_t *target_id,
1965 const char *target_path,
1966 int source_pred_count,
1970 node_revision_t *noderev;
1972 /* Set target's predecessor-id to source_id. */
1973 if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
1974 return svn_error_createf
1975 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1976 _("Unexpected immutable node at '%s'"), target_path);
1977 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
1979 noderev->predecessor_id = source_id;
1980 noderev->predecessor_count = source_pred_count;
1981 if (noderev->predecessor_count != -1)
1982 noderev->predecessor_count++;
1983 return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
1987 /* Set the contents of CONFLICT_PATH to PATH, and return an
1988 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1989 at PATH. Perform all allocations in POOL (except the allocation of
1990 CONFLICT_PATH, which should be handled outside this function). */
1991 static svn_error_t *
1992 conflict_err(svn_stringbuf_t *conflict_path,
1995 svn_stringbuf_set(conflict_path, path);
1996 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1997 _("Conflict at '%s'"), path);
2001 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2002 * TRAIL. ANCESTOR and TARGET must be distinct node revisions.
2003 * TARGET_PATH should correspond to TARGET's full path in its
2004 * filesystem, and is used for reporting conflict location.
2006 * SOURCE, TARGET, and ANCESTOR are generally directories; this
2007 * function recursively merges the directories' contents. If any are
2008 * files, this function simply returns an error whenever SOURCE,
2009 * TARGET, and ANCESTOR are all distinct node revisions.
2011 * If there are differences between ANCESTOR and SOURCE that conflict
2012 * with changes between ANCESTOR and TARGET, this function returns an
2013 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2014 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2016 * If there are no conflicting differences, CONFLICT_P is updated to
2019 * CONFLICT_P must point to a valid svn_stringbuf_t.
2021 * Do any necessary temporary allocation in POOL.
2023 static svn_error_t *
2024 merge(svn_stringbuf_t *conflict_p,
2025 const char *target_path,
2028 dag_node_t *ancestor,
2030 apr_int64_t *mergeinfo_increment_out,
2034 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2035 apr_hash_t *s_entries, *t_entries, *a_entries;
2036 apr_hash_index_t *hi;
2037 apr_pool_t *iterpool;
2040 apr_int64_t mergeinfo_increment = 0;
2041 base_fs_data_t *bfd = trail->fs->fsap_data;
2043 /* Make sure everyone comes from the same filesystem. */
2044 fs = svn_fs_base__dag_get_fs(ancestor);
2045 if ((fs != svn_fs_base__dag_get_fs(source))
2046 || (fs != svn_fs_base__dag_get_fs(target)))
2048 return svn_error_create
2049 (SVN_ERR_FS_CORRUPT, NULL,
2050 _("Bad merge; ancestor, source, and target not all in same fs"));
2053 /* We have the same fs, now check it. */
2054 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2056 source_id = svn_fs_base__dag_get_id(source);
2057 target_id = svn_fs_base__dag_get_id(target);
2058 ancestor_id = svn_fs_base__dag_get_id(ancestor);
2060 /* It's improper to call this function with ancestor == target. */
2061 if (svn_fs_base__id_eq(ancestor_id, target_id))
2063 svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2064 return svn_error_createf
2065 (SVN_ERR_FS_GENERAL, NULL,
2066 _("Bad merge; target '%s' has id '%s', same as ancestor"),
2067 target_path, id_str->data);
2070 svn_stringbuf_setempty(conflict_p);
2073 * Either no change made in source, or same change as made in target.
2074 * Both mean nothing to merge here.
2076 if (svn_fs_base__id_eq(ancestor_id, source_id)
2077 || (svn_fs_base__id_eq(source_id, target_id)))
2078 return SVN_NO_ERROR;
2080 /* Else proceed, knowing all three are distinct node revisions.
2082 * How to merge from this point:
2084 * if (not all 3 are directories)
2086 * early exit with conflict;
2089 * // Property changes may only be made to up-to-date
2090 * // directories, because once the client commits the prop
2091 * // change, it bumps the directory's revision, and therefore
2092 * // must be able to depend on there being no other changes to
2093 * // that directory in the repository.
2094 * if (target's property list differs from ancestor's)
2097 * For each entry NAME in the directory ANCESTOR:
2099 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2100 * the name within ANCESTOR, SOURCE, and TARGET respectively.
2101 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
2103 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2104 * No changes were made to this entry while the transaction was in
2105 * progress, so do nothing to the target.
2107 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2108 * A change was made to this entry while the transaction was in
2109 * process, but the transaction did not touch this entry. Replace
2110 * TARGET-ENTRY with SOURCE-ENTRY.
2113 * Changes were made to this entry both within the transaction and
2114 * to the repository while the transaction was in progress. They
2115 * must be merged or declared to be in conflict.
2117 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2118 * double delete; flag a conflict.
2120 * If any of the three entries is of type file, declare a conflict.
2122 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2123 * modification of ANCESTOR-ENTRY (determine by comparing the
2124 * node-id fields), declare a conflict. A replacement is
2125 * incompatible with a modification or other replacement--even
2126 * an identical replacement.
2128 * Direct modifications were made to the directory ANCESTOR-ENTRY
2129 * in both SOURCE and TARGET. Recursively merge these
2132 * For each leftover entry NAME in the directory SOURCE:
2134 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
2135 * TARGET are adding exactly the same thing, two additions are not
2136 * auto-mergeable with each other.
2138 * Add NAME to TARGET with the entry from SOURCE.
2140 * Now that we are done merging the changes from SOURCE into the
2141 * directory TARGET, update TARGET's predecessor to be SOURCE.
2144 if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2145 || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2146 || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2148 return conflict_err(conflict_p, target_path);
2152 /* Possible early merge failure: if target and ancestor have
2153 different property lists, then the merge should fail.
2154 Propchanges can *only* be committed on an up-to-date directory.
2155 ### TODO: see issue #418 about the inelegance of this.
2157 Another possible, similar, early merge failure: if source and
2158 ancestor have different property lists (meaning someone else
2159 changed directory properties while our commit transaction was
2160 happening), the merge should fail. See issue #2751.
2163 node_revision_t *tgt_nr, *anc_nr, *src_nr;
2165 /* Get node revisions for our id's. */
2166 SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2168 SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2170 SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2173 /* Now compare the prop-keys of the skels. Note that just because
2174 the keys are different -doesn't- mean the proplists have
2175 different contents. But merge() isn't concerned with contents;
2176 it doesn't do a brute-force comparison on textual contents, so
2177 it won't do that here either. Checking to see if the propkey
2178 atoms are `equal' is enough. */
2179 if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2180 return conflict_err(conflict_p, target_path);
2181 if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2182 return conflict_err(conflict_p, target_path);
2185 /* ### todo: it would be more efficient to simply check for a NULL
2186 entries hash where necessary below than to allocate an empty hash
2187 here, but another day, another day... */
2188 SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2190 s_entries = apr_hash_make(pool);
2191 SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2193 t_entries = apr_hash_make(pool);
2194 SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2196 a_entries = apr_hash_make(pool);
2198 /* for each entry E in a_entries... */
2199 iterpool = svn_pool_create(pool);
2200 for (hi = apr_hash_first(pool, a_entries);
2202 hi = apr_hash_next(hi))
2204 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2210 svn_pool_clear(iterpool);
2212 /* KEY will be the entry name in ancestor, VAL the dirent */
2213 apr_hash_this(hi, &key, &klen, &val);
2216 s_entry = apr_hash_get(s_entries, key, klen);
2217 t_entry = apr_hash_get(t_entries, key, klen);
2219 /* No changes were made to this entry while the transaction was
2220 in progress, so do nothing to the target. */
2221 if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2224 /* A change was made to this entry while the transaction was in
2225 process, but the transaction did not touch this entry. */
2226 else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2228 dag_node_t *t_ent_node;
2229 apr_int64_t mergeinfo_start;
2230 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2231 t_entry->id, trail, iterpool));
2232 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2235 mergeinfo_increment -= mergeinfo_start;
2239 dag_node_t *s_ent_node;
2240 apr_int64_t mergeinfo_end;
2241 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2244 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2248 mergeinfo_increment += mergeinfo_end;
2249 SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2250 txn_id, trail, iterpool));
2254 SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2259 /* Changes were made to this entry both within the transaction
2260 and to the repository while the transaction was in progress.
2261 They must be merged or declared to be in conflict. */
2264 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2265 const char *new_tpath;
2266 apr_int64_t sub_mergeinfo_increment;
2268 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2269 double delete; if one of them is null, that's a delete versus
2270 a modification. In any of these cases, flag a conflict. */
2271 if (s_entry == NULL || t_entry == NULL)
2272 return conflict_err(conflict_p,
2273 svn_fspath__join(target_path,
2277 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2278 modification of ANCESTOR-ENTRY, declare a conflict. */
2279 if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2280 svn_fs_base__id_node_id(a_entry->id)) != 0
2281 || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2282 svn_fs_base__id_copy_id(a_entry->id)) != 0
2283 || strcmp(svn_fs_base__id_node_id(t_entry->id),
2284 svn_fs_base__id_node_id(a_entry->id)) != 0
2285 || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2286 svn_fs_base__id_copy_id(a_entry->id)) != 0)
2287 return conflict_err(conflict_p,
2288 svn_fspath__join(target_path,
2292 /* Fetch the nodes for our entries. */
2293 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2294 s_entry->id, trail, iterpool));
2295 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2296 t_entry->id, trail, iterpool));
2297 SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2298 a_entry->id, trail, iterpool));
2300 /* If any of the three entries is of type file, flag a conflict. */
2301 if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2302 || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2303 || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2304 return conflict_err(conflict_p,
2305 svn_fspath__join(target_path,
2309 /* Direct modifications were made to the directory
2310 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2311 merge these modifications. */
2312 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2313 SVN_ERR(merge(conflict_p, new_tpath,
2314 t_ent_node, s_ent_node, a_ent_node,
2315 txn_id, &sub_mergeinfo_increment, trail, iterpool));
2316 mergeinfo_increment += sub_mergeinfo_increment;
2319 /* We've taken care of any possible implications E could have.
2320 Remove it from source_entries, so it's easy later to loop
2321 over all the source entries that didn't exist in
2322 ancestor_entries. */
2324 apr_hash_set(s_entries, key, klen, NULL);
2327 /* For each entry E in source but not in ancestor */
2328 for (hi = apr_hash_first(pool, s_entries);
2330 hi = apr_hash_next(hi))
2332 svn_fs_dirent_t *s_entry, *t_entry;
2336 dag_node_t *s_ent_node;
2337 apr_int64_t mergeinfo_s;
2339 svn_pool_clear(iterpool);
2341 apr_hash_this(hi, &key, &klen, &val);
2343 t_entry = apr_hash_get(t_entries, key, klen);
2345 /* If NAME exists in TARGET, declare a conflict. */
2347 return conflict_err(conflict_p,
2348 svn_fspath__join(target_path,
2352 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2353 s_entry->id, trail, iterpool));
2354 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2357 mergeinfo_increment += mergeinfo_s;
2358 SVN_ERR(svn_fs_base__dag_set_entry
2359 (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2361 svn_pool_destroy(iterpool);
2363 /* Now that TARGET has absorbed all of the history between ANCESTOR
2364 and SOURCE, we can update its predecessor to point to SOURCE. */
2365 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2367 SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2368 pred_count, trail, pool));
2370 /* Tweak mergeinfo data if our format supports it. */
2371 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2373 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2374 mergeinfo_increment,
2375 txn_id, trail, pool));
2378 if (mergeinfo_increment_out)
2379 *mergeinfo_increment_out = mergeinfo_increment;
2381 return SVN_NO_ERROR;
2387 /* The ancestor for the merge. If this is null, then TXN's base is
2388 used as the ancestor for the merge. */
2389 dag_node_t *ancestor_node;
2391 /* This is the SOURCE node for the merge. It may not be null. */
2392 dag_node_t *source_node;
2394 /* This is the TARGET of the merge. It may not be null. If
2395 ancestor_node above is null, then this txn's base is used as the
2396 ancestor for the merge. */
2399 /* If a conflict results, this is updated to the path in the txn that
2400 conflicted. It must point to a valid svn_stringbuf_t before calling
2401 svn_fs_base__retry_txn, as this determines the pool used to allocate any
2403 svn_stringbuf_t *conflict;
2407 /* Merge changes between an ancestor and BATON->source_node into
2408 BATON->txn. The ancestor is either BATON->ancestor_node, or if
2409 that is null, BATON->txn's base node.
2411 If the merge is successful, BATON->txn's base will become
2412 BATON->source_node, and its root node will have a new ID, a
2413 successor of BATON->source_node. */
2414 static svn_error_t *
2415 txn_body_merge(void *baton, trail_t *trail)
2417 struct merge_args *args = baton;
2418 dag_node_t *source_node, *txn_root_node, *ancestor_node;
2419 const svn_fs_id_t *source_id;
2420 svn_fs_t *fs = args->txn->fs;
2421 const char *txn_id = args->txn->id;
2423 source_node = args->source_node;
2424 ancestor_node = args->ancestor_node;
2425 source_id = svn_fs_base__dag_get_id(source_node);
2427 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2428 trail, trail->pool));
2430 if (ancestor_node == NULL)
2432 SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2433 txn_id, trail, trail->pool));
2436 if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2437 svn_fs_base__dag_get_id(txn_root_node)))
2439 /* If no changes have been made in TXN since its current base,
2440 then it can't conflict with any changes since that base. So
2441 we just set *both* its base and root to source, making TXN
2442 in effect a repeat of source. */
2444 /* ### kff todo: this would, of course, be a mighty silly thing
2445 for the caller to do, and we might want to consider whether
2446 this response is really appropriate. */
2448 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2449 trail, trail->pool));
2450 SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2451 trail, trail->pool));
2457 SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2458 ancestor_node, txn_id, NULL, trail, trail->pool));
2460 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2464 /* After the merge, txn's new "ancestor" is now really the node
2465 at source_id, so record that fact. Think of this as
2466 ratcheting the txn forward in time, so it can't backslide and
2467 forget the merging work that's already been done. */
2468 SVN_ERR(update_ancestry(fs, source_id,
2469 svn_fs_base__dag_get_id(txn_root_node),
2470 txn_id, "/", pred_count, trail, trail->pool));
2471 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2472 trail, trail->pool));
2475 return SVN_NO_ERROR;
2479 /* Verify that there are registered with TRAIL->fs all the locks
2480 necessary to permit all the changes associated with TXN_NAME. */
2481 static svn_error_t *
2482 verify_locks(const char *txn_name,
2486 apr_pool_t *subpool = svn_pool_create(pool);
2487 apr_hash_t *changes;
2488 apr_hash_index_t *hi;
2489 apr_array_header_t *changed_paths;
2490 svn_stringbuf_t *last_recursed = NULL;
2493 /* Fetch the changes for this transaction. */
2494 SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2497 /* Make an array of the changed paths, and sort them depth-first-ily. */
2498 changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2499 sizeof(const char *));
2500 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2503 apr_hash_this(hi, &key, NULL, NULL);
2504 APR_ARRAY_PUSH(changed_paths, const char *) = key;
2506 qsort(changed_paths->elts, changed_paths->nelts,
2507 changed_paths->elt_size, svn_sort_compare_paths);
2509 /* Now, traverse the array of changed paths, verify locks. Note
2510 that if we need to do a recursive verification a path, we'll skip
2511 over children of that path when we get to them. */
2512 for (i = 0; i < changed_paths->nelts; i++)
2515 svn_fs_path_change2_t *change;
2516 svn_boolean_t recurse = TRUE;
2518 svn_pool_clear(subpool);
2519 path = APR_ARRAY_IDX(changed_paths, i, const char *);
2521 /* If this path has already been verified as part of a recursive
2522 check of one of its parents, no need to do it again. */
2524 && svn_fspath__skip_ancestor(last_recursed->data, path))
2527 /* Fetch the change associated with our path. */
2528 change = svn_hash_gets(changes, path);
2530 /* What does it mean to succeed at lock verification for a given
2531 path? For an existing file or directory getting modified
2532 (text, props), it means we hold the lock on the file or
2533 directory. For paths being added or removed, we need to hold
2534 the locks for that path and any children of that path.
2536 WHEW! We have no reliable way to determine the node kind of
2537 deleted items, but fortunately we are going to do a recursive
2538 check on deleted paths regardless of their kind. */
2539 if (change->change_kind == svn_fs_path_change_modify)
2541 SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2544 /* If we just did a recursive check, remember the path we
2545 checked (so children can be skipped). */
2548 if (! last_recursed)
2549 last_recursed = svn_stringbuf_create(path, pool);
2551 svn_stringbuf_set(last_recursed, path);
2554 svn_pool_destroy(subpool);
2555 return SVN_NO_ERROR;
2562 svn_revnum_t new_rev;
2566 /* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2567 * revision, if ARGS->txn is up-to-date with respect to the repository.
2569 * Up-to-date means that ARGS->txn's base root is the same as the root
2570 * of the youngest revision. If ARGS->txn is not up-to-date, the
2571 * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2572 * new revision is created, and ARGS->new_rev is not touched.
2574 * If the commit succeeds, ARGS->txn is destroyed.
2576 static svn_error_t *
2577 txn_body_commit(void *baton, trail_t *trail)
2579 struct commit_args *args = baton;
2581 svn_fs_txn_t *txn = args->txn;
2582 svn_fs_t *fs = txn->fs;
2583 const char *txn_name = txn->id;
2585 svn_revnum_t youngest_rev;
2586 const svn_fs_id_t *y_rev_root_id;
2587 dag_node_t *txn_base_root_node;
2589 /* Getting the youngest revision locks the revisions table until
2590 this trail is done. */
2591 SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2593 /* If the root of the youngest revision is the same as txn's base,
2594 then no further merging is necessary and we can commit. */
2595 SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2596 trail, trail->pool));
2597 SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2598 trail, trail->pool));
2599 /* ### kff todo: it seems weird to grab the ID for one, and the node
2600 for the other. We can certainly do the comparison we need, but
2601 it would be nice to grab the same type of information from the
2602 start, instead of having to transform one of them. */
2603 if (! svn_fs_base__id_eq(y_rev_root_id,
2604 svn_fs_base__dag_get_id(txn_base_root_node)))
2606 svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2608 return svn_error_createf
2609 (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2610 _("Transaction '%s' out-of-date with respect to revision '%s'"),
2611 txn_name, id_str->data);
2614 /* Locks may have been added (or stolen) between the calling of
2615 previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2616 to re-examine every changed-path in the txn and re-verify all
2617 discovered locks. */
2618 SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2620 /* Else, commit the txn. */
2621 return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2626 /* Note: it is acceptable for this function to call back into
2627 top-level FS interfaces because it does not itself use trails. */
2629 svn_fs_base__commit_txn(const char **conflict_p,
2630 svn_revnum_t *new_rev,
2634 /* How do commits work in Subversion?
2636 * When you're ready to commit, here's what you have:
2638 * 1. A transaction, with a mutable tree hanging off it.
2639 * 2. A base revision, against which TXN_TREE was made.
2640 * 3. A latest revision, which may be newer than the base rev.
2642 * The problem is that if latest != base, then one can't simply
2643 * attach the txn root as the root of the new revision, because that
2644 * would lose all the changes between base and latest. It is also
2645 * not acceptable to insist that base == latest; in a busy
2646 * repository, commits happen too fast to insist that everyone keep
2647 * their entire tree up-to-date at all times. Non-overlapping
2648 * changes should not interfere with each other.
2650 * The solution is to merge the changes between base and latest into
2651 * the txn tree [see the function merge()]. The txn tree is the
2652 * only one of the three trees that is mutable, so it has to be the
2655 * You might have to adjust it more than once, if a new latest
2656 * revision gets committed while you were merging in the previous
2659 * 1. Jane starts txn T, based at revision 6.
2660 * 2. Someone commits (or already committed) revision 7.
2661 * 3. Jane's starts merging the changes between 6 and 7 into T.
2662 * 4. Meanwhile, someone commits revision 8.
2663 * 5. Jane finishes the 6-->7 merge. T could now be committed
2664 * against a latest revision of 7, if only that were still the
2665 * latest. Unfortunately, 8 is now the latest, so...
2666 * 6. Jane starts merging the changes between 7 and 8 into T.
2667 * 7. Meanwhile, no one commits any new revisions. Whew.
2668 * 8. Jane commits T, creating revision 9, whose tree is exactly
2669 * T's tree, except immutable now.
2671 * Lather, rinse, repeat.
2675 svn_fs_t *fs = txn->fs;
2676 apr_pool_t *subpool = svn_pool_create(pool);
2678 /* Initialize output params. */
2679 *new_rev = SVN_INVALID_REVNUM;
2685 struct get_root_args get_root_args;
2686 struct merge_args merge_args;
2687 struct commit_args commit_args;
2688 svn_revnum_t youngish_rev;
2689 svn_fs_root_t *youngish_root;
2690 dag_node_t *youngish_root_node;
2692 svn_pool_clear(subpool);
2694 /* Get the *current* youngest revision, in one short-lived
2695 Berkeley transaction. (We don't want the revisions table
2696 locked while we do the main merge.) We call it "youngish"
2697 because new revisions might get committed after we've
2700 SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2701 SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2704 /* Get the dag node for the youngest revision, also in one
2705 Berkeley transaction. Later we'll use it as the SOURCE
2706 argument to a merge, and if the merge succeeds, this youngest
2707 root node will become the new base root for the svn txn that
2708 was the target of the merge (but note that the youngest rev
2709 may have changed by then -- that's why we're careful to get
2710 this root in its own bdb txn here). */
2711 get_root_args.root = youngish_root;
2712 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2714 youngish_root_node = get_root_args.node;
2716 /* Try to merge. If the merge succeeds, the base root node of
2717 TARGET's txn will become the same as youngish_root_node, so
2718 any future merges will only be between that node and whatever
2719 the root node of the youngest rev is by then. */
2720 merge_args.ancestor_node = NULL;
2721 merge_args.source_node = youngish_root_node;
2722 merge_args.txn = txn;
2723 merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2724 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2728 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2729 *conflict_p = merge_args.conflict->data;
2730 return svn_error_trace(err);
2733 /* Try to commit. */
2734 commit_args.txn = txn;
2735 err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2737 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2739 /* Did someone else finish committing a new revision while we
2740 were in mid-merge or mid-commit? If so, we'll need to
2741 loop again to merge the new changes in, then try to
2742 commit again. Or if that's not what happened, then just
2743 return the error. */
2744 svn_revnum_t youngest_rev;
2745 svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2749 svn_error_clear(err);
2750 return svn_error_trace(err2); /* err2 is bad,
2751 it should not occur */
2753 else if (youngest_rev == youngish_rev)
2754 return svn_error_trace(err);
2756 svn_error_clear(err);
2760 return svn_error_trace(err);
2764 /* Set the return value -- our brand spankin' new revision! */
2765 *new_rev = commit_args.new_rev;
2770 svn_pool_destroy(subpool);
2771 return SVN_NO_ERROR;
2774 /* Note: it is acceptable for this function to call back into
2775 public FS API interfaces because it does not itself use trails. */
2776 static svn_error_t *
2777 base_merge(const char **conflict_p,
2778 svn_fs_root_t *source_root,
2779 const char *source_path,
2780 svn_fs_root_t *target_root,
2781 const char *target_path,
2782 svn_fs_root_t *ancestor_root,
2783 const char *ancestor_path,
2786 dag_node_t *source, *ancestor;
2787 struct get_root_args get_root_args;
2788 struct merge_args merge_args;
2793 if (! target_root->is_txn_root)
2794 return SVN_FS__NOT_TXN(target_root);
2797 fs = ancestor_root->fs;
2798 if ((source_root->fs != fs) || (target_root->fs != fs))
2800 return svn_error_create
2801 (SVN_ERR_FS_CORRUPT, NULL,
2802 _("Bad merge; ancestor, source, and target not all in same fs"));
2805 /* ### kff todo: is there any compelling reason to get the nodes in
2806 one db transaction? Right now we don't; txn_body_get_root() gets
2807 one node at a time. This will probably need to change:
2809 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2810 > svn_fs_merge needs to be a single transaction, to protect it against
2811 > people deleting parents of nodes it's working on, etc.
2814 /* Get the ancestor node. */
2815 get_root_args.root = ancestor_root;
2816 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2818 ancestor = get_root_args.node;
2820 /* Get the source node. */
2821 get_root_args.root = source_root;
2822 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2824 source = get_root_args.node;
2826 /* Open a txn for the txn root into which we're merging. */
2827 SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2829 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2830 merge_args.source_node = source;
2831 merge_args.ancestor_node = ancestor;
2832 merge_args.txn = txn;
2833 merge_args.conflict = svn_stringbuf_create_empty(pool);
2834 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2837 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2838 *conflict_p = merge_args.conflict->data;
2839 return svn_error_trace(err);
2842 return SVN_NO_ERROR;
2846 struct rev_get_txn_id_args
2848 const char **txn_id;
2849 svn_revnum_t revision;
2853 static svn_error_t *
2854 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2856 struct rev_get_txn_id_args *args = baton;
2857 return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2858 args->revision, trail, trail->pool);
2863 svn_fs_base__deltify(svn_fs_t *fs,
2864 svn_revnum_t revision,
2867 svn_fs_root_t *root;
2869 struct rev_get_txn_id_args args;
2870 base_fs_data_t *bfd = fs->fsap_data;
2872 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2875 svn_revnum_t forward_delta_rev = 0;
2877 SVN_ERR(svn_fs_base__miscellaneous_get
2878 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2880 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2882 /* ### FIXME: Unnecessarily harsh requirement? (cmpilato). */
2883 if (revision <= forward_delta_rev)
2884 return svn_error_createf
2885 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2886 _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2889 SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2891 args.txn_id = &txn_id;
2892 args.revision = revision;
2893 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2896 return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2900 /* Modifying directories */
2903 struct make_dir_args
2905 svn_fs_root_t *root;
2910 static svn_error_t *
2911 txn_body_make_dir(void *baton,
2914 struct make_dir_args *args = baton;
2915 svn_fs_root_t *root = args->root;
2916 const char *path = args->path;
2917 parent_path_t *parent_path;
2918 dag_node_t *sub_dir;
2919 const char *txn_id = root->txn;
2921 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2922 txn_id, trail, trail->pool));
2924 /* If there's already a sub-directory by that name, complain. This
2925 also catches the case of trying to make a subdirectory named `/'. */
2926 if (parent_path->node)
2927 return SVN_FS__ALREADY_EXISTS(root, path);
2929 /* Check to see if some lock is 'reserving' a file-path or dir-path
2930 at that location, or even some child-path; if so, check that we
2932 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2934 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
2935 trail, trail->pool));
2938 /* Create the subdirectory. */
2939 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
2940 trail, trail->pool));
2941 SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
2942 parent_path->parent->node,
2943 parent_path_path(parent_path->parent,
2947 trail, trail->pool));
2949 /* Make a record of this modification in the changes table. */
2950 return add_change(root->fs, txn_id, path,
2951 svn_fs_base__dag_get_id(sub_dir),
2952 svn_fs_path_change_add, FALSE, FALSE,
2953 trail, trail->pool);
2957 static svn_error_t *
2958 base_make_dir(svn_fs_root_t *root,
2962 struct make_dir_args args;
2964 if (! root->is_txn_root)
2965 return SVN_FS__NOT_TXN(root);
2969 return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
2976 svn_fs_root_t *root;
2981 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
2982 basename of PATH is missing from its parent, that is, the final
2983 target of the deletion is missing. */
2984 static svn_error_t *
2985 txn_body_delete(void *baton,
2988 struct delete_args *args = baton;
2989 svn_fs_root_t *root = args->root;
2990 const char *path = args->path;
2991 parent_path_t *parent_path;
2992 const char *txn_id = root->txn;
2993 base_fs_data_t *bfd = trail->fs->fsap_data;
2995 if (! root->is_txn_root)
2996 return SVN_FS__NOT_TXN(root);
2998 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
2999 trail, trail->pool));
3001 /* We can't remove the root of the filesystem. */
3002 if (! parent_path->parent)
3003 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3004 _("The root directory cannot be deleted"));
3006 /* Check to see if path (or any child thereof) is locked; if so,
3007 check that we can use the existing lock(s). */
3008 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3010 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3011 trail, trail->pool));
3014 /* Make the parent directory mutable. */
3015 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3016 trail, trail->pool));
3018 /* Decrement mergeinfo counts on the parents of this node by the
3019 count it previously carried, if our format supports it. */
3020 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3022 apr_int64_t mergeinfo_count;
3023 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3025 trail, trail->pool));
3026 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3027 -mergeinfo_count, txn_id,
3028 trail, trail->pool));
3031 /* Do the deletion. */
3032 SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3034 txn_id, trail, trail->pool));
3037 /* Make a record of this modification in the changes table. */
3038 return add_change(root->fs, txn_id, path,
3039 svn_fs_base__dag_get_id(parent_path->node),
3040 svn_fs_path_change_delete, FALSE, FALSE, trail,
3045 static svn_error_t *
3046 base_delete_node(svn_fs_root_t *root,
3050 struct delete_args args;
3054 return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3061 svn_fs_root_t *from_root;
3062 const char *from_path;
3063 svn_fs_root_t *to_root;
3064 const char *to_path;
3065 svn_boolean_t preserve_history;
3069 static svn_error_t *
3070 txn_body_copy(void *baton,
3073 struct copy_args *args = baton;
3074 svn_fs_root_t *from_root = args->from_root;
3075 const char *from_path = args->from_path;
3076 svn_fs_root_t *to_root = args->to_root;
3077 const char *to_path = args->to_path;
3078 dag_node_t *from_node;
3079 parent_path_t *to_parent_path;
3080 const char *txn_id = to_root->txn;
3082 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3083 SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3085 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
3086 component does not exist, it's not that big a deal. We'll just
3088 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3089 open_path_last_optional, txn_id, trail, trail->pool));
3091 /* Check to see if to-path (or any child thereof) is locked, or at
3092 least 'reserved', whether it exists or not; if so, check that we
3093 can use the existing lock(s). */
3094 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3096 SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3097 trail, trail->pool));
3100 /* If the destination node already exists as the same node as the
3101 source (in other words, this operation would result in nothing
3102 happening at all), just do nothing an return successfully,
3103 proud that you saved yourself from a tiresome task. */
3104 if ((to_parent_path->node)
3105 && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3106 svn_fs_base__dag_get_id
3107 (to_parent_path->node)) == 0))
3108 return SVN_NO_ERROR;
3110 if (! from_root->is_txn_root)
3112 svn_fs_path_change_kind_t kind;
3113 dag_node_t *new_node;
3114 apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3115 base_fs_data_t *bfd = trail->fs->fsap_data;
3117 /* If TO_PATH already existed prior to the copy, note that this
3118 operation is a replacement, not an addition. */
3119 if (to_parent_path->node)
3120 kind = svn_fs_path_change_replace;
3122 kind = svn_fs_path_change_add;
3124 /* Make sure the target node's parents are mutable. */
3125 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3126 to_path, trail, trail->pool));
3128 /* If this is a replacement operation, we need to know the old
3129 node's mergeinfo count. */
3130 if (to_parent_path->node)
3131 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3132 &old_mergeinfo_count,
3133 to_parent_path->node,
3134 trail, trail->pool));
3136 SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3137 to_parent_path->entry,
3139 args->preserve_history,
3141 from_path, txn_id, trail, trail->pool));
3143 /* Adjust the mergeinfo counts of the destination's parents if
3144 our format supports it. */
3145 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3147 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3151 SVN_ERR(adjust_parent_mergeinfo_counts
3152 (to_parent_path->parent,
3153 mergeinfo_count - old_mergeinfo_count,
3154 txn_id, trail, trail->pool));
3157 /* Make a record of this modification in the changes table. */
3158 SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3159 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3160 svn_fs_base__dag_get_id(new_node),
3161 kind, FALSE, FALSE, trail, trail->pool));
3165 /* See IZ Issue #436 */
3166 /* Copying from transaction roots not currently available.
3168 ### cmpilato todo someday: make this not so. :-) Note that
3169 when copying from mutable trees, you have to make sure that
3170 you aren't creating a cyclic graph filesystem, and a simple
3171 referencing operation won't cut it. Currently, we should not
3172 be able to reach this clause, and the interface reports that
3173 this only works from immutable trees anyway, but JimB has
3174 stated that this requirement need not be necessary in the
3177 SVN_ERR_MALFUNCTION();
3180 return SVN_NO_ERROR;
3184 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3185 Use POOL for temporary allocation only.
3186 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3187 static svn_error_t *
3188 fs_same_p(svn_boolean_t *same_p,
3193 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3194 return SVN_NO_ERROR;
3197 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3198 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
3199 the copies table. Perform temporary allocations in POOL. */
3200 static svn_error_t *
3201 copy_helper(svn_fs_root_t *from_root,
3202 const char *from_path,
3203 svn_fs_root_t *to_root,
3204 const char *to_path,
3205 svn_boolean_t preserve_history,
3208 struct copy_args args;
3209 svn_boolean_t same_p;
3211 /* Use an error check, not an assert, because even the caller cannot
3212 guarantee that a filesystem's UUID has not changed "on the fly". */
3213 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3215 return svn_error_createf
3216 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3217 _("Cannot copy between two different filesystems ('%s' and '%s')"),
3218 from_root->fs->path, to_root->fs->path);
3220 if (! to_root->is_txn_root)
3221 return SVN_FS__NOT_TXN(to_root);
3223 if (from_root->is_txn_root)
3224 return svn_error_create
3225 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3226 _("Copy from mutable tree not currently supported"));
3228 args.from_root = from_root;
3229 args.from_path = from_path;
3230 args.to_root = to_root;
3231 args.to_path = to_path;
3232 args.preserve_history = preserve_history;
3234 return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3238 static svn_error_t *
3239 base_copy(svn_fs_root_t *from_root,
3240 const char *from_path,
3241 svn_fs_root_t *to_root,
3242 const char *to_path,
3245 return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3249 static svn_error_t *
3250 base_revision_link(svn_fs_root_t *from_root,
3251 svn_fs_root_t *to_root,
3255 return copy_helper(from_root, path, to_root, path, FALSE, pool);
3259 struct copied_from_args
3261 svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */
3262 const char *path; /* Path for the node whose ancestry we seek. */
3264 svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */
3265 const char *result_path; /* Path, if any, of the ancestor. */
3267 apr_pool_t *pool; /* Allocate `result_path' here. */
3271 static svn_error_t *
3272 txn_body_copied_from(void *baton, trail_t *trail)
3274 struct copied_from_args *args = baton;
3275 const svn_fs_id_t *node_id, *pred_id;
3277 svn_fs_t *fs = args->root->fs;
3279 /* Clear the return variables. */
3280 args->result_path = NULL;
3281 args->result_rev = SVN_INVALID_REVNUM;
3283 /* Fetch the NODE in question. */
3284 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3285 node_id = svn_fs_base__dag_get_id(node);
3287 /* Check the node's predecessor-ID. If it doesn't have one, it
3289 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3290 trail, trail->pool));
3292 return SVN_NO_ERROR;
3294 /* If NODE's copy-ID is the same as that of its predecessor... */
3295 if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
3296 svn_fs_base__id_copy_id(pred_id)) != 0)
3298 /* ... then NODE was either the target of a copy operation,
3299 a copied subtree item. We examine the actual copy record
3300 to determine which is the case. */
3302 SVN_ERR(svn_fs_bdb__get_copy(©, fs,
3303 svn_fs_base__id_copy_id(node_id),
3304 trail, trail->pool));
3305 if ((copy->kind == copy_kind_real)
3306 && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3308 args->result_path = copy->src_path;
3309 SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3311 trail, trail->pool));
3314 return SVN_NO_ERROR;
3318 static svn_error_t *
3319 base_copied_from(svn_revnum_t *rev_p,
3320 const char **path_p,
3321 svn_fs_root_t *root,
3325 struct copied_from_args args;
3326 apr_pool_t *scratch_pool = svn_pool_create(pool);
3331 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3332 FALSE, scratch_pool));
3334 *rev_p = args.result_rev;
3335 *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3337 svn_pool_destroy(scratch_pool);
3338 return SVN_NO_ERROR;
3346 struct make_file_args
3348 svn_fs_root_t *root;
3353 static svn_error_t *
3354 txn_body_make_file(void *baton,
3357 struct make_file_args *args = baton;
3358 svn_fs_root_t *root = args->root;
3359 const char *path = args->path;
3360 parent_path_t *parent_path;
3362 const char *txn_id = root->txn;
3364 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3365 txn_id, trail, trail->pool));
3367 /* If there's already a file by that name, complain.
3368 This also catches the case of trying to make a file named `/'. */
3369 if (parent_path->node)
3370 return SVN_FS__ALREADY_EXISTS(root, path);
3372 /* Check to see if some lock is 'reserving' a file-path or dir-path
3373 at that location, or even some child-path; if so, check that we
3375 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3377 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3378 trail, trail->pool));
3381 /* Create the file. */
3382 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3383 trail, trail->pool));
3384 SVN_ERR(svn_fs_base__dag_make_file(&child,
3385 parent_path->parent->node,
3386 parent_path_path(parent_path->parent,
3390 trail, trail->pool));
3392 /* Make a record of this modification in the changes table. */
3393 return add_change(root->fs, txn_id, path,
3394 svn_fs_base__dag_get_id(child),
3395 svn_fs_path_change_add, TRUE, FALSE,
3396 trail, trail->pool);
3400 static svn_error_t *
3401 base_make_file(svn_fs_root_t *root,
3405 struct make_file_args args;
3409 return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3415 struct file_length_args
3417 svn_fs_root_t *root;
3419 svn_filesize_t length; /* OUT parameter */
3422 static svn_error_t *
3423 txn_body_file_length(void *baton,
3426 struct file_length_args *args = baton;
3429 /* First create a dag_node_t from the root/path pair. */
3430 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3432 /* Now fetch its length */
3433 return svn_fs_base__dag_file_length(&args->length, file,
3434 trail, trail->pool);
3437 static svn_error_t *
3438 base_file_length(svn_filesize_t *length_p,
3439 svn_fs_root_t *root,
3443 struct file_length_args args;
3447 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3450 *length_p = args.length;
3451 return SVN_NO_ERROR;
3455 struct file_checksum_args
3457 svn_fs_root_t *root;
3459 svn_checksum_kind_t kind;
3460 svn_checksum_t **checksum; /* OUT parameter */
3463 static svn_error_t *
3464 txn_body_file_checksum(void *baton,
3467 struct file_checksum_args *args = baton;
3470 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3472 return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3473 trail, trail->pool);
3476 static svn_error_t *
3477 base_file_checksum(svn_checksum_t **checksum,
3478 svn_checksum_kind_t kind,
3479 svn_fs_root_t *root,
3483 struct file_checksum_args args;
3484 apr_pool_t *scratch_pool = svn_pool_create(pool);
3489 args.checksum = checksum;
3490 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3491 FALSE, scratch_pool));
3492 *checksum = svn_checksum_dup(*checksum, pool);
3493 svn_pool_destroy(scratch_pool);
3494 return SVN_NO_ERROR;
3498 /* --- Machinery for svn_fs_file_contents() --- */
3501 /* Local baton type for txn_body_get_file_contents. */
3502 typedef struct file_contents_baton_t
3504 /* The file we want to read. */
3505 svn_fs_root_t *root;
3508 /* The dag_node that will be made from the above. */
3511 /* The pool in which `file_stream' (below) is allocated. */
3514 /* The readable file stream that will be made from the
3515 dag_node. (And returned to the caller.) */
3516 svn_stream_t *file_stream;
3518 } file_contents_baton_t;
3521 /* Main body of svn_fs_file_contents; converts a root/path pair into
3522 a readable file stream (in the context of a db txn). */
3523 static svn_error_t *
3524 txn_body_get_file_contents(void *baton, trail_t *trail)
3526 file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3528 /* First create a dag_node_t from the root/path pair. */
3529 SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3531 /* Then create a readable stream from the dag_node_t. */
3532 return svn_fs_base__dag_get_contents(&(fb->file_stream),
3533 fb->node, trail, fb->pool);
3538 static svn_error_t *
3539 base_file_contents(svn_stream_t **contents,
3540 svn_fs_root_t *root,
3544 file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3549 /* Create the readable stream in the context of a db txn. */
3550 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3553 *contents = fb->file_stream;
3554 return SVN_NO_ERROR;
3557 /* --- End machinery for svn_fs_file_contents() --- */
3561 /* --- Machinery for svn_fs_apply_textdelta() --- */
3564 /* Local baton type for all the helper functions below. */
3565 typedef struct txdelta_baton_t
3567 /* This is the custom-built window consumer given to us by the delta
3568 library; it uniquely knows how to read data from our designated
3569 "source" stream, interpret the window, and write data to our
3570 designated "target" stream (in this case, our repos file.) */
3571 svn_txdelta_window_handler_t interpreter;
3572 void *interpreter_baton;
3574 /* The original file info */
3575 svn_fs_root_t *root;
3578 /* Derived from the file info */
3581 svn_stream_t *source_stream;
3582 svn_stream_t *target_stream;
3583 svn_stream_t *string_stream;
3584 svn_stringbuf_t *target_string;
3586 /* Checksums for the base text against which a delta is to be
3587 applied, and for the resultant fulltext, respectively. Either or
3588 both may be null, in which case ignored. */
3589 svn_checksum_t *base_checksum;
3590 svn_checksum_t *result_checksum;
3592 /* Pool used by db txns */
3598 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3599 * This closes BATON->target_stream.
3601 * Note: If you're confused about how this function relates to another
3602 * of similar name, think of it this way:
3604 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3605 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3607 static svn_error_t *
3608 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3610 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3611 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3612 tb->result_checksum,
3614 trail, trail->pool));
3616 /* Make a record of this modification in the changes table. */
3617 return add_change(tb->root->fs, tb->root->txn, tb->path,
3618 svn_fs_base__dag_get_id(tb->node),
3619 svn_fs_path_change_modify, TRUE, FALSE, trail,
3624 /* ### see comment in window_consumer() regarding this function. */
3626 /* Helper function of generic type `svn_write_fn_t'. Implements a
3627 writable stream which appends to an svn_stringbuf_t. */
3628 static svn_error_t *
3629 write_to_string(void *baton, const char *data, apr_size_t *len)
3631 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3632 svn_stringbuf_appendbytes(tb->target_string, data, *len);
3633 return SVN_NO_ERROR;
3638 /* The main window handler returned by svn_fs_apply_textdelta. */
3639 static svn_error_t *
3640 window_consumer(svn_txdelta_window_t *window, void *baton)
3642 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3644 /* Send the window right through to the custom window interpreter.
3645 In theory, the interpreter will then write more data to
3646 cb->target_string. */
3647 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3649 /* ### the write_to_string() callback for the txdelta's output stream
3650 ### should be doing all the flush determination logic, not here.
3651 ### in a drastic case, a window could generate a LOT more than the
3652 ### maximum buffer size. we want to flush to the underlying target
3653 ### stream much sooner (e.g. also in a streamy fashion). also, by
3654 ### moving this logic inside the stream, the stream becomes nice
3655 ### and encapsulated: it holds all the logic about buffering and
3658 ### further: I believe the buffering should be removed from tree.c
3659 ### the buffering should go into the target_stream itself, which
3660 ### is defined by reps-string.c. Specifically, I think the
3661 ### rep_write_contents() function will handle the buffering and
3662 ### the spill to the underlying DB. by locating it there, then
3663 ### anybody who gets a writable stream for FS content can take
3664 ### advantage of the buffering capability. this will be important
3665 ### when we export an FS API function for writing a fulltext into
3666 ### the FS, rather than forcing that fulltext thru apply_textdelta.
3669 /* Check to see if we need to purge the portion of the contents that
3670 have been written thus far. */
3671 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3673 apr_size_t len = tb->target_string->len;
3674 SVN_ERR(svn_stream_write(tb->target_stream,
3675 tb->target_string->data,
3677 svn_stringbuf_setempty(tb->target_string);
3680 /* Is the window NULL? If so, we're done. */
3683 /* Close the internal-use stream. ### This used to be inside of
3684 txn_body_fulltext_finalize_edits(), but that invoked a nested
3685 Berkeley DB transaction -- scandalous! */
3686 SVN_ERR(svn_stream_close(tb->target_stream));
3688 /* Tell the dag subsystem that we're finished with our edits. */
3689 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3690 txn_body_txdelta_finalize_edits, tb,
3694 return SVN_NO_ERROR;
3698 static svn_error_t *
3699 txn_body_apply_textdelta(void *baton, trail_t *trail)
3701 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3702 parent_path_t *parent_path;
3703 const char *txn_id = tb->root->txn;
3705 /* Call open_path with no flags, as we want this to return an error
3706 if the node for which we are searching doesn't exist. */
3707 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3708 trail, trail->pool));
3710 /* Check to see if path is locked; if so, check that we can use it. */
3711 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3712 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3713 trail, trail->pool));
3715 /* Now, make sure this path is mutable. */
3716 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3717 trail, trail->pool));
3718 tb->node = parent_path->node;
3720 if (tb->base_checksum)
3722 svn_checksum_t *checksum;
3724 /* Until we finalize the node, its data_key points to the old
3725 contents, in other words, the base text. */
3726 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3727 tb->base_checksum->kind,
3728 tb->node, trail, trail->pool));
3729 /* TODO: This only compares checksums if they are the same kind, but
3730 we're calculating both SHA1 and MD5 checksums somewhere in
3731 reps-strings.c. Could we keep them both around somehow so this
3732 check could be more comprehensive? */
3733 if (!svn_checksum_match(tb->base_checksum, checksum))
3734 return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3736 _("Base checksum mismatch on '%s'"),
3740 /* Make a readable "source" stream out of the current contents of
3741 ROOT/PATH; obviously, this must done in the context of a db_txn.
3742 The stream is returned in tb->source_stream. */
3743 SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3744 tb->node, trail, tb->pool));
3746 /* Make a writable "target" stream */
3747 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3748 txn_id, trail, tb->pool));
3750 /* Make a writable "string" stream which writes data to
3751 tb->target_string. */
3752 tb->target_string = svn_stringbuf_create_empty(tb->pool);
3753 tb->string_stream = svn_stream_create(tb, tb->pool);
3754 svn_stream_set_write(tb->string_stream, write_to_string);
3756 /* Now, create a custom window handler that uses our two streams. */
3757 svn_txdelta_apply(tb->source_stream,
3763 &(tb->interpreter_baton));
3765 return SVN_NO_ERROR;
3769 static svn_error_t *
3770 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3771 void **contents_baton_p,
3772 svn_fs_root_t *root,
3774 svn_checksum_t *base_checksum,
3775 svn_checksum_t *result_checksum,
3778 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3783 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3784 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3786 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3789 *contents_p = window_consumer;
3790 *contents_baton_p = tb;
3791 return SVN_NO_ERROR;
3794 /* --- End machinery for svn_fs_apply_textdelta() --- */
3796 /* --- Machinery for svn_fs_apply_text() --- */
3798 /* Baton for svn_fs_apply_text(). */
3801 /* The original file info */
3802 svn_fs_root_t *root;
3805 /* Derived from the file info */
3808 /* The returned stream that will accept the file's new contents. */
3809 svn_stream_t *stream;
3811 /* The actual fs stream that the returned stream will write to. */
3812 svn_stream_t *file_stream;
3814 /* Checksum for the final fulltext written to the file. May
3815 be null, in which case ignored. */
3816 svn_checksum_t *result_checksum;
3818 /* Pool used by db txns */
3823 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3824 * fulltext data, not text deltas. Closes BATON->file_stream.
3826 * Note: If you're confused about how this function relates to another
3827 * of similar name, think of it this way:
3829 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3830 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3832 static svn_error_t *
3833 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3835 struct text_baton_t *tb = baton;
3836 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3837 tb->result_checksum,
3839 trail, trail->pool));
3841 /* Make a record of this modification in the changes table. */
3842 return add_change(tb->root->fs, tb->root->txn, tb->path,
3843 svn_fs_base__dag_get_id(tb->node),
3844 svn_fs_path_change_modify, TRUE, FALSE, trail,
3848 /* Write function for the publically returned stream. */
3849 static svn_error_t *
3850 text_stream_writer(void *baton,
3854 struct text_baton_t *tb = baton;
3856 /* Psst, here's some data. Pass it on to the -real- file stream. */
3857 return svn_stream_write(tb->file_stream, data, len);
3860 /* Close function for the publically returned stream. */
3861 static svn_error_t *
3862 text_stream_closer(void *baton)
3864 struct text_baton_t *tb = baton;
3866 /* Close the internal-use stream. ### This used to be inside of
3867 txn_body_fulltext_finalize_edits(), but that invoked a nested
3868 Berkeley DB transaction -- scandalous! */
3869 SVN_ERR(svn_stream_close(tb->file_stream));
3871 /* Need to tell fs that we're done sending text */
3872 return svn_fs_base__retry_txn(tb->root->fs,
3873 txn_body_fulltext_finalize_edits, tb,
3878 static svn_error_t *
3879 txn_body_apply_text(void *baton, trail_t *trail)
3881 struct text_baton_t *tb = baton;
3882 parent_path_t *parent_path;
3883 const char *txn_id = tb->root->txn;
3885 /* Call open_path with no flags, as we want this to return an error
3886 if the node for which we are searching doesn't exist. */
3887 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3888 trail, trail->pool));
3890 /* Check to see if path is locked; if so, check that we can use it. */
3891 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3892 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3893 trail, trail->pool));
3895 /* Now, make sure this path is mutable. */
3896 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3897 trail, trail->pool));
3898 tb->node = parent_path->node;
3900 /* Make a writable stream for replacing the file's text. */
3901 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3902 txn_id, trail, tb->pool));
3904 /* Create a 'returnable' stream which writes to the file_stream. */
3905 tb->stream = svn_stream_create(tb, tb->pool);
3906 svn_stream_set_write(tb->stream, text_stream_writer);
3907 svn_stream_set_close(tb->stream, text_stream_closer);
3909 return SVN_NO_ERROR;
3913 static svn_error_t *
3914 base_apply_text(svn_stream_t **contents_p,
3915 svn_fs_root_t *root,
3917 svn_checksum_t *result_checksum,
3920 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3925 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3927 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
3930 *contents_p = tb->stream;
3931 return SVN_NO_ERROR;
3934 /* --- End machinery for svn_fs_apply_text() --- */
3937 /* Note: we're sharing the `things_changed_args' struct with
3938 svn_fs_props_changed(). */
3940 static svn_error_t *
3941 txn_body_contents_changed(void *baton, trail_t *trail)
3943 struct things_changed_args *args = baton;
3944 dag_node_t *node1, *node2;
3946 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
3947 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
3948 return svn_fs_base__things_different(NULL, args->changed_p,
3949 node1, node2, trail, trail->pool);
3953 /* Note: it is acceptable for this function to call back into
3954 top-level interfaces because it does not itself use trails. */
3955 static svn_error_t *
3956 base_contents_changed(svn_boolean_t *changed_p,
3957 svn_fs_root_t *root1,
3959 svn_fs_root_t *root2,
3963 struct things_changed_args args;
3965 /* Check that roots are in the same fs. */
3966 if (root1->fs != root2->fs)
3967 return svn_error_create
3968 (SVN_ERR_FS_GENERAL, NULL,
3969 _("Cannot compare file contents between two different filesystems"));
3971 /* Check that both paths are files. */
3973 svn_node_kind_t kind;
3975 SVN_ERR(base_check_path(&kind, root1, path1, pool));
3976 if (kind != svn_node_file)
3977 return svn_error_createf
3978 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
3980 SVN_ERR(base_check_path(&kind, root2, path2, pool));
3981 if (kind != svn_node_file)
3982 return svn_error_createf
3983 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
3990 args.changed_p = changed_p;
3993 return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
3999 /* Public interface to computing file text deltas. */
4001 /* Note: it is acceptable for this function to call back into
4002 public FS API interfaces because it does not itself use trails. */
4003 static svn_error_t *
4004 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4005 svn_fs_root_t *source_root,
4006 const char *source_path,
4007 svn_fs_root_t *target_root,
4008 const char *target_path,
4011 svn_stream_t *source, *target;
4012 svn_txdelta_stream_t *delta_stream;
4014 /* Get read functions for the source file contents. */
4015 if (source_root && source_path)
4016 SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4018 source = svn_stream_empty(pool);
4020 /* Get read functions for the target file contents. */
4021 SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4023 /* Create a delta stream that turns the ancestor into the target. */
4024 svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4026 *stream_p = delta_stream;
4027 return SVN_NO_ERROR;
4032 /* Finding Changes */
4034 struct paths_changed_args
4036 apr_hash_t *changes;
4037 svn_fs_root_t *root;
4041 static svn_error_t *
4042 txn_body_paths_changed(void *baton,
4045 /* WARNING: This is called *without* the protection of a Berkeley DB
4046 transaction. If you modify this function, keep that in mind. */
4048 struct paths_changed_args *args = baton;
4050 svn_fs_t *fs = args->root->fs;
4052 /* Get the transaction ID from ROOT. */
4053 if (! args->root->is_txn_root)
4054 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4055 trail, trail->pool));
4057 txn_id = args->root->txn;
4059 return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4060 trail, trail->pool);
4064 static svn_error_t *
4065 base_paths_changed(apr_hash_t **changed_paths_p,
4066 svn_fs_root_t *root,
4069 struct paths_changed_args args;
4071 args.changes = NULL;
4072 SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4074 *changed_paths_p = args.changes;
4075 return SVN_NO_ERROR;
4080 /* Our coolio opaque history object. */
4081 typedef struct base_history_data_t
4083 /* filesystem object */
4086 /* path and revision of historical location */
4088 svn_revnum_t revision;
4090 /* internal-use hints about where to resume the history search. */
4091 const char *path_hint;
4092 svn_revnum_t rev_hint;
4094 /* FALSE until the first call to svn_fs_history_prev(). */
4095 svn_boolean_t is_interesting;
4096 } base_history_data_t;
4099 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4100 svn_revnum_t revision,
4101 svn_boolean_t is_interesting,
4102 const char *path_hint,
4103 svn_revnum_t rev_hint,
4107 static svn_error_t *
4108 base_node_history(svn_fs_history_t **history_p,
4109 svn_fs_root_t *root,
4113 svn_node_kind_t kind;
4115 /* We require a revision root. */
4116 if (root->is_txn_root)
4117 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4119 /* And we require that the path exist in the root. */
4120 SVN_ERR(base_check_path(&kind, root, path, pool));
4121 if (kind == svn_node_none)
4122 return SVN_FS__NOT_FOUND(root, path);
4124 /* Okay, all seems well. Build our history object and return it. */
4125 *history_p = assemble_history(root->fs,
4126 svn_fs__canonicalize_abspath(path, pool),
4127 root->rev, FALSE, NULL,
4128 SVN_INVALID_REVNUM, pool);
4129 return SVN_NO_ERROR;
4133 /* Examine the PARENT_PATH structure chain to determine how copy IDs
4134 would be doled out in the event that PARENT_PATH was made mutable.
4135 Return the ID of the copy that last affected PARENT_PATH (and the
4136 COPY itself, if we've already fetched it).
4138 static svn_error_t *
4139 examine_copy_inheritance(const char **copy_id,
4142 parent_path_t *parent_path,
4146 /* The default response -- our current copy ID, and no fetched COPY. */
4147 *copy_id = svn_fs_base__id_copy_id
4148 (svn_fs_base__dag_get_id(parent_path->node));
4151 /* If we have no parent (we are looking at the root node), or if
4152 this node is supposed to inherit from itself, return that fact. */
4153 if (! parent_path->parent)
4154 return SVN_NO_ERROR;
4156 /* We could be a branch destination (which would answer our question
4157 altogether)! But then, again, we might just have been modified
4158 in this revision, so all bets are off. */
4159 if (parent_path->copy_inherit == copy_id_inherit_self)
4161 /* A copy ID of "0" means we've never been branched. Therefore,
4162 there are no copies relevant to our history. */
4163 if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4164 return SVN_NO_ERROR;
4166 /* Get the COPY record. If it was a real copy (not an implicit
4167 one), we have our answer. Otherwise, we fall through to the
4169 SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4170 if ((*copy)->kind != copy_kind_soft)
4171 return SVN_NO_ERROR;
4174 /* Otherwise, our answer is dependent upon our parent. */
4175 return examine_copy_inheritance(copy_id, copy, fs,
4176 parent_path->parent, trail, pool);
4180 struct history_prev_args
4182 svn_fs_history_t **prev_history_p;
4183 svn_fs_history_t *history;
4184 svn_boolean_t cross_copies;
4189 static svn_error_t *
4190 txn_body_history_prev(void *baton, trail_t *trail)
4192 struct history_prev_args *args = baton;
4193 svn_fs_history_t **prev_history = args->prev_history_p;
4194 svn_fs_history_t *history = args->history;
4195 base_history_data_t *bhd = history->fsap_data;
4196 const char *commit_path, *src_path, *path = bhd->path;
4197 svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4198 apr_pool_t *retpool = args->pool;
4199 svn_fs_t *fs = bhd->fs;
4200 parent_path_t *parent_path;
4202 svn_fs_root_t *root;
4203 const svn_fs_id_t *node_id;
4204 const char *end_copy_id = NULL;
4205 struct revision_root_args rr_args;
4206 svn_boolean_t reported = bhd->is_interesting;
4208 copy_t *copy = NULL;
4209 svn_boolean_t retry = FALSE;
4211 /* Initialize our return value. */
4212 *prev_history = NULL;
4214 /* If our last history report left us hints about where to pickup
4215 the chase, then our last report was on the destination of a
4216 copy. If we are crossing copies, start from those locations,
4217 otherwise, we're all done here. */
4218 if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4221 if (! args->cross_copies)
4222 return SVN_NO_ERROR;
4223 path = bhd->path_hint;
4224 revision = bhd->rev_hint;
4227 /* Construct a ROOT for the current revision. */
4228 rr_args.root_p = &root;
4229 rr_args.rev = revision;
4230 SVN_ERR(txn_body_revision_root(&rr_args, trail));
4232 /* Open PATH/REVISION, and get its node and a bunch of other
4234 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4236 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4237 trail, trail->pool));
4238 node = parent_path->node;
4239 node_id = svn_fs_base__dag_get_id(node);
4240 commit_path = svn_fs_base__dag_get_created_path(node);
4241 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4242 trail, trail->pool));
4244 /* The Subversion filesystem is written in such a way that a given
4245 line of history may have at most one interesting history point
4246 per filesystem revision. Either that node was edited (and
4247 possibly copied), or it was copied but not edited. And a copy
4248 source cannot be from the same revision as its destination. So,
4249 if our history revision matches its node's commit revision, we
4251 if (revision == commit_rev)
4255 /* ... we either have not yet reported on this revision (and
4256 need now to do so) ... */
4257 *prev_history = assemble_history(fs,
4258 apr_pstrdup(retpool, commit_path),
4259 commit_rev, TRUE, NULL,
4260 SVN_INVALID_REVNUM, retpool);
4261 return SVN_NO_ERROR;
4265 /* ... or we *have* reported on this revision, and must now
4266 progress toward this node's predecessor (unless there is
4267 no predecessor, in which case we're all done!). */
4268 const svn_fs_id_t *pred_id;
4270 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4271 trail, trail->pool));
4273 return SVN_NO_ERROR;
4275 /* Replace NODE and friends with the information from its
4277 SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4278 trail, trail->pool));
4279 node_id = svn_fs_base__dag_get_id(node);
4280 commit_path = svn_fs_base__dag_get_created_path(node);
4281 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4282 trail, trail->pool));
4286 /* Calculate a possibly relevant copy ID. */
4287 SVN_ERR(examine_copy_inheritance(&end_copy_id, ©, fs,
4288 parent_path, trail, trail->pool));
4290 /* Initialize some state variables. */
4292 src_rev = SVN_INVALID_REVNUM;
4293 dst_rev = SVN_INVALID_REVNUM;
4295 /* If our current copy ID (which is either the real copy ID of our
4296 node, or the last copy ID which would affect our node if it were
4297 to be made mutable) diffs at all from that of its predecessor
4298 (which is either a real predecessor, or is the node itself
4299 playing the predecessor role to an imaginary mutable successor),
4300 then we need to report a copy. */
4301 if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
4304 const char *remainder;
4305 dag_node_t *dst_node;
4306 const char *copy_dst;
4308 /* Get the COPY record if we haven't already fetched it. */
4310 SVN_ERR(svn_fs_bdb__get_copy(©, fs, end_copy_id, trail,
4313 /* Figure out the destination path of the copy operation. */
4314 SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4315 copy->dst_noderev_id,
4316 trail, trail->pool));
4317 copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4319 /* If our current path was the very destination of the copy,
4320 then our new current path will be the copy source. If our
4321 current path was instead the *child* of the destination of
4322 the copy, then figure out its previous location by taking its
4323 path relative to the copy destination and appending that to
4324 the copy source. Finally, if our current path doesn't meet
4325 one of these other criteria ... ### for now just fallback to
4326 the old copy hunt algorithm. */
4327 remainder = svn_fspath__skip_ancestor(copy_dst, path);
4331 /* If we get here, then our current path is the destination
4332 of, or the child of the destination of, a copy. Fill
4333 in the return values and get outta here. */
4334 SVN_ERR(svn_fs_base__txn_get_revision
4335 (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4336 SVN_ERR(svn_fs_base__txn_get_revision
4338 svn_fs_base__id_txn_id(copy->dst_noderev_id),
4339 trail, trail->pool));
4340 src_path = svn_fspath__join(copy->src_path, remainder,
4342 if (copy->kind == copy_kind_soft)
4347 /* If we calculated a copy source path and revision, and the
4348 copy source revision doesn't pre-date a revision in which we
4349 *know* our node was modified, we'll make a 'copy-style' history
4351 if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4353 /* It's possible for us to find a copy location that is the same
4354 as the history point we've just reported. If that happens,
4355 we simply need to take another trip through this history
4357 if ((dst_rev == revision) && reported)
4360 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4362 src_path, src_rev, retpool);
4366 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4367 commit_rev, TRUE, NULL,
4368 SVN_INVALID_REVNUM, retpool);
4371 return SVN_NO_ERROR;
4375 static svn_error_t *
4376 base_history_prev(svn_fs_history_t **prev_history_p,
4377 svn_fs_history_t *history,
4378 svn_boolean_t cross_copies,
4381 svn_fs_history_t *prev_history = NULL;
4382 base_history_data_t *bhd = history->fsap_data;
4383 svn_fs_t *fs = bhd->fs;
4385 /* Special case: the root directory changes in every single
4386 revision, no exceptions. And, the root can't be the target (or
4387 child of a target -- duh) of a copy. So, if that's our path,
4388 then we need only decrement our revision by 1, and there you go. */
4389 if (strcmp(bhd->path, "/") == 0)
4391 if (! bhd->is_interesting)
4392 prev_history = assemble_history(fs, "/", bhd->revision,
4393 1, NULL, SVN_INVALID_REVNUM, pool);
4394 else if (bhd->revision > 0)
4395 prev_history = assemble_history(fs, "/", bhd->revision - 1,
4396 1, NULL, SVN_INVALID_REVNUM, pool);
4400 struct history_prev_args args;
4401 prev_history = history;
4405 /* Get a trail, and get to work. */
4407 args.prev_history_p = &prev_history;
4408 args.history = prev_history;
4409 args.cross_copies = cross_copies;
4411 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4415 bhd = prev_history->fsap_data;
4416 if (bhd->is_interesting)
4421 *prev_history_p = prev_history;
4422 return SVN_NO_ERROR;
4426 static svn_error_t *
4427 base_history_location(const char **path,
4428 svn_revnum_t *revision,
4429 svn_fs_history_t *history,
4432 base_history_data_t *bhd = history->fsap_data;
4434 *path = apr_pstrdup(pool, bhd->path);
4435 *revision = bhd->revision;
4436 return SVN_NO_ERROR;
4440 static history_vtable_t history_vtable = {
4442 base_history_location
4447 struct closest_copy_args
4449 svn_fs_root_t **root_p;
4450 const char **path_p;
4451 svn_fs_root_t *root;
4457 static svn_error_t *
4458 txn_body_closest_copy(void *baton, trail_t *trail)
4460 struct closest_copy_args *args = baton;
4461 svn_fs_root_t *root = args->root;
4462 const char *path = args->path;
4463 svn_fs_t *fs = root->fs;
4464 parent_path_t *parent_path;
4465 const svn_fs_id_t *node_id;
4466 const char *txn_id, *copy_id;
4467 copy_t *copy = NULL;
4468 svn_fs_root_t *copy_dst_root;
4469 dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4470 const char *copy_dst_path;
4471 svn_revnum_t copy_dst_rev, created_rev;
4474 *(args->path_p) = NULL;
4475 *(args->root_p) = NULL;
4477 /* Get the transaction ID associated with our root. */
4478 if (root->is_txn_root)
4481 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4482 trail, trail->pool));
4484 /* Open PATH in ROOT -- it must exist. */
4485 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4486 trail, trail->pool));
4487 node_id = svn_fs_base__dag_get_id(parent_path->node);
4489 /* Now, examine the copy inheritance rules in play should our path
4490 be made mutable in the future (if it isn't already). This will
4491 tell us about the youngest affecting copy. */
4492 SVN_ERR(examine_copy_inheritance(©_id, ©, fs, parent_path,
4493 trail, trail->pool));
4495 /* Easy out: if the copy ID is 0, there's nothing of interest here. */
4496 if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4497 return SVN_NO_ERROR;
4499 /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4501 SVN_ERR(svn_fs_bdb__get_copy(©, fs, copy_id, trail, trail->pool));
4503 /* Figure out the destination path and revision of the copy operation. */
4504 SVN_ERR(svn_fs_base__dag_get_node(©_dst_node, fs, copy->dst_noderev_id,
4505 trail, trail->pool));
4506 copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4507 SVN_ERR(svn_fs_base__dag_get_revision(©_dst_rev, copy_dst_node,
4508 trail, trail->pool));
4510 /* Turn that revision into a revision root. */
4511 SVN_ERR(svn_fs_base__dag_revision_root(©_dst_root_node, fs,
4512 copy_dst_rev, trail, args->pool));
4513 copy_dst_root = make_revision_root(fs, copy_dst_rev,
4514 copy_dst_root_node, args->pool);
4516 /* It is possible that this node was created from scratch at some
4517 revision between COPY_DST_REV and the transaction associated with
4518 our ROOT. Make sure that PATH exists as of COPY_DST_REV and is
4519 related to this node-rev. */
4520 err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4521 trail, trail->pool);
4524 if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4525 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4527 svn_error_clear(err);
4528 return SVN_NO_ERROR;
4530 return svn_error_trace(err);
4532 if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4533 || (! (svn_fs_base__id_check_related
4534 (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4536 return SVN_NO_ERROR;
4539 /* One final check must be done here. If you copy a directory and
4540 create a new entity somewhere beneath that directory in the same
4541 txn, then we can't claim that the copy affected the new entity.
4542 For example, if you do:
4545 create dir2/new-thing
4548 then dir2/new-thing was not affected by the copy of dir1 to dir2.
4549 We detect this situation by asking if PATH@COPY_DST_REV's
4550 created-rev is COPY_DST_REV, and that node-revision has no
4551 predecessors, then there is no relevant closest copy.
4553 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4554 trail, trail->pool));
4555 if (created_rev == copy_dst_rev)
4557 const svn_fs_id_t *pred_id;
4558 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4559 path_node_in_copy_dst,
4560 trail, trail->pool));
4562 return SVN_NO_ERROR;
4565 *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4566 *(args->root_p) = copy_dst_root;
4568 return SVN_NO_ERROR;
4572 static svn_error_t *
4573 base_closest_copy(svn_fs_root_t **root_p,
4574 const char **path_p,
4575 svn_fs_root_t *root,
4579 struct closest_copy_args args;
4580 svn_fs_t *fs = root->fs;
4581 svn_fs_root_t *closest_root = NULL;
4582 const char *closest_path = NULL;
4584 args.root_p = &closest_root;
4585 args.path_p = &closest_path;
4589 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4591 *root_p = closest_root;
4592 *path_p = closest_path;
4593 return SVN_NO_ERROR;
4597 /* Return a new history object (marked as "interesting") for PATH and
4598 REVISION, allocated in POOL, and with its members set to the values
4599 of the parameters provided. Note that PATH and PATH_HINT are not
4600 duped into POOL -- it is the responsibility of the caller to ensure
4601 that this happens. */
4602 static svn_fs_history_t *
4603 assemble_history(svn_fs_t *fs,
4605 svn_revnum_t revision,
4606 svn_boolean_t is_interesting,
4607 const char *path_hint,
4608 svn_revnum_t rev_hint,
4611 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4612 base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4614 bhd->revision = revision;
4615 bhd->is_interesting = is_interesting;
4616 bhd->path_hint = path_hint;
4617 bhd->rev_hint = rev_hint;
4619 history->vtable = &history_vtable;
4620 history->fsap_data = bhd;
4626 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4631 svn_revnum_t head_rev;
4632 svn_fs_root_t *root;
4633 dag_node_t *root_dir, *path_node;
4636 /* Get HEAD revision, */
4637 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4639 /* Then convert it into a root_t, */
4640 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4642 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4644 /* And get the dag_node for path in the root_t. */
4645 err = get_dag(&path_node, root, path, trail, pool);
4646 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4648 svn_error_clear(err);
4649 *kind = svn_node_none;
4650 return SVN_NO_ERROR;
4653 return svn_error_trace(err);
4655 *kind = svn_fs_base__dag_node_kind(path_node);
4656 return SVN_NO_ERROR;
4661 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4666 svn_revnum_t head_rev, created_rev;
4667 svn_fs_root_t *root;
4668 dag_node_t *root_dir, *path_node;
4671 /* Get HEAD revision, */
4672 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4674 /* Then convert it into a root_t, */
4675 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4677 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4679 /* And get the dag_node for path in the root_t. */
4680 err = get_dag(&path_node, root, path, trail, pool);
4681 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4683 svn_error_clear(err);
4684 *rev = SVN_INVALID_REVNUM;
4685 return SVN_NO_ERROR;
4688 return svn_error_trace(err);
4690 /* Find the created_rev of the dag_node. */
4691 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4695 return SVN_NO_ERROR;
4700 /*** Finding the Origin of a Line of History ***/
4702 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4703 represent the location at which PATH in FS was located immediately
4704 prior to REVISION iff there was a copy operation (to PATH or one of
4705 its parent directories) between that previous location and
4708 If there was no such copy operation in that portion of PATH's
4709 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4711 WARNING: Do *not* call this from inside a trail. */
4712 static svn_error_t *
4713 prev_location(const char **prev_path,
4714 svn_revnum_t *prev_rev,
4716 svn_fs_root_t *root,
4720 const char *copy_path, *copy_src_path, *remainder;
4721 svn_fs_root_t *copy_root;
4722 svn_revnum_t copy_src_rev;
4724 /* Ask about the most recent copy which affected PATH@REVISION. If
4725 there was no such copy, we're done. */
4726 SVN_ERR(base_closest_copy(©_root, ©_path, root, path, pool));
4729 *prev_rev = SVN_INVALID_REVNUM;
4731 return SVN_NO_ERROR;
4734 /* Ultimately, it's not the path of the closest copy's source that
4735 we care about -- it's our own path's location in the copy source
4736 revision. So we'll tack the relative path that expresses the
4737 difference between the copy destination and our path in the copy
4738 revision onto the copy source path to determine this information.
4740 In other words, if our path is "/branches/my-branch/foo/bar", and
4741 we know that the closest relevant copy was a copy of "/trunk" to
4742 "/branches/my-branch", then that relative path under the copy
4743 destination is "/foo/bar". Tacking that onto the copy source
4744 path tells us that our path was located at "/trunk/foo/bar"
4747 SVN_ERR(base_copied_from(©_src_rev, ©_src_path,
4748 copy_root, copy_path, pool));
4749 remainder = svn_fspath__skip_ancestor(copy_path, path);
4750 *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4751 *prev_rev = copy_src_rev;
4752 return SVN_NO_ERROR;
4756 struct id_created_rev_args {
4757 svn_revnum_t revision;
4758 const svn_fs_id_t *id;
4763 static svn_error_t *
4764 txn_body_id_created_rev(void *baton, trail_t *trail)
4766 struct id_created_rev_args *args = baton;
4769 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4770 trail, trail->pool));
4771 return svn_fs_base__dag_get_revision(&(args->revision), node,
4772 trail, trail->pool);
4776 struct get_set_node_origin_args {
4777 const svn_fs_id_t *origin_id;
4778 const char *node_id;
4782 static svn_error_t *
4783 txn_body_get_node_origin(void *baton, trail_t *trail)
4785 struct get_set_node_origin_args *args = baton;
4786 return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4787 args->node_id, trail, trail->pool);
4790 static svn_error_t *
4791 txn_body_set_node_origin(void *baton, trail_t *trail)
4793 struct get_set_node_origin_args *args = baton;
4794 return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4795 args->origin_id, trail, trail->pool);
4798 static svn_error_t *
4799 base_node_origin_rev(svn_revnum_t *revision,
4800 svn_fs_root_t *root,
4804 svn_fs_t *fs = root->fs;
4805 base_fs_data_t *bfd = fs->fsap_data;
4806 struct get_set_node_origin_args args;
4807 const svn_fs_id_t *origin_id = NULL;
4808 struct id_created_rev_args icr_args;
4810 /* Canonicalize the input path so that the path-math that
4811 prev_location() does below will work. */
4812 path = svn_fs__canonicalize_abspath(path, pool);
4814 /* Special-case the root node (for performance reasons) */
4815 if (strcmp(path, "/") == 0)
4818 return SVN_NO_ERROR;
4821 /* If we have support for the node-origins table, we'll try to use
4823 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4825 const svn_fs_id_t *id;
4828 SVN_ERR(base_node_id(&id, root, path, pool));
4829 args.node_id = svn_fs_base__id_node_id(id);
4830 err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4833 /* If we got a value for the origin node-revision-ID, that's
4834 great. If we didn't, that's sad but non-fatal -- we'll just
4835 figure it out the hard way, then record it so we don't have
4836 suffer again the next time. */
4839 origin_id = args.origin_id;
4841 else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4843 svn_error_clear(err);
4849 /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4852 svn_fs_root_t *curroot = root;
4853 apr_pool_t *subpool = svn_pool_create(pool);
4854 apr_pool_t *predidpool = svn_pool_create(pool);
4855 svn_stringbuf_t *lastpath =
4856 svn_stringbuf_create(path, pool);
4857 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4858 const svn_fs_id_t *pred_id;
4860 /* Walk the closest-copy chain back to the first copy in our history.
4862 NOTE: We merely *assume* that this is faster than walking the
4863 predecessor chain, because we *assume* that copies of parent
4864 directories happen less often than modifications to a given item. */
4867 svn_revnum_t currev;
4868 const char *curpath = lastpath->data;
4870 /* Get a root pointing to LASTREV. (The first time around,
4871 LASTREV is invalid, but that's cool because CURROOT is
4872 already initialized.) */
4873 if (SVN_IS_VALID_REVNUM(lastrev))
4874 SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
4877 /* Find the previous location using the closest-copy shortcut. */
4878 SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
4883 /* Update our LASTPATH and LASTREV variables (which survive
4885 svn_stringbuf_set(lastpath, curpath);
4889 /* Walk the predecessor links back to origin. */
4890 SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
4893 struct txn_pred_id_args pid_args;
4894 svn_pool_clear(subpool);
4895 pid_args.id = pred_id;
4896 pid_args.pred_id = NULL;
4897 pid_args.pool = subpool;
4898 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
4900 if (! pid_args.pred_id)
4902 svn_pool_clear(predidpool);
4903 pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
4906 /* Okay. PRED_ID should hold our origin ID now. */
4907 origin_id = svn_fs_base__id_copy(pred_id, pool);
4909 /* If our filesystem version supports it, let's remember this
4910 value from now on. */
4911 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4913 args.origin_id = origin_id;
4914 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
4915 &args, TRUE, subpool));
4918 svn_pool_destroy(predidpool);
4919 svn_pool_destroy(subpool);
4922 /* Okay. We have an origin node-revision-ID. Let's get a created
4923 revision from it. */
4924 icr_args.id = origin_id;
4925 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
4927 *revision = icr_args.revision;
4928 return SVN_NO_ERROR;
4933 /* Mergeinfo Queries */
4936 /* Examine directory NODE's immediately children for mergeinfo.
4938 For those which have explicit mergeinfo, add their mergeinfo to
4939 RESULT_CATALOG (allocated in RESULT_CATALOG's pool).
4941 For those which don't, but sit atop trees which contain mergeinfo
4942 somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
4943 hash mapping dirent names to dag_node_t * objects, allocated
4944 from that hash's pool.
4946 For those which neither have explicit mergeinfo nor sit atop trees
4947 which contain mergeinfo, ignore them.
4949 Use TRAIL->pool for temporary allocations. */
4951 struct get_mergeinfo_data_and_entries_baton
4953 svn_mergeinfo_catalog_t result_catalog;
4954 apr_hash_t *children_atop_mergeinfo_trees;
4956 const char *node_path;
4959 static svn_error_t *
4960 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
4962 struct get_mergeinfo_data_and_entries_baton *args = baton;
4963 dag_node_t *node = args->node;
4964 apr_hash_t *entries;
4965 apr_hash_index_t *hi;
4966 apr_pool_t *iterpool = svn_pool_create(trail->pool);
4967 apr_pool_t *result_pool = apr_hash_pool_get(args->result_catalog);
4968 apr_pool_t *children_pool =
4969 apr_hash_pool_get(args->children_atop_mergeinfo_trees);
4971 SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
4973 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
4974 for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
4977 svn_fs_dirent_t *dirent;
4978 const svn_fs_id_t *child_id;
4979 dag_node_t *child_node;
4980 svn_boolean_t has_mergeinfo;
4981 apr_int64_t kid_count;
4983 svn_pool_clear(iterpool);
4984 apr_hash_this(hi, NULL, NULL, &val);
4986 child_id = dirent->id;
4988 /* Get the node for this child. */
4989 SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
4992 /* Query the child node's mergeinfo stats. */
4993 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
4997 /* If the child has mergeinfo, add it to the result catalog. */
5001 svn_mergeinfo_t child_mergeinfo;
5005 SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5007 pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5010 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5012 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5013 _("Node-revision '%s' claims to have "
5014 "mergeinfo but doesn't"),
5017 /* Issue #3896: If syntactically invalid mergeinfo is present on
5018 CHILD_NODE then treat it as if no mergeinfo is present rather
5019 than raising a parse error. */
5020 err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5024 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5025 svn_error_clear(err);
5027 return svn_error_trace(err);
5031 svn_hash_sets(args->result_catalog,
5032 svn_fspath__join(args->node_path, dirent->name,
5038 /* If the child has descendants with mergeinfo -- that is, if
5039 the count of descendants beneath it carrying mergeinfo, not
5040 including itself, is non-zero -- then add it to the
5041 children_atop_mergeinfo_trees hash to be crawled later. */
5042 if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5044 if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5046 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5048 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5049 _("Node-revision '%s' claims to sit "
5050 "atop a tree containing mergeinfo "
5051 "but is not a directory"),
5054 svn_hash_sets(args->children_atop_mergeinfo_trees,
5055 apr_pstrdup(children_pool, dirent->name),
5056 svn_fs_base__dag_dup(child_node, children_pool));
5060 svn_pool_destroy(iterpool);
5061 return SVN_NO_ERROR;
5064 static svn_error_t *
5065 crawl_directory_for_mergeinfo(svn_fs_t *fs,
5067 const char *node_path,
5068 svn_mergeinfo_catalog_t result_catalog,
5071 struct get_mergeinfo_data_and_entries_baton gmdae_args;
5072 apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5073 apr_hash_index_t *hi;
5074 apr_pool_t *iterpool;
5076 /* Add mergeinfo for immediate children that have it, and fetch
5077 immediate children that *don't* have it but sit atop trees that do. */
5078 gmdae_args.result_catalog = result_catalog;
5079 gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5080 gmdae_args.node = node;
5081 gmdae_args.node_path = node_path;
5082 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5083 &gmdae_args, FALSE, pool));
5085 /* If no children sit atop trees with mergeinfo, we're done.
5086 Otherwise, recurse on those children. */
5088 if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5089 return SVN_NO_ERROR;
5091 iterpool = svn_pool_create(pool);
5092 for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5094 hi = apr_hash_next(hi))
5098 svn_pool_clear(iterpool);
5099 apr_hash_this(hi, &key, NULL, &val);
5100 SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5101 svn_fspath__join(node_path, key,
5103 result_catalog, iterpool));
5105 svn_pool_destroy(iterpool);
5106 return SVN_NO_ERROR;
5110 /* Calculate the mergeinfo for PATH under revision ROOT using
5111 inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to
5112 NULL if there is none. Results are allocated in POOL; TRAIL->pool
5113 is used for temporary allocations. */
5115 struct get_mergeinfo_for_path_baton
5117 svn_mergeinfo_t *mergeinfo;
5118 svn_fs_root_t *root;
5120 svn_mergeinfo_inheritance_t inherit;
5121 svn_boolean_t adjust_inherited_mergeinfo;
5125 static svn_error_t *
5126 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5128 struct get_mergeinfo_for_path_baton *args = baton;
5129 parent_path_t *parent_path, *nearest_ancestor;
5130 apr_hash_t *proplist;
5131 svn_string_t *mergeinfo_string;
5132 apr_pool_t *iterpool;
5133 dag_node_t *node = NULL;
5135 *(args->mergeinfo) = NULL;
5137 SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5138 NULL, trail, trail->pool));
5140 /* Init the nearest ancestor. */
5141 nearest_ancestor = parent_path;
5142 if (args->inherit == svn_mergeinfo_nearest_ancestor)
5144 if (! parent_path->parent)
5145 return SVN_NO_ERROR;
5146 nearest_ancestor = parent_path->parent;
5149 iterpool = svn_pool_create(trail->pool);
5152 svn_boolean_t has_mergeinfo;
5155 svn_pool_clear(iterpool);
5157 node = nearest_ancestor->node;
5158 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5159 node, trail, iterpool));
5163 /* No need to loop if we're looking for explicit mergeinfo. */
5164 if (args->inherit == svn_mergeinfo_explicit)
5166 svn_pool_destroy(iterpool);
5167 return SVN_NO_ERROR;
5170 nearest_ancestor = nearest_ancestor->parent;
5172 /* Run out? There's no mergeinfo. */
5173 if (! nearest_ancestor)
5175 svn_pool_destroy(iterpool);
5176 return SVN_NO_ERROR;
5179 svn_pool_destroy(iterpool);
5181 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5182 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5183 if (! mergeinfo_string)
5185 svn_string_t *id_str =
5186 svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5187 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5188 _("Node-revision '%s' claims to have "
5189 "mergeinfo but doesn't"), id_str->data);
5192 /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5194 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5195 treat it as if no mergeinfo is present rather than raising a parse
5197 svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5198 mergeinfo_string->data,
5202 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5204 svn_error_clear(err);
5206 args->mergeinfo = NULL;
5208 return svn_error_trace(err);
5212 /* If our nearest ancestor is the very path we inquired about, we
5213 can return the mergeinfo results directly. Otherwise, we're
5214 inheriting the mergeinfo, so we need to a) remove non-inheritable
5215 ranges and b) telescope the merged-from paths. */
5216 if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5218 svn_mergeinfo_t tmp_mergeinfo;
5220 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5221 NULL, SVN_INVALID_REVNUM,
5222 SVN_INVALID_REVNUM, TRUE,
5223 trail->pool, trail->pool));
5224 SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5225 parent_path_relpath(
5226 parent_path, nearest_ancestor,
5231 return SVN_NO_ERROR;
5234 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5235 and query its mergeinfo stats, setting HAS_MERGEINFO and
5236 CHILD_MERGEINFO_COUNT appropriately. */
5238 struct get_node_mergeinfo_stats_baton
5241 svn_boolean_t has_mergeinfo;
5242 apr_int64_t child_mergeinfo_count;
5243 svn_fs_root_t *root;
5247 static svn_error_t *
5248 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5250 struct get_node_mergeinfo_stats_baton *args = baton;
5252 SVN_ERR(get_dag(&(args->node), args->root, args->path,
5253 trail, trail->pool));
5254 return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5255 &(args->child_mergeinfo_count),
5261 /* Get the mergeinfo for a set of paths, returned in
5262 *MERGEINFO_CATALOG. Returned values are allocated in POOL, while
5263 temporary values are allocated in a sub-pool. */
5264 static svn_error_t *
5265 get_mergeinfos_for_paths(svn_fs_root_t *root,
5266 svn_mergeinfo_catalog_t *mergeinfo_catalog,
5267 const apr_array_header_t *paths,
5268 svn_mergeinfo_inheritance_t inherit,
5269 svn_boolean_t include_descendants,
5270 svn_boolean_t adjust_inherited_mergeinfo,
5271 apr_pool_t *result_pool,
5272 apr_pool_t *scratch_pool)
5274 svn_mergeinfo_catalog_t result_catalog = apr_hash_make(result_pool);
5275 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5278 for (i = 0; i < paths->nelts; i++)
5280 svn_mergeinfo_t path_mergeinfo;
5281 struct get_mergeinfo_for_path_baton gmfp_args;
5282 const char *path = APR_ARRAY_IDX(paths, i, const char *);
5284 svn_pool_clear(iterpool);
5286 path = svn_fs__canonicalize_abspath(path, iterpool);
5288 /* Get the mergeinfo for PATH itself. */
5289 gmfp_args.mergeinfo = &path_mergeinfo;
5290 gmfp_args.root = root;
5291 gmfp_args.path = path;
5292 gmfp_args.inherit = inherit;
5293 gmfp_args.pool = result_pool;
5294 gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5295 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5296 txn_body_get_mergeinfo_for_path,
5297 &gmfp_args, FALSE, iterpool));
5299 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, path),
5302 /* If we're including descendants, do so. */
5303 if (include_descendants)
5305 svn_boolean_t do_crawl;
5306 struct get_node_mergeinfo_stats_baton gnms_args;
5308 /* Query the node and its mergeinfo stats. */
5309 gnms_args.root = root;
5310 gnms_args.path = path;
5311 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5312 txn_body_get_node_mergeinfo_stats,
5313 &gnms_args, FALSE, iterpool));
5315 /* Determine if there's anything worth crawling here. */
5316 if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5319 do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5320 || ((gnms_args.child_mergeinfo_count == 1)
5321 && (! gnms_args.has_mergeinfo)));
5323 /* If it's worth crawling, crawl. */
5325 SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5326 path, result_catalog,
5330 svn_pool_destroy(iterpool);
5332 *mergeinfo_catalog = result_catalog;
5333 return SVN_NO_ERROR;
5337 /* Implements svn_fs_get_mergeinfo. */
5338 static svn_error_t *
5339 base_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
5340 svn_fs_root_t *root,
5341 const apr_array_header_t *paths,
5342 svn_mergeinfo_inheritance_t inherit,
5343 svn_boolean_t include_descendants,
5344 svn_boolean_t adjust_inherited_mergeinfo,
5345 apr_pool_t *result_pool,
5346 apr_pool_t *scratch_pool)
5348 /* Verify that our filesystem version supports mergeinfo stuff. */
5349 SVN_ERR(svn_fs_base__test_required_feature_format
5350 (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5352 /* We require a revision root. */
5353 if (root->is_txn_root)
5354 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5356 /* Retrieve a path -> mergeinfo mapping. */
5357 return get_mergeinfos_for_paths(root, catalog, paths,
5358 inherit, include_descendants,
5359 adjust_inherited_mergeinfo,
5360 result_pool, scratch_pool);
5365 /* Creating root objects. */
5368 static root_vtable_t root_vtable = {
5373 base_node_created_rev,
5374 base_node_origin_rev,
5375 base_node_created_path,
5381 base_change_node_prop,
5392 base_apply_textdelta,
5394 base_contents_changed,
5395 base_get_file_delta_stream,
5401 /* Construct a new root object in FS, allocated from POOL. */
5402 static svn_fs_root_t *
5403 make_root(svn_fs_t *fs,
5406 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5407 base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5412 /* Init the node ID cache. */
5413 brd->node_cache = apr_hash_make(pool);
5414 brd->node_cache_idx = 0;
5415 root->vtable = &root_vtable;
5416 root->fsap_data = brd;
5422 /* Construct a root object referring to the root of REVISION in FS,
5423 whose root directory is ROOT_DIR. Create the new root in POOL. */
5424 static svn_fs_root_t *
5425 make_revision_root(svn_fs_t *fs,
5427 dag_node_t *root_dir,
5430 svn_fs_root_t *root = make_root(fs, pool);
5431 base_root_data_t *brd = root->fsap_data;
5433 root->is_txn_root = FALSE;
5435 brd->root_dir = root_dir;
5441 /* Construct a root object referring to the root of the transaction
5442 named TXN and based on revision BASE_REV in FS. FLAGS represents
5443 the behavior of the transaction. Create the new root in POOL. */
5444 static svn_fs_root_t *
5445 make_txn_root(svn_fs_t *fs,
5447 svn_revnum_t base_rev,
5451 svn_fs_root_t *root = make_root(fs, pool);
5452 root->is_txn_root = TRUE;
5453 root->txn = apr_pstrdup(root->pool, txn);
5454 root->txn_flags = flags;
5455 root->rev = base_rev;