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"
71 #include "private/svn_sorts_private.h"
74 /* ### I believe this constant will become internal to reps-strings.c.
75 ### see the comment in window_consumer() for more information. */
77 /* ### the comment also seems to need tweaking: the log file stuff
78 ### is no longer an issue... */
79 /* Data written to the filesystem through the svn_fs_apply_textdelta()
80 interface is cached in memory until the end of the data stream, or
81 until a size trigger is hit. Define that trigger here (in bytes).
82 Setting the value to 0 will result in no filesystem buffering at
83 all. The value only really matters when dealing with file contents
84 bigger than the value itself. Above that point, large values here
85 allow the filesystem to buffer more data in memory before flushing
86 to the database, which increases memory usage but greatly decreases
87 the amount of disk access (and log-file generation) in database.
88 Smaller values will limit your overall memory consumption, but can
89 drastically hurt throughput by necessitating more write operations
90 to the database (which also generates more log-files). */
91 #define WRITE_BUFFER_SIZE 512000
93 /* The maximum number of cache items to maintain in the node cache. */
94 #define NODE_CACHE_MAX_KEYS 32
98 /* The root structure. */
100 /* Structure for svn_fs_root_t's node_cache hash values. */
101 struct dag_node_cache_t
103 dag_node_t *node; /* NODE to be cached. */
104 int idx; /* Index into the keys array for this cache item's key. */
105 apr_pool_t *pool; /* Pool in which NODE is allocated. */
109 typedef struct base_root_data_t
112 /* For revision roots, this is a dag node for the revision's root
113 directory. For transaction roots, we open the root directory
114 afresh every time, since the root may have been cloned, or
115 the transaction may have disappeared altogether. */
116 dag_node_t *root_dir;
118 /* Cache structures, for mapping const char * PATH to const
119 struct dag_node_cache_t * structures.
121 ### Currently this is only used for revision roots. To be safe
122 for transaction roots, you must have the guarantee that there is
123 never more than a single transaction root per Subversion
124 transaction ever open at a given time -- having two roots open to
125 the same Subversion transaction would be a request for pain.
126 Also, you have to ensure that if a 'make_path_mutable()' fails for
127 any reason, you don't leave cached nodes for the portion of that
128 function that succeeded. In other words, this cache must never,
130 apr_hash_t *node_cache;
131 const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
136 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
137 dag_node_t *root_dir,
140 static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
141 svn_revnum_t base_rev, apr_uint32_t flags,
145 /*** Node Caching in the Roots. ***/
147 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
150 dag_node_cache_get(svn_fs_root_t *root,
154 base_root_data_t *brd = root->fsap_data;
155 struct dag_node_cache_t *cache_item;
157 /* Assert valid input. */
158 assert(*path == '/');
160 /* Only allow revision roots. */
161 if (root->is_txn_root)
164 /* Look in the cache for our desired item. */
165 cache_item = svn_hash_gets(brd->node_cache, path);
167 return svn_fs_base__dag_dup(cache_item->node, pool);
173 /* Add the NODE for PATH to ROOT's node cache. Callers should *NOT*
174 call this unless they are adding a currently un-cached item to the
175 cache, or are replacing the NODE for PATH with a new (different)
178 dag_node_cache_set(svn_fs_root_t *root,
182 base_root_data_t *brd = root->fsap_data;
183 const char *cache_path;
184 apr_pool_t *cache_pool;
185 struct dag_node_cache_t *cache_item;
186 int num_keys = apr_hash_count(brd->node_cache);
188 /* What? No POOL passed to this function?
190 To ensure that our cache values live as long as the svn_fs_root_t
191 in which they are ultimately stored, and to allow us to free()
192 them individually without harming the rest, they are each
193 allocated from a subpool of ROOT's pool. We'll keep one subpool
194 around for each cache slot -- as we start expiring stuff
195 to make room for more entries, we'll re-use the expired thing's
198 /* Assert valid input and state. */
199 assert(*path == '/');
200 assert((brd->node_cache_idx <= num_keys)
201 && (num_keys <= NODE_CACHE_MAX_KEYS));
203 /* Only allow revision roots. */
204 if (root->is_txn_root)
207 /* Special case: the caller wants us to replace an existing cached
208 node with a new one. If the callers aren't mindless, this should
209 only happen when a node is made mutable under a transaction
210 root, and that only happens once under that root. So, we'll be a
211 little bit sloppy here, and count on callers doing the right
213 cache_item = svn_hash_gets(brd->node_cache, path);
216 /* ### This section is somehow broken. I don't know how, but it
217 ### is. And I don't want to spend any more time on it. So,
218 ### callers, use only revision root and don't try to update
219 ### an already-cached thing. -- cmpilato */
220 SVN_ERR_MALFUNCTION_NO_RETURN();
223 int cache_index = cache_item->idx;
224 cache_path = brd->node_cache_keys[cache_index];
225 cache_pool = cache_item->pool;
226 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
228 /* Now, move the cache key reference to the end of the keys in
229 the keys array (unless it's already at the end). ### Yes,
230 it's a memmove(), but we're not talking about pages of memory
232 if (cache_index != (num_keys - 1))
234 int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
235 memmove(brd->node_cache_keys + cache_index,
236 brd->node_cache_keys + cache_index + 1,
237 move_num * sizeof(const char *));
238 cache_index = num_keys - 1;
239 brd->node_cache_keys[cache_index] = cache_path;
242 /* Advance the cache pointers. */
243 cache_item->idx = cache_index;
244 brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
249 /* We're adding a new cache item. First, see if we have room for it
250 (otherwise, make some room). */
251 if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
253 /* No room. Expire the oldest thing. */
254 cache_path = brd->node_cache_keys[brd->node_cache_idx];
255 cache_item = svn_hash_gets(brd->node_cache, cache_path);
256 svn_hash_sets(brd->node_cache, cache_path, NULL);
257 cache_pool = cache_item->pool;
258 svn_pool_clear(cache_pool);
262 cache_pool = svn_pool_create(root->pool);
265 /* Make the cache item, allocated in its own pool. */
266 cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
267 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
268 cache_item->idx = brd->node_cache_idx;
269 cache_item->pool = cache_pool;
271 /* Now add it to the cache. */
272 cache_path = apr_pstrdup(cache_pool, path);
273 svn_hash_sets(brd->node_cache, cache_path, cache_item);
274 brd->node_cache_keys[brd->node_cache_idx] = cache_path;
276 /* Advance the cache pointer. */
277 brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
283 /* Creating transaction and revision root nodes. */
287 svn_fs_root_t **root_p;
293 txn_body_txn_root(void *baton,
296 struct txn_root_args *args = baton;
297 svn_fs_root_t **root_p = args->root_p;
298 svn_fs_txn_t *txn = args->txn;
299 svn_fs_t *fs = txn->fs;
300 const char *svn_txn_id = txn->id;
301 const svn_fs_id_t *root_id, *base_root_id;
303 apr_hash_t *txnprops;
304 apr_uint32_t flags = 0;
306 /* Verify that the transaction actually exists. */
307 SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
308 svn_txn_id, trail, trail->pool));
310 /* Look for special txn props that represent the 'flags' behavior of
312 SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
313 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
314 flags |= SVN_FS_TXN_CHECK_OOD;
316 if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
317 flags |= SVN_FS_TXN_CHECK_LOCKS;
319 root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
327 svn_fs_base__txn_root(svn_fs_root_t **root_p,
332 struct txn_root_args args;
336 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args,
344 struct revision_root_args
346 svn_fs_root_t **root_p;
352 txn_body_revision_root(void *baton,
355 struct revision_root_args *args = baton;
356 dag_node_t *root_dir;
359 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
360 trail, trail->pool));
361 root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
363 *args->root_p = root;
369 svn_fs_base__revision_root(svn_fs_root_t **root_p,
374 struct revision_root_args args;
377 SVN_ERR(svn_fs__check_fs(fs, TRUE));
381 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args,
390 /* Getting dag nodes for roots. */
393 /* Set *NODE_P to a freshly opened dag node referring to the root
394 directory of ROOT, as part of TRAIL. */
396 root_node(dag_node_t **node_p,
401 base_root_data_t *brd = root->fsap_data;
403 if (! root->is_txn_root)
405 /* It's a revision root, so we already have its root directory
407 *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
412 /* It's a transaction root. Open a fresh copy. */
413 return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
419 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
420 necessary, as part of TRAIL. ROOT must be a transaction root. Use
421 ERROR_PATH in error messages. */
423 mutable_root_node(dag_node_t **node_p,
425 const char *error_path,
429 if (root->is_txn_root)
430 return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
433 /* If it's not a transaction root, we can't change its contents. */
434 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
439 /* Traversing directory paths. */
441 typedef enum copy_id_inherit_t
443 copy_id_inherit_unknown = 0,
444 copy_id_inherit_self,
445 copy_id_inherit_parent,
450 /* A linked list representing the path from a node up to a root
451 directory. We use this for cloning, and for operations that need
452 to deal with both a node and its parent directory. For example, a
453 `delete' operation needs to know that the node actually exists, but
454 also needs to change the parent directory. */
455 typedef struct parent_path_t
458 /* A node along the path. This could be the final node, one of its
459 parents, or the root. Every parent path ends with an element for
460 the root directory. */
463 /* The name NODE has in its parent directory. This is zero for the
464 root directory, which (obviously) has no name in its parent. */
467 /* The parent of NODE, or zero if NODE is the root directory. */
468 struct parent_path_t *parent;
470 /* The copy ID inheritance style. */
471 copy_id_inherit_t copy_inherit;
473 /* If copy ID inheritance style is copy_id_inherit_new, this is the
474 path which should be implicitly copied; otherwise, this is NULL. */
475 const char *copy_src_path;
480 /* Return the FS path for the parent path chain object PARENT_PATH,
481 allocated in POOL. */
483 parent_path_path(parent_path_t *parent_path,
486 const char *path_so_far = "/";
487 if (parent_path->parent)
488 path_so_far = parent_path_path(parent_path->parent, pool);
489 return parent_path->entry
490 ? svn_fspath__join(path_so_far, parent_path->entry, pool)
495 /* Return the FS path for the parent path chain object CHILD relative
496 to its ANCESTOR in the same chain, allocated in POOL. */
498 parent_path_relpath(parent_path_t *child,
499 parent_path_t *ancestor,
502 const char *path_so_far = "";
503 parent_path_t *this_node = child;
504 while (this_node != ancestor)
506 assert(this_node != NULL);
507 path_so_far = svn_relpath_join(this_node->entry, path_so_far, pool);
508 this_node = this_node->parent;
514 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
515 event that immutable node CHILD in FS needs to be made mutable. If
516 the inheritance method is copy_id_inherit_new, also return a
517 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
518 for that path). CHILD must have a parent (it cannot be the root
519 node). TXN_ID is the transaction in which these items might be
522 get_copy_inheritance(copy_id_inherit_t *inherit_p,
523 const char **copy_src_path,
525 parent_path_t *child,
530 const svn_fs_id_t *child_id, *parent_id;
531 const char *child_copy_id, *parent_copy_id;
532 const char *id_path = NULL;
534 SVN_ERR_ASSERT(child && child->parent && txn_id);
536 /* Initialize our return variables (default: self-inheritance). */
537 *inherit_p = copy_id_inherit_self;
538 *copy_src_path = NULL;
540 /* Initialize some convenience variables. */
541 child_id = svn_fs_base__dag_get_id(child->node);
542 parent_id = svn_fs_base__dag_get_id(child->parent->node);
543 child_copy_id = svn_fs_base__id_copy_id(child_id);
544 parent_copy_id = svn_fs_base__id_copy_id(parent_id);
546 /* Easy out: if this child is already mutable, we have nothing to do. */
547 if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
550 /* If the child and its parent are on the same branch, then the
551 child will inherit the copy ID of its parent when made mutable.
552 This is trivially detectable when the child and its parent have
553 the same copy ID. But that's not the sole indicator of
554 same-branchness. It might be the case that the parent was the
555 result of a copy, but the child has not yet been cloned for
556 mutability since that copy. Detection of this latter case
557 basically means making sure the copy IDs don't differ for some
558 other reason, such as that the child was the direct target of the
559 copy whose ID it has. There is a special case here, too -- if
560 the child's copy ID is the special ID "0", it can't have been the
561 target of any copy, and therefore must be on the same branch as
563 if ((strcmp(child_copy_id, "0") == 0)
564 || (strcmp(child_copy_id, parent_copy_id) == 0))
566 *inherit_p = copy_id_inherit_parent;
572 SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool));
573 if ( svn_fs_base__id_compare(copy->dst_noderev_id, child_id)
574 == svn_fs_node_unrelated)
576 *inherit_p = copy_id_inherit_parent;
581 /* If we get here, the child and its parent are not on speaking
582 terms -- there will be no parental inheritance handed down in
583 *this* generation. */
585 /* If the child was created at a different path than the one we are
586 expecting its clone to live, one of its parents must have been
587 created via a copy since the child was created. The child isn't
588 on the same branch as its parent (we caught those cases early);
589 it can't keep its current copy ID because there's been an
590 affecting copy (its clone won't be on the same branch as the
591 child is). That leaves only one course of action -- to assign
592 the child a brand new "soft" copy ID. */
593 id_path = svn_fs_base__dag_get_created_path(child->node);
594 if (strcmp(id_path, parent_path_path(child, pool)) != 0)
596 *inherit_p = copy_id_inherit_new;
597 *copy_src_path = id_path;
601 /* The node gets to keep its own ID. */
606 /* Allocate a new parent_path_t node from POOL, referring to NODE,
607 ENTRY, PARENT, and COPY_ID. */
608 static parent_path_t *
609 make_parent_path(dag_node_t *node,
611 parent_path_t *parent,
614 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
615 parent_path->node = node;
616 parent_path->entry = entry;
617 parent_path->parent = parent;
618 parent_path->copy_inherit = copy_id_inherit_unknown;
619 parent_path->copy_src_path = NULL;
624 /* Flags for open_path. */
625 typedef enum open_path_flags_t {
627 /* The last component of the PATH need not exist. (All parent
628 directories must exist, as usual.) If the last component doesn't
629 exist, simply leave the `node' member of the bottom parent_path
631 open_path_last_optional = 1
636 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
637 *PARENT_PATH_P to a path from the node up to ROOT, allocated in
638 TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to
639 contain at least one element, for the root directory.
641 If resulting *PARENT_PATH_P will eventually be made mutable and
642 modified, or if copy ID inheritance information is otherwise
643 needed, TXN_ID should be the ID of the mutability transaction. If
644 TXN_ID is NULL, no copy ID in heritance information will be
645 calculated for the *PARENT_PATH_P chain.
647 If FLAGS & open_path_last_optional is zero, return the error
648 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
649 non-zero, require all the parent directories to exist as normal,
650 but if the final path component doesn't exist, simply return a path
651 whose bottom `node' member is zero. This option is useful for
652 callers that create new nodes --- we find the parent directory for
653 them, and tell them whether the entry exists already.
655 NOTE: Public interfaces which only *read* from the filesystem
656 should not call this function directly, but should instead use
660 open_path(parent_path_t **parent_path_p,
668 svn_fs_t *fs = root->fs;
669 dag_node_t *here; /* The directory we're currently looking at. */
670 parent_path_t *parent_path; /* The path from HERE up to the root. */
671 const char *rest; /* The portion of PATH we haven't traversed yet. */
672 const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
673 const char *path_so_far = "/";
675 /* Make a parent_path item for the root node, using its own current
677 SVN_ERR(root_node(&here, root, trail, pool));
678 parent_path = make_parent_path(here, 0, 0, pool);
679 parent_path->copy_inherit = copy_id_inherit_self;
681 rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
683 /* Whenever we are at the top of this loop:
684 - HERE is our current directory,
685 - ID is the node revision ID of HERE,
686 - REST is the path we're going to find in HERE, and
687 - PARENT_PATH includes HERE and all its parents. */
694 /* Parse out the next entry from the path. */
695 entry = svn_fs__next_entry_name(&next, rest, pool);
697 /* Calculate the path traversed thus far. */
698 path_so_far = svn_fspath__join(path_so_far, entry, pool);
702 /* Given the behavior of svn_fs__next_entry_name(), this
703 happens when the path either starts or ends with a slash.
704 In either case, we stay put: the current directory stays
705 the same, and we add nothing to the parent path. */
710 copy_id_inherit_t inherit;
711 const char *copy_path = NULL;
712 svn_error_t *err = SVN_NO_ERROR;
713 dag_node_t *cached_node;
715 /* If we found a directory entry, follow it. First, we
716 check our node cache, and, failing that, we hit the DAG
718 cached_node = dag_node_cache_get(root, path_so_far, pool);
722 err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
724 /* "file not found" requires special handling. */
725 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
727 /* If this was the last path component, and the caller
728 said it was optional, then don't return an error;
729 just put a NULL node pointer in the path. */
731 svn_error_clear(err);
733 if ((flags & open_path_last_optional)
734 && (! next || *next == '\0'))
736 parent_path = make_parent_path(NULL, entry, parent_path,
742 /* Build a better error message than svn_fs_base__dag_open
743 can provide, giving the root and full path name. */
744 return SVN_FS__NOT_FOUND(root, path);
748 /* Other errors we return normally. */
751 /* Now, make a parent_path item for CHILD. */
752 parent_path = make_parent_path(child, entry, parent_path, pool);
755 SVN_ERR(get_copy_inheritance(&inherit, ©_path,
756 fs, parent_path, txn_id,
758 parent_path->copy_inherit = inherit;
759 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
762 /* Cache the node we found (if it wasn't already cached). */
764 dag_node_cache_set(root, path_so_far, child);
767 /* Are we finished traversing the path? */
771 /* The path isn't finished yet; we'd better be in a directory. */
772 if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
773 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
774 apr_psprintf(pool, _("Failure opening '%s'"), path));
780 *parent_path_p = parent_path;
785 /* Make the node referred to by PARENT_PATH mutable, if it isn't
786 already, as part of TRAIL. ROOT must be the root from which
787 PARENT_PATH descends. Clone any parent directories as needed.
788 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
789 ERROR_PATH in error messages. */
791 make_path_mutable(svn_fs_root_t *root,
792 parent_path_t *parent_path,
793 const char *error_path,
797 dag_node_t *cloned_node;
798 const char *txn_id = root->txn;
799 svn_fs_t *fs = root->fs;
801 /* Is the node mutable already? */
802 if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
805 /* Are we trying to clone the root, or somebody's child node? */
806 if (parent_path->parent)
808 const svn_fs_id_t *parent_id;
809 const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
810 const char *copy_id = NULL;
811 const char *copy_src_path = parent_path->copy_src_path;
812 copy_id_inherit_t inherit = parent_path->copy_inherit;
813 const char *clone_path;
815 /* We're trying to clone somebody's child. Make sure our parent
817 SVN_ERR(make_path_mutable(root, parent_path->parent,
818 error_path, trail, pool));
822 case copy_id_inherit_parent:
823 parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
824 copy_id = svn_fs_base__id_copy_id(parent_id);
827 case copy_id_inherit_new:
828 SVN_ERR(svn_fs_bdb__reserve_copy_id(©_id, fs, trail, pool));
831 case copy_id_inherit_self:
835 case copy_id_inherit_unknown:
837 SVN_ERR_MALFUNCTION(); /* uh-oh -- somebody didn't calculate copy-ID
841 /* Now make this node mutable. */
842 clone_path = parent_path_path(parent_path->parent, pool);
843 SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
844 parent_path->parent->node,
850 /* If we just created a brand new copy ID, we need to store a
851 `copies' table entry for it, as well as a notation in the
852 transaction that should this transaction be terminated, our
853 new copy needs to be removed. */
854 if (inherit == copy_id_inherit_new)
856 const svn_fs_id_t *new_node_id =
857 svn_fs_base__dag_get_id(cloned_node);
858 SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
859 svn_fs_base__id_txn_id(node_id),
861 copy_kind_soft, trail, pool));
862 SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
868 /* We're trying to clone the root directory. */
869 SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
872 /* Update the PARENT_PATH link to refer to the clone. */
873 parent_path->node = cloned_node;
879 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
880 mergeinfo count by COUNT_DELTA as part of Subversion transaction
881 TXN_ID and TRAIL. Use POOL for allocations. */
883 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
884 apr_int64_t count_delta,
889 apr_pool_t *iterpool;
890 parent_path_t *pp = parent_path;
892 if (count_delta == 0)
895 iterpool = svn_pool_create(pool);
899 svn_pool_clear(iterpool);
900 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
905 svn_pool_destroy(iterpool);
911 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
912 *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return
913 the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
915 get_dag(dag_node_t **dag_node_p,
921 parent_path_t *parent_path;
922 dag_node_t *node = NULL;
924 /* Canonicalize the input PATH. */
925 path = svn_fs__canonicalize_abspath(path, pool);
927 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
928 node = dag_node_cache_get(root, path, pool);
931 /* Call open_path with no flags, as we want this to return an error
932 if the node for which we are searching doesn't exist. */
933 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
934 node = parent_path->node;
936 /* No need to cache our find -- open_path() will do that for us. */
945 /* Populating the `changes' table. */
947 /* Add a change to the changes table in FS, keyed on transaction id
948 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
949 PATH (whose node revision id is--or was, in the case of a
950 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
951 occurred. Do all this as part of TRAIL. */
953 add_change(svn_fs_t *fs,
956 const svn_fs_id_t *noderev_id,
957 svn_fs_path_change_kind_t change_kind,
958 svn_boolean_t text_mod,
959 svn_boolean_t prop_mod,
964 change.path = svn_fs__canonicalize_abspath(path, pool);
965 change.noderev_id = noderev_id;
966 change.kind = change_kind;
967 change.text_mod = text_mod;
968 change.prop_mod = prop_mod;
969 return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
974 /* Generic node operations. */
977 struct node_id_args {
978 const svn_fs_id_t **id_p;
985 txn_body_node_id(void *baton, trail_t *trail)
987 struct node_id_args *args = baton;
990 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
991 *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
999 base_node_id(const svn_fs_id_t **id_p,
1000 svn_fs_root_t *root,
1004 base_root_data_t *brd = root->fsap_data;
1006 if (! root->is_txn_root
1007 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1009 /* Optimize the case where we don't need any db access at all.
1010 The root directory ("" or "/") node is stored in the
1011 svn_fs_root_t object, and never changes when it's a revision
1012 root, so we can just reach in and grab it directly. */
1013 *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1018 const svn_fs_id_t *id;
1019 struct node_id_args args;
1025 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1029 return SVN_NO_ERROR;
1032 static svn_error_t *
1033 base_node_relation(svn_fs_node_relation_t *relation,
1034 svn_fs_root_t *root_a, const char *path_a,
1035 svn_fs_root_t *root_b, const char *path_b,
1038 const svn_fs_id_t *id_a, *id_b;
1040 /* Paths from different repository are never related. */
1041 if (root_a->fs != root_b->fs)
1043 *relation = svn_fs_node_unrelated;
1044 return SVN_NO_ERROR;
1047 /* Naive implementation. */
1048 SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
1049 SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
1051 *relation = svn_fs_base__id_compare(id_a, id_b);
1053 return SVN_NO_ERROR;
1057 struct node_created_rev_args {
1058 svn_revnum_t revision;
1059 svn_fs_root_t *root;
1064 static svn_error_t *
1065 txn_body_node_created_rev(void *baton, trail_t *trail)
1067 struct node_created_rev_args *args = baton;
1070 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1071 return svn_fs_base__dag_get_revision(&(args->revision), node,
1072 trail, trail->pool);
1076 static svn_error_t *
1077 base_node_created_rev(svn_revnum_t *revision,
1078 svn_fs_root_t *root,
1082 struct node_created_rev_args args;
1084 args.revision = SVN_INVALID_REVNUM;
1087 SVN_ERR(svn_fs_base__retry_txn
1088 (root->fs, txn_body_node_created_rev, &args, TRUE, pool));
1089 *revision = args.revision;
1090 return SVN_NO_ERROR;
1094 struct node_created_path_args {
1095 const char **created_path;
1096 svn_fs_root_t *root;
1101 static svn_error_t *
1102 txn_body_node_created_path(void *baton, trail_t *trail)
1104 struct node_created_path_args *args = baton;
1107 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1108 *args->created_path = svn_fs_base__dag_get_created_path(node);
1109 return SVN_NO_ERROR;
1113 static svn_error_t *
1114 base_node_created_path(const char **created_path,
1115 svn_fs_root_t *root,
1119 struct node_created_path_args args;
1120 apr_pool_t *scratch_pool = svn_pool_create(pool);
1122 args.created_path = created_path;
1126 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_created_path, &args,
1127 FALSE, scratch_pool));
1129 *created_path = apr_pstrdup(pool, *created_path);
1130 svn_pool_destroy(scratch_pool);
1131 return SVN_NO_ERROR;
1135 struct node_kind_args {
1136 const svn_fs_id_t *id;
1137 svn_node_kind_t kind; /* OUT parameter */
1141 static svn_error_t *
1142 txn_body_node_kind(void *baton, trail_t *trail)
1144 struct node_kind_args *args = baton;
1147 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1148 trail, trail->pool));
1149 args->kind = svn_fs_base__dag_node_kind(node);
1151 return SVN_NO_ERROR;
1155 static svn_error_t *
1156 node_kind(svn_node_kind_t *kind_p,
1157 svn_fs_root_t *root,
1161 struct node_kind_args args;
1162 const svn_fs_id_t *node_id;
1164 /* Get the node id. */
1165 SVN_ERR(base_node_id(&node_id, root, path, pool));
1167 /* Use the node id to get the real kind. */
1169 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args,
1172 *kind_p = args.kind;
1173 return SVN_NO_ERROR;
1177 static svn_error_t *
1178 base_check_path(svn_node_kind_t *kind_p,
1179 svn_fs_root_t *root,
1183 svn_error_t *err = node_kind(kind_p, root, path, pool);
1185 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1186 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1188 svn_error_clear(err);
1190 *kind_p = svn_node_none;
1193 return svn_error_trace(err);
1197 struct node_prop_args
1199 svn_string_t **value_p;
1200 svn_fs_root_t *root;
1202 const char *propname;
1206 static svn_error_t *
1207 txn_body_node_prop(void *baton,
1210 struct node_prop_args *args = baton;
1212 apr_hash_t *proplist;
1214 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1215 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1216 trail, trail->pool));
1217 *(args->value_p) = NULL;
1219 *(args->value_p) = svn_hash_gets(proplist, args->propname);
1220 return SVN_NO_ERROR;
1224 static svn_error_t *
1225 base_node_prop(svn_string_t **value_p,
1226 svn_fs_root_t *root,
1228 const char *propname,
1231 struct node_prop_args args;
1232 svn_string_t *value;
1233 apr_pool_t *scratch_pool = svn_pool_create(pool);
1235 args.value_p = &value;
1238 args.propname = propname;
1239 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
1240 FALSE, scratch_pool));
1241 *value_p = svn_string_dup(value, pool);
1242 svn_pool_destroy(scratch_pool);
1243 return SVN_NO_ERROR;
1247 struct node_proplist_args {
1248 apr_hash_t **table_p;
1249 svn_fs_root_t *root;
1254 static svn_error_t *
1255 txn_body_node_proplist(void *baton, trail_t *trail)
1257 struct node_proplist_args *args = baton;
1259 apr_hash_t *proplist;
1261 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1262 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1263 trail, trail->pool));
1264 *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1265 return SVN_NO_ERROR;
1269 static svn_error_t *
1270 base_node_proplist(apr_hash_t **table_p,
1271 svn_fs_root_t *root,
1276 struct node_proplist_args args;
1278 args.table_p = &table;
1282 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1286 return SVN_NO_ERROR;
1289 static svn_error_t *
1290 base_node_has_props(svn_boolean_t *has_props,
1291 svn_fs_root_t *root,
1293 apr_pool_t *scratch_pool)
1297 SVN_ERR(base_node_proplist(&props, root, path, scratch_pool));
1299 *has_props = (0 < apr_hash_count(props));
1301 return SVN_NO_ERROR;
1305 struct change_node_prop_args {
1306 svn_fs_root_t *root;
1309 const svn_string_t *value;
1313 static svn_error_t *
1314 txn_body_change_node_prop(void *baton,
1317 struct change_node_prop_args *args = baton;
1318 parent_path_t *parent_path;
1319 apr_hash_t *proplist;
1320 const char *txn_id = args->root->txn;
1321 base_fs_data_t *bfd = trail->fs->fsap_data;
1323 SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1324 trail, trail->pool));
1326 /* Check to see if path is locked; if so, check that we can use it.
1327 Notice that we're doing this non-recursively, regardless of node kind. */
1328 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1329 SVN_ERR(svn_fs_base__allow_locked_operation
1330 (args->path, FALSE, trail, trail->pool));
1332 SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1333 trail, trail->pool));
1334 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1335 trail, trail->pool));
1337 /* If there's no proplist, but we're just deleting a property, exit now. */
1338 if ((! proplist) && (! args->value))
1339 return SVN_NO_ERROR;
1341 /* Now, if there's no proplist, we know we need to make one. */
1343 proplist = apr_hash_make(trail->pool);
1345 /* Set the property. */
1346 svn_hash_sets(proplist, args->name, args->value);
1348 /* Overwrite the node's proplist. */
1349 SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1350 txn_id, trail, trail->pool));
1352 /* If this was a change to the mergeinfo property, and our version
1353 of the filesystem cares, we have some extra recording to do.
1355 ### If the format *doesn't* support mergeinfo recording, should
1356 ### we fuss about attempts to change the svn:mergeinfo property
1357 ### in any way save to delete it? */
1358 if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1359 && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1361 svn_boolean_t had_mergeinfo, has_mergeinfo = args->value != NULL;
1363 /* First, note on our node that it has mergeinfo. */
1364 SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1366 &had_mergeinfo, txn_id,
1367 trail, trail->pool));
1369 /* If this is a change from the old state, we need to update our
1370 node's parents' mergeinfo counts by a factor of 1. */
1371 if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1372 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1373 has_mergeinfo ? 1 : -1,
1374 txn_id, trail, trail->pool));
1377 /* Make a record of this modification in the changes table. */
1378 return add_change(args->root->fs, txn_id,
1379 args->path, svn_fs_base__dag_get_id(parent_path->node),
1380 svn_fs_path_change_modify, FALSE, TRUE, trail,
1385 static svn_error_t *
1386 base_change_node_prop(svn_fs_root_t *root,
1389 const svn_string_t *value,
1392 struct change_node_prop_args args;
1394 if (! root->is_txn_root)
1395 return SVN_FS__NOT_TXN(root);
1401 return svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1406 struct things_changed_args
1408 svn_boolean_t *changed_p;
1409 svn_fs_root_t *root1;
1410 svn_fs_root_t *root2;
1413 svn_boolean_t strict;
1418 static svn_error_t *
1419 txn_body_props_changed(void *baton, trail_t *trail)
1421 struct things_changed_args *args = baton;
1422 dag_node_t *node1, *node2;
1423 apr_hash_t *proplist1, *proplist2;
1425 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1426 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1427 SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
1428 node1, node2, trail, trail->pool));
1430 /* Is there a potential false positive and do we want to correct it? */
1431 if (!args->strict || !*args->changed_p)
1432 return SVN_NO_ERROR;
1434 /* Different representations. They might still have equal contents. */
1435 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1,
1436 trail, trail->pool));
1437 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2,
1438 trail, trail->pool));
1440 *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2,
1442 return SVN_NO_ERROR;
1446 static svn_error_t *
1447 base_props_changed(svn_boolean_t *changed_p,
1448 svn_fs_root_t *root1,
1450 svn_fs_root_t *root2,
1452 svn_boolean_t strict,
1455 struct things_changed_args args;
1457 /* Check that roots are in the same fs. */
1458 if (root1->fs != root2->fs)
1459 return svn_error_create
1460 (SVN_ERR_FS_GENERAL, NULL,
1461 _("Cannot compare property value between two different filesystems"));
1467 args.changed_p = changed_p;
1469 args.strict = strict;
1471 return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
1477 /* Miscellaneous table handling */
1479 struct miscellaneous_set_args
1485 static svn_error_t *
1486 txn_body_miscellaneous_set(void *baton, trail_t *trail)
1488 struct miscellaneous_set_args *msa = baton;
1490 return svn_fs_bdb__miscellaneous_set(trail->fs, msa->key, msa->val, trail,
1495 svn_fs_base__miscellaneous_set(svn_fs_t *fs,
1500 struct miscellaneous_set_args msa;
1504 return svn_fs_base__retry_txn(fs, txn_body_miscellaneous_set, &msa,
1508 struct miscellaneous_get_args
1514 static svn_error_t *
1515 txn_body_miscellaneous_get(void *baton, trail_t *trail)
1517 struct miscellaneous_get_args *mga = baton;
1518 return svn_fs_bdb__miscellaneous_get(mga->val, trail->fs, mga->key, trail,
1523 svn_fs_base__miscellaneous_get(const char **val,
1528 struct miscellaneous_get_args mga;
1529 apr_pool_t *scratch_pool = svn_pool_create(pool);
1533 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_miscellaneous_get, &mga,
1534 FALSE, scratch_pool));
1536 *val = apr_pstrdup(pool, *val);
1537 svn_pool_destroy(scratch_pool);
1538 return SVN_NO_ERROR;
1543 /* Getting a directory's entries */
1546 struct dir_entries_args
1548 apr_hash_t **table_p;
1549 svn_fs_root_t *root;
1554 /* *(BATON->table_p) will never be NULL on successful return */
1555 static svn_error_t *
1556 txn_body_dir_entries(void *baton,
1559 struct dir_entries_args *args = baton;
1561 apr_hash_t *entries;
1563 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1565 /* Get the entries for PARENT_PATH. */
1566 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1568 /* Potentially initialize the return value to an empty hash. */
1569 *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1570 return SVN_NO_ERROR;
1574 static svn_error_t *
1575 base_dir_entries(apr_hash_t **table_p,
1576 svn_fs_root_t *root,
1580 struct dir_entries_args args;
1581 apr_pool_t *iterpool;
1583 svn_fs_t *fs = root->fs;
1584 apr_hash_index_t *hi;
1586 args.table_p = &table;
1589 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1592 iterpool = svn_pool_create(pool);
1594 /* Add in the kind data. */
1595 for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1597 svn_fs_dirent_t *entry;
1598 struct node_kind_args nk_args;
1601 svn_pool_clear(iterpool);
1603 /* KEY will be the entry name in ancestor (about which we
1604 simply don't care), VAL the dirent. */
1605 apr_hash_this(hi, NULL, NULL, &val);
1607 nk_args.id = entry->id;
1609 /* We don't need to have the retry function destroy the trail
1610 pool because we're already doing that via the use of an
1612 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1614 entry->kind = nk_args.kind;
1617 svn_pool_destroy(iterpool);
1620 return SVN_NO_ERROR;
1623 static svn_error_t *
1624 base_dir_optimal_order(apr_array_header_t **ordered_p,
1625 svn_fs_root_t *root,
1626 apr_hash_t *entries,
1627 apr_pool_t *result_pool,
1628 apr_pool_t *scratch_pool)
1630 /* 1:1 copy of entries with no differnce in ordering */
1631 apr_hash_index_t *hi;
1632 apr_array_header_t *result
1633 = apr_array_make(result_pool, apr_hash_count(entries),
1634 sizeof(svn_fs_dirent_t *));
1635 for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
1636 APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
1638 *ordered_p = result;
1639 return SVN_NO_ERROR;
1644 /* Merges and commits. */
1647 struct deltify_committed_args
1649 svn_fs_t *fs; /* the filesystem */
1650 svn_revnum_t rev; /* revision just committed */
1651 const char *txn_id; /* transaction just committed */
1655 struct txn_deltify_args
1657 /* The transaction ID whose nodes are being deltified. */
1660 /* The target is what we're deltifying. */
1661 const svn_fs_id_t *tgt_id;
1663 /* The base is what we're deltifying against. It's not necessarily
1664 the "next" revision of the node; skip deltas mean we sometimes
1665 deltify against a successor many generations away. This may be
1666 NULL, in which case we'll avoid deltification and simply index
1667 TGT_ID's data checksum. */
1668 const svn_fs_id_t *base_id;
1670 /* We only deltify props for directories.
1671 ### Didn't we try removing this horrid little optimization once?
1672 ### What was the result? I would have thought that skip deltas
1673 ### mean directory undeltification is cheap enough now. */
1674 svn_boolean_t is_dir;
1678 static svn_error_t *
1679 txn_body_txn_deltify(void *baton, trail_t *trail)
1681 struct txn_deltify_args *args = baton;
1682 dag_node_t *tgt_node, *base_node;
1683 base_fs_data_t *bfd = trail->fs->fsap_data;
1685 SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1686 trail, trail->pool));
1687 /* If we have something to deltify against, do so. */
1690 SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1691 trail, trail->pool));
1692 SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1693 args->txn_id, trail, trail->pool));
1696 /* If we support rep sharing, and this isn't a directory, record a
1697 mapping of TGT_NODE's data checksum to its representation key. */
1698 if (bfd->format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
1699 SVN_ERR(svn_fs_base__dag_index_checksums(tgt_node, trail, trail->pool));
1701 return SVN_NO_ERROR;
1705 struct txn_pred_count_args
1707 const svn_fs_id_t *id;
1712 static svn_error_t *
1713 txn_body_pred_count(void *baton, trail_t *trail)
1715 node_revision_t *noderev;
1716 struct txn_pred_count_args *args = baton;
1718 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1719 args->id, trail, trail->pool));
1720 args->pred_count = noderev->predecessor_count;
1721 return SVN_NO_ERROR;
1725 struct txn_pred_id_args
1727 const svn_fs_id_t *id; /* The node id whose predecessor we want. */
1728 const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1729 apr_pool_t *pool; /* The pool in which to allocate pred_id. */
1733 static svn_error_t *
1734 txn_body_pred_id(void *baton, trail_t *trail)
1736 node_revision_t *nr;
1737 struct txn_pred_id_args *args = baton;
1739 SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1740 trail, trail->pool));
1741 if (nr->predecessor_id)
1742 args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1744 args->pred_id = NULL;
1746 return SVN_NO_ERROR;
1750 /* Deltify PATH in ROOT's predecessor iff PATH is mutable under TXN_ID
1751 in FS. If PATH is a mutable directory, recurse.
1753 NODE_ID is the node revision ID for PATH in ROOT, or NULL if that
1754 value isn't known. KIND is the node kind for PATH in ROOT, or
1755 svn_node_unknown is the kind isn't known.
1757 Use POOL for necessary allocations. */
1758 static svn_error_t *
1759 deltify_mutable(svn_fs_t *fs,
1760 svn_fs_root_t *root,
1762 const svn_fs_id_t *node_id,
1763 svn_node_kind_t kind,
1767 const svn_fs_id_t *id = node_id;
1768 apr_hash_t *entries = NULL;
1769 struct txn_deltify_args td_args;
1770 base_fs_data_t *bfd = fs->fsap_data;
1772 /* Get the ID for PATH under ROOT if it wasn't provided. */
1774 SVN_ERR(base_node_id(&id, root, path, pool));
1776 /* Check for mutability. Not mutable? Go no further. This is safe
1777 to do because for items in the tree to be mutable, their parent
1778 dirs must also be mutable. Therefore, if a directory is not
1779 mutable under TXN_ID, its children cannot be. */
1780 if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1781 return SVN_NO_ERROR;
1783 /* Is this a directory? */
1784 if (kind == svn_node_unknown)
1785 SVN_ERR(base_check_path(&kind, root, path, pool));
1787 /* If this is a directory, read its entries. */
1788 if (kind == svn_node_dir)
1789 SVN_ERR(base_dir_entries(&entries, root, path, pool));
1791 /* If there are entries, recurse on 'em. */
1794 apr_pool_t *subpool = svn_pool_create(pool);
1795 apr_hash_index_t *hi;
1797 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1799 /* KEY will be the entry name, VAL the dirent */
1802 svn_fs_dirent_t *entry;
1803 svn_pool_clear(subpool);
1804 apr_hash_this(hi, &key, NULL, &val);
1806 SVN_ERR(deltify_mutable(fs, root,
1807 svn_fspath__join(path, key, subpool),
1808 entry->id, entry->kind, txn_id, subpool));
1811 svn_pool_destroy(subpool);
1814 /* Index ID's data checksum. */
1815 td_args.txn_id = txn_id;
1816 td_args.tgt_id = id;
1817 td_args.base_id = NULL;
1818 td_args.is_dir = (kind == svn_node_dir);
1819 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1822 /* Finally, deltify old data against this node. */
1824 /* Prior to 1.6, we use the following algorithm to deltify nodes:
1826 Redeltify predecessor node-revisions of the one we added. The
1827 idea is to require at most 2*lg(N) deltas to be applied to get
1828 to any node-revision in a chain of N predecessors. We do this
1829 using a technique derived from skip lists:
1831 - Always redeltify the immediate parent
1833 - If the number of predecessors is divisible by 2,
1834 redeltify the revision two predecessors back
1836 - If the number of predecessors is divisible by 4,
1837 redeltify the revision four predecessors back
1841 That's the theory, anyway. Unfortunately, if we strictly
1842 follow that theory we get a bunch of overhead up front and no
1843 great benefit until the number of predecessors gets large. So,
1844 stop at redeltifying the parent if the number of predecessors
1845 is less than 32, and also skip the second level (redeltifying
1846 two predecessors back), since that doesn't help much. Also,
1847 don't redeltify the oldest node-revision; it's potentially
1848 expensive and doesn't help retrieve any other revision.
1849 (Retrieving the oldest node-revision will still be fast, just
1850 not as blindingly so.)
1852 For 1.6 and beyond, we just deltify the current node against its
1853 predecessors, using skip deltas similar to the way FSFS does. */
1856 const svn_fs_id_t *pred_id;
1857 struct txn_pred_count_args tpc_args;
1858 apr_pool_t *subpools[2];
1859 int active_subpool = 0;
1860 svn_revnum_t forward_delta_rev = 0;
1863 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1865 pred_count = tpc_args.pred_count;
1867 /* If nothing to deltify, then we're done. */
1868 if (pred_count == 0)
1869 return SVN_NO_ERROR;
1871 subpools[0] = svn_pool_create(pool);
1872 subpools[1] = svn_pool_create(pool);
1874 /* If we support the 'miscellaneous' table, check it to see if
1875 there is a point in time before which we don't want to do
1877 /* ### FIXME: I think this is an unnecessary restriction. We
1878 ### should be able to do something meaningful for most
1879 ### deltification requests -- what that is depends on the
1880 ### directory of the deltas for that revision, though. */
1881 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
1884 SVN_ERR(svn_fs_base__miscellaneous_get
1885 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
1887 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
1890 if (bfd->format >= SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT
1891 && forward_delta_rev <= root->rev)
1893 /**** FORWARD DELTA STORAGE ****/
1895 /* Decide which predecessor to deltify against. Flip the rightmost '1'
1896 bit of the predecessor count to determine which file rev (counting
1897 from 0) we want to use. (To see why count & (count - 1) unsets the
1898 rightmost set bit, think about how you decrement a binary number. */
1899 pred_count = pred_count & (pred_count - 1);
1901 /* Walk back a number of predecessors equal to the difference between
1902 pred_count and the original predecessor count. (For example, if
1903 the node has ten predecessors and we want the eighth node, walk back
1904 two predecessors. */
1907 /* We need to use two alternating pools because the id used in the
1908 call to txn_body_pred_id is allocated by the previous inner
1909 loop iteration. If we would clear the pool each iteration we
1910 would free the previous result. */
1911 while ((pred_count++) < tpc_args.pred_count)
1913 struct txn_pred_id_args tpi_args;
1915 active_subpool = !active_subpool;
1916 svn_pool_clear(subpools[active_subpool]);
1918 tpi_args.id = pred_id;
1919 tpi_args.pool = subpools[active_subpool];
1920 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1921 FALSE, subpools[active_subpool]));
1922 pred_id = tpi_args.pred_id;
1924 if (pred_id == NULL)
1925 return svn_error_create
1926 (SVN_ERR_FS_CORRUPT, 0,
1927 _("Corrupt DB: faulty predecessor count"));
1931 /* Finally, do the deltification. */
1932 td_args.txn_id = txn_id;
1933 td_args.tgt_id = id;
1934 td_args.base_id = pred_id;
1935 td_args.is_dir = (kind == svn_node_dir);
1936 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1937 TRUE, subpools[active_subpool]));
1941 int nlevels, lev, count;
1943 /**** REVERSE DELTA STORAGE ****/
1945 /* Decide how many predecessors to redeltify. To save overhead,
1946 don't redeltify anything but the immediate predecessor if there
1947 are less than 32 predecessors. */
1949 if (pred_count >= 32)
1951 while (pred_count % 2 == 0)
1957 /* Don't redeltify the oldest revision. */
1958 if (1 << (nlevels - 1) == pred_count)
1962 /* Redeltify the desired number of predecessors. */
1966 /* We need to use two alternating pools because the id used in the
1967 call to txn_body_pred_id is allocated by the previous inner
1968 loop iteration. If we would clear the pool each iteration we
1969 would free the previous result. */
1970 for (lev = 0; lev < nlevels; lev++)
1972 /* To save overhead, skip the second level (that is, never
1973 redeltify the node-revision two predecessors back). */
1977 /* Note that COUNT is not reset between levels, and neither is
1978 PREDNODE; we just keep counting from where we were up to
1979 where we're supposed to get. */
1980 while (count < (1 << lev))
1982 struct txn_pred_id_args tpi_args;
1984 active_subpool = !active_subpool;
1985 svn_pool_clear(subpools[active_subpool]);
1987 tpi_args.id = pred_id;
1988 tpi_args.pool = subpools[active_subpool];
1989 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
1991 subpools[active_subpool]));
1992 pred_id = tpi_args.pred_id;
1994 if (pred_id == NULL)
1995 return svn_error_create
1996 (SVN_ERR_FS_CORRUPT, 0,
1997 _("Corrupt DB: faulty predecessor count"));
2002 /* Finally, do the deltification. */
2003 td_args.txn_id = NULL; /* Don't require mutable reps */
2004 td_args.tgt_id = pred_id;
2005 td_args.base_id = id;
2006 td_args.is_dir = (kind == svn_node_dir);
2007 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
2008 TRUE, subpools[active_subpool]));
2013 svn_pool_destroy(subpools[0]);
2014 svn_pool_destroy(subpools[1]);
2017 return SVN_NO_ERROR;
2021 struct get_root_args
2023 svn_fs_root_t *root;
2028 /* Set ARGS->node to the root node of ARGS->root. */
2029 static svn_error_t *
2030 txn_body_get_root(void *baton, trail_t *trail)
2032 struct get_root_args *args = baton;
2033 return get_dag(&(args->node), args->root, "", trail, trail->pool);
2038 static svn_error_t *
2039 update_ancestry(svn_fs_t *fs,
2040 const svn_fs_id_t *source_id,
2041 const svn_fs_id_t *target_id,
2043 const char *target_path,
2044 int source_pred_count,
2048 node_revision_t *noderev;
2050 /* Set target's predecessor-id to source_id. */
2051 if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
2052 return svn_error_createf
2053 (SVN_ERR_FS_NOT_MUTABLE, NULL,
2054 _("Unexpected immutable node at '%s'"), target_path);
2055 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
2057 noderev->predecessor_id = source_id;
2058 noderev->predecessor_count = source_pred_count;
2059 if (noderev->predecessor_count != -1)
2060 noderev->predecessor_count++;
2061 return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
2065 /* Set the contents of CONFLICT_PATH to PATH, and return an
2066 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
2067 at PATH. Perform all allocations in POOL (except the allocation of
2068 CONFLICT_PATH, which should be handled outside this function). */
2069 static svn_error_t *
2070 conflict_err(svn_stringbuf_t *conflict_path,
2073 svn_stringbuf_set(conflict_path, path);
2074 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
2075 _("Conflict at '%s'"), path);
2079 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
2080 * TRAIL. ANCESTOR and TARGET must be distinct node revisions.
2081 * TARGET_PATH should correspond to TARGET's full path in its
2082 * filesystem, and is used for reporting conflict location.
2084 * SOURCE, TARGET, and ANCESTOR are generally directories; this
2085 * function recursively merges the directories' contents. If any are
2086 * files, this function simply returns an error whenever SOURCE,
2087 * TARGET, and ANCESTOR are all distinct node revisions.
2089 * If there are differences between ANCESTOR and SOURCE that conflict
2090 * with changes between ANCESTOR and TARGET, this function returns an
2091 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
2092 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
2094 * If there are no conflicting differences, CONFLICT_P is updated to
2097 * CONFLICT_P must point to a valid svn_stringbuf_t.
2099 * Do any necessary temporary allocation in POOL.
2101 static svn_error_t *
2102 merge(svn_stringbuf_t *conflict_p,
2103 const char *target_path,
2106 dag_node_t *ancestor,
2108 apr_int64_t *mergeinfo_increment_out,
2112 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
2113 apr_hash_t *s_entries, *t_entries, *a_entries;
2114 apr_hash_index_t *hi;
2115 apr_pool_t *iterpool;
2118 apr_int64_t mergeinfo_increment = 0;
2119 base_fs_data_t *bfd = trail->fs->fsap_data;
2121 /* Make sure everyone comes from the same filesystem. */
2122 fs = svn_fs_base__dag_get_fs(ancestor);
2123 if ((fs != svn_fs_base__dag_get_fs(source))
2124 || (fs != svn_fs_base__dag_get_fs(target)))
2126 return svn_error_create
2127 (SVN_ERR_FS_CORRUPT, NULL,
2128 _("Bad merge; ancestor, source, and target not all in same fs"));
2131 /* We have the same fs, now check it. */
2132 SVN_ERR(svn_fs__check_fs(fs, TRUE));
2134 source_id = svn_fs_base__dag_get_id(source);
2135 target_id = svn_fs_base__dag_get_id(target);
2136 ancestor_id = svn_fs_base__dag_get_id(ancestor);
2138 /* It's improper to call this function with ancestor == target. */
2139 if (svn_fs_base__id_eq(ancestor_id, target_id))
2141 svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
2142 return svn_error_createf
2143 (SVN_ERR_FS_GENERAL, NULL,
2144 _("Bad merge; target '%s' has id '%s', same as ancestor"),
2145 target_path, id_str->data);
2148 svn_stringbuf_setempty(conflict_p);
2151 * Either no change made in source, or same change as made in target.
2152 * Both mean nothing to merge here.
2154 if (svn_fs_base__id_eq(ancestor_id, source_id)
2155 || (svn_fs_base__id_eq(source_id, target_id)))
2156 return SVN_NO_ERROR;
2158 /* Else proceed, knowing all three are distinct node revisions.
2160 * How to merge from this point:
2162 * if (not all 3 are directories)
2164 * early exit with conflict;
2167 * // Property changes may only be made to up-to-date
2168 * // directories, because once the client commits the prop
2169 * // change, it bumps the directory's revision, and therefore
2170 * // must be able to depend on there being no other changes to
2171 * // that directory in the repository.
2172 * if (target's property list differs from ancestor's)
2175 * For each entry NAME in the directory ANCESTOR:
2177 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
2178 * the name within ANCESTOR, SOURCE, and TARGET respectively.
2179 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
2181 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
2182 * No changes were made to this entry while the transaction was in
2183 * progress, so do nothing to the target.
2185 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
2186 * A change was made to this entry while the transaction was in
2187 * process, but the transaction did not touch this entry. Replace
2188 * TARGET-ENTRY with SOURCE-ENTRY.
2191 * Changes were made to this entry both within the transaction and
2192 * to the repository while the transaction was in progress. They
2193 * must be merged or declared to be in conflict.
2195 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2196 * double delete; flag a conflict.
2198 * If any of the three entries is of type file, declare a conflict.
2200 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2201 * modification of ANCESTOR-ENTRY (determine by comparing the
2202 * node-id fields), declare a conflict. A replacement is
2203 * incompatible with a modification or other replacement--even
2204 * an identical replacement.
2206 * Direct modifications were made to the directory ANCESTOR-ENTRY
2207 * in both SOURCE and TARGET. Recursively merge these
2210 * For each leftover entry NAME in the directory SOURCE:
2212 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
2213 * TARGET are adding exactly the same thing, two additions are not
2214 * auto-mergeable with each other.
2216 * Add NAME to TARGET with the entry from SOURCE.
2218 * Now that we are done merging the changes from SOURCE into the
2219 * directory TARGET, update TARGET's predecessor to be SOURCE.
2222 if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
2223 || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
2224 || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
2226 return conflict_err(conflict_p, target_path);
2230 /* Possible early merge failure: if target and ancestor have
2231 different property lists, then the merge should fail.
2232 Propchanges can *only* be committed on an up-to-date directory.
2233 ### TODO: see issue #418 about the inelegance of this.
2235 Another possible, similar, early merge failure: if source and
2236 ancestor have different property lists (meaning someone else
2237 changed directory properties while our commit transaction was
2238 happening), the merge should fail. See issue #2751.
2241 node_revision_t *tgt_nr, *anc_nr, *src_nr;
2243 /* Get node revisions for our id's. */
2244 SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
2246 SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
2248 SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
2251 /* Now compare the prop-keys of the skels. Note that just because
2252 the keys are different -doesn't- mean the proplists have
2253 different contents. But merge() isn't concerned with contents;
2254 it doesn't do a brute-force comparison on textual contents, so
2255 it won't do that here either. Checking to see if the propkey
2256 atoms are `equal' is enough. */
2257 if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
2258 return conflict_err(conflict_p, target_path);
2259 if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
2260 return conflict_err(conflict_p, target_path);
2263 /* ### todo: it would be more efficient to simply check for a NULL
2264 entries hash where necessary below than to allocate an empty hash
2265 here, but another day, another day... */
2266 SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2268 s_entries = apr_hash_make(pool);
2269 SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2271 t_entries = apr_hash_make(pool);
2272 SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2274 a_entries = apr_hash_make(pool);
2276 /* for each entry E in a_entries... */
2277 iterpool = svn_pool_create(pool);
2278 for (hi = apr_hash_first(pool, a_entries);
2280 hi = apr_hash_next(hi))
2282 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2288 svn_pool_clear(iterpool);
2290 /* KEY will be the entry name in ancestor, VAL the dirent */
2291 apr_hash_this(hi, &key, &klen, &val);
2294 s_entry = apr_hash_get(s_entries, key, klen);
2295 t_entry = apr_hash_get(t_entries, key, klen);
2297 /* No changes were made to this entry while the transaction was
2298 in progress, so do nothing to the target. */
2299 if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2302 /* A change was made to this entry while the transaction was in
2303 process, but the transaction did not touch this entry. */
2304 else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2306 dag_node_t *t_ent_node;
2307 apr_int64_t mergeinfo_start;
2308 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2309 t_entry->id, trail, iterpool));
2310 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2313 mergeinfo_increment -= mergeinfo_start;
2317 dag_node_t *s_ent_node;
2318 apr_int64_t mergeinfo_end;
2319 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2322 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2326 mergeinfo_increment += mergeinfo_end;
2327 SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2328 txn_id, trail, iterpool));
2332 SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2337 /* Changes were made to this entry both within the transaction
2338 and to the repository while the transaction was in progress.
2339 They must be merged or declared to be in conflict. */
2342 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2343 const char *new_tpath;
2344 apr_int64_t sub_mergeinfo_increment;
2346 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2347 double delete; if one of them is null, that's a delete versus
2348 a modification. In any of these cases, flag a conflict. */
2349 if (s_entry == NULL || t_entry == NULL)
2350 return conflict_err(conflict_p,
2351 svn_fspath__join(target_path,
2355 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2356 modification of ANCESTOR-ENTRY, declare a conflict. */
2357 if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2358 svn_fs_base__id_node_id(a_entry->id)) != 0
2359 || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2360 svn_fs_base__id_copy_id(a_entry->id)) != 0
2361 || strcmp(svn_fs_base__id_node_id(t_entry->id),
2362 svn_fs_base__id_node_id(a_entry->id)) != 0
2363 || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2364 svn_fs_base__id_copy_id(a_entry->id)) != 0)
2365 return conflict_err(conflict_p,
2366 svn_fspath__join(target_path,
2370 /* Fetch the nodes for our entries. */
2371 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2372 s_entry->id, trail, iterpool));
2373 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2374 t_entry->id, trail, iterpool));
2375 SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2376 a_entry->id, trail, iterpool));
2378 /* If any of the three entries is of type file, flag a conflict. */
2379 if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2380 || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2381 || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2382 return conflict_err(conflict_p,
2383 svn_fspath__join(target_path,
2387 /* Direct modifications were made to the directory
2388 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2389 merge these modifications. */
2390 new_tpath = svn_fspath__join(target_path, t_entry->name, iterpool);
2391 SVN_ERR(merge(conflict_p, new_tpath,
2392 t_ent_node, s_ent_node, a_ent_node,
2393 txn_id, &sub_mergeinfo_increment, trail, iterpool));
2394 mergeinfo_increment += sub_mergeinfo_increment;
2397 /* We've taken care of any possible implications E could have.
2398 Remove it from source_entries, so it's easy later to loop
2399 over all the source entries that didn't exist in
2400 ancestor_entries. */
2402 apr_hash_set(s_entries, key, klen, NULL);
2405 /* For each entry E in source but not in ancestor */
2406 for (hi = apr_hash_first(pool, s_entries);
2408 hi = apr_hash_next(hi))
2410 svn_fs_dirent_t *s_entry, *t_entry;
2414 dag_node_t *s_ent_node;
2415 apr_int64_t mergeinfo_s;
2417 svn_pool_clear(iterpool);
2419 apr_hash_this(hi, &key, &klen, &val);
2421 t_entry = apr_hash_get(t_entries, key, klen);
2423 /* If NAME exists in TARGET, declare a conflict. */
2425 return conflict_err(conflict_p,
2426 svn_fspath__join(target_path,
2430 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2431 s_entry->id, trail, iterpool));
2432 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2435 mergeinfo_increment += mergeinfo_s;
2436 SVN_ERR(svn_fs_base__dag_set_entry
2437 (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2439 svn_pool_destroy(iterpool);
2441 /* Now that TARGET has absorbed all of the history between ANCESTOR
2442 and SOURCE, we can update its predecessor to point to SOURCE. */
2443 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2445 SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2446 pred_count, trail, pool));
2448 /* Tweak mergeinfo data if our format supports it. */
2449 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2451 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2452 mergeinfo_increment,
2453 txn_id, trail, pool));
2456 if (mergeinfo_increment_out)
2457 *mergeinfo_increment_out = mergeinfo_increment;
2459 return SVN_NO_ERROR;
2465 /* The ancestor for the merge. If this is null, then TXN's base is
2466 used as the ancestor for the merge. */
2467 dag_node_t *ancestor_node;
2469 /* This is the SOURCE node for the merge. It may not be null. */
2470 dag_node_t *source_node;
2472 /* This is the TARGET of the merge. It may not be null. If
2473 ancestor_node above is null, then this txn's base is used as the
2474 ancestor for the merge. */
2477 /* If a conflict results, this is updated to the path in the txn that
2478 conflicted. It must point to a valid svn_stringbuf_t before calling
2479 svn_fs_base__retry_txn, as this determines the pool used to allocate any
2481 svn_stringbuf_t *conflict;
2485 /* Merge changes between an ancestor and BATON->source_node into
2486 BATON->txn. The ancestor is either BATON->ancestor_node, or if
2487 that is null, BATON->txn's base node.
2489 If the merge is successful, BATON->txn's base will become
2490 BATON->source_node, and its root node will have a new ID, a
2491 successor of BATON->source_node. */
2492 static svn_error_t *
2493 txn_body_merge(void *baton, trail_t *trail)
2495 struct merge_args *args = baton;
2496 dag_node_t *source_node, *txn_root_node, *ancestor_node;
2497 const svn_fs_id_t *source_id;
2498 svn_fs_t *fs = args->txn->fs;
2499 const char *txn_id = args->txn->id;
2501 source_node = args->source_node;
2502 ancestor_node = args->ancestor_node;
2503 source_id = svn_fs_base__dag_get_id(source_node);
2505 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2506 trail, trail->pool));
2508 if (ancestor_node == NULL)
2510 SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2511 txn_id, trail, trail->pool));
2514 if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2515 svn_fs_base__dag_get_id(txn_root_node)))
2517 /* If no changes have been made in TXN since its current base,
2518 then it can't conflict with any changes since that base. So
2519 we just set *both* its base and root to source, making TXN
2520 in effect a repeat of source. */
2522 /* ### kff todo: this would, of course, be a mighty silly thing
2523 for the caller to do, and we might want to consider whether
2524 this response is really appropriate. */
2526 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2527 trail, trail->pool));
2528 SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2529 trail, trail->pool));
2535 SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2536 ancestor_node, txn_id, NULL, trail, trail->pool));
2538 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2542 /* After the merge, txn's new "ancestor" is now really the node
2543 at source_id, so record that fact. Think of this as
2544 ratcheting the txn forward in time, so it can't backslide and
2545 forget the merging work that's already been done. */
2546 SVN_ERR(update_ancestry(fs, source_id,
2547 svn_fs_base__dag_get_id(txn_root_node),
2548 txn_id, "/", pred_count, trail, trail->pool));
2549 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2550 trail, trail->pool));
2553 return SVN_NO_ERROR;
2557 /* Verify that there are registered with TRAIL->fs all the locks
2558 necessary to permit all the changes associated with TXN_NAME. */
2559 static svn_error_t *
2560 verify_locks(const char *txn_name,
2564 apr_pool_t *subpool = svn_pool_create(pool);
2565 apr_hash_t *changes;
2566 apr_hash_index_t *hi;
2567 apr_array_header_t *changed_paths;
2568 svn_stringbuf_t *last_recursed = NULL;
2571 /* Fetch the changes for this transaction. */
2572 SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2575 /* Make an array of the changed paths, and sort them depth-first-ily. */
2576 changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2577 sizeof(const char *));
2578 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2581 apr_hash_this(hi, &key, NULL, NULL);
2582 APR_ARRAY_PUSH(changed_paths, const char *) = key;
2584 svn_sort__array(changed_paths, 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, *txn_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 /* Ensure every txn has a mutable root as then the new revision will
2698 have a distinct root node-revision-id. This is necessary as
2699 future transactions use the root node-revision-id as a proxy for
2700 the transaction base revision. */
2701 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_name,
2702 trail, trail->pool));
2703 if (!svn_fs_base__dag_check_mutable(txn_root_node, txn->id))
2706 SVN_ERR(svn_fs_base__dag_clone_root(&clone, fs, txn->id,
2707 trail, trail->pool));
2710 /* Else, commit the txn. */
2711 return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2716 /* Note: it is acceptable for this function to call back into
2717 top-level FS interfaces because it does not itself use trails. */
2719 svn_fs_base__commit_txn(const char **conflict_p,
2720 svn_revnum_t *new_rev,
2724 /* How do commits work in Subversion?
2726 * When you're ready to commit, here's what you have:
2728 * 1. A transaction, with a mutable tree hanging off it.
2729 * 2. A base revision, against which TXN_TREE was made.
2730 * 3. A latest revision, which may be newer than the base rev.
2732 * The problem is that if latest != base, then one can't simply
2733 * attach the txn root as the root of the new revision, because that
2734 * would lose all the changes between base and latest. It is also
2735 * not acceptable to insist that base == latest; in a busy
2736 * repository, commits happen too fast to insist that everyone keep
2737 * their entire tree up-to-date at all times. Non-overlapping
2738 * changes should not interfere with each other.
2740 * The solution is to merge the changes between base and latest into
2741 * the txn tree [see the function merge()]. The txn tree is the
2742 * only one of the three trees that is mutable, so it has to be the
2745 * You might have to adjust it more than once, if a new latest
2746 * revision gets committed while you were merging in the previous
2749 * 1. Jane starts txn T, based at revision 6.
2750 * 2. Someone commits (or already committed) revision 7.
2751 * 3. Jane's starts merging the changes between 6 and 7 into T.
2752 * 4. Meanwhile, someone commits revision 8.
2753 * 5. Jane finishes the 6-->7 merge. T could now be committed
2754 * against a latest revision of 7, if only that were still the
2755 * latest. Unfortunately, 8 is now the latest, so...
2756 * 6. Jane starts merging the changes between 7 and 8 into T.
2757 * 7. Meanwhile, no one commits any new revisions. Whew.
2758 * 8. Jane commits T, creating revision 9, whose tree is exactly
2759 * T's tree, except immutable now.
2761 * Lather, rinse, repeat.
2765 svn_fs_t *fs = txn->fs;
2766 apr_pool_t *subpool = svn_pool_create(pool);
2768 /* Initialize output params. */
2769 *new_rev = SVN_INVALID_REVNUM;
2775 struct get_root_args get_root_args;
2776 struct merge_args merge_args;
2777 struct commit_args commit_args;
2778 svn_revnum_t youngish_rev;
2779 svn_fs_root_t *youngish_root;
2780 dag_node_t *youngish_root_node;
2782 svn_pool_clear(subpool);
2784 /* Get the *current* youngest revision, in one short-lived
2785 Berkeley transaction. (We don't want the revisions table
2786 locked while we do the main merge.) We call it "youngish"
2787 because new revisions might get committed after we've
2790 SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2791 SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2794 /* Get the dag node for the youngest revision, also in one
2795 Berkeley transaction. Later we'll use it as the SOURCE
2796 argument to a merge, and if the merge succeeds, this youngest
2797 root node will become the new base root for the svn txn that
2798 was the target of the merge (but note that the youngest rev
2799 may have changed by then -- that's why we're careful to get
2800 this root in its own bdb txn here). */
2801 get_root_args.root = youngish_root;
2802 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2804 youngish_root_node = get_root_args.node;
2806 /* Try to merge. If the merge succeeds, the base root node of
2807 TARGET's txn will become the same as youngish_root_node, so
2808 any future merges will only be between that node and whatever
2809 the root node of the youngest rev is by then. */
2810 merge_args.ancestor_node = NULL;
2811 merge_args.source_node = youngish_root_node;
2812 merge_args.txn = txn;
2813 merge_args.conflict = svn_stringbuf_create_empty(pool); /* use pool */
2814 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args,
2818 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2819 *conflict_p = merge_args.conflict->data;
2820 return svn_error_trace(err);
2823 /* Try to commit. */
2824 commit_args.txn = txn;
2825 err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2827 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2829 /* Did someone else finish committing a new revision while we
2830 were in mid-merge or mid-commit? If so, we'll need to
2831 loop again to merge the new changes in, then try to
2832 commit again. Or if that's not what happened, then just
2833 return the error. */
2834 svn_revnum_t youngest_rev;
2835 svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2839 svn_error_clear(err);
2840 return svn_error_trace(err2); /* err2 is bad,
2841 it should not occur */
2843 else if (youngest_rev == youngish_rev)
2844 return svn_error_trace(err);
2846 svn_error_clear(err);
2850 return svn_error_trace(err);
2854 /* Set the return value -- our brand spankin' new revision! */
2855 *new_rev = commit_args.new_rev;
2860 svn_pool_destroy(subpool);
2861 return SVN_NO_ERROR;
2864 /* Note: it is acceptable for this function to call back into
2865 public FS API interfaces because it does not itself use trails. */
2866 static svn_error_t *
2867 base_merge(const char **conflict_p,
2868 svn_fs_root_t *source_root,
2869 const char *source_path,
2870 svn_fs_root_t *target_root,
2871 const char *target_path,
2872 svn_fs_root_t *ancestor_root,
2873 const char *ancestor_path,
2876 dag_node_t *source, *ancestor;
2877 struct get_root_args get_root_args;
2878 struct merge_args merge_args;
2883 if (! target_root->is_txn_root)
2884 return SVN_FS__NOT_TXN(target_root);
2887 fs = ancestor_root->fs;
2888 if ((source_root->fs != fs) || (target_root->fs != fs))
2890 return svn_error_create
2891 (SVN_ERR_FS_CORRUPT, NULL,
2892 _("Bad merge; ancestor, source, and target not all in same fs"));
2895 /* ### kff todo: is there any compelling reason to get the nodes in
2896 one db transaction? Right now we don't; txn_body_get_root() gets
2897 one node at a time. This will probably need to change:
2899 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2900 > svn_fs_merge needs to be a single transaction, to protect it against
2901 > people deleting parents of nodes it's working on, etc.
2904 /* Get the ancestor node. */
2905 get_root_args.root = ancestor_root;
2906 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2908 ancestor = get_root_args.node;
2910 /* Get the source node. */
2911 get_root_args.root = source_root;
2912 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2914 source = get_root_args.node;
2916 /* Open a txn for the txn root into which we're merging. */
2917 SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2919 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2920 merge_args.source_node = source;
2921 merge_args.ancestor_node = ancestor;
2922 merge_args.txn = txn;
2923 merge_args.conflict = svn_stringbuf_create_empty(pool);
2924 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, FALSE, pool);
2927 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2928 *conflict_p = merge_args.conflict->data;
2929 return svn_error_trace(err);
2932 return SVN_NO_ERROR;
2936 struct rev_get_txn_id_args
2938 const char **txn_id;
2939 svn_revnum_t revision;
2943 static svn_error_t *
2944 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2946 struct rev_get_txn_id_args *args = baton;
2947 return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2948 args->revision, trail, trail->pool);
2953 svn_fs_base__deltify(svn_fs_t *fs,
2954 svn_revnum_t revision,
2957 svn_fs_root_t *root;
2959 struct rev_get_txn_id_args args;
2960 base_fs_data_t *bfd = fs->fsap_data;
2962 if (bfd->format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
2965 svn_revnum_t forward_delta_rev = 0;
2967 SVN_ERR(svn_fs_base__miscellaneous_get
2968 (&val, fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE, pool));
2970 SVN_ERR(svn_revnum_parse(&forward_delta_rev, val, NULL));
2972 /* ### FIXME: Unnecessarily harsh requirement? (cmpilato). */
2973 if (revision <= forward_delta_rev)
2974 return svn_error_createf
2975 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
2976 _("Cannot deltify revisions prior to r%ld"), forward_delta_rev+1);
2979 SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2981 args.txn_id = &txn_id;
2982 args.revision = revision;
2983 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args,
2986 return deltify_mutable(fs, root, "/", NULL, svn_node_dir, txn_id, pool);
2990 /* Modifying directories */
2993 struct make_dir_args
2995 svn_fs_root_t *root;
3000 static svn_error_t *
3001 txn_body_make_dir(void *baton,
3004 struct make_dir_args *args = baton;
3005 svn_fs_root_t *root = args->root;
3006 const char *path = args->path;
3007 parent_path_t *parent_path;
3008 dag_node_t *sub_dir;
3009 const char *txn_id = root->txn;
3011 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3012 txn_id, trail, trail->pool));
3014 /* If there's already a sub-directory by that name, complain. This
3015 also catches the case of trying to make a subdirectory named `/'. */
3016 if (parent_path->node)
3017 return SVN_FS__ALREADY_EXISTS(root, path);
3019 /* Check to see if some lock is 'reserving' a file-path or dir-path
3020 at that location, or even some child-path; if so, check that we
3022 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3024 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3025 trail, trail->pool));
3028 /* Create the subdirectory. */
3029 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3030 trail, trail->pool));
3031 SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
3032 parent_path->parent->node,
3033 parent_path_path(parent_path->parent,
3037 trail, trail->pool));
3039 /* Make a record of this modification in the changes table. */
3040 return add_change(root->fs, txn_id, path,
3041 svn_fs_base__dag_get_id(sub_dir),
3042 svn_fs_path_change_add, FALSE, FALSE,
3043 trail, trail->pool);
3047 static svn_error_t *
3048 base_make_dir(svn_fs_root_t *root,
3052 struct make_dir_args args;
3054 if (! root->is_txn_root)
3055 return SVN_FS__NOT_TXN(root);
3059 return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args,
3066 svn_fs_root_t *root;
3071 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
3072 basename of PATH is missing from its parent, that is, the final
3073 target of the deletion is missing. */
3074 static svn_error_t *
3075 txn_body_delete(void *baton,
3078 struct delete_args *args = baton;
3079 svn_fs_root_t *root = args->root;
3080 const char *path = args->path;
3081 parent_path_t *parent_path;
3082 const char *txn_id = root->txn;
3083 base_fs_data_t *bfd = trail->fs->fsap_data;
3085 if (! root->is_txn_root)
3086 return SVN_FS__NOT_TXN(root);
3088 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
3089 trail, trail->pool));
3091 /* We can't remove the root of the filesystem. */
3092 if (! parent_path->parent)
3093 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
3094 _("The root directory cannot be deleted"));
3096 /* Check to see if path (or any child thereof) is locked; if so,
3097 check that we can use the existing lock(s). */
3098 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3100 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3101 trail, trail->pool));
3104 /* Make the parent directory mutable. */
3105 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3106 trail, trail->pool));
3108 /* Decrement mergeinfo counts on the parents of this node by the
3109 count it previously carried, if our format supports it. */
3110 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3112 apr_int64_t mergeinfo_count;
3113 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
3115 trail, trail->pool));
3116 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
3117 -mergeinfo_count, txn_id,
3118 trail, trail->pool));
3121 /* Do the deletion. */
3122 SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
3124 txn_id, trail, trail->pool));
3127 /* Make a record of this modification in the changes table. */
3128 return add_change(root->fs, txn_id, path,
3129 svn_fs_base__dag_get_id(parent_path->node),
3130 svn_fs_path_change_delete, FALSE, FALSE, trail,
3135 static svn_error_t *
3136 base_delete_node(svn_fs_root_t *root,
3140 struct delete_args args;
3144 return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args,
3151 svn_fs_root_t *from_root;
3152 const char *from_path;
3153 svn_fs_root_t *to_root;
3154 const char *to_path;
3155 svn_boolean_t preserve_history;
3159 static svn_error_t *
3160 txn_body_copy(void *baton,
3163 struct copy_args *args = baton;
3164 svn_fs_root_t *from_root = args->from_root;
3165 const char *from_path = args->from_path;
3166 svn_fs_root_t *to_root = args->to_root;
3167 const char *to_path = args->to_path;
3168 dag_node_t *from_node;
3169 parent_path_t *to_parent_path;
3170 const char *txn_id = to_root->txn;
3172 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
3173 SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
3175 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
3176 component does not exist, it's not that big a deal. We'll just
3178 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
3179 open_path_last_optional, txn_id, trail, trail->pool));
3181 /* Check to see if to-path (or any child thereof) is locked, or at
3182 least 'reserved', whether it exists or not; if so, check that we
3183 can use the existing lock(s). */
3184 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3186 SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
3187 trail, trail->pool));
3190 /* If the destination node already exists as the same node as the
3191 source (in other words, this operation would result in nothing
3192 happening at all), just do nothing an return successfully,
3193 proud that you saved yourself from a tiresome task. */
3194 if ((to_parent_path->node)
3195 && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
3196 svn_fs_base__dag_get_id
3197 (to_parent_path->node))
3198 == svn_fs_node_unchanged))
3199 return SVN_NO_ERROR;
3201 if (! from_root->is_txn_root)
3203 svn_fs_path_change_kind_t kind;
3204 dag_node_t *new_node;
3205 apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
3206 base_fs_data_t *bfd = trail->fs->fsap_data;
3208 /* If TO_PATH already existed prior to the copy, note that this
3209 operation is a replacement, not an addition. */
3210 if (to_parent_path->node)
3211 kind = svn_fs_path_change_replace;
3213 kind = svn_fs_path_change_add;
3215 /* Make sure the target node's parents are mutable. */
3216 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
3217 to_path, trail, trail->pool));
3219 /* If this is a replacement operation, we need to know the old
3220 node's mergeinfo count. */
3221 if (to_parent_path->node)
3222 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3223 &old_mergeinfo_count,
3224 to_parent_path->node,
3225 trail, trail->pool));
3227 SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
3228 to_parent_path->entry,
3230 args->preserve_history,
3232 from_path, txn_id, trail, trail->pool));
3234 /* Adjust the mergeinfo counts of the destination's parents if
3235 our format supports it. */
3236 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
3238 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
3242 SVN_ERR(adjust_parent_mergeinfo_counts
3243 (to_parent_path->parent,
3244 mergeinfo_count - old_mergeinfo_count,
3245 txn_id, trail, trail->pool));
3248 /* Make a record of this modification in the changes table. */
3249 SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
3250 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
3251 svn_fs_base__dag_get_id(new_node),
3252 kind, FALSE, FALSE, trail, trail->pool));
3256 /* See IZ Issue #436 */
3257 /* Copying from transaction roots not currently available.
3259 ### cmpilato todo someday: make this not so. :-) Note that
3260 when copying from mutable trees, you have to make sure that
3261 you aren't creating a cyclic graph filesystem, and a simple
3262 referencing operation won't cut it. Currently, we should not
3263 be able to reach this clause, and the interface reports that
3264 this only works from immutable trees anyway, but JimB has
3265 stated that this requirement need not be necessary in the
3268 SVN_ERR_MALFUNCTION();
3271 return SVN_NO_ERROR;
3275 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
3276 Use POOL for temporary allocation only.
3277 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
3278 static svn_error_t *
3279 fs_same_p(svn_boolean_t *same_p,
3284 *same_p = ! strcmp(fs1->uuid, fs2->uuid);
3285 return SVN_NO_ERROR;
3288 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
3289 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
3290 the copies table. Perform temporary allocations in POOL. */
3291 static svn_error_t *
3292 copy_helper(svn_fs_root_t *from_root,
3293 const char *from_path,
3294 svn_fs_root_t *to_root,
3295 const char *to_path,
3296 svn_boolean_t preserve_history,
3299 struct copy_args args;
3300 svn_boolean_t same_p;
3302 /* Use an error check, not an assert, because even the caller cannot
3303 guarantee that a filesystem's UUID has not changed "on the fly". */
3304 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3306 return svn_error_createf
3307 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3308 _("Cannot copy between two different filesystems ('%s' and '%s')"),
3309 from_root->fs->path, to_root->fs->path);
3311 if (! to_root->is_txn_root)
3312 return SVN_FS__NOT_TXN(to_root);
3314 if (from_root->is_txn_root)
3315 return svn_error_create
3316 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3317 _("Copy from mutable tree not currently supported"));
3319 args.from_root = from_root;
3320 args.from_path = from_path;
3321 args.to_root = to_root;
3322 args.to_path = to_path;
3323 args.preserve_history = preserve_history;
3325 return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args,
3329 static svn_error_t *
3330 base_copy(svn_fs_root_t *from_root,
3331 const char *from_path,
3332 svn_fs_root_t *to_root,
3333 const char *to_path,
3336 return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3340 static svn_error_t *
3341 base_revision_link(svn_fs_root_t *from_root,
3342 svn_fs_root_t *to_root,
3346 return copy_helper(from_root, path, to_root, path, FALSE, pool);
3350 struct copied_from_args
3352 svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */
3353 const char *path; /* Path for the node whose ancestry we seek. */
3355 svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */
3356 const char *result_path; /* Path, if any, of the ancestor. */
3358 apr_pool_t *pool; /* Allocate `result_path' here. */
3362 static svn_error_t *
3363 txn_body_copied_from(void *baton, trail_t *trail)
3365 struct copied_from_args *args = baton;
3366 const svn_fs_id_t *node_id, *pred_id;
3368 svn_fs_t *fs = args->root->fs;
3370 /* Clear the return variables. */
3371 args->result_path = NULL;
3372 args->result_rev = SVN_INVALID_REVNUM;
3374 /* Fetch the NODE in question. */
3375 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3376 node_id = svn_fs_base__dag_get_id(node);
3378 /* Check the node's predecessor-ID. If it doesn't have one, it
3380 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3381 trail, trail->pool));
3383 return SVN_NO_ERROR;
3385 /* If NODE's copy-ID is the same as that of its predecessor... */
3386 if (strcmp(svn_fs_base__id_copy_id(node_id),
3387 svn_fs_base__id_copy_id(pred_id)) != 0)
3389 /* ... then NODE was either the target of a copy operation,
3390 a copied subtree item. We examine the actual copy record
3391 to determine which is the case. */
3393 SVN_ERR(svn_fs_bdb__get_copy(©, fs,
3394 svn_fs_base__id_copy_id(node_id),
3395 trail, trail->pool));
3396 if ((copy->kind == copy_kind_real)
3397 && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3399 args->result_path = copy->src_path;
3400 SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3402 trail, trail->pool));
3405 return SVN_NO_ERROR;
3409 static svn_error_t *
3410 base_copied_from(svn_revnum_t *rev_p,
3411 const char **path_p,
3412 svn_fs_root_t *root,
3416 struct copied_from_args args;
3417 apr_pool_t *scratch_pool = svn_pool_create(pool);
3422 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_copied_from, &args,
3423 FALSE, scratch_pool));
3425 *rev_p = args.result_rev;
3426 *path_p = args.result_path ? apr_pstrdup(pool, args.result_path) : NULL;
3428 svn_pool_destroy(scratch_pool);
3429 return SVN_NO_ERROR;
3437 struct make_file_args
3439 svn_fs_root_t *root;
3444 static svn_error_t *
3445 txn_body_make_file(void *baton,
3448 struct make_file_args *args = baton;
3449 svn_fs_root_t *root = args->root;
3450 const char *path = args->path;
3451 parent_path_t *parent_path;
3453 const char *txn_id = root->txn;
3455 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3456 txn_id, trail, trail->pool));
3458 /* If there's already a file by that name, complain.
3459 This also catches the case of trying to make a file named `/'. */
3460 if (parent_path->node)
3461 return SVN_FS__ALREADY_EXISTS(root, path);
3463 /* Check to see if some lock is 'reserving' a file-path or dir-path
3464 at that location, or even some child-path; if so, check that we
3466 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3468 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3469 trail, trail->pool));
3472 /* Create the file. */
3473 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3474 trail, trail->pool));
3475 SVN_ERR(svn_fs_base__dag_make_file(&child,
3476 parent_path->parent->node,
3477 parent_path_path(parent_path->parent,
3481 trail, trail->pool));
3483 /* Make a record of this modification in the changes table. */
3484 return add_change(root->fs, txn_id, path,
3485 svn_fs_base__dag_get_id(child),
3486 svn_fs_path_change_add, TRUE, FALSE,
3487 trail, trail->pool);
3491 static svn_error_t *
3492 base_make_file(svn_fs_root_t *root,
3496 struct make_file_args args;
3500 return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args,
3506 struct file_length_args
3508 svn_fs_root_t *root;
3510 svn_filesize_t length; /* OUT parameter */
3513 static svn_error_t *
3514 txn_body_file_length(void *baton,
3517 struct file_length_args *args = baton;
3520 /* First create a dag_node_t from the root/path pair. */
3521 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3523 /* Now fetch its length */
3524 return svn_fs_base__dag_file_length(&args->length, file,
3525 trail, trail->pool);
3528 static svn_error_t *
3529 base_file_length(svn_filesize_t *length_p,
3530 svn_fs_root_t *root,
3534 struct file_length_args args;
3538 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3541 *length_p = args.length;
3542 return SVN_NO_ERROR;
3546 struct file_checksum_args
3548 svn_fs_root_t *root;
3550 svn_checksum_kind_t kind;
3551 svn_checksum_t **checksum; /* OUT parameter */
3554 static svn_error_t *
3555 txn_body_file_checksum(void *baton,
3558 struct file_checksum_args *args = baton;
3561 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3563 return svn_fs_base__dag_file_checksum(args->checksum, args->kind, file,
3564 trail, trail->pool);
3567 static svn_error_t *
3568 base_file_checksum(svn_checksum_t **checksum,
3569 svn_checksum_kind_t kind,
3570 svn_fs_root_t *root,
3574 struct file_checksum_args args;
3575 apr_pool_t *scratch_pool = svn_pool_create(pool);
3580 args.checksum = checksum;
3581 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3582 FALSE, scratch_pool));
3583 *checksum = svn_checksum_dup(*checksum, pool);
3584 svn_pool_destroy(scratch_pool);
3585 return SVN_NO_ERROR;
3589 /* --- Machinery for svn_fs_file_contents() --- */
3592 /* Local baton type for txn_body_get_file_contents. */
3593 typedef struct file_contents_baton_t
3595 /* The file we want to read. */
3596 svn_fs_root_t *root;
3599 /* The dag_node that will be made from the above. */
3602 /* The pool in which `file_stream' (below) is allocated. */
3605 /* The readable file stream that will be made from the
3606 dag_node. (And returned to the caller.) */
3607 svn_stream_t *file_stream;
3609 } file_contents_baton_t;
3612 /* Main body of svn_fs_file_contents; converts a root/path pair into
3613 a readable file stream (in the context of a db txn). */
3614 static svn_error_t *
3615 txn_body_get_file_contents(void *baton, trail_t *trail)
3617 file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3619 /* First create a dag_node_t from the root/path pair. */
3620 SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3622 /* Then create a readable stream from the dag_node_t. */
3623 return svn_fs_base__dag_get_contents(&(fb->file_stream),
3624 fb->node, trail, fb->pool);
3629 static svn_error_t *
3630 base_file_contents(svn_stream_t **contents,
3631 svn_fs_root_t *root,
3635 file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3640 /* Create the readable stream in the context of a db txn. */
3641 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents, fb,
3644 *contents = fb->file_stream;
3645 return SVN_NO_ERROR;
3648 /* --- End machinery for svn_fs_file_contents() --- */
3652 /* --- Machinery for svn_fs_apply_textdelta() --- */
3655 /* Local baton type for all the helper functions below. */
3656 typedef struct txdelta_baton_t
3658 /* This is the custom-built window consumer given to us by the delta
3659 library; it uniquely knows how to read data from our designated
3660 "source" stream, interpret the window, and write data to our
3661 designated "target" stream (in this case, our repos file.) */
3662 svn_txdelta_window_handler_t interpreter;
3663 void *interpreter_baton;
3665 /* The original file info */
3666 svn_fs_root_t *root;
3669 /* Derived from the file info */
3672 svn_stream_t *source_stream;
3673 svn_stream_t *target_stream;
3674 svn_stream_t *string_stream;
3675 svn_stringbuf_t *target_string;
3677 /* Checksums for the base text against which a delta is to be
3678 applied, and for the resultant fulltext, respectively. Either or
3679 both may be null, in which case ignored. */
3680 svn_checksum_t *base_checksum;
3681 svn_checksum_t *result_checksum;
3683 /* Pool used by db txns */
3689 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3690 * This closes BATON->target_stream.
3692 * Note: If you're confused about how this function relates to another
3693 * of similar name, think of it this way:
3695 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3696 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3698 static svn_error_t *
3699 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3701 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3702 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3703 tb->result_checksum,
3705 trail, trail->pool));
3707 /* Make a record of this modification in the changes table. */
3708 return add_change(tb->root->fs, tb->root->txn, tb->path,
3709 svn_fs_base__dag_get_id(tb->node),
3710 svn_fs_path_change_modify, TRUE, FALSE, trail,
3715 /* ### see comment in window_consumer() regarding this function. */
3717 /* Helper function of generic type `svn_write_fn_t'. Implements a
3718 writable stream which appends to an svn_stringbuf_t. */
3719 static svn_error_t *
3720 write_to_string(void *baton, const char *data, apr_size_t *len)
3722 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3723 svn_stringbuf_appendbytes(tb->target_string, data, *len);
3724 return SVN_NO_ERROR;
3729 /* The main window handler returned by svn_fs_apply_textdelta. */
3730 static svn_error_t *
3731 window_consumer(svn_txdelta_window_t *window, void *baton)
3733 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3735 /* Send the window right through to the custom window interpreter.
3736 In theory, the interpreter will then write more data to
3737 cb->target_string. */
3738 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3740 /* ### the write_to_string() callback for the txdelta's output stream
3741 ### should be doing all the flush determination logic, not here.
3742 ### in a drastic case, a window could generate a LOT more than the
3743 ### maximum buffer size. we want to flush to the underlying target
3744 ### stream much sooner (e.g. also in a streamy fashion). also, by
3745 ### moving this logic inside the stream, the stream becomes nice
3746 ### and encapsulated: it holds all the logic about buffering and
3749 ### further: I believe the buffering should be removed from tree.c
3750 ### the buffering should go into the target_stream itself, which
3751 ### is defined by reps-string.c. Specifically, I think the
3752 ### rep_write_contents() function will handle the buffering and
3753 ### the spill to the underlying DB. by locating it there, then
3754 ### anybody who gets a writable stream for FS content can take
3755 ### advantage of the buffering capability. this will be important
3756 ### when we export an FS API function for writing a fulltext into
3757 ### the FS, rather than forcing that fulltext thru apply_textdelta.
3760 /* Check to see if we need to purge the portion of the contents that
3761 have been written thus far. */
3762 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3764 apr_size_t len = tb->target_string->len;
3765 SVN_ERR(svn_stream_write(tb->target_stream,
3766 tb->target_string->data,
3768 svn_stringbuf_setempty(tb->target_string);
3771 /* Is the window NULL? If so, we're done. */
3774 /* Close the internal-use stream. ### This used to be inside of
3775 txn_body_fulltext_finalize_edits(), but that invoked a nested
3776 Berkeley DB transaction -- scandalous! */
3777 SVN_ERR(svn_stream_close(tb->target_stream));
3779 /* Tell the dag subsystem that we're finished with our edits. */
3780 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3781 txn_body_txdelta_finalize_edits, tb,
3785 return SVN_NO_ERROR;
3789 static svn_error_t *
3790 txn_body_apply_textdelta(void *baton, trail_t *trail)
3792 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3793 parent_path_t *parent_path;
3794 const char *txn_id = tb->root->txn;
3796 /* Call open_path with no flags, as we want this to return an error
3797 if the node for which we are searching doesn't exist. */
3798 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3799 trail, trail->pool));
3801 /* Check to see if path is locked; if so, check that we can use it. */
3802 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3803 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3804 trail, trail->pool));
3806 /* Now, make sure this path is mutable. */
3807 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3808 trail, trail->pool));
3809 tb->node = parent_path->node;
3811 if (tb->base_checksum)
3813 svn_checksum_t *checksum;
3815 /* Until we finalize the node, its data_key points to the old
3816 contents, in other words, the base text. */
3817 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum,
3818 tb->base_checksum->kind,
3819 tb->node, trail, trail->pool));
3820 /* TODO: This only compares checksums if they are the same kind, but
3821 we're calculating both SHA1 and MD5 checksums somewhere in
3822 reps-strings.c. Could we keep them both around somehow so this
3823 check could be more comprehensive? */
3824 if (!svn_checksum_match(tb->base_checksum, checksum))
3825 return svn_checksum_mismatch_err(tb->base_checksum, checksum,
3827 _("Base checksum mismatch on '%s'"),
3831 /* Make a readable "source" stream out of the current contents of
3832 ROOT/PATH; obviously, this must done in the context of a db_txn.
3833 The stream is returned in tb->source_stream. */
3834 SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3835 tb->node, trail, tb->pool));
3837 /* Make a writable "target" stream */
3838 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3839 txn_id, trail, tb->pool));
3841 /* Make a writable "string" stream which writes data to
3842 tb->target_string. */
3843 tb->target_string = svn_stringbuf_create_empty(tb->pool);
3844 tb->string_stream = svn_stream_create(tb, tb->pool);
3845 svn_stream_set_write(tb->string_stream, write_to_string);
3847 /* Now, create a custom window handler that uses our two streams. */
3848 svn_txdelta_apply(tb->source_stream,
3854 &(tb->interpreter_baton));
3856 return SVN_NO_ERROR;
3860 static svn_error_t *
3861 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3862 void **contents_baton_p,
3863 svn_fs_root_t *root,
3865 svn_checksum_t *base_checksum,
3866 svn_checksum_t *result_checksum,
3869 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3874 tb->base_checksum = svn_checksum_dup(base_checksum, pool);
3875 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
3877 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3880 *contents_p = window_consumer;
3881 *contents_baton_p = tb;
3882 return SVN_NO_ERROR;
3885 /* --- End machinery for svn_fs_apply_textdelta() --- */
3887 /* --- Machinery for svn_fs_apply_text() --- */
3889 /* Baton for svn_fs_apply_text(). */
3892 /* The original file info */
3893 svn_fs_root_t *root;
3896 /* Derived from the file info */
3899 /* The returned stream that will accept the file's new contents. */
3900 svn_stream_t *stream;
3902 /* The actual fs stream that the returned stream will write to. */
3903 svn_stream_t *file_stream;
3905 /* Checksum for the final fulltext written to the file. May
3906 be null, in which case ignored. */
3907 svn_checksum_t *result_checksum;
3909 /* Pool used by db txns */
3914 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3915 * fulltext data, not text deltas. Closes BATON->file_stream.
3917 * Note: If you're confused about how this function relates to another
3918 * of similar name, think of it this way:
3920 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3921 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3923 static svn_error_t *
3924 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3926 struct text_baton_t *tb = baton;
3927 SVN_ERR(svn_fs_base__dag_finalize_edits(tb->node,
3928 tb->result_checksum,
3930 trail, trail->pool));
3932 /* Make a record of this modification in the changes table. */
3933 return add_change(tb->root->fs, tb->root->txn, tb->path,
3934 svn_fs_base__dag_get_id(tb->node),
3935 svn_fs_path_change_modify, TRUE, FALSE, trail,
3939 /* Write function for the publically returned stream. */
3940 static svn_error_t *
3941 text_stream_writer(void *baton,
3945 struct text_baton_t *tb = baton;
3947 /* Psst, here's some data. Pass it on to the -real- file stream. */
3948 return svn_stream_write(tb->file_stream, data, len);
3951 /* Close function for the publically returned stream. */
3952 static svn_error_t *
3953 text_stream_closer(void *baton)
3955 struct text_baton_t *tb = baton;
3957 /* Close the internal-use stream. ### This used to be inside of
3958 txn_body_fulltext_finalize_edits(), but that invoked a nested
3959 Berkeley DB transaction -- scandalous! */
3960 SVN_ERR(svn_stream_close(tb->file_stream));
3962 /* Need to tell fs that we're done sending text */
3963 return svn_fs_base__retry_txn(tb->root->fs,
3964 txn_body_fulltext_finalize_edits, tb,
3969 static svn_error_t *
3970 txn_body_apply_text(void *baton, trail_t *trail)
3972 struct text_baton_t *tb = baton;
3973 parent_path_t *parent_path;
3974 const char *txn_id = tb->root->txn;
3976 /* Call open_path with no flags, as we want this to return an error
3977 if the node for which we are searching doesn't exist. */
3978 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3979 trail, trail->pool));
3981 /* Check to see if path is locked; if so, check that we can use it. */
3982 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3983 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3984 trail, trail->pool));
3986 /* Now, make sure this path is mutable. */
3987 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3988 trail, trail->pool));
3989 tb->node = parent_path->node;
3991 /* Make a writable stream for replacing the file's text. */
3992 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3993 txn_id, trail, tb->pool));
3995 /* Create a 'returnable' stream which writes to the file_stream. */
3996 tb->stream = svn_stream_create(tb, tb->pool);
3997 svn_stream_set_write(tb->stream, text_stream_writer);
3998 svn_stream_set_close(tb->stream, text_stream_closer);
4000 return SVN_NO_ERROR;
4004 static svn_error_t *
4005 base_apply_text(svn_stream_t **contents_p,
4006 svn_fs_root_t *root,
4008 svn_checksum_t *result_checksum,
4011 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
4016 tb->result_checksum = svn_checksum_dup(result_checksum, pool);
4018 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb,
4021 *contents_p = tb->stream;
4022 return SVN_NO_ERROR;
4025 /* --- End machinery for svn_fs_apply_text() --- */
4028 /* Note: we're sharing the `things_changed_args' struct with
4029 svn_fs_props_changed(). */
4031 static svn_error_t *
4032 txn_body_contents_changed(void *baton, trail_t *trail)
4034 struct things_changed_args *args = baton;
4035 dag_node_t *node1, *node2;
4036 svn_checksum_t *checksum1, *checksum2;
4037 svn_stream_t *stream1, *stream2;
4040 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
4041 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
4042 SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
4043 node1, node2, trail, trail->pool));
4045 /* Is there a potential false positive and do we want to correct it? */
4046 if (!args->strict || !*args->changed_p)
4047 return SVN_NO_ERROR;
4049 /* Different representations. They might still have equal contents. */
4051 /* Compare MD5 checksums. These should be readily accessible. */
4052 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5,
4053 node1, trail, trail->pool));
4054 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5,
4055 node2, trail, trail->pool));
4057 /* Different MD5 checksums -> different contents */
4058 if (!svn_checksum_match(checksum1, checksum2))
4059 return SVN_NO_ERROR;
4061 /* Paranoia. Compare SHA1 checksums because that's the level of
4062 confidence we require for e.g. the working copy. */
4063 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1,
4064 node1, trail, trail->pool));
4065 SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1,
4066 node2, trail, trail->pool));
4068 /* Different SHA1 checksums -> different contents */
4069 if (checksum1 && checksum2)
4071 *args->changed_p = !svn_checksum_match(checksum1, checksum2);
4072 return SVN_NO_ERROR;
4075 /* SHA1 checksums are not available for very old reps / repositories. */
4076 SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool));
4077 SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool));
4078 SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool));
4080 /* Now, it's definitive. */
4081 *args->changed_p = !same;
4082 return SVN_NO_ERROR;
4086 /* Note: it is acceptable for this function to call back into
4087 top-level interfaces because it does not itself use trails. */
4088 static svn_error_t *
4089 base_contents_changed(svn_boolean_t *changed_p,
4090 svn_fs_root_t *root1,
4092 svn_fs_root_t *root2,
4094 svn_boolean_t strict,
4097 struct things_changed_args args;
4099 /* Check that roots are in the same fs. */
4100 if (root1->fs != root2->fs)
4101 return svn_error_create
4102 (SVN_ERR_FS_GENERAL, NULL,
4103 _("Cannot compare file contents between two different filesystems"));
4105 /* Check that both paths are files. */
4107 svn_node_kind_t kind;
4109 SVN_ERR(base_check_path(&kind, root1, path1, pool));
4110 if (kind != svn_node_file)
4111 return svn_error_createf
4112 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
4114 SVN_ERR(base_check_path(&kind, root2, path2, pool));
4115 if (kind != svn_node_file)
4116 return svn_error_createf
4117 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
4124 args.changed_p = changed_p;
4126 args.strict = strict;
4128 return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
4134 /* Public interface to computing file text deltas. */
4136 /* Note: it is acceptable for this function to call back into
4137 public FS API interfaces because it does not itself use trails. */
4138 static svn_error_t *
4139 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
4140 svn_fs_root_t *source_root,
4141 const char *source_path,
4142 svn_fs_root_t *target_root,
4143 const char *target_path,
4146 svn_stream_t *source, *target;
4147 svn_txdelta_stream_t *delta_stream;
4149 /* Get read functions for the source file contents. */
4150 if (source_root && source_path)
4151 SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
4153 source = svn_stream_empty(pool);
4155 /* Get read functions for the target file contents. */
4156 SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
4158 /* Create a delta stream that turns the ancestor into the target. */
4159 svn_txdelta2(&delta_stream, source, target, TRUE, pool);
4161 *stream_p = delta_stream;
4162 return SVN_NO_ERROR;
4167 /* Finding Changes */
4169 struct paths_changed_args
4171 apr_hash_t *changes;
4172 svn_fs_root_t *root;
4176 static svn_error_t *
4177 txn_body_paths_changed(void *baton,
4180 /* WARNING: This is called *without* the protection of a Berkeley DB
4181 transaction. If you modify this function, keep that in mind. */
4183 struct paths_changed_args *args = baton;
4185 svn_fs_t *fs = args->root->fs;
4187 /* Get the transaction ID from ROOT. */
4188 if (! args->root->is_txn_root)
4189 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
4190 trail, trail->pool));
4192 txn_id = args->root->txn;
4194 return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
4195 trail, trail->pool);
4199 static svn_error_t *
4200 base_paths_changed(apr_hash_t **changed_paths_p,
4201 svn_fs_root_t *root,
4204 struct paths_changed_args args;
4206 args.changes = NULL;
4207 SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args,
4209 *changed_paths_p = args.changes;
4210 return SVN_NO_ERROR;
4215 /* Our coolio opaque history object. */
4216 typedef struct base_history_data_t
4218 /* filesystem object */
4221 /* path and revision of historical location */
4223 svn_revnum_t revision;
4225 /* internal-use hints about where to resume the history search. */
4226 const char *path_hint;
4227 svn_revnum_t rev_hint;
4229 /* FALSE until the first call to svn_fs_history_prev(). */
4230 svn_boolean_t is_interesting;
4231 } base_history_data_t;
4234 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
4235 svn_revnum_t revision,
4236 svn_boolean_t is_interesting,
4237 const char *path_hint,
4238 svn_revnum_t rev_hint,
4242 static svn_error_t *
4243 base_node_history(svn_fs_history_t **history_p,
4244 svn_fs_root_t *root,
4246 apr_pool_t *result_pool,
4247 apr_pool_t *scratch_pool)
4249 svn_node_kind_t kind;
4251 /* We require a revision root. */
4252 if (root->is_txn_root)
4253 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
4255 /* And we require that the path exist in the root. */
4256 SVN_ERR(base_check_path(&kind, root, path, scratch_pool));
4257 if (kind == svn_node_none)
4258 return SVN_FS__NOT_FOUND(root, path);
4260 /* Okay, all seems well. Build our history object and return it. */
4261 *history_p = assemble_history(root->fs,
4262 svn_fs__canonicalize_abspath(path,
4264 root->rev, FALSE, NULL,
4265 SVN_INVALID_REVNUM, result_pool);
4266 return SVN_NO_ERROR;
4270 /* Examine the PARENT_PATH structure chain to determine how copy IDs
4271 would be doled out in the event that PARENT_PATH was made mutable.
4272 Return the ID of the copy that last affected PARENT_PATH (and the
4273 COPY itself, if we've already fetched it).
4275 static svn_error_t *
4276 examine_copy_inheritance(const char **copy_id,
4279 parent_path_t *parent_path,
4283 /* The default response -- our current copy ID, and no fetched COPY. */
4284 *copy_id = svn_fs_base__id_copy_id
4285 (svn_fs_base__dag_get_id(parent_path->node));
4288 /* If we have no parent (we are looking at the root node), or if
4289 this node is supposed to inherit from itself, return that fact. */
4290 if (! parent_path->parent)
4291 return SVN_NO_ERROR;
4293 /* We could be a branch destination (which would answer our question
4294 altogether)! But then, again, we might just have been modified
4295 in this revision, so all bets are off. */
4296 if (parent_path->copy_inherit == copy_id_inherit_self)
4298 /* A copy ID of "0" means we've never been branched. Therefore,
4299 there are no copies relevant to our history. */
4300 if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
4301 return SVN_NO_ERROR;
4303 /* Get the COPY record. If it was a real copy (not an implicit
4304 one), we have our answer. Otherwise, we fall through to the
4306 SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
4307 if ((*copy)->kind != copy_kind_soft)
4308 return SVN_NO_ERROR;
4311 /* Otherwise, our answer is dependent upon our parent. */
4312 return examine_copy_inheritance(copy_id, copy, fs,
4313 parent_path->parent, trail, pool);
4317 struct history_prev_args
4319 svn_fs_history_t **prev_history_p;
4320 svn_fs_history_t *history;
4321 svn_boolean_t cross_copies;
4326 static svn_error_t *
4327 txn_body_history_prev(void *baton, trail_t *trail)
4329 struct history_prev_args *args = baton;
4330 svn_fs_history_t **prev_history = args->prev_history_p;
4331 svn_fs_history_t *history = args->history;
4332 base_history_data_t *bhd = history->fsap_data;
4333 const char *commit_path, *src_path, *path = bhd->path;
4334 svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4335 apr_pool_t *retpool = args->pool;
4336 svn_fs_t *fs = bhd->fs;
4337 parent_path_t *parent_path;
4339 svn_fs_root_t *root;
4340 const svn_fs_id_t *node_id;
4341 const char *end_copy_id = NULL;
4342 struct revision_root_args rr_args;
4343 svn_boolean_t reported = bhd->is_interesting;
4345 copy_t *copy = NULL;
4346 svn_boolean_t retry = FALSE;
4348 /* Initialize our return value. */
4349 *prev_history = NULL;
4351 /* If our last history report left us hints about where to pickup
4352 the chase, then our last report was on the destination of a
4353 copy. If we are crossing copies, start from those locations,
4354 otherwise, we're all done here. */
4355 if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4358 if (! args->cross_copies)
4359 return SVN_NO_ERROR;
4360 path = bhd->path_hint;
4361 revision = bhd->rev_hint;
4364 /* Construct a ROOT for the current revision. */
4365 rr_args.root_p = &root;
4366 rr_args.rev = revision;
4367 SVN_ERR(txn_body_revision_root(&rr_args, trail));
4369 /* Open PATH/REVISION, and get its node and a bunch of other
4371 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4373 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4374 trail, trail->pool));
4375 node = parent_path->node;
4376 node_id = svn_fs_base__dag_get_id(node);
4377 commit_path = svn_fs_base__dag_get_created_path(node);
4378 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4379 trail, trail->pool));
4381 /* The Subversion filesystem is written in such a way that a given
4382 line of history may have at most one interesting history point
4383 per filesystem revision. Either that node was edited (and
4384 possibly copied), or it was copied but not edited. And a copy
4385 source cannot be from the same revision as its destination. So,
4386 if our history revision matches its node's commit revision, we
4388 if (revision == commit_rev)
4392 /* ... we either have not yet reported on this revision (and
4393 need now to do so) ... */
4394 *prev_history = assemble_history(fs,
4395 apr_pstrdup(retpool, commit_path),
4396 commit_rev, TRUE, NULL,
4397 SVN_INVALID_REVNUM, retpool);
4398 return SVN_NO_ERROR;
4402 /* ... or we *have* reported on this revision, and must now
4403 progress toward this node's predecessor (unless there is
4404 no predecessor, in which case we're all done!). */
4405 const svn_fs_id_t *pred_id;
4407 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4408 trail, trail->pool));
4410 return SVN_NO_ERROR;
4412 /* Replace NODE and friends with the information from its
4414 SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4415 trail, trail->pool));
4416 node_id = svn_fs_base__dag_get_id(node);
4417 commit_path = svn_fs_base__dag_get_created_path(node);
4418 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4419 trail, trail->pool));
4423 /* Calculate a possibly relevant copy ID. */
4424 SVN_ERR(examine_copy_inheritance(&end_copy_id, ©, fs,
4425 parent_path, trail, trail->pool));
4427 /* Initialize some state variables. */
4429 src_rev = SVN_INVALID_REVNUM;
4430 dst_rev = SVN_INVALID_REVNUM;
4432 /* If our current copy ID (which is either the real copy ID of our
4433 node, or the last copy ID which would affect our node if it were
4434 to be made mutable) diffs at all from that of its predecessor
4435 (which is either a real predecessor, or is the node itself
4436 playing the predecessor role to an imaginary mutable successor),
4437 then we need to report a copy. */
4438 if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)
4440 const char *remainder;
4441 dag_node_t *dst_node;
4442 const char *copy_dst;
4444 /* Get the COPY record if we haven't already fetched it. */
4446 SVN_ERR(svn_fs_bdb__get_copy(©, fs, end_copy_id, trail,
4449 /* Figure out the destination path of the copy operation. */
4450 SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4451 copy->dst_noderev_id,
4452 trail, trail->pool));
4453 copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4455 /* If our current path was the very destination of the copy,
4456 then our new current path will be the copy source. If our
4457 current path was instead the *child* of the destination of
4458 the copy, then figure out its previous location by taking its
4459 path relative to the copy destination and appending that to
4460 the copy source. Finally, if our current path doesn't meet
4461 one of these other criteria ... ### for now just fallback to
4462 the old copy hunt algorithm. */
4463 remainder = svn_fspath__skip_ancestor(copy_dst, path);
4467 /* If we get here, then our current path is the destination
4468 of, or the child of the destination of, a copy. Fill
4469 in the return values and get outta here. */
4470 SVN_ERR(svn_fs_base__txn_get_revision
4471 (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4472 SVN_ERR(svn_fs_base__txn_get_revision
4474 svn_fs_base__id_txn_id(copy->dst_noderev_id),
4475 trail, trail->pool));
4476 src_path = svn_fspath__join(copy->src_path, remainder,
4478 if (copy->kind == copy_kind_soft)
4483 /* If we calculated a copy source path and revision, and the
4484 copy source revision doesn't pre-date a revision in which we
4485 *know* our node was modified, we'll make a 'copy-style' history
4487 if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4489 /* It's possible for us to find a copy location that is the same
4490 as the history point we've just reported. If that happens,
4491 we simply need to take another trip through this history
4493 if ((dst_rev == revision) && reported)
4496 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4498 src_path, src_rev, retpool);
4502 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4503 commit_rev, TRUE, NULL,
4504 SVN_INVALID_REVNUM, retpool);
4507 return SVN_NO_ERROR;
4511 static svn_error_t *
4512 base_history_prev(svn_fs_history_t **prev_history_p,
4513 svn_fs_history_t *history,
4514 svn_boolean_t cross_copies,
4515 apr_pool_t *result_pool,
4516 apr_pool_t *scratch_pool)
4518 svn_fs_history_t *prev_history = NULL;
4519 base_history_data_t *bhd = history->fsap_data;
4520 svn_fs_t *fs = bhd->fs;
4522 /* Special case: the root directory changes in every single
4523 revision, no exceptions. And, the root can't be the target (or
4524 child of a target -- duh) of a copy. So, if that's our path,
4525 then we need only decrement our revision by 1, and there you go. */
4526 if (strcmp(bhd->path, "/") == 0)
4528 if (! bhd->is_interesting)
4529 prev_history = assemble_history(fs, "/", bhd->revision,
4530 1, NULL, SVN_INVALID_REVNUM,
4532 else if (bhd->revision > 0)
4533 prev_history = assemble_history(fs, "/", bhd->revision - 1,
4534 1, NULL, SVN_INVALID_REVNUM,
4539 struct history_prev_args args;
4540 prev_history = history;
4544 /* Get a trail, and get to work. */
4546 args.prev_history_p = &prev_history;
4547 args.history = prev_history;
4548 args.cross_copies = cross_copies;
4549 args.pool = result_pool;
4550 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4551 FALSE, result_pool));
4554 bhd = prev_history->fsap_data;
4555 if (bhd->is_interesting)
4560 *prev_history_p = prev_history;
4561 return SVN_NO_ERROR;
4565 static svn_error_t *
4566 base_history_location(const char **path,
4567 svn_revnum_t *revision,
4568 svn_fs_history_t *history,
4571 base_history_data_t *bhd = history->fsap_data;
4573 *path = apr_pstrdup(pool, bhd->path);
4574 *revision = bhd->revision;
4575 return SVN_NO_ERROR;
4579 static history_vtable_t history_vtable = {
4581 base_history_location
4586 struct closest_copy_args
4588 svn_fs_root_t **root_p;
4589 const char **path_p;
4590 svn_fs_root_t *root;
4596 static svn_error_t *
4597 txn_body_closest_copy(void *baton, trail_t *trail)
4599 struct closest_copy_args *args = baton;
4600 svn_fs_root_t *root = args->root;
4601 const char *path = args->path;
4602 svn_fs_t *fs = root->fs;
4603 parent_path_t *parent_path;
4604 const svn_fs_id_t *node_id;
4605 const char *txn_id, *copy_id;
4606 copy_t *copy = NULL;
4607 svn_fs_root_t *copy_dst_root;
4608 dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4609 const char *copy_dst_path;
4610 svn_revnum_t copy_dst_rev, created_rev;
4613 *(args->path_p) = NULL;
4614 *(args->root_p) = NULL;
4616 /* Get the transaction ID associated with our root. */
4617 if (root->is_txn_root)
4620 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4621 trail, trail->pool));
4623 /* Open PATH in ROOT -- it must exist. */
4624 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4625 trail, trail->pool));
4626 node_id = svn_fs_base__dag_get_id(parent_path->node);
4628 /* Now, examine the copy inheritance rules in play should our path
4629 be made mutable in the future (if it isn't already). This will
4630 tell us about the youngest affecting copy. */
4631 SVN_ERR(examine_copy_inheritance(©_id, ©, fs, parent_path,
4632 trail, trail->pool));
4634 /* Easy out: if the copy ID is 0, there's nothing of interest here. */
4635 if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4636 return SVN_NO_ERROR;
4638 /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4640 SVN_ERR(svn_fs_bdb__get_copy(©, fs, copy_id, trail, trail->pool));
4642 /* Figure out the destination path and revision of the copy operation. */
4643 SVN_ERR(svn_fs_base__dag_get_node(©_dst_node, fs, copy->dst_noderev_id,
4644 trail, trail->pool));
4645 copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4646 SVN_ERR(svn_fs_base__dag_get_revision(©_dst_rev, copy_dst_node,
4647 trail, trail->pool));
4649 /* Turn that revision into a revision root. */
4650 SVN_ERR(svn_fs_base__dag_revision_root(©_dst_root_node, fs,
4651 copy_dst_rev, trail, args->pool));
4652 copy_dst_root = make_revision_root(fs, copy_dst_rev,
4653 copy_dst_root_node, args->pool);
4655 /* It is possible that this node was created from scratch at some
4656 revision between COPY_DST_REV and the transaction associated with
4657 our ROOT. Make sure that PATH exists as of COPY_DST_REV and is
4658 related to this node-rev. */
4659 err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4660 trail, trail->pool);
4663 if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4664 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4666 svn_error_clear(err);
4667 return SVN_NO_ERROR;
4669 return svn_error_trace(err);
4671 if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4672 || (! (svn_fs_base__id_check_related
4673 (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4675 return SVN_NO_ERROR;
4678 /* One final check must be done here. If you copy a directory and
4679 create a new entity somewhere beneath that directory in the same
4680 txn, then we can't claim that the copy affected the new entity.
4681 For example, if you do:
4684 create dir2/new-thing
4687 then dir2/new-thing was not affected by the copy of dir1 to dir2.
4688 We detect this situation by asking if PATH@COPY_DST_REV's
4689 created-rev is COPY_DST_REV, and that node-revision has no
4690 predecessors, then there is no relevant closest copy.
4692 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4693 trail, trail->pool));
4694 if (created_rev == copy_dst_rev)
4696 const svn_fs_id_t *pred_id;
4697 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4698 path_node_in_copy_dst,
4699 trail, trail->pool));
4701 return SVN_NO_ERROR;
4704 *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4705 *(args->root_p) = copy_dst_root;
4707 return SVN_NO_ERROR;
4711 static svn_error_t *
4712 base_closest_copy(svn_fs_root_t **root_p,
4713 const char **path_p,
4714 svn_fs_root_t *root,
4718 struct closest_copy_args args;
4719 svn_fs_t *fs = root->fs;
4720 svn_fs_root_t *closest_root = NULL;
4721 const char *closest_path = NULL;
4723 args.root_p = &closest_root;
4724 args.path_p = &closest_path;
4728 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args,
4730 *root_p = closest_root;
4731 *path_p = closest_path;
4732 return SVN_NO_ERROR;
4736 /* Return a new history object (marked as "interesting") for PATH and
4737 REVISION, allocated in POOL, and with its members set to the values
4738 of the parameters provided. Note that PATH and PATH_HINT are not
4739 duped into POOL -- it is the responsibility of the caller to ensure
4740 that this happens. */
4741 static svn_fs_history_t *
4742 assemble_history(svn_fs_t *fs,
4744 svn_revnum_t revision,
4745 svn_boolean_t is_interesting,
4746 const char *path_hint,
4747 svn_revnum_t rev_hint,
4750 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4751 base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4753 bhd->revision = revision;
4754 bhd->is_interesting = is_interesting;
4755 bhd->path_hint = path_hint;
4756 bhd->rev_hint = rev_hint;
4758 history->vtable = &history_vtable;
4759 history->fsap_data = bhd;
4765 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4770 svn_revnum_t head_rev;
4771 svn_fs_root_t *root;
4772 dag_node_t *root_dir, *path_node;
4775 /* Get HEAD revision, */
4776 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4778 /* Then convert it into a root_t, */
4779 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4781 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4783 /* And get the dag_node for path in the root_t. */
4784 err = get_dag(&path_node, root, path, trail, pool);
4785 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4787 svn_error_clear(err);
4788 *kind = svn_node_none;
4789 return SVN_NO_ERROR;
4792 return svn_error_trace(err);
4794 *kind = svn_fs_base__dag_node_kind(path_node);
4795 return SVN_NO_ERROR;
4800 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4805 svn_revnum_t head_rev, created_rev;
4806 svn_fs_root_t *root;
4807 dag_node_t *root_dir, *path_node;
4810 /* Get HEAD revision, */
4811 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4813 /* Then convert it into a root_t, */
4814 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4816 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4818 /* And get the dag_node for path in the root_t. */
4819 err = get_dag(&path_node, root, path, trail, pool);
4820 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4822 svn_error_clear(err);
4823 *rev = SVN_INVALID_REVNUM;
4824 return SVN_NO_ERROR;
4827 return svn_error_trace(err);
4829 /* Find the created_rev of the dag_node. */
4830 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4834 return SVN_NO_ERROR;
4839 /*** Finding the Origin of a Line of History ***/
4841 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4842 represent the location at which PATH in FS was located immediately
4843 prior to REVISION iff there was a copy operation (to PATH or one of
4844 its parent directories) between that previous location and
4847 If there was no such copy operation in that portion of PATH's
4848 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4850 WARNING: Do *not* call this from inside a trail. */
4851 static svn_error_t *
4852 prev_location(const char **prev_path,
4853 svn_revnum_t *prev_rev,
4855 svn_fs_root_t *root,
4859 const char *copy_path, *copy_src_path, *remainder;
4860 svn_fs_root_t *copy_root;
4861 svn_revnum_t copy_src_rev;
4863 /* Ask about the most recent copy which affected PATH@REVISION. If
4864 there was no such copy, we're done. */
4865 SVN_ERR(base_closest_copy(©_root, ©_path, root, path, pool));
4868 *prev_rev = SVN_INVALID_REVNUM;
4870 return SVN_NO_ERROR;
4873 /* Ultimately, it's not the path of the closest copy's source that
4874 we care about -- it's our own path's location in the copy source
4875 revision. So we'll tack the relative path that expresses the
4876 difference between the copy destination and our path in the copy
4877 revision onto the copy source path to determine this information.
4879 In other words, if our path is "/branches/my-branch/foo/bar", and
4880 we know that the closest relevant copy was a copy of "/trunk" to
4881 "/branches/my-branch", then that relative path under the copy
4882 destination is "/foo/bar". Tacking that onto the copy source
4883 path tells us that our path was located at "/trunk/foo/bar"
4886 SVN_ERR(base_copied_from(©_src_rev, ©_src_path,
4887 copy_root, copy_path, pool));
4888 remainder = svn_fspath__skip_ancestor(copy_path, path);
4889 *prev_path = svn_fspath__join(copy_src_path, remainder, pool);
4890 *prev_rev = copy_src_rev;
4891 return SVN_NO_ERROR;
4895 struct id_created_rev_args {
4896 svn_revnum_t revision;
4897 const svn_fs_id_t *id;
4902 static svn_error_t *
4903 txn_body_id_created_rev(void *baton, trail_t *trail)
4905 struct id_created_rev_args *args = baton;
4908 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4909 trail, trail->pool));
4910 return svn_fs_base__dag_get_revision(&(args->revision), node,
4911 trail, trail->pool);
4915 struct get_set_node_origin_args {
4916 const svn_fs_id_t *origin_id;
4917 const char *node_id;
4921 static svn_error_t *
4922 txn_body_get_node_origin(void *baton, trail_t *trail)
4924 struct get_set_node_origin_args *args = baton;
4925 return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4926 args->node_id, trail, trail->pool);
4929 static svn_error_t *
4930 txn_body_set_node_origin(void *baton, trail_t *trail)
4932 struct get_set_node_origin_args *args = baton;
4933 return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4934 args->origin_id, trail, trail->pool);
4937 static svn_error_t *
4938 base_node_origin_rev(svn_revnum_t *revision,
4939 svn_fs_root_t *root,
4943 svn_fs_t *fs = root->fs;
4944 base_fs_data_t *bfd = fs->fsap_data;
4945 struct get_set_node_origin_args args;
4946 const svn_fs_id_t *origin_id = NULL;
4947 struct id_created_rev_args icr_args;
4949 /* Canonicalize the input path so that the path-math that
4950 prev_location() does below will work. */
4951 path = svn_fs__canonicalize_abspath(path, pool);
4953 /* Special-case the root node (for performance reasons) */
4954 if (strcmp(path, "/") == 0)
4957 return SVN_NO_ERROR;
4960 /* If we have support for the node-origins table, we'll try to use
4962 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
4964 const svn_fs_id_t *id;
4967 SVN_ERR(base_node_id(&id, root, path, pool));
4968 args.node_id = svn_fs_base__id_node_id(id);
4969 err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin, &args,
4972 /* If we got a value for the origin node-revision-ID, that's
4973 great. If we didn't, that's sad but non-fatal -- we'll just
4974 figure it out the hard way, then record it so we don't have
4975 suffer again the next time. */
4978 origin_id = args.origin_id;
4980 else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4982 svn_error_clear(err);
4988 /* If we haven't yet found a node origin ID, we'll go spelunking for one. */
4991 svn_fs_root_t *curroot = root;
4992 apr_pool_t *subpool = svn_pool_create(pool);
4993 apr_pool_t *predidpool = svn_pool_create(pool);
4994 svn_stringbuf_t *lastpath =
4995 svn_stringbuf_create(path, pool);
4996 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4997 const svn_fs_id_t *pred_id;
4999 /* Walk the closest-copy chain back to the first copy in our history.
5001 NOTE: We merely *assume* that this is faster than walking the
5002 predecessor chain, because we *assume* that copies of parent
5003 directories happen less often than modifications to a given item. */
5006 svn_revnum_t currev;
5007 const char *curpath = lastpath->data;
5009 /* Get a root pointing to LASTREV. (The first time around,
5010 LASTREV is invalid, but that's cool because CURROOT is
5011 already initialized.) */
5012 if (SVN_IS_VALID_REVNUM(lastrev))
5013 SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
5016 /* Find the previous location using the closest-copy shortcut. */
5017 SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
5022 /* Update our LASTPATH and LASTREV variables (which survive
5024 svn_stringbuf_set(lastpath, curpath);
5028 /* Walk the predecessor links back to origin. */
5029 SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
5032 struct txn_pred_id_args pid_args;
5033 svn_pool_clear(subpool);
5034 pid_args.id = pred_id;
5035 pid_args.pred_id = NULL;
5036 pid_args.pool = subpool;
5037 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &pid_args,
5039 if (! pid_args.pred_id)
5041 svn_pool_clear(predidpool);
5042 pred_id = svn_fs_base__id_copy(pid_args.pred_id, predidpool);
5045 /* Okay. PRED_ID should hold our origin ID now. */
5046 origin_id = svn_fs_base__id_copy(pred_id, pool);
5048 /* If our filesystem version supports it, let's remember this
5049 value from now on. */
5050 if (bfd->format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
5052 args.origin_id = origin_id;
5053 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
5054 &args, TRUE, subpool));
5057 svn_pool_destroy(predidpool);
5058 svn_pool_destroy(subpool);
5061 /* Okay. We have an origin node-revision-ID. Let's get a created
5062 revision from it. */
5063 icr_args.id = origin_id;
5064 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev, &icr_args,
5066 *revision = icr_args.revision;
5067 return SVN_NO_ERROR;
5072 /* Mergeinfo Queries */
5075 /* Examine directory NODE's immediately children for mergeinfo.
5077 For those which have explicit mergeinfo, invoke RECEIVER with
5080 For those which don't, but sit atop trees which contain mergeinfo
5081 somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
5082 hash mapping dirent names to dag_node_t * objects, allocated
5083 from that hash's pool.
5085 For those which neither have explicit mergeinfo nor sit atop trees
5086 which contain mergeinfo, ignore them.
5088 Use TRAIL->pool for temporary allocations. */
5090 struct get_mergeinfo_data_and_entries_baton
5092 apr_hash_t *children_atop_mergeinfo_trees;
5094 const char *node_path;
5095 svn_fs_mergeinfo_receiver_t receiver;
5096 void *receiver_baton;
5099 static svn_error_t *
5100 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
5102 struct get_mergeinfo_data_and_entries_baton *args = baton;
5103 dag_node_t *node = args->node;
5104 apr_hash_t *entries;
5105 apr_hash_index_t *hi;
5106 apr_pool_t *iterpool = svn_pool_create(trail->pool);
5107 apr_pool_t *children_pool =
5108 apr_hash_pool_get(args->children_atop_mergeinfo_trees);
5110 SVN_ERR_ASSERT(svn_fs_base__dag_node_kind(node) == svn_node_dir);
5112 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
5113 for (hi = apr_hash_first(trail->pool, entries); hi; hi = apr_hash_next(hi))
5116 svn_fs_dirent_t *dirent;
5117 const svn_fs_id_t *child_id;
5118 dag_node_t *child_node;
5119 svn_boolean_t has_mergeinfo;
5120 apr_int64_t kid_count;
5122 svn_pool_clear(iterpool);
5123 apr_hash_this(hi, NULL, NULL, &val);
5125 child_id = dirent->id;
5127 /* Get the node for this child. */
5128 SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
5131 /* Query the child node's mergeinfo stats. */
5132 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
5136 /* If the child has mergeinfo, add it to the result catalog. */
5140 svn_mergeinfo_t child_mergeinfo;
5144 SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
5146 pval = svn_hash_gets(plist, SVN_PROP_MERGEINFO);
5149 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5151 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5152 _("Node-revision '%s' claims to have "
5153 "mergeinfo but doesn't"),
5156 /* Issue #3896: If syntactically invalid mergeinfo is present on
5157 CHILD_NODE then treat it as if no mergeinfo is present rather
5158 than raising a parse error. */
5159 err = svn_mergeinfo_parse(&child_mergeinfo, pval->data,
5163 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5164 svn_error_clear(err);
5166 return svn_error_trace(err);
5170 SVN_ERR(args->receiver(svn_fspath__join(args->node_path,
5174 args->receiver_baton,
5179 /* If the child has descendants with mergeinfo -- that is, if
5180 the count of descendants beneath it carrying mergeinfo, not
5181 including itself, is non-zero -- then add it to the
5182 children_atop_mergeinfo_trees hash to be crawled later. */
5183 if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
5185 if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
5187 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
5189 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5190 _("Node-revision '%s' claims to sit "
5191 "atop a tree containing mergeinfo "
5192 "but is not a directory"),
5195 svn_hash_sets(args->children_atop_mergeinfo_trees,
5196 apr_pstrdup(children_pool, dirent->name),
5197 svn_fs_base__dag_dup(child_node, children_pool));
5201 svn_pool_destroy(iterpool);
5202 return SVN_NO_ERROR;
5205 static svn_error_t *
5206 crawl_directory_for_mergeinfo(svn_fs_t *fs,
5208 const char *node_path,
5209 svn_fs_mergeinfo_receiver_t receiver,
5213 struct get_mergeinfo_data_and_entries_baton gmdae_args;
5214 apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
5215 apr_hash_index_t *hi;
5216 apr_pool_t *iterpool;
5218 /* Add mergeinfo for immediate children that have it, and fetch
5219 immediate children that *don't* have it but sit atop trees that do. */
5220 gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
5221 gmdae_args.node = node;
5222 gmdae_args.node_path = node_path;
5223 gmdae_args.receiver = receiver;
5224 gmdae_args.receiver_baton = baton;
5225 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
5226 &gmdae_args, FALSE, pool));
5228 /* If no children sit atop trees with mergeinfo, we're done.
5229 Otherwise, recurse on those children. */
5231 if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
5232 return SVN_NO_ERROR;
5234 iterpool = svn_pool_create(pool);
5235 for (hi = apr_hash_first(pool, children_atop_mergeinfo_trees);
5237 hi = apr_hash_next(hi))
5241 svn_pool_clear(iterpool);
5242 apr_hash_this(hi, &key, NULL, &val);
5243 SVN_ERR(crawl_directory_for_mergeinfo(fs, val,
5244 svn_fspath__join(node_path, key,
5246 receiver, baton, iterpool));
5248 svn_pool_destroy(iterpool);
5249 return SVN_NO_ERROR;
5253 /* Calculate the mergeinfo for PATH under revision ROOT using
5254 inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to
5255 NULL if there is none. Results are allocated in POOL; TRAIL->pool
5256 is used for temporary allocations. */
5258 struct get_mergeinfo_for_path_baton
5260 svn_mergeinfo_t *mergeinfo;
5261 svn_fs_root_t *root;
5263 svn_mergeinfo_inheritance_t inherit;
5264 svn_boolean_t adjust_inherited_mergeinfo;
5268 static svn_error_t *
5269 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
5271 struct get_mergeinfo_for_path_baton *args = baton;
5272 parent_path_t *parent_path, *nearest_ancestor;
5273 apr_hash_t *proplist;
5274 svn_string_t *mergeinfo_string;
5275 apr_pool_t *iterpool;
5276 dag_node_t *node = NULL;
5278 *(args->mergeinfo) = NULL;
5280 SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
5281 NULL, trail, trail->pool));
5283 /* Init the nearest ancestor. */
5284 nearest_ancestor = parent_path;
5285 if (args->inherit == svn_mergeinfo_nearest_ancestor)
5287 if (! parent_path->parent)
5288 return SVN_NO_ERROR;
5289 nearest_ancestor = parent_path->parent;
5292 iterpool = svn_pool_create(trail->pool);
5295 svn_boolean_t has_mergeinfo;
5298 svn_pool_clear(iterpool);
5300 node = nearest_ancestor->node;
5301 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
5302 node, trail, iterpool));
5306 /* No need to loop if we're looking for explicit mergeinfo. */
5307 if (args->inherit == svn_mergeinfo_explicit)
5309 svn_pool_destroy(iterpool);
5310 return SVN_NO_ERROR;
5313 nearest_ancestor = nearest_ancestor->parent;
5315 /* Run out? There's no mergeinfo. */
5316 if (! nearest_ancestor)
5318 svn_pool_destroy(iterpool);
5319 return SVN_NO_ERROR;
5322 svn_pool_destroy(iterpool);
5324 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
5325 mergeinfo_string = svn_hash_gets(proplist, SVN_PROP_MERGEINFO);
5326 if (! mergeinfo_string)
5328 svn_string_t *id_str =
5329 svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
5330 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
5331 _("Node-revision '%s' claims to have "
5332 "mergeinfo but doesn't"), id_str->data);
5335 /* Parse the mergeinfo; store the result in ARGS->MERGEINFO. */
5337 /* Issue #3896: If a node has syntactically invalid mergeinfo, then
5338 treat it as if no mergeinfo is present rather than raising a parse
5340 svn_error_t *err = svn_mergeinfo_parse(args->mergeinfo,
5341 mergeinfo_string->data,
5345 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
5347 svn_error_clear(err);
5349 args->mergeinfo = NULL;
5351 return svn_error_trace(err);
5355 /* If our nearest ancestor is the very path we inquired about, we
5356 can return the mergeinfo results directly. Otherwise, we're
5357 inheriting the mergeinfo, so we need to a) remove non-inheritable
5358 ranges and b) telescope the merged-from paths. */
5359 if (args->adjust_inherited_mergeinfo && (nearest_ancestor != parent_path))
5361 svn_mergeinfo_t tmp_mergeinfo;
5363 SVN_ERR(svn_mergeinfo_inheritable2(&tmp_mergeinfo, *args->mergeinfo,
5364 NULL, SVN_INVALID_REVNUM,
5365 SVN_INVALID_REVNUM, TRUE,
5366 trail->pool, trail->pool));
5367 SVN_ERR(svn_fs__append_to_merged_froms(args->mergeinfo, tmp_mergeinfo,
5368 parent_path_relpath(
5369 parent_path, nearest_ancestor,
5374 return SVN_NO_ERROR;
5377 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5378 and query its mergeinfo stats, setting HAS_MERGEINFO and
5379 CHILD_MERGEINFO_COUNT appropriately. */
5381 struct get_node_mergeinfo_stats_baton
5384 svn_boolean_t has_mergeinfo;
5385 apr_int64_t child_mergeinfo_count;
5386 svn_fs_root_t *root;
5390 static svn_error_t *
5391 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5393 struct get_node_mergeinfo_stats_baton *args = baton;
5395 SVN_ERR(get_dag(&(args->node), args->root, args->path,
5396 trail, trail->pool));
5397 return svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5398 &(args->child_mergeinfo_count),
5404 /* Find all the mergeinfo for a set of PATHS under ROOT and report it
5405 through RECEIVER with BATON. INHERITED, INCLUDE_DESCENDANTS and
5406 ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
5408 Allocate temporary values are allocated in SCRATCH_POOL. */
5409 static svn_error_t *
5410 get_mergeinfos_for_paths(svn_fs_root_t *root,
5411 const apr_array_header_t *paths,
5412 svn_mergeinfo_inheritance_t inherit,
5413 svn_boolean_t include_descendants,
5414 svn_boolean_t adjust_inherited_mergeinfo,
5415 svn_fs_mergeinfo_receiver_t receiver,
5417 apr_pool_t *scratch_pool)
5419 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5422 for (i = 0; i < paths->nelts; i++)
5424 svn_mergeinfo_t path_mergeinfo;
5425 struct get_mergeinfo_for_path_baton gmfp_args;
5426 const char *path = APR_ARRAY_IDX(paths, i, const char *);
5428 svn_pool_clear(iterpool);
5430 path = svn_fs__canonicalize_abspath(path, iterpool);
5432 /* Get the mergeinfo for PATH itself. */
5433 gmfp_args.mergeinfo = &path_mergeinfo;
5434 gmfp_args.root = root;
5435 gmfp_args.path = path;
5436 gmfp_args.inherit = inherit;
5437 gmfp_args.pool = iterpool;
5438 gmfp_args.adjust_inherited_mergeinfo = adjust_inherited_mergeinfo;
5439 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5440 txn_body_get_mergeinfo_for_path,
5441 &gmfp_args, FALSE, iterpool));
5443 SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
5445 /* If we're including descendants, do so. */
5446 if (include_descendants)
5448 svn_boolean_t do_crawl;
5449 struct get_node_mergeinfo_stats_baton gnms_args;
5451 /* Query the node and its mergeinfo stats. */
5452 gnms_args.root = root;
5453 gnms_args.path = path;
5454 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5455 txn_body_get_node_mergeinfo_stats,
5456 &gnms_args, FALSE, iterpool));
5458 /* Determine if there's anything worth crawling here. */
5459 if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5462 do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5463 || ((gnms_args.child_mergeinfo_count == 1)
5464 && (! gnms_args.has_mergeinfo)));
5466 /* If it's worth crawling, crawl. */
5468 SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5469 path, receiver, baton,
5473 svn_pool_destroy(iterpool);
5475 return SVN_NO_ERROR;
5479 /* Implements svn_fs_get_mergeinfo. */
5480 static svn_error_t *
5481 base_get_mergeinfo(svn_fs_root_t *root,
5482 const apr_array_header_t *paths,
5483 svn_mergeinfo_inheritance_t inherit,
5484 svn_boolean_t include_descendants,
5485 svn_boolean_t adjust_inherited_mergeinfo,
5486 svn_fs_mergeinfo_receiver_t receiver,
5488 apr_pool_t *scratch_pool)
5490 /* Verify that our filesystem version supports mergeinfo stuff. */
5491 SVN_ERR(svn_fs_base__test_required_feature_format
5492 (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5494 /* We require a revision root. */
5495 if (root->is_txn_root)
5496 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5498 /* Retrieve a path -> mergeinfo mapping. */
5499 return get_mergeinfos_for_paths(root, paths,
5500 inherit, include_descendants,
5501 adjust_inherited_mergeinfo,
5508 /* Creating root objects. */
5511 static root_vtable_t root_vtable = {
5518 base_node_created_rev,
5519 base_node_origin_rev,
5520 base_node_created_path,
5528 base_node_has_props,
5529 base_change_node_prop,
5532 base_dir_optimal_order,
5539 base_apply_textdelta,
5541 base_contents_changed,
5542 base_get_file_delta_stream,
5548 /* Construct a new root object in FS, allocated from POOL. */
5549 static svn_fs_root_t *
5550 make_root(svn_fs_t *fs,
5553 svn_fs_root_t *root = apr_pcalloc(pool, sizeof(*root));
5554 base_root_data_t *brd = apr_palloc(pool, sizeof(*brd));
5559 /* Init the node ID cache. */
5560 brd->node_cache = apr_hash_make(pool);
5561 brd->node_cache_idx = 0;
5562 root->vtable = &root_vtable;
5563 root->fsap_data = brd;
5569 /* Construct a root object referring to the root of REVISION in FS,
5570 whose root directory is ROOT_DIR. Create the new root in POOL. */
5571 static svn_fs_root_t *
5572 make_revision_root(svn_fs_t *fs,
5574 dag_node_t *root_dir,
5577 svn_fs_root_t *root = make_root(fs, pool);
5578 base_root_data_t *brd = root->fsap_data;
5580 root->is_txn_root = FALSE;
5582 brd->root_dir = root_dir;
5588 /* Construct a root object referring to the root of the transaction
5589 named TXN and based on revision BASE_REV in FS. FLAGS represents
5590 the behavior of the transaction. Create the new root in POOL. */
5591 static svn_fs_root_t *
5592 make_txn_root(svn_fs_t *fs,
5594 svn_revnum_t base_rev,
5598 svn_fs_root_t *root = make_root(fs, pool);
5599 root->is_txn_root = TRUE;
5600 root->txn = apr_pstrdup(root->pool, txn);
5601 root->txn_flags = flags;
5602 root->rev = base_rev;