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 (strcmp(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 || (strcmp(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)
573 == svn_fs_node_unrelated)
575 *inherit_p = copy_id_inherit_parent;
580 /* If we get here, the child and its parent are not on speaking
581 terms -- there will be no parental inheritance handed down in
582 *this* generation. */
584 /* If the child was created at a different path than the one we are
585 expecting its clone to live, one of its parents must have been
586 created via a copy since the child was created. The child isn't
587 on the same branch as its parent (we caught those cases early);
588 it can't keep its current copy ID because there's been an
589 affecting copy (its clone won't be on the same branch as the
590 child is). That leaves only one course of action -- to assign
591 the child a brand new "soft" copy ID. */
592 id_path = svn_fs_base__dag_get_created_path(child->node);
593 if (strcmp(id_path, parent_path_path(child, pool)) != 0)
595 *inherit_p = copy_id_inherit_new;
596 *copy_src_path = id_path;
600 /* The node gets to keep its own ID. */
605 /* Allocate a new parent_path_t node from POOL, referring to NODE,
606 ENTRY, PARENT, and COPY_ID. */
607 static parent_path_t *
608 make_parent_path(dag_node_t *node,
610 parent_path_t *parent,
613 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
614 parent_path->node = node;
615 parent_path->entry = entry;
616 parent_path->parent = parent;
617 parent_path->copy_inherit = copy_id_inherit_unknown;
618 parent_path->copy_src_path = NULL;
623 /* Flags for open_path. */
624 typedef enum open_path_flags_t {
626 /* The last component of the PATH need not exist. (All parent
627 directories must exist, as usual.) If the last component doesn't
628 exist, simply leave the `node' member of the bottom parent_path
630 open_path_last_optional = 1
635 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
636 *PARENT_PATH_P to a path from the node up to ROOT, allocated in
637 TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to
638 contain at least one element, for the root directory.
640 If resulting *PARENT_PATH_P will eventually be made mutable and
641 modified, or if copy ID inheritance information is otherwise
642 needed, TXN_ID should be the ID of the mutability transaction. If
643 TXN_ID is NULL, no copy ID in heritance information will be
644 calculated for the *PARENT_PATH_P chain.
646 If FLAGS & open_path_last_optional is zero, return the error
647 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
648 non-zero, require all the parent directories to exist as normal,
649 but if the final path component doesn't exist, simply return a path
650 whose bottom `node' member is zero. This option is useful for
651 callers that create new nodes --- we find the parent directory for
652 them, and tell them whether the entry exists already.
654 NOTE: Public interfaces which only *read* from the filesystem
655 should not call this function directly, but should instead use
659 open_path(parent_path_t **parent_path_p,
667 svn_fs_t *fs = root->fs;
668 dag_node_t *here; /* The directory we're currently looking at. */
669 parent_path_t *parent_path; /* The path from HERE up to the root. */
670 const char *rest; /* The portion of PATH we haven't traversed yet. */
671 const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
672 const char *path_so_far = "/";
674 /* Make a parent_path item for the root node, using its own current
676 SVN_ERR(root_node(&here, root, trail, pool));
677 parent_path = make_parent_path(here, 0, 0, pool);
678 parent_path->copy_inherit = copy_id_inherit_self;
680 rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
682 /* Whenever we are at the top of this loop:
683 - HERE is our current directory,
684 - ID is the node revision ID of HERE,
685 - REST is the path we're going to find in HERE, and
686 - PARENT_PATH includes HERE and all its parents. */
693 /* Parse out the next entry from the path. */
694 entry = svn_fs__next_entry_name(&next, rest, pool);
696 /* Calculate the path traversed thus far. */
697 path_so_far = svn_fspath__join(path_so_far, entry, pool);
701 /* Given the behavior of svn_fs__next_entry_name(), this
702 happens when the path either starts or ends with a slash.
703 In either case, we stay put: the current directory stays
704 the same, and we add nothing to the parent path. */
709 copy_id_inherit_t inherit;
710 const char *copy_path = NULL;
711 svn_error_t *err = SVN_NO_ERROR;
712 dag_node_t *cached_node;
714 /* If we found a directory entry, follow it. First, we
715 check our node cache, and, failing that, we hit the DAG
717 cached_node = dag_node_cache_get(root, path_so_far, pool);
721 err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
723 /* "file not found" requires special handling. */
724 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
726 /* If this was the last path component, and the caller
727 said it was optional, then don't return an error;
728 just put a NULL node pointer in the path. */
730 svn_error_clear(err);
732 if ((flags & open_path_last_optional)
733 && (! next || *next == '\0'))
735 parent_path = make_parent_path(NULL, entry, parent_path,
741 /* Build a better error message than svn_fs_base__dag_open
742 can provide, giving the root and full path name. */
743 return SVN_FS__NOT_FOUND(root, path);
747 /* Other errors we return normally. */
750 /* Now, make a parent_path item for CHILD. */
751 parent_path = make_parent_path(child, entry, parent_path, pool);
754 SVN_ERR(get_copy_inheritance(&inherit, ©_path,
755 fs, parent_path, txn_id,
757 parent_path->copy_inherit = inherit;
758 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
761 /* Cache the node we found (if it wasn't already cached). */
763 dag_node_cache_set(root, path_so_far, child);
766 /* Are we finished traversing the path? */
770 /* The path isn't finished yet; we'd better be in a directory. */
771 if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
772 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
773 apr_psprintf(pool, _("Failure opening '%s'"), path));
779 *parent_path_p = parent_path;
784 /* Make the node referred to by PARENT_PATH mutable, if it isn't
785 already, as part of TRAIL. ROOT must be the root from which
786 PARENT_PATH descends. Clone any parent directories as needed.
787 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
788 ERROR_PATH in error messages. */
790 make_path_mutable(svn_fs_root_t *root,
791 parent_path_t *parent_path,
792 const char *error_path,
796 dag_node_t *cloned_node;
797 const char *txn_id = root->txn;
798 svn_fs_t *fs = root->fs;
800 /* Is the node mutable already? */
801 if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
804 /* Are we trying to clone the root, or somebody's child node? */
805 if (parent_path->parent)
807 const svn_fs_id_t *parent_id;
808 const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
809 const char *copy_id = NULL;
810 const char *copy_src_path = parent_path->copy_src_path;
811 copy_id_inherit_t inherit = parent_path->copy_inherit;
812 const char *clone_path;
814 /* We're trying to clone somebody's child. Make sure our parent
816 SVN_ERR(make_path_mutable(root, parent_path->parent,
817 error_path, trail, pool));
821 case copy_id_inherit_parent:
822 parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
823 copy_id = svn_fs_base__id_copy_id(parent_id);
826 case copy_id_inherit_new:
827 SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool));
830 case copy_id_inherit_self:
834 case copy_id_inherit_unknown:
836 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
840 /* Now make this node mutable. */
841 clone_path = parent_path_path(parent_path->parent, pool);
842 SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
843 parent_path->parent->node,
849 /* If we just created a brand new copy ID, we need to store a
850 `copies' table entry for it, as well as a notation in the
851 transaction that should this transaction be terminated, our
852 new copy needs to be removed. */
853 if (inherit == copy_id_inherit_new)
855 const svn_fs_id_t *new_node_id =
856 svn_fs_base__dag_get_id(cloned_node);
857 SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
858 svn_fs_base__id_txn_id(node_id),
860 copy_kind_soft, trail, pool));
861 SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
867 /* We're trying to clone the root directory. */
868 SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
871 /* Update the PARENT_PATH link to refer to the clone. */
872 parent_path->node = cloned_node;
878 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
879 mergeinfo count by COUNT_DELTA as part of Subversion transaction
880 TXN_ID and TRAIL. Use POOL for allocations. */
882 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
883 apr_int64_t count_delta,
888 apr_pool_t *iterpool;
889 parent_path_t *pp = parent_path;
891 if (count_delta == 0)
894 iterpool = svn_pool_create(pool);
898 svn_pool_clear(iterpool);
899 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
904 svn_pool_destroy(iterpool);
910 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
911 *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return
912 the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
914 get_dag(dag_node_t **dag_node_p,
920 parent_path_t *parent_path;
921 dag_node_t *node = NULL;
923 /* Canonicalize the input PATH. */
924 path = svn_fs__canonicalize_abspath(path, pool);
926 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
927 node = dag_node_cache_get(root, path, pool);
930 /* Call open_path with no flags, as we want this to return an error
931 if the node for which we are searching doesn't exist. */
932 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
933 node = parent_path->node;
935 /* No need to cache our find -- open_path() will do that for us. */
944 /* Populating the `changes' table. */
946 /* Add a change to the changes table in FS, keyed on transaction id
947 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
948 PATH (whose node revision id is--or was, in the case of a
949 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
950 occurred. Do all this as part of TRAIL. */
952 add_change(svn_fs_t *fs,
955 const svn_fs_id_t *noderev_id,
956 svn_fs_path_change_kind_t change_kind,
957 svn_boolean_t text_mod,
958 svn_boolean_t prop_mod,
963 change.path = svn_fs__canonicalize_abspath(path, pool);
964 change.noderev_id = noderev_id;
965 change.kind = change_kind;
966 change.text_mod = text_mod;
967 change.prop_mod = prop_mod;
968 return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
973 /* Generic node operations. */
976 struct node_id_args {
977 const svn_fs_id_t **id_p;
984 txn_body_node_id(void *baton, trail_t *trail)
986 struct node_id_args *args = baton;
989 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
990 *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
998 base_node_id(const svn_fs_id_t **id_p,
1003 base_root_data_t *brd = root->fsap_data;
1005 if (! root->is_txn_root
1006 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1008 /* Optimize the case where we don't need any db access at all.
1009 The root directory ("" or "/") node is stored in the
1010 svn_fs_root_t object, and never changes when it's a revision
1011 root, so we can just reach in and grab it directly. */
1012 *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1017 const svn_fs_id_t *id;
1018 struct node_id_args args;
1024 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1028 return SVN_NO_ERROR;
1031 static svn_error_t *
1032 base_node_relation(svn_fs_node_relation_t *relation,
1033 svn_fs_root_t *root_a, const char *path_a,
1034 svn_fs_root_t *root_b, const char *path_b,
1037 const svn_fs_id_t *id_a, *id_b;
1039 /* Paths from different repository are never related. */
1040 if (root_a->fs != root_b->fs)
1042 *relation = svn_fs_node_unrelated;
1043 return SVN_NO_ERROR;
1046 /* Naive implementation. */
1047 SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
1048 SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
1050 *relation = svn_fs_base__id_compare(id_a, id_b);
1052 return SVN_NO_ERROR;
1056 struct node_created_rev_args {
1057 svn_revnum_t revision;
1058 svn_fs_root_t *root;
1063 static svn_error_t *
1064 txn_body_node_created_rev(void *baton, trail_t *trail)
1066 struct node_created_rev_args *args = baton;
1069 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1070 return svn_fs_base__dag_get_revision(&(args->revision), node,
1071 trail, trail->pool);
1075 static svn_error_t *
1076 base_node_created_rev(svn_revnum_t *revision,
1077 svn_fs_root_t *root,
1081 struct node_created_rev_args args;
1083 args.revision = SVN_INVALID_REVNUM;
1086 SVN_ERR(svn_fs_base__retry_txn
1087 (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1088 *revision = args.revision;
1089 return SVN_NO_ERROR;
1093 struct node_created_path_args {
1094 const char **created_path;
1095 svn_fs_root_t *root;
1100 static svn_error_t *
1101 txn_body_node_created_path(void *baton, trail_t *trail)
1103 struct node_created_path_args *args = baton;
1106 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1107 *args->created_path = svn_fs_base__dag_get_created_path(node);
1108 return SVN_NO_ERROR;
1112 static svn_error_t *
1113 base_node_created_path(const char **created_path,
1114 svn_fs_root_t *root,
1118 struct node_created_path_args args;
1119 apr_pool_t *scratch_pool = svn_pool_create(pool);
1121 args.created_path = created_path;
1125 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1126 FALSE, scratch_pool));
1128 *created_path = apr_pstrdup(pool, *created_path);
1129 svn_pool_destroy(scratch_pool);
1130 return SVN_NO_ERROR;
1134 struct node_kind_args {
1135 const svn_fs_id_t *id;
1136 svn_node_kind_t kind; /* OUT parameter */
1140 static svn_error_t *
1141 txn_body_node_kind(void *baton, trail_t *trail)
1143 struct node_kind_args *args = baton;
1146 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1147 trail, trail->pool));
1148 args->kind = svn_fs_base__dag_node_kind(node);
1150 return SVN_NO_ERROR;
1154 static svn_error_t *
1155 node_kind(svn_node_kind_t *kind_p,
1156 svn_fs_root_t *root,
1160 struct node_kind_args args;
1161 const svn_fs_id_t *node_id;
1163 /* Get the node id. */
1164 SVN_ERR(base_node_id(&node_id, root, path, pool));
1166 /* Use the node id to get the real kind. */
1168 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1171 *kind_p = args.kind;
1172 return SVN_NO_ERROR;
1176 static svn_error_t *
1177 base_check_path(svn_node_kind_t *kind_p,
1178 svn_fs_root_t *root,
1182 svn_error_t *err = node_kind(kind_p, root, path, pool);
1184 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1185 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1187 svn_error_clear(err);
1189 *kind_p = svn_node_none;
1192 return svn_error_trace(err);
1196 struct node_prop_args
1198 svn_string_t **value_p;
1199 svn_fs_root_t *root;
1201 const char *propname;
1205 static svn_error_t *
1206 txn_body_node_prop(void *baton,
1209 struct node_prop_args *args = baton;
1211 apr_hash_t *proplist;
1213 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1214 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1215 trail, trail->pool));
1216 *(args->value_p) = NULL;
1218 *(args->value_p) = svn_hash_gets(proplist, args->propname);
1219 return SVN_NO_ERROR;
1223 static svn_error_t *
1224 base_node_prop(svn_string_t **value_p,
1225 svn_fs_root_t *root,
1227 const char *propname,
1230 struct node_prop_args args;
1231 svn_string_t *value;
1232 apr_pool_t *scratch_pool = svn_pool_create(pool);
1234 args.value_p = &value;
1237 args.propname = propname;
1238 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1239 FALSE, scratch_pool));
1240 *value_p = svn_string_dup(value, pool);
1241 svn_pool_destroy(scratch_pool);
1242 return SVN_NO_ERROR;
1246 struct node_proplist_args {
1247 apr_hash_t **table_p;
1248 svn_fs_root_t *root;
1253 static svn_error_t *
1254 txn_body_node_proplist(void *baton, trail_t *trail)
1256 struct node_proplist_args *args = baton;
1258 apr_hash_t *proplist;
1260 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1261 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1262 trail, trail->pool));
1263 *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1264 return SVN_NO_ERROR;
1268 static svn_error_t *
1269 base_node_proplist(apr_hash_t **table_p,
1270 svn_fs_root_t *root,
1275 struct node_proplist_args args;
1277 args.table_p = &table;
1281 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1285 return SVN_NO_ERROR;
1288 static svn_error_t *
1289 base_node_has_props(svn_boolean_t *has_props,
1290 svn_fs_root_t *root,
1292 apr_pool_t *scratch_pool)
1296 SVN_ERR(base_node_proplist(&props, root, path, scratch_pool));
1298 *has_props = (0 < apr_hash_count(props));
1300 return SVN_NO_ERROR;
1304 struct change_node_prop_args {
1305 svn_fs_root_t *root;
1308 const svn_string_t *value;
1312 static svn_error_t *
1313 txn_body_change_node_prop(void *baton,
1316 struct change_node_prop_args *args = baton;
1317 parent_path_t *parent_path;
1318 apr_hash_t *proplist;
1319 const char *txn_id = args->root->txn;
1320 base_fs_data_t *bfd = trail->fs->fsap_data;
1322 SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1323 trail, trail->pool));
1325 /* Check to see if path is locked; if so, check that we can use it.
1326 Notice that we're doing this non-recursively, regardless of node kind. */
1327 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1328 SVN_ERR(svn_fs_base__allow_locked_operation
1329 (args->path, FALSE, trail, trail->pool));
1331 SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1332 trail, trail->pool));
1333 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1334 trail, trail->pool));
1336 /* If there's no proplist, but we're just deleting a property, exit now. */
1337 if ((! proplist) && (! args->value))
1338 return SVN_NO_ERROR;
1340 /* Now, if there's no proplist, we know we need to make one. */
1342 proplist = apr_hash_make(trail->pool);
1344 /* Set the property. */
1345 svn_hash_sets(proplist, args->name, args->value);
1347 /* Overwrite the node's proplist. */
1348 SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1349 txn_id, trail, trail->pool));
1351 /* If this was a change to the mergeinfo property, and our version
1352 of the filesystem cares, we have some extra recording to do.
1354 ### If the format *doesn't* support mergeinfo recording, should
1355 ### we fuss about attempts to change the svn:mergeinfo property
1356 ### in any way save to delete it? */
1357 if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1358 && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1360 svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1362 /* First, note on our node that it has mergeinfo. */
1363 SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1365 &had_mergeinfo, txn_id,
1366 trail, trail->pool));
1368 /* If this is a change from the old state, we need to update our
1369 node's parents' mergeinfo counts by a factor of 1. */
1370 if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1371 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1372 has_mergeinfo ? 1 : -1,
1373 txn_id, trail, trail->pool));
1376 /* Make a record of this modification in the changes table. */
1377 return add_change(args->root->fs, txn_id,
1378 args->path, svn_fs_base__dag_get_id(parent_path->node),
1379 svn_fs_path_change_modify, FALSE, TRUE, trail,
1384 static svn_error_t *
1385 base_change_node_prop(svn_fs_root_t *root,
1388 const svn_string_t *value,
1391 struct change_node_prop_args args;
1393 if (! root->is_txn_root)
1394 return SVN_FS__NOT_TXN(root);
1400 return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1405 struct things_changed_args
1407 svn_boolean_t *changed_p;
1408 svn_fs_root_t *root1;
1409 svn_fs_root_t *root2;
1412 svn_boolean_t strict;
1417 static svn_error_t *
1418 txn_body_props_changed(void *baton, trail_t *trail)
1420 struct things_changed_args *args = baton;
1421 dag_node_t *node1, *node2;
1422 apr_hash_t *proplist1, *proplist2;
1424 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1425 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1426 SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
1427 node1, node2, trail, trail->pool));
1429 /* Is there a potential false positive and do we want to correct it? */
1430 if (!args->strict || !*args->changed_p)
1431 return SVN_NO_ERROR;
1433 /* Different representations. They might still have equal contents. */
1434 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1,
1435 trail, trail->pool));
1436 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2,
1437 trail, trail->pool));
1439 *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2,
1441 return SVN_NO_ERROR;
1445 static svn_error_t *
1446 base_props_changed(svn_boolean_t *changed_p,
1447 svn_fs_root_t *root1,
1449 svn_fs_root_t *root2,
1451 svn_boolean_t strict,
1454 struct things_changed_args args;
1456 /* Check that roots are in the same fs. */
1457 if (root1->fs != root2->fs)
1458 return svn_error_create
1459 (SVN_ERR_FS_GENERAL, NULL,
1460 _("Cannot compare property value between two different filesystems"));
1466 args.changed_p = changed_p;
1468 args.strict = strict;
1470 return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1476 /* Miscellaneous table handling */
1478 struct miscellaneous_set_args
1484 static svn_error_t *
1485 txn_body_miscellaneous_set(void *baton, trail_t *trail)
1487 struct miscellaneous_set_args *msa = baton;
1489 return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1494 svn_fs_base__miscellaneous_set(svn_fs_t *fs,
1499 struct miscellaneous_set_args msa;
1503 return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1507 struct miscellaneous_get_args
1513 static svn_error_t *
1514 txn_body_miscellaneous_get(void *baton, trail_t *trail)
1516 struct miscellaneous_get_args *mga = baton;
1517 return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1522 svn_fs_base__miscellaneous_get(const char **val,
1527 struct miscellaneous_get_args mga;
1528 apr_pool_t *scratch_pool = svn_pool_create(pool);
1532 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1533 FALSE, scratch_pool));
1535 *val = apr_pstrdup(pool, *val);
1536 svn_pool_destroy(scratch_pool);
1537 return SVN_NO_ERROR;
1542 /* Getting a directory's entries */
1545 struct dir_entries_args
1547 apr_hash_t **table_p;
1548 svn_fs_root_t *root;
1553 /* *(BATON->table_p) will never be NULL on successful return */
1554 static svn_error_t *
1555 txn_body_dir_entries(void *baton,
1558 struct dir_entries_args *args = baton;
1560 apr_hash_t *entries;
1562 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1564 /* Get the entries for PARENT_PATH. */
1565 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1567 /* Potentially initialize the return value to an empty hash. */
1568 *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1569 return SVN_NO_ERROR;
1573 static svn_error_t *
1574 base_dir_entries(apr_hash_t **table_p,
1575 svn_fs_root_t *root,
1579 struct dir_entries_args args;
1580 apr_pool_t *iterpool;
1582 svn_fs_t *fs = root->fs;
1583 apr_hash_index_t *hi;
1585 args.table_p = &table;
1588 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1591 iterpool = svn_pool_create(pool);
1593 /* Add in the kind data. */
1594 for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1596 svn_fs_dirent_t *entry;
1597 struct node_kind_args nk_args;
1600 svn_pool_clear(iterpool);
1602 /* KEY will be the entry name in ancestor (about which we
1603 simply don't care), VAL the dirent. */
1604 apr_hash_this(hi, NULL, NULL, &val);
1606 nk_args.id = entry->id;
1608 /* We don't need to have the retry function destroy the trail
1609 pool because we're already doing that via the use of an
1611 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1613 entry->kind = nk_args.kind;
1616 svn_pool_destroy(iterpool);
1619 return SVN_NO_ERROR;
1622 static svn_error_t *
1623 base_dir_optimal_order(apr_array_header_t **ordered_p,
1624 svn_fs_root_t *root,
1625 apr_hash_t *entries,
1626 apr_pool_t *result_pool,
1627 apr_pool_t *scratch_pool)
1629 /* 1:1 copy of entries with no differnce in ordering */
1630 apr_hash_index_t *hi;
1631 apr_array_header_t *result
1632 = apr_array_make(result_pool, apr_hash_count(entries),
1633 sizeof(svn_fs_dirent_t *));
1634 for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
1635 APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
1637 *ordered_p = result;
1638 return SVN_NO_ERROR;
1643 /* Merges and commits. */
1646 struct deltify_committed_args
1648 svn_fs_t *fs; /* the filesystem */
1649 svn_revnum_t rev; /* revision just committed */
1650 const char *txn_id; /* transaction just committed */
1654 struct txn_deltify_args
1656 /* The transaction ID whose nodes are being deltified. */
1659 /* The target is what we're deltifying. */
1660 const svn_fs_id_t *tgt_id;
1662 /* The base is what we're deltifying against. It's not necessarily
1663 the "next" revision of the node; skip deltas mean we sometimes
1664 deltify against a successor many generations away. This may be
1665 NULL, in which case we'll avoid deltification and simply index
1666 TGT_ID's data checksum. */
1667 const svn_fs_id_t *base_id;
1669 /* We only deltify props for directories.
1670 ### Didn't we try removing this horrid little optimization once?
1671 ### What was the result? I would have thought that skip deltas
1672 ### mean directory undeltification is cheap enough now. */
1673 svn_boolean_t is_dir;
1677 static svn_error_t *
1678 txn_body_txn_deltify(void *baton, trail_t *trail)
1680 struct txn_deltify_args *args = baton;
1681 dag_node_t *tgt_node, *base_node;
1682 base_fs_data_t *bfd = trail->fs->fsap_data;
1684 SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1685 trail, trail->pool));
1686 /* If we have something to deltify against, do so. */
1689 SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1690 trail, trail->pool));
1691 SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1692 args->txn_id, trail, trail->pool));
1695 /* If we support rep sharing, and this isn't a directory, record a
1696 mapping of TGT_NODE's data checksum to its representation key. */
1697 if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1698 SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1700 return SVN_NO_ERROR;
1704 struct txn_pred_count_args
1706 const svn_fs_id_t *id;
1711 static svn_error_t *
1712 txn_body_pred_count(void *baton, trail_t *trail)
1714 node_revision_t *noderev;
1715 struct txn_pred_count_args *args = baton;
1717 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1718 args->id, trail, trail->pool));
1719 args->pred_count = noderev->predecessor_count;
1720 return SVN_NO_ERROR;
1724 struct txn_pred_id_args
1726 const svn_fs_id_t *id; /* The node id whose predecessor we want. */
1727 const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1728 apr_pool_t *pool; /* The pool in which to allocate pred_id. */
1732 static svn_error_t *
1733 txn_body_pred_id(void *baton, trail_t *trail)
1735 node_revision_t *nr;
1736 struct txn_pred_id_args *args = baton;
1738 SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1739 trail, trail->pool));
1740 if (nr->predecessor_id)
1741 args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1743 args->pred_id = NULL;
1745 return SVN_NO_ERROR;
1749 /* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1750 in FS. If PATH is a mutable directory, recurse.
1752 NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1753 value isn't known. KIND is the node kind for PATH in ROOT, or
1754 svn_node_unknown is the kind isn't known.
1756 Use POOL for necessary allocations. */
1757 static svn_error_t *
1758 deltify_mutable(svn_fs_t *fs,
1759 svn_fs_root_t *root,
1761 const svn_fs_id_t *node_id,
1762 svn_node_kind_t kind,
1766 const svn_fs_id_t *id = node_id;
1767 apr_hash_t *entries = NULL;
1768 struct txn_deltify_args td_args;
1769 base_fs_data_t *bfd = fs->fsap_data;
1771 /* Get the ID for PATH under ROOT if it wasn't provided. */
1773 SVN_ERR(base_node_id(&id, root, path, pool));
1775 /* Check for mutability. Not mutable? Go no further. This is safe
1776 to do because for items in the tree to be mutable, their parent
1777 dirs must also be mutable. Therefore, if a directory is not
1778 mutable under TXN_ID, its children cannot be. */
1779 if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1780 return SVN_NO_ERROR;
1782 /* Is this a directory? */
1783 if (kind == svn_node_unknown)
1784 SVN_ERR(base_check_path(&kind, root, path, pool));
1786 /* If this is a directory, read its entries. */
1787 if (kind == svn_node_dir)
1788 SVN_ERR(base_dir_entries(&entries, root, path, pool));
1790 /* If there are entries, recurse on 'em. */
1793 apr_pool_t *subpool = svn_pool_create(pool);
1794 apr_hash_index_t *hi;
1796 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1798 /* KEY will be the entry name, VAL the dirent */
1801 svn_fs_dirent_t *entry;
1802 svn_pool_clear(subpool);
1803 apr_hash_this(hi, &key, NULL, &val);
1805 SVN_ERR(deltify_mutable(fs, root,
1806 svn_fspath__join(path, key, subpool),
1807 entry->id, entry->kind, txn_id, subpool));
1810 svn_pool_destroy(subpool);
1813 /* Index ID's data checksum. */
1814 td_args.txn_id = txn_id;
1815 td_args.tgt_id = id;
1816 td_args.base_id = NULL;
1817 td_args.is_dir = (kind == svn_node_dir);
1818 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1821 /* Finally, deltify old data against this node. */
1823 /* Prior to 1.6, we use the following algorithm to deltify nodes:
1825 Redeltify predecessor node-revisions of the one we added. The
1826 idea is to require at most 2*lg(N) deltas to be applied to get
1827 to any node-revision in a chain of N predecessors. We do this
1828 using a technique derived from skip lists:
1830 - Always redeltify the immediate parent
1832 - If the number of predecessors is divisible by 2,
1833 redeltify the revision two predecessors back
1835 - If the number of predecessors is divisible by 4,
1836 redeltify the revision four predecessors back
1840 That's the theory, anyway. Unfortunately, if we strictly
1841 follow that theory we get a bunch of overhead up front and no
1842 great benefit until the number of predecessors gets large. So,
1843 stop at redeltifying the parent if the number of predecessors
1844 is less than 32, and also skip the second level (redeltifying
1845 two predecessors back), since that doesn't help much. Also,
1846 don't redeltify the oldest node-revision; it's potentially
1847 expensive and doesn't help retrieve any other revision.
1848 (Retrieving the oldest node-revision will still be fast, just
1849 not as blindingly so.)
1851 For 1.6 and beyond, we just deltify the current node against its
1852 predecessors, using skip deltas similar to the way FSFS does. */
1855 const svn_fs_id_t *pred_id;
1856 struct txn_pred_count_args tpc_args;
1857 apr_pool_t *subpools[2];
1858 int active_subpool = 0;
1859 svn_revnum_t forward_delta_rev = 0;
1862 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1864 pred_count = tpc_args.pred_count;
1866 /* If nothing to deltify, then we're done. */
1867 if (pred_count == 0)
1868 return SVN_NO_ERROR;
1870 subpools[0] = svn_pool_create(pool);
1871 subpools[1] = svn_pool_create(pool);
1873 /* If we support the 'miscellaneous' table, check it to see if
1874 there is a point in time before which we don't want to do
1876 /* ### FIXME: I think this is an unnecessary restriction. We
1877 ### should be able to do something meaningful for most
1878 ### deltification requests -- what that is depends on the
1879 ### directory of the deltas for that revision, though. */
1880 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1883 SVN_ERR(svn_fs_base__miscellaneous_get
1884 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1886 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1889 if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1890 && forward_delta_rev <= root->rev)
1892 /**** FORWARD DELTA STORAGE ****/
1894 /* Decide which predecessor to deltify against. Flip the rightmost '1'
1895 bit of the predecessor count to determine which file rev (counting
1896 from 0) we want to use. (To see why count & (count - 1) unsets the
1897 rightmost set bit, think about how you decrement a binary number. */
1898 pred_count = pred_count & (pred_count - 1);
1900 /* Walk back a number of predecessors equal to the difference between
1901 pred_count and the original predecessor count. (For example, if
1902 the node has ten predecessors and we want the eighth node, walk back
1903 two predecessors. */
1906 /* We need to use two alternating pools because the id used in the
1907 call to txn_body_pred_id is allocated by the previous inner
1908 loop iteration. If we would clear the pool each iteration we
1909 would free the previous result. */
1910 while ((pred_count++) < tpc_args.pred_count)
1912 struct txn_pred_id_args tpi_args;
1914 active_subpool = !active_subpool;
1915 svn_pool_clear(subpools[active_subpool]);
1917 tpi_args.id = pred_id;
1918 tpi_args.pool = subpools[active_subpool];
1919 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1920 FALSE, subpools[active_subpool]));
1921 pred_id = tpi_args.pred_id;
1923 if (pred_id == NULL)
1924 return svn_error_create
1925 (SVN_ERR_FS_CORRUPT, 0,
1926 _("Corrupt DB: faulty predecessor count"));
1930 /* Finally, do the deltification. */
1931 td_args.txn_id = txn_id;
1932 td_args.tgt_id = id;
1933 td_args.base_id = pred_id;
1934 td_args.is_dir = (kind == svn_node_dir);
1935 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1936 TRUE, subpools[active_subpool]));
1940 int nlevels, lev, count;
1942 /**** REVERSE DELTA STORAGE ****/
1944 /* Decide how many predecessors to redeltify. To save overhead,
1945 don't redeltify anything but the immediate predecessor if there
1946 are less than 32 predecessors. */
1948 if (pred_count >= 32)
1950 while (pred_count % 2 == 0)
1956 /* Don't redeltify the oldest revision. */
1957 if (1 << (nlevels - 1) == pred_count)
1961 /* Redeltify the desired number of predecessors. */
1965 /* We need to use two alternating pools because the id used in the
1966 call to txn_body_pred_id is allocated by the previous inner
1967 loop iteration. If we would clear the pool each iteration we
1968 would free the previous result. */
1969 for (lev = 0; lev < nlevels; lev++)
1971 /* To save overhead, skip the second level (that is, never
1972 redeltify the node-revision two predecessors back). */
1976 /* Note that COUNT is not reset between levels, and neither is
1977 PREDNODE; we just keep counting from where we were up to
1978 where we're supposed to get. */
1979 while (count < (1 << lev))
1981 struct txn_pred_id_args tpi_args;
1983 active_subpool = !active_subpool;
1984 svn_pool_clear(subpools[active_subpool]);
1986 tpi_args.id = pred_id;
1987 tpi_args.pool = subpools[active_subpool];
1988 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1990 subpools[active_subpool]));
1991 pred_id = tpi_args.pred_id;
1993 if (pred_id == NULL)
1994 return svn_error_create
1995 (SVN_ERR_FS_CORRUPT, 0,
1996 _("Corrupt DB: faulty predecessor count"));
2001 /* Finally, do the deltification. */
2002 td_args.txn_id = NULL; /* Don't require mutable reps */
2003 td_args.tgt_id = pred_id;
2004 td_args.base_id = id;
2005 td_args.is_dir = (kind == svn_node_dir);
2006 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
2007 TRUE, subpools[active_subpool]));
2012 svn_pool_destroy(subpools[0]);
2013 svn_pool_destroy(subpools[1]);
2016 return SVN_NO_ERROR;
2020 struct get_root_args
2022 svn_fs_root_t *root;
2027 /* Set ARGS->node to the root node of ARGS->root. */
2028 static svn_error_t *
2029 txn_body_get_root(void *baton, trail_t *trail)
2031 struct get_root_args *args = baton;
2032 return get_dag(&(args->node), args->root, "", trail, trail->pool);
2037 static svn_error_t *
2038 update_ancestry(svn_fs_t *fs,
2039 const svn_fs_id_t *source_id,
2040 const svn_fs_id_t *target_id,
2042 const char *target_path,
2043 int source_pred_count,
2047 node_revision_t *noderev;
2049 /* Set target's predecessor-id to source_id. */
2050 if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
2051 return svn_error_createf
2052 (SVN_ERR_FS_NOT_MUTABLE, NULL,
2053 _("Unexpected immutable node at '%s'"), target_path);
2054 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
2056 noderev->predecessor_id = source_id;
2057 noderev->predecessor_count = source_pred_count;
2058 if (noderev->predecessor_count != -1)
2059 noderev->predecessor_count++;
2060 return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
2064 /* Set the contents of CONFLICT_PATH to PATH, and return an
2065 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
2066 at PATH. Perform all allocations in POOL (except the allocation of
2067 CONFLICT_PATH, which should be handled outside this function). */
2068 static svn_error_t *
2069 conflict_err(svn_stringbuf_t *conflict_path,
2072 svn_stringbuf_set(conflict_path, path);
2073 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
2074 _("Conflict at '%s'"), path);
2078 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2079 * TRAIL. ANCESTOR and TARGET must be distinct node revisions.
2080 * TARGET_PATH should correspond to TARGET's full path in its
2081 * filesystem, and is used for reporting conflict location.
2083 * SOURCE, TARGET, and ANCESTOR are generally directories; this
2084 * function recursively merges the directories' contents. If any are
2085 * files, this function simply returns an error whenever SOURCE,
2086 * TARGET, and ANCESTOR are all distinct node revisions.
2088 * If there are differences between ANCESTOR and SOURCE that conflict
2089 * with changes between ANCESTOR and TARGET, this function returns an
2090 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2091 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2093 * If there are no conflicting differences, CONFLICT_P is updated to
2096 * CONFLICT_P must point to a valid svn_stringbuf_t.
2098 * Do any necessary temporary allocation in POOL.
2100 static svn_error_t *
2101 merge(svn_stringbuf_t *conflict_p,
2102 const char *target_path,
2105 dag_node_t *ancestor,
2107 apr_int64_t *mergeinfo_increment_out,
2111 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2112 apr_hash_t *s_entries, *t_entries, *a_entries;
2113 apr_hash_index_t *hi;
2114 apr_pool_t *iterpool;
2117 apr_int64_t mergeinfo_increment = 0;
2118 base_fs_data_t *bfd = trail->fs->fsap_data;
2120 /* Make sure everyone comes from the same filesystem. */
2121 fs = svn_fs_base__dag_get_fs(ancestor);
2122 if ((fs != svn_fs_base__dag_get_fs(source))
2123 || (fs != svn_fs_base__dag_get_fs(target)))
2125 return svn_error_create
2126 (SVN_ERR_FS_CORRUPT, NULL,
2127 _("Bad merge; ancestor, source, and target not all in same fs"));
2130 /* We have the same fs, now check it. */
2131 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2133 source_id = svn_fs_base__dag_get_id(source);
2134 target_id = svn_fs_base__dag_get_id(target);
2135 ancestor_id = svn_fs_base__dag_get_id(ancestor);
2137 /* It's improper to call this function with ancestor == target. */
2138 if (svn_fs_base__id_eq(ancestor_id, target_id))
2140 svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2141 return svn_error_createf
2142 (SVN_ERR_FS_GENERAL, NULL,
2143 _("Bad merge; target '%s' has id '%s', same as ancestor"),
2144 target_path, id_str->data);
2147 svn_stringbuf_setempty(conflict_p);
2150 * Either no change made in source, or same change as made in target.
2151 * Both mean nothing to merge here.
2153 if (svn_fs_base__id_eq(ancestor_id, source_id)
2154 || (svn_fs_base__id_eq(source_id, target_id)))
2155 return SVN_NO_ERROR;
2157 /* Else proceed, knowing all three are distinct node revisions.
2159 * How to merge from this point:
2161 * if (not all 3 are directories)
2163 * early exit with conflict;
2166 * // Property changes may only be made to up-to-date
2167 * // directories, because once the client commits the prop
2168 * // change, it bumps the directory's revision, and therefore
2169 * // must be able to depend on there being no other changes to
2170 * // that directory in the repository.
2171 * if (target's property list differs from ancestor's)
2174 * For each entry NAME in the directory ANCESTOR:
2176 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2177 * the name within ANCESTOR, SOURCE, and TARGET respectively.
2178 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
2180 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2181 * No changes were made to this entry while the transaction was in
2182 * progress, so do nothing to the target.
2184 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2185 * A change was made to this entry while the transaction was in
2186 * process, but the transaction did not touch this entry. Replace
2187 * TARGET-ENTRY with SOURCE-ENTRY.
2190 * Changes were made to this entry both within the transaction and
2191 * to the repository while the transaction was in progress. They
2192 * must be merged or declared to be in conflict.
2194 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2195 * double delete; flag a conflict.
2197 * If any of the three entries is of type file, declare a conflict.
2199 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2200 * modification of ANCESTOR-ENTRY (determine by comparing the
2201 * node-id fields), declare a conflict. A replacement is
2202 * incompatible with a modification or other replacement--even
2203 * an identical replacement.
2205 * Direct modifications were made to the directory ANCESTOR-ENTRY
2206 * in both SOURCE and TARGET. Recursively merge these
2209 * For each leftover entry NAME in the directory SOURCE:
2211 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
2212 * TARGET are adding exactly the same thing, two additions are not
2213 * auto-mergeable with each other.
2215 * Add NAME to TARGET with the entry from SOURCE.
2217 * Now that we are done merging the changes from SOURCE into the
2218 * directory TARGET, update TARGET's predecessor to be SOURCE.
2221 if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2222 || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2223 || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2225 return conflict_err(conflict_p, target_path);
2229 /* Possible early merge failure: if target and ancestor have
2230 different property lists, then the merge should fail.
2231 Propchanges can *only* be committed on an up-to-date directory.
2232 ### TODO: see issue #418 about the inelegance of this.
2234 Another possible, similar, early merge failure: if source and
2235 ancestor have different property lists (meaning someone else
2236 changed directory properties while our commit transaction was
2237 happening), the merge should fail. See issue #2751.
2240 node_revision_t *tgt_nr, *anc_nr, *src_nr;
2242 /* Get node revisions for our id's. */
2243 SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2245 SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2247 SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2250 /* Now compare the prop-keys of the skels. Note that just because
2251 the keys are different -doesn't- mean the proplists have
2252 different contents. But merge() isn't concerned with contents;
2253 it doesn't do a brute-force comparison on textual contents, so
2254 it won't do that here either. Checking to see if the propkey
2255 atoms are `equal' is enough. */
2256 if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2257 return conflict_err(conflict_p, target_path);
2258 if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2259 return conflict_err(conflict_p, target_path);
2262 /* ### todo: it would be more efficient to simply check for a NULL
2263 entries hash where necessary below than to allocate an empty hash
2264 here, but another day, another day... */
2265 SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2267 s_entries = apr_hash_make(pool);
2268 SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2270 t_entries = apr_hash_make(pool);
2271 SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2273 a_entries = apr_hash_make(pool);
2275 /* for each entry E in a_entries... */
2276 iterpool = svn_pool_create(pool);
2277 for (hi = apr_hash_first(pool, a_entries);
2279 hi = apr_hash_next(hi))
2281 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2287 svn_pool_clear(iterpool);
2289 /* KEY will be the entry name in ancestor, VAL the dirent */
2290 apr_hash_this(hi, &key, &klen, &val);
2293 s_entry = apr_hash_get(s_entries, key, klen);
2294 t_entry = apr_hash_get(t_entries, key, klen);
2296 /* No changes were made to this entry while the transaction was
2297 in progress, so do nothing to the target. */
2298 if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2301 /* A change was made to this entry while the transaction was in
2302 process, but the transaction did not touch this entry. */
2303 else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2305 dag_node_t *t_ent_node;
2306 apr_int64_t mergeinfo_start;
2307 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2308 t_entry->id, trail, iterpool));
2309 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2312 mergeinfo_increment -= mergeinfo_start;
2316 dag_node_t *s_ent_node;
2317 apr_int64_t mergeinfo_end;
2318 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2321 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2325 mergeinfo_increment += mergeinfo_end;
2326 SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2327 txn_id, trail, iterpool));
2331 SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2336 /* Changes were made to this entry both within the transaction
2337 and to the repository while the transaction was in progress.
2338 They must be merged or declared to be in conflict. */
2341 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2342 const char *new_tpath;
2343 apr_int64_t sub_mergeinfo_increment;
2345 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2346 double delete; if one of them is null, that's a delete versus
2347 a modification. In any of these cases, flag a conflict. */
2348 if (s_entry == NULL || t_entry == NULL)
2349 return conflict_err(conflict_p,
2350 svn_fspath__join(target_path,
2354 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2355 modification of ANCESTOR-ENTRY, declare a conflict. */
2356 if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2357 svn_fs_base__id_node_id(a_entry->id)) != 0
2358 || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2359 svn_fs_base__id_copy_id(a_entry->id)) != 0
2360 || strcmp(svn_fs_base__id_node_id(t_entry->id),
2361 svn_fs_base__id_node_id(a_entry->id)) != 0
2362 || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2363 svn_fs_base__id_copy_id(a_entry->id)) != 0)
2364 return conflict_err(conflict_p,
2365 svn_fspath__join(target_path,
2369 /* Fetch the nodes for our entries. */
2370 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2371 s_entry->id, trail, iterpool));
2372 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2373 t_entry->id, trail, iterpool));
2374 SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2375 a_entry->id, trail, iterpool));
2377 /* If any of the three entries is of type file, flag a conflict. */
2378 if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2379 || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2380 || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2381 return conflict_err(conflict_p,
2382 svn_fspath__join(target_path,
2386 /* Direct modifications were made to the directory
2387 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2388 merge these modifications. */
2389 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2390 SVN_ERR(merge(conflict_p, new_tpath,
2391 t_ent_node, s_ent_node, a_ent_node,
2392 txn_id, &sub_mergeinfo_increment, trail, iterpool));
2393 mergeinfo_increment += sub_mergeinfo_increment;
2396 /* We've taken care of any possible implications E could have.
2397 Remove it from source_entries, so it's easy later to loop
2398 over all the source entries that didn't exist in
2399 ancestor_entries. */
2401 apr_hash_set(s_entries, key, klen, NULL);
2404 /* For each entry E in source but not in ancestor */
2405 for (hi = apr_hash_first(pool, s_entries);
2407 hi = apr_hash_next(hi))
2409 svn_fs_dirent_t *s_entry, *t_entry;
2413 dag_node_t *s_ent_node;
2414 apr_int64_t mergeinfo_s;
2416 svn_pool_clear(iterpool);
2418 apr_hash_this(hi, &key, &klen, &val);
2420 t_entry = apr_hash_get(t_entries, key, klen);
2422 /* If NAME exists in TARGET, declare a conflict. */
2424 return conflict_err(conflict_p,
2425 svn_fspath__join(target_path,
2429 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2430 s_entry->id, trail, iterpool));
2431 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2434 mergeinfo_increment += mergeinfo_s;
2435 SVN_ERR(svn_fs_base__dag_set_entry
2436 (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2438 svn_pool_destroy(iterpool);
2440 /* Now that TARGET has absorbed all of the history between ANCESTOR
2441 and SOURCE, we can update its predecessor to point to SOURCE. */
2442 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2444 SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2445 pred_count, trail, pool));
2447 /* Tweak mergeinfo data if our format supports it. */
2448 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2450 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2451 mergeinfo_increment,
2452 txn_id, trail, pool));
2455 if (mergeinfo_increment_out)
2456 *mergeinfo_increment_out = mergeinfo_increment;
2458 return SVN_NO_ERROR;
2464 /* The ancestor for the merge. If this is null, then TXN's base is
2465 used as the ancestor for the merge. */
2466 dag_node_t *ancestor_node;
2468 /* This is the SOURCE node for the merge. It may not be null. */
2469 dag_node_t *source_node;
2471 /* This is the TARGET of the merge. It may not be null. If
2472 ancestor_node above is null, then this txn's base is used as the
2473 ancestor for the merge. */
2476 /* If a conflict results, this is updated to the path in the txn that
2477 conflicted. It must point to a valid svn_stringbuf_t before calling
2478 svn_fs_base__retry_txn, as this determines the pool used to allocate any
2480 svn_stringbuf_t *conflict;
2484 /* Merge changes between an ancestor and BATON->source_node into
2485 BATON->txn. The ancestor is either BATON->ancestor_node, or if
2486 that is null, BATON->txn's base node.
2488 If the merge is successful, BATON->txn's base will become
2489 BATON->source_node, and its root node will have a new ID, a
2490 successor of BATON->source_node. */
2491 static svn_error_t *
2492 txn_body_merge(void *baton, trail_t *trail)
2494 struct merge_args *args = baton;
2495 dag_node_t *source_node, *txn_root_node, *ancestor_node;
2496 const svn_fs_id_t *source_id;
2497 svn_fs_t *fs = args->txn->fs;
2498 const char *txn_id = args->txn->id;
2500 source_node = args->source_node;
2501 ancestor_node = args->ancestor_node;
2502 source_id = svn_fs_base__dag_get_id(source_node);
2504 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2505 trail, trail->pool));
2507 if (ancestor_node == NULL)
2509 SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2510 txn_id, trail, trail->pool));
2513 if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2514 svn_fs_base__dag_get_id(txn_root_node)))
2516 /* If no changes have been made in TXN since its current base,
2517 then it can't conflict with any changes since that base. So
2518 we just set *both* its base and root to source, making TXN
2519 in effect a repeat of source. */
2521 /* ### kff todo: this would, of course, be a mighty silly thing
2522 for the caller to do, and we might want to consider whether
2523 this response is really appropriate. */
2525 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2526 trail, trail->pool));
2527 SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2528 trail, trail->pool));
2534 SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2535 ancestor_node, txn_id, NULL, trail, trail->pool));
2537 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2541 /* After the merge, txn's new "ancestor" is now really the node
2542 at source_id, so record that fact. Think of this as
2543 ratcheting the txn forward in time, so it can't backslide and
2544 forget the merging work that's already been done. */
2545 SVN_ERR(update_ancestry(fs, source_id,
2546 svn_fs_base__dag_get_id(txn_root_node),
2547 txn_id, "/", pred_count, trail, trail->pool));
2548 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2549 trail, trail->pool));
2552 return SVN_NO_ERROR;
2556 /* Verify that there are registered with TRAIL->fs all the locks
2557 necessary to permit all the changes associated with TXN_NAME. */
2558 static svn_error_t *
2559 verify_locks(const char *txn_name,
2563 apr_pool_t *subpool = svn_pool_create(pool);
2564 apr_hash_t *changes;
2565 apr_hash_index_t *hi;
2566 apr_array_header_t *changed_paths;
2567 svn_stringbuf_t *last_recursed = NULL;
2570 /* Fetch the changes for this transaction. */
2571 SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2574 /* Make an array of the changed paths, and sort them depth-first-ily. */
2575 changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2576 sizeof(const char *));
2577 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2580 apr_hash_this(hi, &key, NULL, NULL);
2581 APR_ARRAY_PUSH(changed_paths, const char *) = key;
2583 qsort(changed_paths->elts, changed_paths->nelts,
2584 changed_paths->elt_size, svn_sort_compare_paths);
2586 /* Now, traverse the array of changed paths, verify locks. Note
2587 that if we need to do a recursive verification a path, we'll skip
2588 over children of that path when we get to them. */
2589 for (i = 0; i < changed_paths->nelts; i++)
2592 svn_fs_path_change2_t *change;
2593 svn_boolean_t recurse = TRUE;
2595 svn_pool_clear(subpool);
2596 path = APR_ARRAY_IDX(changed_paths, i, const char *);
2598 /* If this path has already been verified as part of a recursive
2599 check of one of its parents, no need to do it again. */
2601 && svn_fspath__skip_ancestor(last_recursed->data, path))
2604 /* Fetch the change associated with our path. */
2605 change = svn_hash_gets(changes, path);
2607 /* What does it mean to succeed at lock verification for a given
2608 path? For an existing file or directory getting modified
2609 (text, props), it means we hold the lock on the file or
2610 directory. For paths being added or removed, we need to hold
2611 the locks for that path and any children of that path.
2613 WHEW! We have no reliable way to determine the node kind of
2614 deleted items, but fortunately we are going to do a recursive
2615 check on deleted paths regardless of their kind. */
2616 if (change->change_kind == svn_fs_path_change_modify)
2618 SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2621 /* If we just did a recursive check, remember the path we
2622 checked (so children can be skipped). */
2625 if (! last_recursed)
2626 last_recursed = svn_stringbuf_create(path, pool);
2628 svn_stringbuf_set(last_recursed, path);
2631 svn_pool_destroy(subpool);
2632 return SVN_NO_ERROR;
2639 svn_revnum_t new_rev;
2643 /* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2644 * revision, if ARGS->txn is up-to-date with respect to the repository.
2646 * Up-to-date means that ARGS->txn's base root is the same as the root
2647 * of the youngest revision. If ARGS->txn is not up-to-date, the
2648 * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2649 * new revision is created, and ARGS->new_rev is not touched.
2651 * If the commit succeeds, ARGS->txn is destroyed.
2653 static svn_error_t *
2654 txn_body_commit(void *baton, trail_t *trail)
2656 struct commit_args *args = baton;
2658 svn_fs_txn_t *txn = args->txn;
2659 svn_fs_t *fs = txn->fs;
2660 const char *txn_name = txn->id;
2662 svn_revnum_t youngest_rev;
2663 const svn_fs_id_t *y_rev_root_id;
2664 dag_node_t *txn_base_root_node;
2666 /* Getting the youngest revision locks the revisions table until
2667 this trail is done. */
2668 SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2670 /* If the root of the youngest revision is the same as txn's base,
2671 then no further merging is necessary and we can commit. */
2672 SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2673 trail, trail->pool));
2674 SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2675 trail, trail->pool));
2676 /* ### kff todo: it seems weird to grab the ID for one, and the node
2677 for the other. We can certainly do the comparison we need, but
2678 it would be nice to grab the same type of information from the
2679 start, instead of having to transform one of them. */
2680 if (! svn_fs_base__id_eq(y_rev_root_id,
2681 svn_fs_base__dag_get_id(txn_base_root_node)))
2683 svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2685 return svn_error_createf
2686 (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2687 _("Transaction '%s' out-of-date with respect to revision '%s'"),
2688 txn_name, id_str->data);
2691 /* Locks may have been added (or stolen) between the calling of
2692 previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2693 to re-examine every changed-path in the txn and re-verify all
2694 discovered locks. */
2695 SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2697 /* Else, commit the txn. */
2698 return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2703 /* Note: it is acceptable for this function to call back into
2704 top-level FS interfaces because it does not itself use trails. */
2706 svn_fs_base__commit_txn(const char **conflict_p,
2707 svn_revnum_t *new_rev,
2711 /* How do commits work in Subversion?
2713 * When you're ready to commit, here's what you have:
2715 * 1. A transaction, with a mutable tree hanging off it.
2716 * 2. A base revision, against which TXN_TREE was made.
2717 * 3. A latest revision, which may be newer than the base rev.
2719 * The problem is that if latest != base, then one can't simply
2720 * attach the txn root as the root of the new revision, because that
2721 * would lose all the changes between base and latest. It is also
2722 * not acceptable to insist that base == latest; in a busy
2723 * repository, commits happen too fast to insist that everyone keep
2724 * their entire tree up-to-date at all times. Non-overlapping
2725 * changes should not interfere with each other.
2727 * The solution is to merge the changes between base and latest into
2728 * the txn tree [see the function merge()]. The txn tree is the
2729 * only one of the three trees that is mutable, so it has to be the
2732 * You might have to adjust it more than once, if a new latest
2733 * revision gets committed while you were merging in the previous
2736 * 1. Jane starts txn T, based at revision 6.
2737 * 2. Someone commits (or already committed) revision 7.
2738 * 3. Jane's starts merging the changes between 6 and 7 into T.
2739 * 4. Meanwhile, someone commits revision 8.
2740 * 5. Jane finishes the 6-->7 merge. T could now be committed
2741 * against a latest revision of 7, if only that were still the
2742 * latest. Unfortunately, 8 is now the latest, so...
2743 * 6. Jane starts merging the changes between 7 and 8 into T.
2744 * 7. Meanwhile, no one commits any new revisions. Whew.
2745 * 8. Jane commits T, creating revision 9, whose tree is exactly
2746 * T's tree, except immutable now.
2748 * Lather, rinse, repeat.
2752 svn_fs_t *fs = txn->fs;
2753 apr_pool_t *subpool = svn_pool_create(pool);
2755 /* Initialize output params. */
2756 *new_rev = SVN_INVALID_REVNUM;
2762 struct get_root_args get_root_args;
2763 struct merge_args merge_args;
2764 struct commit_args commit_args;
2765 svn_revnum_t youngish_rev;
2766 svn_fs_root_t *youngish_root;
2767 dag_node_t *youngish_root_node;
2769 svn_pool_clear(subpool);
2771 /* Get the *current* youngest revision, in one short-lived
2772 Berkeley transaction. (We don't want the revisions table
2773 locked while we do the main merge.) We call it "youngish"
2774 because new revisions might get committed after we've
2777 SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2778 SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2781 /* Get the dag node for the youngest revision, also in one
2782 Berkeley transaction. Later we'll use it as the SOURCE
2783 argument to a merge, and if the merge succeeds, this youngest
2784 root node will become the new base root for the svn txn that
2785 was the target of the merge (but note that the youngest rev
2786 may have changed by then -- that's why we're careful to get
2787 this root in its own bdb txn here). */
2788 get_root_args.root = youngish_root;
2789 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2791 youngish_root_node = get_root_args.node;
2793 /* Try to merge. If the merge succeeds, the base root node of
2794 TARGET's txn will become the same as youngish_root_node, so
2795 any future merges will only be between that node and whatever
2796 the root node of the youngest rev is by then. */
2797 merge_args.ancestor_node = NULL;
2798 merge_args.source_node = youngish_root_node;
2799 merge_args.txn = txn;
2800 merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2801 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2805 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2806 *conflict_p = merge_args.conflict->data;
2807 return svn_error_trace(err);
2810 /* Try to commit. */
2811 commit_args.txn = txn;
2812 err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2814 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2816 /* Did someone else finish committing a new revision while we
2817 were in mid-merge or mid-commit? If so, we'll need to
2818 loop again to merge the new changes in, then try to
2819 commit again. Or if that's not what happened, then just
2820 return the error. */
2821 svn_revnum_t youngest_rev;
2822 svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2826 svn_error_clear(err);
2827 return svn_error_trace(err2); /* err2 is bad,
2828 it should not occur */
2830 else if (youngest_rev == youngish_rev)
2831 return svn_error_trace(err);
2833 svn_error_clear(err);
2837 return svn_error_trace(err);
2841 /* Set the return value -- our brand spankin' new revision! */
2842 *new_rev = commit_args.new_rev;
2847 svn_pool_destroy(subpool);
2848 return SVN_NO_ERROR;
2851 /* Note: it is acceptable for this function to call back into
2852 public FS API interfaces because it does not itself use trails. */
2853 static svn_error_t *
2854 base_merge(const char **conflict_p,
2855 svn_fs_root_t *source_root,
2856 const char *source_path,
2857 svn_fs_root_t *target_root,
2858 const char *target_path,
2859 svn_fs_root_t *ancestor_root,
2860 const char *ancestor_path,
2863 dag_node_t *source, *ancestor;
2864 struct get_root_args get_root_args;
2865 struct merge_args merge_args;
2870 if (! target_root->is_txn_root)
2871 return SVN_FS__NOT_TXN(target_root);
2874 fs = ancestor_root->fs;
2875 if ((source_root->fs != fs) || (target_root->fs != fs))
2877 return svn_error_create
2878 (SVN_ERR_FS_CORRUPT, NULL,
2879 _("Bad merge; ancestor, source, and target not all in same fs"));
2882 /* ### kff todo: is there any compelling reason to get the nodes in
2883 one db transaction? Right now we don't; txn_body_get_root() gets
2884 one node at a time. This will probably need to change:
2886 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2887 > svn_fs_merge needs to be a single transaction, to protect it against
2888 > people deleting parents of nodes it's working on, etc.
2891 /* Get the ancestor node. */
2892 get_root_args.root = ancestor_root;
2893 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2895 ancestor = get_root_args.node;
2897 /* Get the source node. */
2898 get_root_args.root = source_root;
2899 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2901 source = get_root_args.node;
2903 /* Open a txn for the txn root into which we're merging. */
2904 SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2906 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2907 merge_args.source_node = source;
2908 merge_args.ancestor_node = ancestor;
2909 merge_args.txn = txn;
2910 merge_args.conflict = svn_stringbuf_create_empty(pool);
2911 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2914 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2915 *conflict_p = merge_args.conflict->data;
2916 return svn_error_trace(err);
2919 return SVN_NO_ERROR;
2923 struct rev_get_txn_id_args
2925 const char **txn_id;
2926 svn_revnum_t revision;
2930 static svn_error_t *
2931 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2933 struct rev_get_txn_id_args *args = baton;
2934 return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2935 args->revision, trail, trail->pool);
2940 svn_fs_base__deltify(svn_fs_t *fs,
2941 svn_revnum_t revision,
2944 svn_fs_root_t *root;
2946 struct rev_get_txn_id_args args;
2947 base_fs_data_t *bfd = fs->fsap_data;
2949 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2952 svn_revnum_t forward_delta_rev = 0;
2954 SVN_ERR(svn_fs_base__miscellaneous_get
2955 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2957 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2959 /* ### FIXME: Unnecessarily harsh requirement? (cmpilato). */
2960 if (revision <= forward_delta_rev)
2961 return svn_error_createf
2962 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2963 _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2966 SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2968 args.txn_id = &txn_id;
2969 args.revision = revision;
2970 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2973 return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2977 /* Modifying directories */
2980 struct make_dir_args
2982 svn_fs_root_t *root;
2987 static svn_error_t *
2988 txn_body_make_dir(void *baton,
2991 struct make_dir_args *args = baton;
2992 svn_fs_root_t *root = args->root;
2993 const char *path = args->path;
2994 parent_path_t *parent_path;
2995 dag_node_t *sub_dir;
2996 const char *txn_id = root->txn;
2998 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2999 txn_id, trail, trail->pool));
3001 /* If there's already a sub-directory by that name, complain. This
3002 also catches the case of trying to make a subdirectory named `/'. */
3003 if (parent_path->node)
3004 return SVN_FS__ALREADY_EXISTS(root, path);
3006 /* Check to see if some lock is 'reserving' a file-path or dir-path
3007 at that location, or even some child-path; if so, check that we
3009 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3011 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3012 trail, trail->pool));
3015 /* Create the subdirectory. */
3016 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3017 trail, trail->pool));
3018 SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
3019 parent_path->parent->node,
3020 parent_path_path(parent_path->parent,
3024 trail, trail->pool));
3026 /* Make a record of this modification in the changes table. */
3027 return add_change(root->fs, txn_id, path,
3028 svn_fs_base__dag_get_id(sub_dir),
3029 svn_fs_path_change_add, FALSE, FALSE,
3030 trail, trail->pool);
3034 static svn_error_t *
3035 base_make_dir(svn_fs_root_t *root,
3039 struct make_dir_args args;
3041 if (! root->is_txn_root)
3042 return SVN_FS__NOT_TXN(root);
3046 return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
3053 svn_fs_root_t *root;
3058 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
3059 basename of PATH is missing from its parent, that is, the final
3060 target of the deletion is missing. */
3061 static svn_error_t *
3062 txn_body_delete(void *baton,
3065 struct delete_args *args = baton;
3066 svn_fs_root_t *root = args->root;
3067 const char *path = args->path;
3068 parent_path_t *parent_path;
3069 const char *txn_id = root->txn;
3070 base_fs_data_t *bfd = trail->fs->fsap_data;
3072 if (! root->is_txn_root)
3073 return SVN_FS__NOT_TXN(root);
3075 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
3076 trail, trail->pool));
3078 /* We can't remove the root of the filesystem. */
3079 if (! parent_path->parent)
3080 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3081 _("The root directory cannot be deleted"));
3083 /* Check to see if path (or any child thereof) is locked; if so,
3084 check that we can use the existing lock(s). */
3085 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3087 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3088 trail, trail->pool));
3091 /* Make the parent directory mutable. */
3092 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3093 trail, trail->pool));
3095 /* Decrement mergeinfo counts on the parents of this node by the
3096 count it previously carried, if our format supports it. */
3097 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3099 apr_int64_t mergeinfo_count;
3100 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3102 trail, trail->pool));
3103 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3104 -mergeinfo_count, txn_id,
3105 trail, trail->pool));
3108 /* Do the deletion. */
3109 SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3111 txn_id, trail, trail->pool));
3114 /* Make a record of this modification in the changes table. */
3115 return add_change(root->fs, txn_id, path,
3116 svn_fs_base__dag_get_id(parent_path->node),
3117 svn_fs_path_change_delete, FALSE, FALSE, trail,
3122 static svn_error_t *
3123 base_delete_node(svn_fs_root_t *root,
3127 struct delete_args args;
3131 return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3138 svn_fs_root_t *from_root;
3139 const char *from_path;
3140 svn_fs_root_t *to_root;
3141 const char *to_path;
3142 svn_boolean_t preserve_history;
3146 static svn_error_t *
3147 txn_body_copy(void *baton,
3150 struct copy_args *args = baton;
3151 svn_fs_root_t *from_root = args->from_root;
3152 const char *from_path = args->from_path;
3153 svn_fs_root_t *to_root = args->to_root;
3154 const char *to_path = args->to_path;
3155 dag_node_t *from_node;
3156 parent_path_t *to_parent_path;
3157 const char *txn_id = to_root->txn;
3159 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3160 SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3162 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
3163 component does not exist, it's not that big a deal. We'll just
3165 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3166 open_path_last_optional, txn_id, trail, trail->pool));
3168 /* Check to see if to-path (or any child thereof) is locked, or at
3169 least 'reserved', whether it exists or not; if so, check that we
3170 can use the existing lock(s). */
3171 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3173 SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3174 trail, trail->pool));
3177 /* If the destination node already exists as the same node as the
3178 source (in other words, this operation would result in nothing
3179 happening at all), just do nothing an return successfully,
3180 proud that you saved yourself from a tiresome task. */
3181 if ((to_parent_path->node)
3182 && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3183 svn_fs_base__dag_get_id
3184 (to_parent_path->node))
3185 == svn_fs_node_unchanged))
3186 return SVN_NO_ERROR;
3188 if (! from_root->is_txn_root)
3190 svn_fs_path_change_kind_t kind;
3191 dag_node_t *new_node;
3192 apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3193 base_fs_data_t *bfd = trail->fs->fsap_data;
3195 /* If TO_PATH already existed prior to the copy, note that this
3196 operation is a replacement, not an addition. */
3197 if (to_parent_path->node)
3198 kind = svn_fs_path_change_replace;
3200 kind = svn_fs_path_change_add;
3202 /* Make sure the target node's parents are mutable. */
3203 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3204 to_path, trail, trail->pool));
3206 /* If this is a replacement operation, we need to know the old
3207 node's mergeinfo count. */
3208 if (to_parent_path->node)
3209 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3210 &old_mergeinfo_count,
3211 to_parent_path->node,
3212 trail, trail->pool));
3214 SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3215 to_parent_path->entry,
3217 args->preserve_history,
3219 from_path, txn_id, trail, trail->pool));
3221 /* Adjust the mergeinfo counts of the destination's parents if
3222 our format supports it. */
3223 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3225 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3229 SVN_ERR(adjust_parent_mergeinfo_counts
3230 (to_parent_path->parent,
3231 mergeinfo_count - old_mergeinfo_count,
3232 txn_id, trail, trail->pool));
3235 /* Make a record of this modification in the changes table. */
3236 SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3237 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3238 svn_fs_base__dag_get_id(new_node),
3239 kind, FALSE, FALSE, trail, trail->pool));
3243 /* See IZ Issue #436 */
3244 /* Copying from transaction roots not currently available.
3246 ### cmpilato todo someday: make this not so. :-) Note that
3247 when copying from mutable trees, you have to make sure that
3248 you aren't creating a cyclic graph filesystem, and a simple
3249 referencing operation won't cut it. Currently, we should not
3250 be able to reach this clause, and the interface reports that
3251 this only works from immutable trees anyway, but JimB has
3252 stated that this requirement need not be necessary in the
3255 SVN_ERR_MALFUNCTION();
3258 return SVN_NO_ERROR;
3262 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3263 Use POOL for temporary allocation only.
3264 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3265 static svn_error_t *
3266 fs_same_p(svn_boolean_t *same_p,
3271 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3272 return SVN_NO_ERROR;
3275 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3276 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
3277 the copies table. Perform temporary allocations in POOL. */
3278 static svn_error_t *
3279 copy_helper(svn_fs_root_t *from_root,
3280 const char *from_path,
3281 svn_fs_root_t *to_root,
3282 const char *to_path,
3283 svn_boolean_t preserve_history,
3286 struct copy_args args;
3287 svn_boolean_t same_p;
3289 /* Use an error check, not an assert, because even the caller cannot
3290 guarantee that a filesystem's UUID has not changed "on the fly". */
3291 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3293 return svn_error_createf
3294 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3295 _("Cannot copy between two different filesystems ('%s' and '%s')"),
3296 from_root->fs->path, to_root->fs->path);
3298 if (! to_root->is_txn_root)
3299 return SVN_FS__NOT_TXN(to_root);
3301 if (from_root->is_txn_root)
3302 return svn_error_create
3303 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3304 _("Copy from mutable tree not currently supported"));
3306 args.from_root = from_root;
3307 args.from_path = from_path;
3308 args.to_root = to_root;
3309 args.to_path = to_path;
3310 args.preserve_history = preserve_history;
3312 return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3316 static svn_error_t *
3317 base_copy(svn_fs_root_t *from_root,
3318 const char *from_path,
3319 svn_fs_root_t *to_root,
3320 const char *to_path,
3323 return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3327 static svn_error_t *
3328 base_revision_link(svn_fs_root_t *from_root,
3329 svn_fs_root_t *to_root,
3333 return copy_helper(from_root, path, to_root, path, FALSE, pool);
3337 struct copied_from_args
3339 svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */
3340 const char *path; /* Path for the node whose ancestry we seek. */
3342 svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */
3343 const char *result_path; /* Path, if any, of the ancestor. */
3345 apr_pool_t *pool; /* Allocate `result_path' here. */
3349 static svn_error_t *
3350 txn_body_copied_from(void *baton, trail_t *trail)
3352 struct copied_from_args *args = baton;
3353 const svn_fs_id_t *node_id, *pred_id;
3355 svn_fs_t *fs = args->root->fs;
3357 /* Clear the return variables. */
3358 args->result_path = NULL;
3359 args->result_rev = SVN_INVALID_REVNUM;
3361 /* Fetch the NODE in question. */
3362 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3363 node_id = svn_fs_base__dag_get_id(node);
3365 /* Check the node's predecessor-ID. If it doesn't have one, it
3367 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3368 trail, trail->pool));
3370 return SVN_NO_ERROR;
3372 /* If NODE's copy-ID is the same as that of its predecessor... */
3373 if (strcmp(svn_fs_base__id_copy_id(node_id),
3374 svn_fs_base__id_copy_id(pred_id)) != 0)
3376 /* ... then NODE was either the target of a copy operation,
3377 a copied subtree item. We examine the actual copy record
3378 to determine which is the case. */
3380 SVN_ERR(svn_fs_bdb__get_copy(©, fs,
3381 svn_fs_base__id_copy_id(node_id),
3382 trail, trail->pool));
3383 if ((copy->kind == copy_kind_real)
3384 && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3386 args->result_path = copy->src_path;
3387 SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3389 trail, trail->pool));
3392 return SVN_NO_ERROR;
3396 static svn_error_t *
3397 base_copied_from(svn_revnum_t *rev_p,
3398 const char **path_p,
3399 svn_fs_root_t *root,
3403 struct copied_from_args args;
3404 apr_pool_t *scratch_pool = svn_pool_create(pool);
3409 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3410 FALSE, scratch_pool));
3412 *rev_p = args.result_rev;
3413 *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3415 svn_pool_destroy(scratch_pool);
3416 return SVN_NO_ERROR;
3424 struct make_file_args
3426 svn_fs_root_t *root;
3431 static svn_error_t *
3432 txn_body_make_file(void *baton,
3435 struct make_file_args *args = baton;
3436 svn_fs_root_t *root = args->root;
3437 const char *path = args->path;
3438 parent_path_t *parent_path;
3440 const char *txn_id = root->txn;
3442 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3443 txn_id, trail, trail->pool));
3445 /* If there's already a file by that name, complain.
3446 This also catches the case of trying to make a file named `/'. */
3447 if (parent_path->node)
3448 return SVN_FS__ALREADY_EXISTS(root, path);
3450 /* Check to see if some lock is 'reserving' a file-path or dir-path
3451 at that location, or even some child-path; if so, check that we
3453 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3455 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3456 trail, trail->pool));
3459 /* Create the file. */
3460 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3461 trail, trail->pool));
3462 SVN_ERR(svn_fs_base__dag_make_file(&child,
3463 parent_path->parent->node,
3464 parent_path_path(parent_path->parent,
3468 trail, trail->pool));
3470 /* Make a record of this modification in the changes table. */
3471 return add_change(root->fs, txn_id, path,
3472 svn_fs_base__dag_get_id(child),
3473 svn_fs_path_change_add, TRUE, FALSE,
3474 trail, trail->pool);
3478 static svn_error_t *
3479 base_make_file(svn_fs_root_t *root,
3483 struct make_file_args args;
3487 return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3493 struct file_length_args
3495 svn_fs_root_t *root;
3497 svn_filesize_t length; /* OUT parameter */
3500 static svn_error_t *
3501 txn_body_file_length(void *baton,
3504 struct file_length_args *args = baton;
3507 /* First create a dag_node_t from the root/path pair. */
3508 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3510 /* Now fetch its length */
3511 return svn_fs_base__dag_file_length(&args->length, file,
3512 trail, trail->pool);
3515 static svn_error_t *
3516 base_file_length(svn_filesize_t *length_p,
3517 svn_fs_root_t *root,
3521 struct file_length_args args;
3525 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3528 *length_p = args.length;
3529 return SVN_NO_ERROR;
3533 struct file_checksum_args
3535 svn_fs_root_t *root;
3537 svn_checksum_kind_t kind;
3538 svn_checksum_t **checksum; /* OUT parameter */
3541 static svn_error_t *
3542 txn_body_file_checksum(void *baton,
3545 struct file_checksum_args *args = baton;
3548 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3550 return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3551 trail, trail->pool);
3554 static svn_error_t *
3555 base_file_checksum(svn_checksum_t **checksum,
3556 svn_checksum_kind_t kind,
3557 svn_fs_root_t *root,
3561 struct file_checksum_args args;
3562 apr_pool_t *scratch_pool = svn_pool_create(pool);
3567 args.checksum = checksum;
3568 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3569 FALSE, scratch_pool));
3570 *checksum = svn_checksum_dup(*checksum, pool);
3571 svn_pool_destroy(scratch_pool);
3572 return SVN_NO_ERROR;
3576 /* --- Machinery for svn_fs_file_contents() --- */
3579 /* Local baton type for txn_body_get_file_contents. */
3580 typedef struct file_contents_baton_t
3582 /* The file we want to read. */
3583 svn_fs_root_t *root;
3586 /* The dag_node that will be made from the above. */
3589 /* The pool in which `file_stream' (below) is allocated. */
3592 /* The readable file stream that will be made from the
3593 dag_node. (And returned to the caller.) */
3594 svn_stream_t *file_stream;
3596 } file_contents_baton_t;
3599 /* Main body of svn_fs_file_contents; converts a root/path pair into
3600 a readable file stream (in the context of a db txn). */
3601 static svn_error_t *
3602 txn_body_get_file_contents(void *baton, trail_t *trail)
3604 file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3606 /* First create a dag_node_t from the root/path pair. */
3607 SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3609 /* Then create a readable stream from the dag_node_t. */
3610 return svn_fs_base__dag_get_contents(&(fb->file_stream),
3611 fb->node, trail, fb->pool);
3616 static svn_error_t *
3617 base_file_contents(svn_stream_t **contents,
3618 svn_fs_root_t *root,
3622 file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3627 /* Create the readable stream in the context of a db txn. */
3628 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3631 *contents = fb->file_stream;
3632 return SVN_NO_ERROR;
3635 /* --- End machinery for svn_fs_file_contents() --- */
3639 /* --- Machinery for svn_fs_apply_textdelta() --- */
3642 /* Local baton type for all the helper functions below. */
3643 typedef struct txdelta_baton_t
3645 /* This is the custom-built window consumer given to us by the delta
3646 library; it uniquely knows how to read data from our designated
3647 "source" stream, interpret the window, and write data to our
3648 designated "target" stream (in this case, our repos file.) */
3649 svn_txdelta_window_handler_t interpreter;
3650 void *interpreter_baton;
3652 /* The original file info */
3653 svn_fs_root_t *root;
3656 /* Derived from the file info */
3659 svn_stream_t *source_stream;
3660 svn_stream_t *target_stream;
3661 svn_stream_t *string_stream;
3662 svn_stringbuf_t *target_string;
3664 /* Checksums for the base text against which a delta is to be
3665 applied, and for the resultant fulltext, respectively. Either or
3666 both may be null, in which case ignored. */
3667 svn_checksum_t *base_checksum;
3668 svn_checksum_t *result_checksum;
3670 /* Pool used by db txns */
3676 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3677 * This closes BATON->target_stream.
3679 * Note: If you're confused about how this function relates to another
3680 * of similar name, think of it this way:
3682 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3683 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3685 static svn_error_t *
3686 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3688 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3689 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3690 tb->result_checksum,
3692 trail, trail->pool));
3694 /* Make a record of this modification in the changes table. */
3695 return add_change(tb->root->fs, tb->root->txn, tb->path,
3696 svn_fs_base__dag_get_id(tb->node),
3697 svn_fs_path_change_modify, TRUE, FALSE, trail,
3702 /* ### see comment in window_consumer() regarding this function. */
3704 /* Helper function of generic type `svn_write_fn_t'. Implements a
3705 writable stream which appends to an svn_stringbuf_t. */
3706 static svn_error_t *
3707 write_to_string(void *baton, const char *data, apr_size_t *len)
3709 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3710 svn_stringbuf_appendbytes(tb->target_string, data, *len);
3711 return SVN_NO_ERROR;
3716 /* The main window handler returned by svn_fs_apply_textdelta. */
3717 static svn_error_t *
3718 window_consumer(svn_txdelta_window_t *window, void *baton)
3720 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3722 /* Send the window right through to the custom window interpreter.
3723 In theory, the interpreter will then write more data to
3724 cb->target_string. */
3725 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3727 /* ### the write_to_string() callback for the txdelta's output stream
3728 ### should be doing all the flush determination logic, not here.
3729 ### in a drastic case, a window could generate a LOT more than the
3730 ### maximum buffer size. we want to flush to the underlying target
3731 ### stream much sooner (e.g. also in a streamy fashion). also, by
3732 ### moving this logic inside the stream, the stream becomes nice
3733 ### and encapsulated: it holds all the logic about buffering and
3736 ### further: I believe the buffering should be removed from tree.c
3737 ### the buffering should go into the target_stream itself, which
3738 ### is defined by reps-string.c. Specifically, I think the
3739 ### rep_write_contents() function will handle the buffering and
3740 ### the spill to the underlying DB. by locating it there, then
3741 ### anybody who gets a writable stream for FS content can take
3742 ### advantage of the buffering capability. this will be important
3743 ### when we export an FS API function for writing a fulltext into
3744 ### the FS, rather than forcing that fulltext thru apply_textdelta.
3747 /* Check to see if we need to purge the portion of the contents that
3748 have been written thus far. */
3749 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3751 apr_size_t len = tb->target_string->len;
3752 SVN_ERR(svn_stream_write(tb->target_stream,
3753 tb->target_string->data,
3755 svn_stringbuf_setempty(tb->target_string);
3758 /* Is the window NULL? If so, we're done. */
3761 /* Close the internal-use stream. ### This used to be inside of
3762 txn_body_fulltext_finalize_edits(), but that invoked a nested
3763 Berkeley DB transaction -- scandalous! */
3764 SVN_ERR(svn_stream_close(tb->target_stream));
3766 /* Tell the dag subsystem that we're finished with our edits. */
3767 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3768 txn_body_txdelta_finalize_edits, tb,
3772 return SVN_NO_ERROR;
3776 static svn_error_t *
3777 txn_body_apply_textdelta(void *baton, trail_t *trail)
3779 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3780 parent_path_t *parent_path;
3781 const char *txn_id = tb->root->txn;
3783 /* Call open_path with no flags, as we want this to return an error
3784 if the node for which we are searching doesn't exist. */
3785 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3786 trail, trail->pool));
3788 /* Check to see if path is locked; if so, check that we can use it. */
3789 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3790 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3791 trail, trail->pool));
3793 /* Now, make sure this path is mutable. */
3794 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3795 trail, trail->pool));
3796 tb->node = parent_path->node;
3798 if (tb->base_checksum)
3800 svn_checksum_t *checksum;
3802 /* Until we finalize the node, its data_key points to the old
3803 contents, in other words, the base text. */
3804 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3805 tb->base_checksum->kind,
3806 tb->node, trail, trail->pool));
3807 /* TODO: This only compares checksums if they are the same kind, but
3808 we're calculating both SHA1 and MD5 checksums somewhere in
3809 reps-strings.c. Could we keep them both around somehow so this
3810 check could be more comprehensive? */
3811 if (!svn_checksum_match(tb->base_checksum, checksum))
3812 return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3814 _("Base checksum mismatch on '%s'"),
3818 /* Make a readable "source" stream out of the current contents of
3819 ROOT/PATH; obviously, this must done in the context of a db_txn.
3820 The stream is returned in tb->source_stream. */
3821 SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3822 tb->node, trail, tb->pool));
3824 /* Make a writable "target" stream */
3825 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3826 txn_id, trail, tb->pool));
3828 /* Make a writable "string" stream which writes data to
3829 tb->target_string. */
3830 tb->target_string = svn_stringbuf_create_empty(tb->pool);
3831 tb->string_stream = svn_stream_create(tb, tb->pool);
3832 svn_stream_set_write(tb->string_stream, write_to_string);
3834 /* Now, create a custom window handler that uses our two streams. */
3835 svn_txdelta_apply(tb->source_stream,
3841 &(tb->interpreter_baton));
3843 return SVN_NO_ERROR;
3847 static svn_error_t *
3848 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3849 void **contents_baton_p,
3850 svn_fs_root_t *root,
3852 svn_checksum_t *base_checksum,
3853 svn_checksum_t *result_checksum,
3856 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3861 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3862 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3864 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3867 *contents_p = window_consumer;
3868 *contents_baton_p = tb;
3869 return SVN_NO_ERROR;
3872 /* --- End machinery for svn_fs_apply_textdelta() --- */
3874 /* --- Machinery for svn_fs_apply_text() --- */
3876 /* Baton for svn_fs_apply_text(). */
3879 /* The original file info */
3880 svn_fs_root_t *root;
3883 /* Derived from the file info */
3886 /* The returned stream that will accept the file's new contents. */
3887 svn_stream_t *stream;
3889 /* The actual fs stream that the returned stream will write to. */
3890 svn_stream_t *file_stream;
3892 /* Checksum for the final fulltext written to the file. May
3893 be null, in which case ignored. */
3894 svn_checksum_t *result_checksum;
3896 /* Pool used by db txns */
3901 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3902 * fulltext data, not text deltas. Closes BATON->file_stream.
3904 * Note: If you're confused about how this function relates to another
3905 * of similar name, think of it this way:
3907 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3908 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3910 static svn_error_t *
3911 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3913 struct text_baton_t *tb = baton;
3914 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3915 tb->result_checksum,
3917 trail, trail->pool));
3919 /* Make a record of this modification in the changes table. */
3920 return add_change(tb->root->fs, tb->root->txn, tb->path,
3921 svn_fs_base__dag_get_id(tb->node),
3922 svn_fs_path_change_modify, TRUE, FALSE, trail,
3926 /* Write function for the publically returned stream. */
3927 static svn_error_t *
3928 text_stream_writer(void *baton,
3932 struct text_baton_t *tb = baton;
3934 /* Psst, here's some data. Pass it on to the -real- file stream. */
3935 return svn_stream_write(tb->file_stream, data, len);
3938 /* Close function for the publically returned stream. */
3939 static svn_error_t *
3940 text_stream_closer(void *baton)
3942 struct text_baton_t *tb = baton;
3944 /* Close the internal-use stream. ### This used to be inside of
3945 txn_body_fulltext_finalize_edits(), but that invoked a nested
3946 Berkeley DB transaction -- scandalous! */
3947 SVN_ERR(svn_stream_close(tb->file_stream));
3949 /* Need to tell fs that we're done sending text */
3950 return svn_fs_base__retry_txn(tb->root->fs,
3951 txn_body_fulltext_finalize_edits, tb,
3956 static svn_error_t *
3957 txn_body_apply_text(void *baton, trail_t *trail)
3959 struct text_baton_t *tb = baton;
3960 parent_path_t *parent_path;
3961 const char *txn_id = tb->root->txn;
3963 /* Call open_path with no flags, as we want this to return an error
3964 if the node for which we are searching doesn't exist. */
3965 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3966 trail, trail->pool));
3968 /* Check to see if path is locked; if so, check that we can use it. */
3969 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3970 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3971 trail, trail->pool));
3973 /* Now, make sure this path is mutable. */
3974 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3975 trail, trail->pool));
3976 tb->node = parent_path->node;
3978 /* Make a writable stream for replacing the file's text. */
3979 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3980 txn_id, trail, tb->pool));
3982 /* Create a 'returnable' stream which writes to the file_stream. */
3983 tb->stream = svn_stream_create(tb, tb->pool);
3984 svn_stream_set_write(tb->stream, text_stream_writer);
3985 svn_stream_set_close(tb->stream, text_stream_closer);
3987 return SVN_NO_ERROR;
3991 static svn_error_t *
3992 base_apply_text(svn_stream_t **contents_p,
3993 svn_fs_root_t *root,
3995 svn_checksum_t *result_checksum,
3998 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
4003 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
4005 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
4008 *contents_p = tb->stream;
4009 return SVN_NO_ERROR;
4012 /* --- End machinery for svn_fs_apply_text() --- */
4015 /* Note: we're sharing the `things_changed_args' struct with
4016 svn_fs_props_changed(). */
4018 static svn_error_t *
4019 txn_body_contents_changed(void *baton, trail_t *trail)
4021 struct things_changed_args *args = baton;
4022 dag_node_t *node1, *node2;
4023 svn_checksum_t *checksum1, *checksum2;
4024 svn_stream_t *stream1, *stream2;
4027 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
4028 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
4029 SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
4030 node1, node2, trail, trail->pool));
4032 /* Is there a potential false positive and do we want to correct it? */
4033 if (!args->strict || !*args->changed_p)
4034 return SVN_NO_ERROR;
4036 /* Different representations. They might still have equal contents. */
4038 /* Compare MD5 checksums. These should be readily accessible. */
4039 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5,
4040 node1, trail, trail->pool));
4041 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5,
4042 node2, trail, trail->pool));
4044 /* Different MD5 checksums -> different contents */
4045 if (!svn_checksum_match(checksum1, checksum2))
4046 return SVN_NO_ERROR;
4048 /* Paranoia. Compare SHA1 checksums because that's the level of
4049 confidence we require for e.g. the working copy. */
4050 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1,
4051 node1, trail, trail->pool));
4052 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1,
4053 node2, trail, trail->pool));
4055 /* Different SHA1 checksums -> different contents */
4056 if (checksum1 && checksum2)
4058 *args->changed_p = !svn_checksum_match(checksum1, checksum2);
4059 return SVN_NO_ERROR;
4062 /* SHA1 checksums are not available for very old reps / repositories. */
4063 SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool));
4064 SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool));
4065 SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool));
4067 /* Now, it's definitive. */
4068 *args->changed_p = !same;
4069 return SVN_NO_ERROR;
4073 /* Note: it is acceptable for this function to call back into
4074 top-level interfaces because it does not itself use trails. */
4075 static svn_error_t *
4076 base_contents_changed(svn_boolean_t *changed_p,
4077 svn_fs_root_t *root1,
4079 svn_fs_root_t *root2,
4081 svn_boolean_t strict,
4084 struct things_changed_args args;
4086 /* Check that roots are in the same fs. */
4087 if (root1->fs != root2->fs)
4088 return svn_error_create
4089 (SVN_ERR_FS_GENERAL, NULL,
4090 _("Cannot compare file contents between two different filesystems"));
4092 /* Check that both paths are files. */
4094 svn_node_kind_t kind;
4096 SVN_ERR(base_check_path(&kind, root1, path1, pool));
4097 if (kind != svn_node_file)
4098 return svn_error_createf
4099 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
4101 SVN_ERR(base_check_path(&kind, root2, path2, pool));
4102 if (kind != svn_node_file)
4103 return svn_error_createf
4104 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
4111 args.changed_p = changed_p;
4113 args.strict = strict;
4115 return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
4121 /* Public interface to computing file text deltas. */
4123 /* Note: it is acceptable for this function to call back into
4124 public FS API interfaces because it does not itself use trails. */
4125 static svn_error_t *
4126 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4127 svn_fs_root_t *source_root,
4128 const char *source_path,
4129 svn_fs_root_t *target_root,
4130 const char *target_path,
4133 svn_stream_t *source, *target;
4134 svn_txdelta_stream_t *delta_stream;
4136 /* Get read functions for the source file contents. */
4137 if (source_root && source_path)
4138 SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4140 source = svn_stream_empty(pool);
4142 /* Get read functions for the target file contents. */
4143 SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4145 /* Create a delta stream that turns the ancestor into the target. */
4146 svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4148 *stream_p = delta_stream;
4149 return SVN_NO_ERROR;
4154 /* Finding Changes */
4156 struct paths_changed_args
4158 apr_hash_t *changes;
4159 svn_fs_root_t *root;
4163 static svn_error_t *
4164 txn_body_paths_changed(void *baton,
4167 /* WARNING: This is called *without* the protection of a Berkeley DB
4168 transaction. If you modify this function, keep that in mind. */
4170 struct paths_changed_args *args = baton;
4172 svn_fs_t *fs = args->root->fs;
4174 /* Get the transaction ID from ROOT. */
4175 if (! args->root->is_txn_root)
4176 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4177 trail, trail->pool));
4179 txn_id = args->root->txn;
4181 return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4182 trail, trail->pool);
4186 static svn_error_t *
4187 base_paths_changed(apr_hash_t **changed_paths_p,
4188 svn_fs_root_t *root,
4191 struct paths_changed_args args;
4193 args.changes = NULL;
4194 SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4196 *changed_paths_p = args.changes;
4197 return SVN_NO_ERROR;
4202 /* Our coolio opaque history object. */
4203 typedef struct base_history_data_t
4205 /* filesystem object */
4208 /* path and revision of historical location */
4210 svn_revnum_t revision;
4212 /* internal-use hints about where to resume the history search. */
4213 const char *path_hint;
4214 svn_revnum_t rev_hint;
4216 /* FALSE until the first call to svn_fs_history_prev(). */
4217 svn_boolean_t is_interesting;
4218 } base_history_data_t;
4221 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4222 svn_revnum_t revision,
4223 svn_boolean_t is_interesting,
4224 const char *path_hint,
4225 svn_revnum_t rev_hint,
4229 static svn_error_t *
4230 base_node_history(svn_fs_history_t **history_p,
4231 svn_fs_root_t *root,
4233 apr_pool_t *result_pool,
4234 apr_pool_t *scratch_pool)
4236 svn_node_kind_t kind;
4238 /* We require a revision root. */
4239 if (root->is_txn_root)
4240 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4242 /* And we require that the path exist in the root. */
4243 SVN_ERR(base_check_path(&kind, root, path, scratch_pool));
4244 if (kind == svn_node_none)
4245 return SVN_FS__NOT_FOUND(root, path);
4247 /* Okay, all seems well. Build our history object and return it. */
4248 *history_p = assemble_history(root->fs,
4249 svn_fs__canonicalize_abspath(path,
4251 root->rev, FALSE, NULL,
4252 SVN_INVALID_REVNUM, result_pool);
4253 return SVN_NO_ERROR;
4257 /* Examine the PARENT_PATH structure chain to determine how copy IDs
4258 would be doled out in the event that PARENT_PATH was made mutable.
4259 Return the ID of the copy that last affected PARENT_PATH (and the
4260 COPY itself, if we've already fetched it).
4262 static svn_error_t *
4263 examine_copy_inheritance(const char **copy_id,
4266 parent_path_t *parent_path,
4270 /* The default response -- our current copy ID, and no fetched COPY. */
4271 *copy_id = svn_fs_base__id_copy_id
4272 (svn_fs_base__dag_get_id(parent_path->node));
4275 /* If we have no parent (we are looking at the root node), or if
4276 this node is supposed to inherit from itself, return that fact. */
4277 if (! parent_path->parent)
4278 return SVN_NO_ERROR;
4280 /* We could be a branch destination (which would answer our question
4281 altogether)! But then, again, we might just have been modified
4282 in this revision, so all bets are off. */
4283 if (parent_path->copy_inherit == copy_id_inherit_self)
4285 /* A copy ID of "0" means we've never been branched. Therefore,
4286 there are no copies relevant to our history. */
4287 if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4288 return SVN_NO_ERROR;
4290 /* Get the COPY record. If it was a real copy (not an implicit
4291 one), we have our answer. Otherwise, we fall through to the
4293 SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4294 if ((*copy)->kind != copy_kind_soft)
4295 return SVN_NO_ERROR;
4298 /* Otherwise, our answer is dependent upon our parent. */
4299 return examine_copy_inheritance(copy_id, copy, fs,
4300 parent_path->parent, trail, pool);
4304 struct history_prev_args
4306 svn_fs_history_t **prev_history_p;
4307 svn_fs_history_t *history;
4308 svn_boolean_t cross_copies;
4313 static svn_error_t *
4314 txn_body_history_prev(void *baton, trail_t *trail)
4316 struct history_prev_args *args = baton;
4317 svn_fs_history_t **prev_history = args->prev_history_p;
4318 svn_fs_history_t *history = args->history;
4319 base_history_data_t *bhd = history->fsap_data;
4320 const char *commit_path, *src_path, *path = bhd->path;
4321 svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4322 apr_pool_t *retpool = args->pool;
4323 svn_fs_t *fs = bhd->fs;
4324 parent_path_t *parent_path;
4326 svn_fs_root_t *root;
4327 const svn_fs_id_t *node_id;
4328 const char *end_copy_id = NULL;
4329 struct revision_root_args rr_args;
4330 svn_boolean_t reported = bhd->is_interesting;
4332 copy_t *copy = NULL;
4333 svn_boolean_t retry = FALSE;
4335 /* Initialize our return value. */
4336 *prev_history = NULL;
4338 /* If our last history report left us hints about where to pickup
4339 the chase, then our last report was on the destination of a
4340 copy. If we are crossing copies, start from those locations,
4341 otherwise, we're all done here. */
4342 if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4345 if (! args->cross_copies)
4346 return SVN_NO_ERROR;
4347 path = bhd->path_hint;
4348 revision = bhd->rev_hint;
4351 /* Construct a ROOT for the current revision. */
4352 rr_args.root_p = &root;
4353 rr_args.rev = revision;
4354 SVN_ERR(txn_body_revision_root(&rr_args, trail));
4356 /* Open PATH/REVISION, and get its node and a bunch of other
4358 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4360 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4361 trail, trail->pool));
4362 node = parent_path->node;
4363 node_id = svn_fs_base__dag_get_id(node);
4364 commit_path = svn_fs_base__dag_get_created_path(node);
4365 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4366 trail, trail->pool));
4368 /* The Subversion filesystem is written in such a way that a given
4369 line of history may have at most one interesting history point
4370 per filesystem revision. Either that node was edited (and
4371 possibly copied), or it was copied but not edited. And a copy
4372 source cannot be from the same revision as its destination. So,
4373 if our history revision matches its node's commit revision, we
4375 if (revision == commit_rev)
4379 /* ... we either have not yet reported on this revision (and
4380 need now to do so) ... */
4381 *prev_history = assemble_history(fs,
4382 apr_pstrdup(retpool, commit_path),
4383 commit_rev, TRUE, NULL,
4384 SVN_INVALID_REVNUM, retpool);
4385 return SVN_NO_ERROR;
4389 /* ... or we *have* reported on this revision, and must now
4390 progress toward this node's predecessor (unless there is
4391 no predecessor, in which case we're all done!). */
4392 const svn_fs_id_t *pred_id;
4394 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4395 trail, trail->pool));
4397 return SVN_NO_ERROR;
4399 /* Replace NODE and friends with the information from its
4401 SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4402 trail, trail->pool));
4403 node_id = svn_fs_base__dag_get_id(node);
4404 commit_path = svn_fs_base__dag_get_created_path(node);
4405 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4406 trail, trail->pool));
4410 /* Calculate a possibly relevant copy ID. */
4411 SVN_ERR(examine_copy_inheritance(&end_copy_id, ©, fs,
4412 parent_path, trail, trail->pool));
4414 /* Initialize some state variables. */
4416 src_rev = SVN_INVALID_REVNUM;
4417 dst_rev = SVN_INVALID_REVNUM;
4419 /* If our current copy ID (which is either the real copy ID of our
4420 node, or the last copy ID which would affect our node if it were
4421 to be made mutable) diffs at all from that of its predecessor
4422 (which is either a real predecessor, or is the node itself
4423 playing the predecessor role to an imaginary mutable successor),
4424 then we need to report a copy. */
4425 if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)
4427 const char *remainder;
4428 dag_node_t *dst_node;
4429 const char *copy_dst;
4431 /* Get the COPY record if we haven't already fetched it. */
4433 SVN_ERR(svn_fs_bdb__get_copy(©, fs, end_copy_id, trail,
4436 /* Figure out the destination path of the copy operation. */
4437 SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4438 copy->dst_noderev_id,
4439 trail, trail->pool));
4440 copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4442 /* If our current path was the very destination of the copy,
4443 then our new current path will be the copy source. If our
4444 current path was instead the *child* of the destination of
4445 the copy, then figure out its previous location by taking its
4446 path relative to the copy destination and appending that to
4447 the copy source. Finally, if our current path doesn't meet
4448 one of these other criteria ... ### for now just fallback to
4449 the old copy hunt algorithm. */
4450 remainder = svn_fspath__skip_ancestor(copy_dst, path);
4454 /* If we get here, then our current path is the destination
4455 of, or the child of the destination of, a copy. Fill
4456 in the return values and get outta here. */
4457 SVN_ERR(svn_fs_base__txn_get_revision
4458 (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4459 SVN_ERR(svn_fs_base__txn_get_revision
4461 svn_fs_base__id_txn_id(copy->dst_noderev_id),
4462 trail, trail->pool));
4463 src_path = svn_fspath__join(copy->src_path, remainder,
4465 if (copy->kind == copy_kind_soft)
4470 /* If we calculated a copy source path and revision, and the
4471 copy source revision doesn't pre-date a revision in which we
4472 *know* our node was modified, we'll make a 'copy-style' history
4474 if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4476 /* It's possible for us to find a copy location that is the same
4477 as the history point we've just reported. If that happens,
4478 we simply need to take another trip through this history
4480 if ((dst_rev == revision) && reported)
4483 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4485 src_path, src_rev, retpool);
4489 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4490 commit_rev, TRUE, NULL,
4491 SVN_INVALID_REVNUM, retpool);
4494 return SVN_NO_ERROR;
4498 static svn_error_t *
4499 base_history_prev(svn_fs_history_t **prev_history_p,
4500 svn_fs_history_t *history,
4501 svn_boolean_t cross_copies,
4502 apr_pool_t *result_pool,
4503 apr_pool_t *scratch_pool)
4505 svn_fs_history_t *prev_history = NULL;
4506 base_history_data_t *bhd = history->fsap_data;
4507 svn_fs_t *fs = bhd->fs;
4509 /* Special case: the root directory changes in every single
4510 revision, no exceptions. And, the root can't be the target (or
4511 child of a target -- duh) of a copy. So, if that's our path,
4512 then we need only decrement our revision by 1, and there you go. */
4513 if (strcmp(bhd->path, "/") == 0)
4515 if (! bhd->is_interesting)
4516 prev_history = assemble_history(fs, "/", bhd->revision,
4517 1, NULL, SVN_INVALID_REVNUM,
4519 else if (bhd->revision > 0)
4520 prev_history = assemble_history(fs, "/", bhd->revision - 1,
4521 1, NULL, SVN_INVALID_REVNUM,
4526 struct history_prev_args args;
4527 prev_history = history;
4531 /* Get a trail, and get to work. */
4533 args.prev_history_p = &prev_history;
4534 args.history = prev_history;
4535 args.cross_copies = cross_copies;
4536 args.pool = result_pool;
4537 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4538 FALSE, result_pool));
4541 bhd = prev_history->fsap_data;
4542 if (bhd->is_interesting)
4547 *prev_history_p = prev_history;
4548 return SVN_NO_ERROR;
4552 static svn_error_t *
4553 base_history_location(const char **path,
4554 svn_revnum_t *revision,
4555 svn_fs_history_t *history,
4558 base_history_data_t *bhd = history->fsap_data;
4560 *path = apr_pstrdup(pool, bhd->path);
4561 *revision = bhd->revision;
4562 return SVN_NO_ERROR;
4566 static history_vtable_t history_vtable = {
4568 base_history_location
4573 struct closest_copy_args
4575 svn_fs_root_t **root_p;
4576 const char **path_p;
4577 svn_fs_root_t *root;
4583 static svn_error_t *
4584 txn_body_closest_copy(void *baton, trail_t *trail)
4586 struct closest_copy_args *args = baton;
4587 svn_fs_root_t *root = args->root;
4588 const char *path = args->path;
4589 svn_fs_t *fs = root->fs;
4590 parent_path_t *parent_path;
4591 const svn_fs_id_t *node_id;
4592 const char *txn_id, *copy_id;
4593 copy_t *copy = NULL;
4594 svn_fs_root_t *copy_dst_root;
4595 dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4596 const char *copy_dst_path;
4597 svn_revnum_t copy_dst_rev, created_rev;
4600 *(args->path_p) = NULL;
4601 *(args->root_p) = NULL;
4603 /* Get the transaction ID associated with our root. */
4604 if (root->is_txn_root)
4607 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4608 trail, trail->pool));
4610 /* Open PATH in ROOT -- it must exist. */
4611 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4612 trail, trail->pool));
4613 node_id = svn_fs_base__dag_get_id(parent_path->node);
4615 /* Now, examine the copy inheritance rules in play should our path
4616 be made mutable in the future (if it isn't already). This will
4617 tell us about the youngest affecting copy. */
4618 SVN_ERR(examine_copy_inheritance(©_id, ©, fs, parent_path,
4619 trail, trail->pool));
4621 /* Easy out: if the copy ID is 0, there's nothing of interest here. */
4622 if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4623 return SVN_NO_ERROR;
4625 /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4627 SVN_ERR(svn_fs_bdb__get_copy(©, fs, copy_id, trail, trail->pool));
4629 /* Figure out the destination path and revision of the copy operation. */
4630 SVN_ERR(svn_fs_base__dag_get_node(©_dst_node, fs, copy->dst_noderev_id,
4631 trail, trail->pool));
4632 copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4633 SVN_ERR(svn_fs_base__dag_get_revision(©_dst_rev, copy_dst_node,
4634 trail, trail->pool));
4636 /* Turn that revision into a revision root. */
4637 SVN_ERR(svn_fs_base__dag_revision_root(©_dst_root_node, fs,
4638 copy_dst_rev, trail, args->pool));
4639 copy_dst_root = make_revision_root(fs, copy_dst_rev,
4640 copy_dst_root_node, args->pool);
4642 /* It is possible that this node was created from scratch at some
4643 revision between COPY_DST_REV and the transaction associated with
4644 our ROOT. Make sure that PATH exists as of COPY_DST_REV and is
4645 related to this node-rev. */
4646 err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4647 trail, trail->pool);
4650 if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4651 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4653 svn_error_clear(err);
4654 return SVN_NO_ERROR;
4656 return svn_error_trace(err);
4658 if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4659 || (! (svn_fs_base__id_check_related
4660 (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4662 return SVN_NO_ERROR;
4665 /* One final check must be done here. If you copy a directory and
4666 create a new entity somewhere beneath that directory in the same
4667 txn, then we can't claim that the copy affected the new entity.
4668 For example, if you do:
4671 create dir2/new-thing
4674 then dir2/new-thing was not affected by the copy of dir1 to dir2.
4675 We detect this situation by asking if PATH@COPY_DST_REV's
4676 created-rev is COPY_DST_REV, and that node-revision has no
4677 predecessors, then there is no relevant closest copy.
4679 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4680 trail, trail->pool));
4681 if (created_rev == copy_dst_rev)
4683 const svn_fs_id_t *pred_id;
4684 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4685 path_node_in_copy_dst,
4686 trail, trail->pool));
4688 return SVN_NO_ERROR;
4691 *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4692 *(args->root_p) = copy_dst_root;
4694 return SVN_NO_ERROR;
4698 static svn_error_t *
4699 base_closest_copy(svn_fs_root_t **root_p,
4700 const char **path_p,
4701 svn_fs_root_t *root,
4705 struct closest_copy_args args;
4706 svn_fs_t *fs = root->fs;
4707 svn_fs_root_t *closest_root = NULL;
4708 const char *closest_path = NULL;
4710 args.root_p = &closest_root;
4711 args.path_p = &closest_path;
4715 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4717 *root_p = closest_root;
4718 *path_p = closest_path;
4719 return SVN_NO_ERROR;
4723 /* Return a new history object (marked as "interesting") for PATH and
4724 REVISION, allocated in POOL, and with its members set to the values
4725 of the parameters provided. Note that PATH and PATH_HINT are not
4726 duped into POOL -- it is the responsibility of the caller to ensure
4727 that this happens. */
4728 static svn_fs_history_t *
4729 assemble_history(svn_fs_t *fs,
4731 svn_revnum_t revision,
4732 svn_boolean_t is_interesting,
4733 const char *path_hint,
4734 svn_revnum_t rev_hint,
4737 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4738 base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4740 bhd->revision = revision;
4741 bhd->is_interesting = is_interesting;
4742 bhd->path_hint = path_hint;
4743 bhd->rev_hint = rev_hint;
4745 history->vtable = &history_vtable;
4746 history->fsap_data = bhd;
4752 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4757 svn_revnum_t head_rev;
4758 svn_fs_root_t *root;
4759 dag_node_t *root_dir, *path_node;
4762 /* Get HEAD revision, */
4763 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4765 /* Then convert it into a root_t, */
4766 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4768 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4770 /* And get the dag_node for path in the root_t. */
4771 err = get_dag(&path_node, root, path, trail, pool);
4772 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4774 svn_error_clear(err);
4775 *kind = svn_node_none;
4776 return SVN_NO_ERROR;
4779 return svn_error_trace(err);
4781 *kind = svn_fs_base__dag_node_kind(path_node);
4782 return SVN_NO_ERROR;
4787 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4792 svn_revnum_t head_rev, created_rev;
4793 svn_fs_root_t *root;
4794 dag_node_t *root_dir, *path_node;
4797 /* Get HEAD revision, */
4798 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4800 /* Then convert it into a root_t, */
4801 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4803 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4805 /* And get the dag_node for path in the root_t. */
4806 err = get_dag(&path_node, root, path, trail, pool);
4807 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4809 svn_error_clear(err);
4810 *rev = SVN_INVALID_REVNUM;
4811 return SVN_NO_ERROR;
4814 return svn_error_trace(err);
4816 /* Find the created_rev of the dag_node. */
4817 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4821 return SVN_NO_ERROR;
4826 /*** Finding the Origin of a Line of History ***/
4828 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4829 represent the location at which PATH in FS was located immediately
4830 prior to REVISION iff there was a copy operation (to PATH or one of
4831 its parent directories) between that previous location and
4834 If there was no such copy operation in that portion of PATH's
4835 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4837 WARNING: Do *not* call this from inside a trail. */
4838 static svn_error_t *
4839 prev_location(const char **prev_path,
4840 svn_revnum_t *prev_rev,
4842 svn_fs_root_t *root,
4846 const char *copy_path, *copy_src_path, *remainder;
4847 svn_fs_root_t *copy_root;
4848 svn_revnum_t copy_src_rev;
4850 /* Ask about the most recent copy which affected PATH@REVISION. If
4851 there was no such copy, we're done. */
4852 SVN_ERR(base_closest_copy(©_root, ©_path, root, path, pool));
4855 *prev_rev = SVN_INVALID_REVNUM;
4857 return SVN_NO_ERROR;
4860 /* Ultimately, it's not the path of the closest copy's source that
4861 we care about -- it's our own path's location in the copy source
4862 revision. So we'll tack the relative path that expresses the
4863 difference between the copy destination and our path in the copy
4864 revision onto the copy source path to determine this information.
4866 In other words, if our path is "/branches/my-branch/foo/bar", and
4867 we know that the closest relevant copy was a copy of "/trunk" to
4868 "/branches/my-branch", then that relative path under the copy
4869 destination is "/foo/bar". Tacking that onto the copy source
4870 path tells us that our path was located at "/trunk/foo/bar"
4873 SVN_ERR(base_copied_from(©_src_rev, ©_src_path,
4874 copy_root, copy_path, pool));
4875 remainder = svn_fspath__skip_ancestor(copy_path, path);
4876 *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4877 *prev_rev = copy_src_rev;
4878 return SVN_NO_ERROR;
4882 struct id_created_rev_args {
4883 svn_revnum_t revision;
4884 const svn_fs_id_t *id;
4889 static svn_error_t *
4890 txn_body_id_created_rev(void *baton, trail_t *trail)
4892 struct id_created_rev_args *args = baton;
4895 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4896 trail, trail->pool));
4897 return svn_fs_base__dag_get_revision(&(args->revision), node,
4898 trail, trail->pool);
4902 struct get_set_node_origin_args {
4903 const svn_fs_id_t *origin_id;
4904 const char *node_id;
4908 static svn_error_t *
4909 txn_body_get_node_origin(void *baton, trail_t *trail)
4911 struct get_set_node_origin_args *args = baton;
4912 return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4913 args->node_id, trail, trail->pool);
4916 static svn_error_t *
4917 txn_body_set_node_origin(void *baton, trail_t *trail)
4919 struct get_set_node_origin_args *args = baton;
4920 return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4921 args->origin_id, trail, trail->pool);
4924 static svn_error_t *
4925 base_node_origin_rev(svn_revnum_t *revision,
4926 svn_fs_root_t *root,
4930 svn_fs_t *fs = root->fs;
4931 base_fs_data_t *bfd = fs->fsap_data;
4932 struct get_set_node_origin_args args;
4933 const svn_fs_id_t *origin_id = NULL;
4934 struct id_created_rev_args icr_args;
4936 /* Canonicalize the input path so that the path-math that
4937 prev_location() does below will work. */
4938 path = svn_fs__canonicalize_abspath(path, pool);
4940 /* Special-case the root node (for performance reasons) */
4941 if (strcmp(path, "/") == 0)
4944 return SVN_NO_ERROR;
4947 /* If we have support for the node-origins table, we'll try to use
4949 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4951 const svn_fs_id_t *id;
4954 SVN_ERR(base_node_id(&id, root, path, pool));
4955 args.node_id = svn_fs_base__id_node_id(id);
4956 err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4959 /* If we got a value for the origin node-revision-ID, that's
4960 great. If we didn't, that's sad but non-fatal -- we'll just
4961 figure it out the hard way, then record it so we don't have
4962 suffer again the next time. */
4965 origin_id = args.origin_id;
4967 else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4969 svn_error_clear(err);
4975 /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4978 svn_fs_root_t *curroot = root;
4979 apr_pool_t *subpool = svn_pool_create(pool);
4980 apr_pool_t *predidpool = svn_pool_create(pool);
4981 svn_stringbuf_t *lastpath =
4982 svn_stringbuf_create(path, pool);
4983 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4984 const svn_fs_id_t *pred_id;
4986 /* Walk the closest-copy chain back to the first copy in our history.
4988 NOTE: We merely *assume* that this is faster than walking the
4989 predecessor chain, because we *assume* that copies of parent
4990 directories happen less often than modifications to a given item. */
4993 svn_revnum_t currev;
4994 const char *curpath = lastpath->data;
4996 /* Get a root pointing to LASTREV. (The first time around,
4997 LASTREV is invalid, but that's cool because CURROOT is
4998 already initialized.) */
4999 if (SVN_IS_VALID_REVNUM(lastrev))
5000 SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
5003 /* Find the previous location using the closest-copy shortcut. */
5004 SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
5009 /* Update our LASTPATH and LASTREV variables (which survive
5011 svn_stringbuf_set(lastpath, curpath);
5015 /* Walk the predecessor links back to origin. */
5016 SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
5019 struct txn_pred_id_args pid_args;
5020 svn_pool_clear(subpool);
5021 pid_args.id = pred_id;
5022 pid_args.pred_id = NULL;
5023 pid_args.pool = subpool;
5024 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
5026 if (! pid_args.pred_id)
5028 svn_pool_clear(predidpool);
5029 pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
5032 /* Okay. PRED_ID should hold our origin ID now. */
5033 origin_id = svn_fs_base__id_copy(pred_id, pool);
5035 /* If our filesystem version supports it, let's remember this
5036 value from now on. */
5037 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
5039 args.origin_id = origin_id;
5040 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
5041 &args, TRUE, subpool));
5044 svn_pool_destroy(predidpool);
5045 svn_pool_destroy(subpool);
5048 /* Okay. We have an origin node-revision-ID. Let's get a created
5049 revision from it. */
5050 icr_args.id = origin_id;
5051 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
5053 *revision = icr_args.revision;
5054 return SVN_NO_ERROR;
5059 /* Mergeinfo Queries */
5062 /* Examine directory NODE's immediately children for mergeinfo.
5064 For those which have explicit mergeinfo, add their mergeinfo to
5065 RESULT_CATALOG (allocated in RESULT_CATALOG's pool).
5067 For those which don't, but sit atop trees which contain mergeinfo
5068 somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
5069 hash mapping dirent names to dag_node_t * objects, allocated
5070 from that hash's pool.
5072 For those which neither have explicit mergeinfo nor sit atop trees
5073 which contain mergeinfo, ignore them.
5075 Use TRAIL->pool for temporary allocations. */
5077 struct get_mergeinfo_data_and_entries_baton
5079 svn_mergeinfo_catalog_t result_catalog;
5080 apr_hash_t *children_atop_mergeinfo_trees;
5082 const char *node_path;
5085 static svn_error_t *
5086 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
5088 struct get_mergeinfo_data_and_entries_baton *args = baton;
5089 dag_node_t *node = args->node;
5090 apr_hash_t *entries;
5091 apr_hash_index_t *hi;
5092 apr_pool_t *iterpool = svn_pool_create(trail->pool);
5093 apr_pool_t *result_pool = apr_hash_pool_get(args->result_catalog);
5094 apr_pool_t *children_pool =
5095 apr_hash_pool_get(args->children_atop_mergeinfo_trees);
5097 SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
5099 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
5100 for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
5103 svn_fs_dirent_t *dirent;
5104 const svn_fs_id_t *child_id;
5105 dag_node_t *child_node;
5106 svn_boolean_t has_mergeinfo;
5107 apr_int64_t kid_count;
5109 svn_pool_clear(iterpool);
5110 apr_hash_this(hi, NULL, NULL, &val);
5112 child_id = dirent->id;
5114 /* Get the node for this child. */
5115 SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
5118 /* Query the child node's mergeinfo stats. */
5119 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
5123 /* If the child has mergeinfo, add it to the result catalog. */
5127 svn_mergeinfo_t child_mergeinfo;
5131 SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5133 pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5136 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5138 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5139 _("Node-revision '%s' claims to have "
5140 "mergeinfo but doesn't"),
5143 /* Issue #3896: If syntactically invalid mergeinfo is present on
5144 CHILD_NODE then treat it as if no mergeinfo is present rather
5145 than raising a parse error. */
5146 err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5150 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5151 svn_error_clear(err);
5153 return svn_error_trace(err);
5157 svn_hash_sets(args->result_catalog,
5158 svn_fspath__join(args->node_path, dirent->name,
5164 /* If the child has descendants with mergeinfo -- that is, if
5165 the count of descendants beneath it carrying mergeinfo, not
5166 including itself, is non-zero -- then add it to the
5167 children_atop_mergeinfo_trees hash to be crawled later. */
5168 if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5170 if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5172 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5174 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5175 _("Node-revision '%s' claims to sit "
5176 "atop a tree containing mergeinfo "
5177 "but is not a directory"),
5180 svn_hash_sets(args->children_atop_mergeinfo_trees,
5181 apr_pstrdup(children_pool, dirent->name),
5182 svn_fs_base__dag_dup(child_node, children_pool));
5186 svn_pool_destroy(iterpool);
5187 return SVN_NO_ERROR;
5190 static svn_error_t *
5191 crawl_directory_for_mergeinfo(svn_fs_t *fs,
5193 const char *node_path,
5194 svn_mergeinfo_catalog_t result_catalog,
5197 struct get_mergeinfo_data_and_entries_baton gmdae_args;
5198 apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5199 apr_hash_index_t *hi;
5200 apr_pool_t *iterpool;
5202 /* Add mergeinfo for immediate children that have it, and fetch
5203 immediate children that *don't* have it but sit atop trees that do. */
5204 gmdae_args.result_catalog = result_catalog;
5205 gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5206 gmdae_args.node = node;
5207 gmdae_args.node_path = node_path;
5208 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5209 &gmdae_args, FALSE, pool));
5211 /* If no children sit atop trees with mergeinfo, we're done.
5212 Otherwise, recurse on those children. */
5214 if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5215 return SVN_NO_ERROR;
5217 iterpool = svn_pool_create(pool);
5218 for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5220 hi = apr_hash_next(hi))
5224 svn_pool_clear(iterpool);
5225 apr_hash_this(hi, &key, NULL, &val);
5226 SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5227 svn_fspath__join(node_path, key,
5229 result_catalog, iterpool));
5231 svn_pool_destroy(iterpool);
5232 return SVN_NO_ERROR;
5236 /* Calculate the mergeinfo for PATH under revision ROOT using
5237 inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to
5238 NULL if there is none. Results are allocated in POOL; TRAIL->pool
5239 is used for temporary allocations. */
5241 struct get_mergeinfo_for_path_baton
5243 svn_mergeinfo_t *mergeinfo;
5244 svn_fs_root_t *root;
5246 svn_mergeinfo_inheritance_t inherit;
5247 svn_boolean_t adjust_inherited_mergeinfo;
5251 static svn_error_t *
5252 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5254 struct get_mergeinfo_for_path_baton *args = baton;
5255 parent_path_t *parent_path, *nearest_ancestor;
5256 apr_hash_t *proplist;
5257 svn_string_t *mergeinfo_string;
5258 apr_pool_t *iterpool;
5259 dag_node_t *node = NULL;
5261 *(args->mergeinfo) = NULL;
5263 SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5264 NULL, trail, trail->pool));
5266 /* Init the nearest ancestor. */
5267 nearest_ancestor = parent_path;
5268 if (args->inherit == svn_mergeinfo_nearest_ancestor)
5270 if (! parent_path->parent)
5271 return SVN_NO_ERROR;
5272 nearest_ancestor = parent_path->parent;
5275 iterpool = svn_pool_create(trail->pool);
5278 svn_boolean_t has_mergeinfo;
5281 svn_pool_clear(iterpool);
5283 node = nearest_ancestor->node;
5284 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5285 node, trail, iterpool));
5289 /* No need to loop if we're looking for explicit mergeinfo. */
5290 if (args->inherit == svn_mergeinfo_explicit)
5292 svn_pool_destroy(iterpool);
5293 return SVN_NO_ERROR;
5296 nearest_ancestor = nearest_ancestor->parent;
5298 /* Run out? There's no mergeinfo. */
5299 if (! nearest_ancestor)
5301 svn_pool_destroy(iterpool);
5302 return SVN_NO_ERROR;
5305 svn_pool_destroy(iterpool);
5307 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5308 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5309 if (! mergeinfo_string)
5311 svn_string_t *id_str =
5312 svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5313 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5314 _("Node-revision '%s' claims to have "
5315 "mergeinfo but doesn't"), id_str->data);
5318 /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5320 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5321 treat it as if no mergeinfo is present rather than raising a parse
5323 svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5324 mergeinfo_string->data,
5328 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5330 svn_error_clear(err);
5332 args->mergeinfo = NULL;
5334 return svn_error_trace(err);
5338 /* If our nearest ancestor is the very path we inquired about, we
5339 can return the mergeinfo results directly. Otherwise, we're
5340 inheriting the mergeinfo, so we need to a) remove non-inheritable
5341 ranges and b) telescope the merged-from paths. */
5342 if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5344 svn_mergeinfo_t tmp_mergeinfo;
5346 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5347 NULL, SVN_INVALID_REVNUM,
5348 SVN_INVALID_REVNUM, TRUE,
5349 trail->pool, trail->pool));
5350 SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5351 parent_path_relpath(
5352 parent_path, nearest_ancestor,
5357 return SVN_NO_ERROR;
5360 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5361 and query its mergeinfo stats, setting HAS_MERGEINFO and
5362 CHILD_MERGEINFO_COUNT appropriately. */
5364 struct get_node_mergeinfo_stats_baton
5367 svn_boolean_t has_mergeinfo;
5368 apr_int64_t child_mergeinfo_count;
5369 svn_fs_root_t *root;
5373 static svn_error_t *
5374 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5376 struct get_node_mergeinfo_stats_baton *args = baton;
5378 SVN_ERR(get_dag(&(args->node), args->root, args->path,
5379 trail, trail->pool));
5380 return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5381 &(args->child_mergeinfo_count),
5387 /* Get the mergeinfo for a set of paths, returned in
5388 *MERGEINFO_CATALOG. Returned values are allocated in POOL, while
5389 temporary values are allocated in a sub-pool. */
5390 static svn_error_t *
5391 get_mergeinfos_for_paths(svn_fs_root_t *root,
5392 svn_mergeinfo_catalog_t *mergeinfo_catalog,
5393 const apr_array_header_t *paths,
5394 svn_mergeinfo_inheritance_t inherit,
5395 svn_boolean_t include_descendants,
5396 svn_boolean_t adjust_inherited_mergeinfo,
5397 apr_pool_t *result_pool,
5398 apr_pool_t *scratch_pool)
5400 svn_mergeinfo_catalog_t result_catalog = apr_hash_make(result_pool);
5401 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5404 for (i = 0; i < paths->nelts; i++)
5406 svn_mergeinfo_t path_mergeinfo;
5407 struct get_mergeinfo_for_path_baton gmfp_args;
5408 const char *path = APR_ARRAY_IDX(paths, i, const char *);
5410 svn_pool_clear(iterpool);
5412 path = svn_fs__canonicalize_abspath(path, iterpool);
5414 /* Get the mergeinfo for PATH itself. */
5415 gmfp_args.mergeinfo = &path_mergeinfo;
5416 gmfp_args.root = root;
5417 gmfp_args.path = path;
5418 gmfp_args.inherit = inherit;
5419 gmfp_args.pool = result_pool;
5420 gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5421 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5422 txn_body_get_mergeinfo_for_path,
5423 &gmfp_args, FALSE, iterpool));
5425 svn_hash_sets(result_catalog, apr_pstrdup(result_pool, path),
5428 /* If we're including descendants, do so. */
5429 if (include_descendants)
5431 svn_boolean_t do_crawl;
5432 struct get_node_mergeinfo_stats_baton gnms_args;
5434 /* Query the node and its mergeinfo stats. */
5435 gnms_args.root = root;
5436 gnms_args.path = path;
5437 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5438 txn_body_get_node_mergeinfo_stats,
5439 &gnms_args, FALSE, iterpool));
5441 /* Determine if there's anything worth crawling here. */
5442 if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5445 do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5446 || ((gnms_args.child_mergeinfo_count == 1)
5447 && (! gnms_args.has_mergeinfo)));
5449 /* If it's worth crawling, crawl. */
5451 SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5452 path, result_catalog,
5456 svn_pool_destroy(iterpool);
5458 *mergeinfo_catalog = result_catalog;
5459 return SVN_NO_ERROR;
5463 /* Implements svn_fs_get_mergeinfo. */
5464 static svn_error_t *
5465 base_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
5466 svn_fs_root_t *root,
5467 const apr_array_header_t *paths,
5468 svn_mergeinfo_inheritance_t inherit,
5469 svn_boolean_t include_descendants,
5470 svn_boolean_t adjust_inherited_mergeinfo,
5471 apr_pool_t *result_pool,
5472 apr_pool_t *scratch_pool)
5474 /* Verify that our filesystem version supports mergeinfo stuff. */
5475 SVN_ERR(svn_fs_base__test_required_feature_format
5476 (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5478 /* We require a revision root. */
5479 if (root->is_txn_root)
5480 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5482 /* Retrieve a path -> mergeinfo mapping. */
5483 return get_mergeinfos_for_paths(root, catalog, paths,
5484 inherit, include_descendants,
5485 adjust_inherited_mergeinfo,
5486 result_pool, scratch_pool);
5491 /* Creating root objects. */
5494 static root_vtable_t root_vtable = {
5500 base_node_created_rev,
5501 base_node_origin_rev,
5502 base_node_created_path,
5510 base_node_has_props,
5511 base_change_node_prop,
5514 base_dir_optimal_order,
5521 base_apply_textdelta,
5523 base_contents_changed,
5524 base_get_file_delta_stream,
5530 /* Construct a new root object in FS, allocated from POOL. */
5531 static svn_fs_root_t *
5532 make_root(svn_fs_t *fs,
5535 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5536 base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5541 /* Init the node ID cache. */
5542 brd->node_cache = apr_hash_make(pool);
5543 brd->node_cache_idx = 0;
5544 root->vtable = &root_vtable;
5545 root->fsap_data = brd;
5551 /* Construct a root object referring to the root of REVISION in FS,
5552 whose root directory is ROOT_DIR. Create the new root in POOL. */
5553 static svn_fs_root_t *
5554 make_revision_root(svn_fs_t *fs,
5556 dag_node_t *root_dir,
5559 svn_fs_root_t *root = make_root(fs, pool);
5560 base_root_data_t *brd = root->fsap_data;
5562 root->is_txn_root = FALSE;
5564 brd->root_dir = root_dir;
5570 /* Construct a root object referring to the root of the transaction
5571 named TXN and based on revision BASE_REV in FS. FLAGS represents
5572 the behavior of the transaction. Create the new root in POOL. */
5573 static svn_fs_root_t *
5574 make_txn_root(svn_fs_t *fs,
5576 svn_revnum_t base_rev,
5580 svn_fs_root_t *root = make_root(fs, pool);
5581 root->is_txn_root = TRUE;
5582 root->txn = apr_pstrdup(root->pool, txn);
5583 root->txn_flags = flags;
5584 root->rev = base_rev;