Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / libsvn_fs_base / tree.c
blobfbb7f184d1db6e5163c4fbe3a3d83e76c7568447
1 /* tree.c : tree-like filesystem, built on DAG filesystem
3 * ====================================================================
4 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
19 /* The job of this layer is to take a filesystem with lots of node
20 sharing going on --- the real DAG filesystem as it appears in the
21 database --- and make it look and act like an ordinary tree
22 filesystem, with no sharing.
24 We do just-in-time cloning: you can walk from some unfinished
25 transaction's root down into directories and files shared with
26 committed revisions; as soon as you try to change something, the
27 appropriate nodes get cloned (and parent directory entries updated)
28 invisibly, behind your back. Any other references you have to
29 nodes that have been cloned by other changes, even made by other
30 processes, are automatically updated to point to the right clones. */
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36 #include "svn_private_config.h"
37 #include "svn_pools.h"
38 #include "svn_error.h"
39 #include "svn_path.h"
40 #include "svn_md5.h"
41 #include "svn_mergeinfo.h"
42 #include "svn_fs.h"
43 #include "svn_sorts.h"
44 #include "fs.h"
45 #include "err.h"
46 #include "trail.h"
47 #include "node-rev.h"
48 #include "key-gen.h"
49 #include "dag.h"
50 #include "tree.h"
51 #include "lock.h"
52 #include "revs-txns.h"
53 #include "id.h"
54 #include "bdb/txn-table.h"
55 #include "bdb/rev-table.h"
56 #include "bdb/nodes-table.h"
57 #include "bdb/changes-table.h"
58 #include "bdb/copies-table.h"
59 #include "bdb/node-origins-table.h"
60 #include "../libsvn_fs/fs-loader.h"
61 #include "private/svn_fs_util.h"
62 #include "private/svn_mergeinfo_private.h"
65 /* ### I believe this constant will become internal to reps-strings.c.
66 ### see the comment in window_consumer() for more information. */
68 /* ### the comment also seems to need tweaking: the log file stuff
69 ### is no longer an issue... */
70 /* Data written to the filesystem through the svn_fs_apply_textdelta()
71 interface is cached in memory until the end of the data stream, or
72 until a size trigger is hit. Define that trigger here (in bytes).
73 Setting the value to 0 will result in no filesystem buffering at
74 all. The value only really matters when dealing with file contents
75 bigger than the value itself. Above that point, large values here
76 allow the filesystem to buffer more data in memory before flushing
77 to the database, which increases memory usage but greatly decreases
78 the amount of disk access (and log-file generation) in database.
79 Smaller values will limit your overall memory consumption, but can
80 drastically hurt throughput by necessitating more write operations
81 to the database (which also generates more log-files). */
82 #define WRITE_BUFFER_SIZE 512000
84 /* The maximum number of cache items to maintain in the node cache. */
85 #define NODE_CACHE_MAX_KEYS 32
89 /* The root structure. */
91 /* Structure for svn_fs_root_t's node_cache hash values. */
92 struct dag_node_cache_t
94 dag_node_t *node; /* NODE to be cached. */
95 int idx; /* Index into the keys array for this cache item's key. */
96 apr_pool_t *pool; /* Pool in which NODE is allocated. */
100 typedef struct
103 /* For revision roots, this is a dag node for the revision's root
104 directory. For transaction roots, we open the root directory
105 afresh every time, since the root may have been cloned, or
106 the transaction may have disappeared altogether. */
107 dag_node_t *root_dir;
109 /* Cache structures, for mapping const char * PATH to const
110 struct dag_node_cache_t * structures.
112 ### Currently this is only used for revision roots. To be safe
113 for transaction roots, you must have the guarantee that there is
114 never more than a single transaction root per Subversion
115 transaction ever open at a given time -- having two roots open to
116 the same Subversion transaction would be a request for pain.
117 Also, you have to ensure that if a 'make_path_mutable()' fails for
118 any reason, you don't leave cached nodes for the portion of that
119 function that succeeded. In other words, this cache must never,
120 ever, lie. */
121 apr_hash_t *node_cache;
122 const char *node_cache_keys[NODE_CACHE_MAX_KEYS];
123 int node_cache_idx;
124 } base_root_data_t;
127 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
128 dag_node_t *root_dir,
129 apr_pool_t *pool);
131 static svn_fs_root_t *make_txn_root(svn_fs_t *fs, const char *txn,
132 svn_revnum_t base_rev, apr_uint32_t flags,
133 apr_pool_t *pool);
136 /*** Node Caching in the Roots. ***/
138 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
139 isn't cached. */
140 static dag_node_t *
141 dag_node_cache_get(svn_fs_root_t *root,
142 const char *path,
143 apr_pool_t *pool)
145 base_root_data_t *brd = root->fsap_data;
146 struct dag_node_cache_t *cache_item;
148 /* Assert valid input. */
149 assert(*path == '/');
151 /* Only allow revision roots. */
152 if (root->is_txn_root)
153 return NULL;
155 /* Look in the cache for our desired item. */
156 cache_item = apr_hash_get(brd->node_cache, path, APR_HASH_KEY_STRING);
157 if (cache_item)
158 return svn_fs_base__dag_dup(cache_item->node, pool);
160 return NULL;
164 /* Add the NODE for PATH to ROOT's node cache. Callers should *NOT*
165 call this unless they are adding a currently un-cached item to the
166 cache, or are replacing the NODE for PATH with a new (different)
167 one. */
168 static void
169 dag_node_cache_set(svn_fs_root_t *root,
170 const char *path,
171 dag_node_t *node)
173 base_root_data_t *brd = root->fsap_data;
174 const char *cache_path;
175 apr_pool_t *cache_pool;
176 struct dag_node_cache_t *cache_item;
177 int num_keys = apr_hash_count(brd->node_cache);
179 /* What? No POOL passed to this function?
181 To ensure that our cache values live as long as the svn_fs_root_t
182 in which they are ultimately stored, and to allow us to free()
183 them individually without harming the rest, they are each
184 allocated from a subpool of ROOT's pool. We'll keep one subpool
185 around for each cache slot -- as we start expiring stuff
186 to make room for more entries, we'll re-use the expired thing's
187 pool. */
189 /* Assert valid input and state. */
190 assert(*path == '/');
191 assert((brd->node_cache_idx <= num_keys)
192 && (num_keys <= NODE_CACHE_MAX_KEYS));
194 /* Only allow revision roots. */
195 if (root->is_txn_root)
196 return;
198 /* Special case: the caller wants us to replace an existing cached
199 node with a new one. If the callers aren't mindless, this should
200 only happen when a node is made mutable under a transaction
201 root, and that only happens once under that root. So, we'll be a
202 little bit sloppy here, and count on callers doing the right
203 thing. */
204 cache_item = apr_hash_get(brd->node_cache, path, APR_HASH_KEY_STRING);
205 if (cache_item)
207 /* ### This section is somehow broken. I don't know how, but it
208 ### is. And I don't want to spend any more time on it. So,
209 ### callers, use only revision root and don't try to update
210 ### an already-cached thing. -- cmpilato */
211 abort();
213 #if 0
214 int cache_index = cache_item->idx;
215 cache_path = brd->node_cache_keys[cache_index];
216 cache_pool = cache_item->pool;
217 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
219 /* Now, move the cache key reference to the end of the keys in
220 the keys array (unless it's already at the end). ### Yes,
221 it's a memmove(), but we're not talking about pages of memory
222 here. */
223 if (cache_index != (num_keys - 1))
225 int move_num = NODE_CACHE_MAX_KEYS - cache_index - 1;
226 memmove(brd->node_cache_keys + cache_index,
227 brd->node_cache_keys + cache_index + 1,
228 move_num * sizeof(const char *));
229 cache_index = num_keys - 1;
230 brd->node_cache_keys[cache_index] = cache_path;
233 /* Advance the cache pointers. */
234 cache_item->idx = cache_index;
235 brd->node_cache_idx = (cache_index + 1) % NODE_CACHE_MAX_KEYS;
236 return;
237 #endif
240 /* We're adding a new cache item. First, see if we have room for it
241 (otherwise, make some room). */
242 if (apr_hash_count(brd->node_cache) == NODE_CACHE_MAX_KEYS)
244 /* No room. Expire the oldest thing. */
245 cache_path = brd->node_cache_keys[brd->node_cache_idx];
246 cache_item = apr_hash_get(brd->node_cache, cache_path,
247 APR_HASH_KEY_STRING);
248 apr_hash_set(brd->node_cache, cache_path, APR_HASH_KEY_STRING, NULL);
249 cache_pool = cache_item->pool;
250 svn_pool_clear(cache_pool);
252 else
254 cache_pool = svn_pool_create(root->pool);
257 /* Make the cache item, allocated in its own pool. */
258 cache_item = apr_palloc(cache_pool, sizeof(*cache_item));
259 cache_item->node = svn_fs_base__dag_dup(node, cache_pool);
260 cache_item->idx = brd->node_cache_idx;
261 cache_item->pool = cache_pool;
263 /* Now add it to the cache. */
264 cache_path = apr_pstrdup(cache_pool, path);
265 apr_hash_set(brd->node_cache, cache_path, APR_HASH_KEY_STRING, cache_item);
266 brd->node_cache_keys[brd->node_cache_idx] = cache_path;
268 /* Advance the cache pointer. */
269 brd->node_cache_idx = (brd->node_cache_idx + 1) % NODE_CACHE_MAX_KEYS;
275 /* Creating transaction and revision root nodes. */
277 struct txn_root_args
279 svn_fs_root_t **root_p;
280 svn_fs_txn_t *txn;
284 static svn_error_t *
285 txn_body_txn_root(void *baton,
286 trail_t *trail)
288 struct txn_root_args *args = baton;
289 svn_fs_root_t **root_p = args->root_p;
290 svn_fs_txn_t *txn = args->txn;
291 svn_fs_t *fs = txn->fs;
292 const char *svn_txn_id = txn->id;
293 const svn_fs_id_t *root_id, *base_root_id;
294 svn_fs_root_t *root;
295 apr_hash_t *txnprops;
296 apr_uint32_t flags = 0;
298 /* Verify that the transaction actually exists. */
299 SVN_ERR(svn_fs_base__get_txn_ids(&root_id, &base_root_id, fs,
300 svn_txn_id, trail, trail->pool));
302 /* Look for special txn props that represent the 'flags' behavior of
303 the transaction. */
304 SVN_ERR(svn_fs_base__txn_proplist_in_trail(&txnprops, svn_txn_id, trail));
305 if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, APR_HASH_KEY_STRING))
306 flags |= SVN_FS_TXN_CHECK_OOD;
308 if (apr_hash_get(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS,
309 APR_HASH_KEY_STRING))
310 flags |= SVN_FS_TXN_CHECK_LOCKS;
312 root = make_txn_root(fs, svn_txn_id, txn->base_rev, flags, trail->pool);
314 *root_p = root;
315 return SVN_NO_ERROR;
319 svn_error_t *
320 svn_fs_base__txn_root(svn_fs_root_t **root_p,
321 svn_fs_txn_t *txn,
322 apr_pool_t *pool)
324 svn_fs_root_t *root;
325 struct txn_root_args args;
327 args.root_p = &root;
328 args.txn = txn;
329 SVN_ERR(svn_fs_base__retry_txn(txn->fs, txn_body_txn_root, &args, pool));
331 *root_p = root;
332 return SVN_NO_ERROR;
336 struct revision_root_args
338 svn_fs_root_t **root_p;
339 svn_revnum_t rev;
343 static svn_error_t *
344 txn_body_revision_root(void *baton,
345 trail_t *trail)
347 struct revision_root_args *args = baton;
348 dag_node_t *root_dir;
349 svn_fs_root_t *root;
351 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, args->rev,
352 trail, trail->pool));
353 root = make_revision_root(trail->fs, args->rev, root_dir, trail->pool);
355 *args->root_p = root;
356 return SVN_NO_ERROR;
360 svn_error_t *
361 svn_fs_base__revision_root(svn_fs_root_t **root_p,
362 svn_fs_t *fs,
363 svn_revnum_t rev,
364 apr_pool_t *pool)
366 struct revision_root_args args;
367 svn_fs_root_t *root;
369 SVN_ERR(svn_fs__check_fs(fs, TRUE));
371 args.root_p = &root;
372 args.rev = rev;
373 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_revision_root, &args, pool));
375 *root_p = root;
376 return SVN_NO_ERROR;
381 /* Getting dag nodes for roots. */
384 /* Set *NODE_P to a freshly opened dag node referring to the root
385 directory of ROOT, as part of TRAIL. */
386 static svn_error_t *
387 root_node(dag_node_t **node_p,
388 svn_fs_root_t *root,
389 trail_t *trail,
390 apr_pool_t *pool)
392 base_root_data_t *brd = root->fsap_data;
394 if (! root->is_txn_root)
396 /* It's a revision root, so we already have its root directory
397 opened. */
398 *node_p = svn_fs_base__dag_dup(brd->root_dir, pool);
399 return SVN_NO_ERROR;
401 else
403 /* It's a transaction root. Open a fresh copy. */
404 return svn_fs_base__dag_txn_root(node_p, root->fs, root->txn,
405 trail, pool);
410 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
411 necessary, as part of TRAIL. ROOT must be a transaction root. Use
412 ERROR_PATH in error messages. */
413 static svn_error_t *
414 mutable_root_node(dag_node_t **node_p,
415 svn_fs_root_t *root,
416 const char *error_path,
417 trail_t *trail,
418 apr_pool_t *pool)
420 if (root->is_txn_root)
421 return svn_fs_base__dag_clone_root(node_p, root->fs, root->txn,
422 trail, pool);
423 else
424 /* If it's not a transaction root, we can't change its contents. */
425 return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
430 /* Traversing directory paths. */
432 typedef enum copy_id_inherit_t
434 copy_id_inherit_unknown = 0,
435 copy_id_inherit_self,
436 copy_id_inherit_parent,
437 copy_id_inherit_new
439 } copy_id_inherit_t;
441 /* A linked list representing the path from a node up to a root
442 directory. We use this for cloning, and for operations that need
443 to deal with both a node and its parent directory. For example, a
444 `delete' operation needs to know that the node actually exists, but
445 also needs to change the parent directory. */
446 typedef struct parent_path_t
449 /* A node along the path. This could be the final node, one of its
450 parents, or the root. Every parent path ends with an element for
451 the root directory. */
452 dag_node_t *node;
454 /* The name NODE has in its parent directory. This is zero for the
455 root directory, which (obviously) has no name in its parent. */
456 char *entry;
458 /* The parent of NODE, or zero if NODE is the root directory. */
459 struct parent_path_t *parent;
461 /* The copy ID inheritence style. */
462 copy_id_inherit_t copy_inherit;
464 /* If copy ID inheritence style is copy_id_inherit_new, this is the
465 path which should be implicitly copied; otherwise, this is NULL. */
466 const char *copy_src_path;
468 } parent_path_t;
471 /* Return the FS path for the parent path chain object PARENT_PATH,
472 allocated in POOL. */
473 static const char *
474 parent_path_path(parent_path_t *parent_path,
475 apr_pool_t *pool)
477 const char *path_so_far = "/";
478 if (parent_path->parent)
479 path_so_far = parent_path_path(parent_path->parent, pool);
480 return parent_path->entry
481 ? svn_path_join(path_so_far, parent_path->entry, pool)
482 : path_so_far;
486 /* Return the FS path for the parent path chain object CHILD relative
487 to its ANCESTOR in the same chain, allocated in POOL. */
488 static const char *
489 parent_path_relpath(parent_path_t *child,
490 parent_path_t *ancestor,
491 apr_pool_t *pool)
493 const char *path_so_far = "";
494 parent_path_t *this_node = child;
495 while (this_node != ancestor)
497 assert(this_node != NULL);
498 path_so_far = svn_path_join(this_node->entry, path_so_far, pool);
499 this_node = this_node->parent;
501 return path_so_far;
505 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
506 event that immutable node CHILD in FS needs to be made mutable. If
507 the inheritance method is copy_id_inherit_new, also return a
508 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
509 for that path). CHILD must have a parent (it cannot be the root
510 node). TXN_ID is the transaction in which these items might be
511 mutable. */
512 static svn_error_t *
513 get_copy_inheritance(copy_id_inherit_t *inherit_p,
514 const char **copy_src_path,
515 svn_fs_t *fs,
516 parent_path_t *child,
517 const char *txn_id,
518 trail_t *trail,
519 apr_pool_t *pool)
521 const svn_fs_id_t *child_id, *parent_id;
522 const char *child_copy_id, *parent_copy_id;
523 const char *id_path = NULL;
525 /* Make some assertions about the function input. */
526 assert(child && child->parent && txn_id);
528 /* Initialize our return variables (default: self-inheritance). */
529 *inherit_p = copy_id_inherit_self;
530 *copy_src_path = NULL;
532 /* Initialize some convenience variables. */
533 child_id = svn_fs_base__dag_get_id(child->node);
534 parent_id = svn_fs_base__dag_get_id(child->parent->node);
535 child_copy_id = svn_fs_base__id_copy_id(child_id);
536 parent_copy_id = svn_fs_base__id_copy_id(parent_id);
538 /* Easy out: if this child is already mutable, we have nothing to do. */
539 if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
540 return SVN_NO_ERROR;
542 /* If the child and its parent are on the same branch, then the
543 child will inherit the copy ID of its parent when made mutable.
544 This is trivially detectable when the child and its parent have
545 the same copy ID. But that's not the sole indicator of
546 same-branchness. It might be the case that the parent was the
547 result of a copy, but the child has not yet been cloned for
548 mutability since that copy. Detection of this latter case
549 basically means making sure the copy IDs don't differ for some
550 other reason, such as that the child was the direct target of the
551 copy whose ID it has. There is a special case here, too -- if
552 the child's copy ID is the special ID "0", it can't have been the
553 target of any copy, and therefore must be on the same branch as
554 its parent. */
555 if ((strcmp(child_copy_id, "0") == 0)
556 || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0))
558 *inherit_p = copy_id_inherit_parent;
559 return SVN_NO_ERROR;
561 else
563 copy_t *copy;
564 SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, child_copy_id, trail, pool));
565 if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1)
567 *inherit_p = copy_id_inherit_parent;
568 return SVN_NO_ERROR;
572 /* If we get here, the child and its parent are not on speaking
573 terms -- there will be no parental inheritence handed down in
574 *this* generation. */
576 /* If the child was created at a different path than the one we are
577 expecting its clone to live, one of its parents must have been
578 created via a copy since the child was created. The child isn't
579 on the same branch as its parent (we caught those cases early);
580 it can't keep its current copy ID because there's been an
581 affecting copy (its clone won't be on the same branch as the
582 child is). That leaves only one course of action -- to assign
583 the child a brand new "soft" copy ID. */
584 id_path = svn_fs_base__dag_get_created_path(child->node);
585 if (strcmp(id_path, parent_path_path(child, pool)) != 0)
587 *inherit_p = copy_id_inherit_new;
588 *copy_src_path = id_path;
589 return SVN_NO_ERROR;
592 /* The node gets to keep its own ID. */
593 return SVN_NO_ERROR;
597 /* Allocate a new parent_path_t node from POOL, referring to NODE,
598 ENTRY, PARENT, and COPY_ID. */
599 static parent_path_t *
600 make_parent_path(dag_node_t *node,
601 char *entry,
602 parent_path_t *parent,
603 apr_pool_t *pool)
605 parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
606 parent_path->node = node;
607 parent_path->entry = entry;
608 parent_path->parent = parent;
609 parent_path->copy_inherit = copy_id_inherit_unknown;
610 parent_path->copy_src_path = NULL;
611 return parent_path;
615 /* Flags for open_path. */
616 typedef enum open_path_flags_t {
618 /* The last component of the PATH need not exist. (All parent
619 directories must exist, as usual.) If the last component doesn't
620 exist, simply leave the `node' member of the bottom parent_path
621 component zero. */
622 open_path_last_optional = 1
624 } open_path_flags_t;
627 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
628 *PARENT_PATH_P to a path from the node up to ROOT, allocated in
629 TRAIL->pool. The resulting *PARENT_PATH_P value is guaranteed to
630 contain at least one element, for the root directory.
632 If resulting *PARENT_PATH_P will eventually be made mutable and
633 modified, or if copy ID inheritance information is otherwise
634 needed, TXN_ID should be the ID of the mutability transaction. If
635 TXN_ID is NULL, no copy ID in heritance information will be
636 calculated for the *PARENT_PATH_P chain.
638 If FLAGS & open_path_last_optional is zero, return the error
639 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
640 non-zero, require all the parent directories to exist as normal,
641 but if the final path component doesn't exist, simply return a path
642 whose bottom `node' member is zero. This option is useful for
643 callers that create new nodes --- we find the parent directory for
644 them, and tell them whether the entry exists already.
646 NOTE: Public interfaces which only *read* from the filesystem
647 should not call this function directly, but should instead use
648 get_dag().
650 static svn_error_t *
651 open_path(parent_path_t **parent_path_p,
652 svn_fs_root_t *root,
653 const char *path,
654 int flags,
655 const char *txn_id,
656 trail_t *trail,
657 apr_pool_t *pool)
659 svn_fs_t *fs = root->fs;
660 const svn_fs_id_t *id;
661 dag_node_t *here; /* The directory we're currently looking at. */
662 parent_path_t *parent_path; /* The path from HERE up to the root. */
663 const char *rest; /* The portion of PATH we haven't traversed yet. */
664 const char *canon_path = svn_fs__canonicalize_abspath(path, pool);
665 const char *path_so_far = "/";
667 /* Make a parent_path item for the root node, using its own current
668 copy id. */
669 SVN_ERR(root_node(&here, root, trail, pool));
670 id = svn_fs_base__dag_get_id(here);
671 parent_path = make_parent_path(here, 0, 0, pool);
672 parent_path->copy_inherit = copy_id_inherit_self;
674 rest = canon_path + 1; /* skip the leading '/', it saves in iteration */
676 /* Whenever we are at the top of this loop:
677 - HERE is our current directory,
678 - ID is the node revision ID of HERE,
679 - REST is the path we're going to find in HERE, and
680 - PARENT_PATH includes HERE and all its parents. */
681 for (;;)
683 const char *next;
684 char *entry;
685 dag_node_t *child;
687 /* Parse out the next entry from the path. */
688 entry = svn_fs__next_entry_name(&next, rest, pool);
690 /* Calculate the path traversed thus far. */
691 path_so_far = svn_path_join(path_so_far, entry, pool);
693 if (*entry == '\0')
695 /* Given the behavior of svn_fs__next_entry_name(), this
696 happens when the path either starts or ends with a slash.
697 In either case, we stay put: the current directory stays
698 the same, and we add nothing to the parent path. */
699 child = here;
701 else
703 copy_id_inherit_t inherit;
704 const char *copy_path = NULL;
705 svn_error_t *err = SVN_NO_ERROR;
706 dag_node_t *cached_node;
708 /* If we found a directory entry, follow it. First, we
709 check our node cache, and, failing that, we hit the DAG
710 layer. */
711 cached_node = dag_node_cache_get(root, path_so_far, pool);
712 if (cached_node)
713 child = cached_node;
714 else
715 err = svn_fs_base__dag_open(&child, here, entry, trail, pool);
717 /* "file not found" requires special handling. */
718 if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
720 /* If this was the last path component, and the caller
721 said it was optional, then don't return an error;
722 just put a NULL node pointer in the path. */
724 svn_error_clear(err);
726 if ((flags & open_path_last_optional)
727 && (! next || *next == '\0'))
729 parent_path = make_parent_path(NULL, entry, parent_path,
730 pool);
731 break;
733 else
735 /* Build a better error message than svn_fs_base__dag_open
736 can provide, giving the root and full path name. */
737 return SVN_FS__NOT_FOUND(root, path);
741 /* Other errors we return normally. */
742 SVN_ERR(err);
744 /* Now, make a parent_path item for CHILD. */
745 parent_path = make_parent_path(child, entry, parent_path, pool);
746 if (txn_id)
748 SVN_ERR(get_copy_inheritance(&inherit, &copy_path,
749 fs, parent_path, txn_id,
750 trail, pool));
751 parent_path->copy_inherit = inherit;
752 parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
755 /* Cache the node we found (if it wasn't already cached). */
756 if (! cached_node)
757 dag_node_cache_set(root, path_so_far, child);
760 /* Are we finished traversing the path? */
761 if (! next)
762 break;
764 /* The path isn't finished yet; we'd better be in a directory. */
765 if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
766 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
767 apr_psprintf(pool, _("Failure opening '%s'"), path));
769 rest = next;
770 here = child;
773 *parent_path_p = parent_path;
774 return SVN_NO_ERROR;
778 /* Make the node referred to by PARENT_PATH mutable, if it isn't
779 already, as part of TRAIL. ROOT must be the root from which
780 PARENT_PATH descends. Clone any parent directories as needed.
781 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
782 ERROR_PATH in error messages. */
783 static svn_error_t *
784 make_path_mutable(svn_fs_root_t *root,
785 parent_path_t *parent_path,
786 const char *error_path,
787 trail_t *trail,
788 apr_pool_t *pool)
790 dag_node_t *cloned_node;
791 const char *txn_id = root->txn;
792 svn_fs_t *fs = root->fs;
794 /* Is the node mutable already? */
795 if (svn_fs_base__dag_check_mutable(parent_path->node, txn_id))
796 return SVN_NO_ERROR;
798 /* Are we trying to clone the root, or somebody's child node? */
799 if (parent_path->parent)
801 const svn_fs_id_t *parent_id;
802 const svn_fs_id_t *node_id = svn_fs_base__dag_get_id(parent_path->node);
803 const char *copy_id = NULL;
804 const char *copy_src_path = parent_path->copy_src_path;
805 copy_id_inherit_t inherit = parent_path->copy_inherit;
806 const char *clone_path;
808 /* We're trying to clone somebody's child. Make sure our parent
809 is mutable. */
810 SVN_ERR(make_path_mutable(root, parent_path->parent,
811 error_path, trail, pool));
813 switch (inherit)
815 case copy_id_inherit_parent:
816 parent_id = svn_fs_base__dag_get_id(parent_path->parent->node);
817 copy_id = svn_fs_base__id_copy_id(parent_id);
818 break;
820 case copy_id_inherit_new:
821 SVN_ERR(svn_fs_bdb__reserve_copy_id(&copy_id, fs, trail, pool));
822 break;
824 case copy_id_inherit_self:
825 copy_id = NULL;
826 break;
828 case copy_id_inherit_unknown:
829 default:
830 abort(); /* uh-oh -- somebody didn't calculate copy-ID
831 inheritance data. */
834 /* Now make this node mutable. */
835 clone_path = parent_path_path(parent_path->parent, pool);
836 SVN_ERR(svn_fs_base__dag_clone_child(&cloned_node,
837 parent_path->parent->node,
838 clone_path,
839 parent_path->entry,
840 copy_id, txn_id,
841 trail, pool));
843 /* If we just created a brand new copy ID, we need to store a
844 `copies' table entry for it, as well as a notation in the
845 transaction that should this transaction be terminated, our
846 new copy needs to be removed. */
847 if (inherit == copy_id_inherit_new)
849 const svn_fs_id_t *new_node_id =
850 svn_fs_base__dag_get_id(cloned_node);
851 SVN_ERR(svn_fs_bdb__create_copy(fs, copy_id, copy_src_path,
852 svn_fs_base__id_txn_id(node_id),
853 new_node_id,
854 copy_kind_soft, trail, pool));
855 SVN_ERR(svn_fs_base__add_txn_copy(fs, txn_id, copy_id,
856 trail, pool));
859 else
861 /* We're trying to clone the root directory. */
862 SVN_ERR(mutable_root_node(&cloned_node, root, error_path, trail, pool));
865 /* Update the PARENT_PATH link to refer to the clone. */
866 parent_path->node = cloned_node;
868 return SVN_NO_ERROR;
872 /* Walk up PARENT_PATH to the root of the tree, adjusting each node's
873 mergeinfo count by COUNT_DELTA as part of Subversion transaction
874 TXN_ID and TRAIL. Use POOL for allocations. */
875 static svn_error_t *
876 adjust_parent_mergeinfo_counts(parent_path_t *parent_path,
877 apr_int64_t count_delta,
878 const char *txn_id,
879 trail_t *trail,
880 apr_pool_t *pool)
882 apr_pool_t *iterpool;
883 parent_path_t *pp = parent_path;
885 if (count_delta == 0)
886 return SVN_NO_ERROR;
888 iterpool = svn_pool_create(pool);
890 while (pp)
892 svn_pool_clear(iterpool);
893 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(pp->node, count_delta,
894 txn_id, trail,
895 iterpool));
896 pp = pp->parent;
898 svn_pool_destroy(iterpool);
900 return SVN_NO_ERROR;
904 /* Open the node identified by PATH in ROOT, as part of TRAIL. Set
905 *DAG_NODE_P to the node we find, allocated in TRAIL->pool. Return
906 the error SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
907 static svn_error_t *
908 get_dag(dag_node_t **dag_node_p,
909 svn_fs_root_t *root,
910 const char *path,
911 trail_t *trail,
912 apr_pool_t *pool)
914 parent_path_t *parent_path;
915 dag_node_t *node = NULL;
917 /* Canonicalize the input PATH. */
918 path = svn_fs__canonicalize_abspath(path, pool);
920 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
921 node = dag_node_cache_get(root, path, pool);
922 if (! node)
924 /* Call open_path with no flags, as we want this to return an error
925 if the node for which we are searching doesn't exist. */
926 SVN_ERR(open_path(&parent_path, root, path, 0, NULL, trail, pool));
927 node = parent_path->node;
929 /* No need to cache our find -- open_path() will do that for us. */
932 *dag_node_p = node;
933 return SVN_NO_ERROR;
938 /* Populating the `changes' table. */
940 /* Add a change to the changes table in FS, keyed on transaction id
941 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
942 PATH (whose node revision id is--or was, in the case of a
943 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
944 occurred. Do all this as part of TRAIL. */
945 static svn_error_t *
946 add_change(svn_fs_t *fs,
947 const char *txn_id,
948 const char *path,
949 const svn_fs_id_t *noderev_id,
950 svn_fs_path_change_kind_t change_kind,
951 svn_boolean_t text_mod,
952 svn_boolean_t prop_mod,
953 trail_t *trail,
954 apr_pool_t *pool)
956 change_t change;
957 change.path = svn_fs__canonicalize_abspath(path, pool);
958 change.noderev_id = noderev_id;
959 change.kind = change_kind;
960 change.text_mod = text_mod;
961 change.prop_mod = prop_mod;
962 return svn_fs_bdb__changes_add(fs, txn_id, &change, trail, pool);
967 /* Generic node operations. */
970 struct node_id_args {
971 const svn_fs_id_t **id_p;
972 svn_fs_root_t *root;
973 const char *path;
977 static svn_error_t *
978 txn_body_node_id(void *baton, trail_t *trail)
980 struct node_id_args *args = baton;
981 dag_node_t *node;
983 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
984 *args->id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(node),
985 trail->pool);
987 return SVN_NO_ERROR;
991 static svn_error_t *
992 base_node_id(const svn_fs_id_t **id_p,
993 svn_fs_root_t *root,
994 const char *path,
995 apr_pool_t *pool)
997 base_root_data_t *brd = root->fsap_data;
999 if (! root->is_txn_root
1000 && (path[0] == '\0' || ((path[0] == '/') && (path[1] == '\0'))))
1002 /* Optimize the case where we don't need any db access at all.
1003 The root directory ("" or "/") node is stored in the
1004 svn_fs_root_t object, and never changes when it's a revision
1005 root, so we can just reach in and grab it directly. */
1006 *id_p = svn_fs_base__id_copy(svn_fs_base__dag_get_id(brd->root_dir),
1007 pool);
1009 else
1011 const svn_fs_id_t *id;
1012 struct node_id_args args;
1014 args.id_p = &id;
1015 args.root = root;
1016 args.path = path;
1018 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_id, &args,
1019 pool));
1020 *id_p = id;
1022 return SVN_NO_ERROR;
1026 struct node_created_rev_args {
1027 svn_revnum_t revision;
1028 svn_fs_root_t *root;
1029 const char *path;
1033 static svn_error_t *
1034 txn_body_node_created_rev(void *baton, trail_t *trail)
1036 struct node_created_rev_args *args = baton;
1037 dag_node_t *node;
1039 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1040 SVN_ERR(svn_fs_base__dag_get_revision(&(args->revision), node,
1041 trail, trail->pool));
1042 return SVN_NO_ERROR;
1046 static svn_error_t *
1047 base_node_created_rev(svn_revnum_t *revision,
1048 svn_fs_root_t *root,
1049 const char *path,
1050 apr_pool_t *pool)
1052 struct node_created_rev_args args;
1054 args.revision = SVN_INVALID_REVNUM;
1055 args.root = root;
1056 args.path = path;
1057 SVN_ERR(svn_fs_base__retry_txn
1058 (root->fs, txn_body_node_created_rev, &args, pool));
1059 *revision = args.revision;
1060 return SVN_NO_ERROR;
1064 struct node_created_path_args {
1065 const char **created_path;
1066 svn_fs_root_t *root;
1067 const char *path;
1071 static svn_error_t *
1072 txn_body_node_created_path(void *baton, trail_t *trail)
1074 struct node_created_path_args *args = baton;
1075 dag_node_t *node;
1077 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1078 *args->created_path = svn_fs_base__dag_get_created_path(node);
1079 return SVN_NO_ERROR;
1083 static svn_error_t *
1084 base_node_created_path(const char **created_path,
1085 svn_fs_root_t *root,
1086 const char *path,
1087 apr_pool_t *pool)
1089 struct node_created_path_args args;
1091 args.created_path = created_path;
1092 args.root = root;
1093 args.path = path;
1094 SVN_ERR(svn_fs_base__retry_txn
1095 (root->fs, txn_body_node_created_path, &args, pool));
1096 return SVN_NO_ERROR;
1100 struct node_kind_args {
1101 const svn_fs_id_t *id;
1102 svn_node_kind_t kind; /* OUT parameter */
1106 static svn_error_t *
1107 txn_body_node_kind(void *baton, trail_t *trail)
1109 struct node_kind_args *args = baton;
1110 dag_node_t *node;
1112 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
1113 trail, trail->pool));
1114 args->kind = svn_fs_base__dag_node_kind(node);
1116 return SVN_NO_ERROR;
1120 static svn_error_t *
1121 node_kind(svn_node_kind_t *kind_p,
1122 svn_fs_root_t *root,
1123 const char *path,
1124 apr_pool_t *pool)
1126 struct node_kind_args args;
1127 const svn_fs_id_t *node_id;
1129 /* Get the node id. */
1130 SVN_ERR(base_node_id(&node_id, root, path, pool));
1132 /* Use the node id to get the real kind. */
1133 args.id = node_id;
1134 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_kind, &args, pool));
1136 *kind_p = args.kind;
1137 return SVN_NO_ERROR;
1141 static svn_error_t *
1142 base_check_path(svn_node_kind_t *kind_p,
1143 svn_fs_root_t *root,
1144 const char *path,
1145 apr_pool_t *pool)
1147 svn_error_t *err = node_kind(kind_p, root, path, pool);
1148 if (err &&
1149 ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
1150 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY)))
1152 svn_error_clear(err);
1153 *kind_p = svn_node_none;
1155 else if (err)
1157 return err;
1159 return SVN_NO_ERROR;
1163 struct node_prop_args
1165 svn_string_t **value_p;
1166 svn_fs_root_t *root;
1167 const char *path;
1168 const char *propname;
1172 static svn_error_t *
1173 txn_body_node_prop(void *baton,
1174 trail_t *trail)
1176 struct node_prop_args *args = baton;
1177 dag_node_t *node;
1178 apr_hash_t *proplist;
1180 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1181 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1182 trail, trail->pool));
1183 *(args->value_p) = NULL;
1184 if (proplist)
1185 *(args->value_p) = apr_hash_get(proplist, args->propname,
1186 APR_HASH_KEY_STRING);
1187 return SVN_NO_ERROR;
1191 static svn_error_t *
1192 base_node_prop(svn_string_t **value_p,
1193 svn_fs_root_t *root,
1194 const char *path,
1195 const char *propname,
1196 apr_pool_t *pool)
1198 struct node_prop_args args;
1199 svn_string_t *value;
1201 args.value_p = &value;
1202 args.root = root;
1203 args.path = path;
1204 args.propname = propname;
1205 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args, pool));
1207 *value_p = value;
1208 return SVN_NO_ERROR;
1212 struct node_proplist_args {
1213 apr_hash_t **table_p;
1214 svn_fs_root_t *root;
1215 const char *path;
1219 static svn_error_t *
1220 txn_body_node_proplist(void *baton, trail_t *trail)
1222 struct node_proplist_args *args = baton;
1223 dag_node_t *node;
1224 apr_hash_t *proplist;
1226 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1227 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node,
1228 trail, trail->pool));
1229 *args->table_p = proplist ? proplist : apr_hash_make(trail->pool);
1230 return SVN_NO_ERROR;
1234 static svn_error_t *
1235 base_node_proplist(apr_hash_t **table_p,
1236 svn_fs_root_t *root,
1237 const char *path,
1238 apr_pool_t *pool)
1240 apr_hash_t *table;
1241 struct node_proplist_args args;
1243 args.table_p = &table;
1244 args.root = root;
1245 args.path = path;
1247 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_proplist, &args,
1248 pool));
1250 *table_p = table;
1251 return SVN_NO_ERROR;
1255 struct change_node_prop_args {
1256 svn_fs_root_t *root;
1257 const char *path;
1258 const char *name;
1259 const svn_string_t *value;
1263 static svn_error_t *
1264 txn_body_change_node_prop(void *baton,
1265 trail_t *trail)
1267 struct change_node_prop_args *args = baton;
1268 parent_path_t *parent_path;
1269 apr_hash_t *proplist;
1270 const char *txn_id = args->root->txn;
1271 base_fs_data_t *bfd = trail->fs->fsap_data;
1273 SVN_ERR(open_path(&parent_path, args->root, args->path, 0, txn_id,
1274 trail, trail->pool));
1276 /* Check to see if path is locked; if so, check that we can use it.
1277 Notice that we're doing this non-recursively, regardless of node kind. */
1278 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
1279 SVN_ERR(svn_fs_base__allow_locked_operation
1280 (args->path, FALSE, trail, trail->pool));
1282 SVN_ERR(make_path_mutable(args->root, parent_path, args->path,
1283 trail, trail->pool));
1284 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, parent_path->node,
1285 trail, trail->pool));
1287 /* If there's no proplist, but we're just deleting a property, exit now. */
1288 if ((! proplist) && (! args->value))
1289 return SVN_NO_ERROR;
1291 /* Now, if there's no proplist, we know we need to make one. */
1292 if (! proplist)
1293 proplist = apr_hash_make(trail->pool);
1295 /* Set the property. */
1296 apr_hash_set(proplist, args->name, APR_HASH_KEY_STRING, args->value);
1298 /* Overwrite the node's proplist. */
1299 SVN_ERR(svn_fs_base__dag_set_proplist(parent_path->node, proplist,
1300 txn_id, trail, trail->pool));
1302 /* If this was a change to the mergeinfo property, and our version
1303 of the filesystem cares, we have some extra recording to do.
1305 ### If the format *doesn't* support mergeinfo recording, should
1306 ### we fuss about attempts to change the svn:mergeinfo property
1307 ### in any way save to delete it? */
1308 if ((bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
1309 && (strcmp(args->name, SVN_PROP_MERGEINFO) == 0))
1311 svn_boolean_t had_mergeinfo, has_mergeinfo = args->value ? TRUE : FALSE;
1313 /* First, note on our node that it has mergeinfo. */
1314 SVN_ERR(svn_fs_base__dag_set_has_mergeinfo(parent_path->node,
1315 has_mergeinfo,
1316 &had_mergeinfo, txn_id,
1317 trail, trail->pool));
1319 /* If this is a change from the old state, we need to update our
1320 node's parents' mergeinfo counts by a factor of 1. */
1321 if (parent_path->parent && ((! had_mergeinfo) != (! has_mergeinfo)))
1322 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
1323 has_mergeinfo ? 1 : -1,
1324 txn_id, trail, trail->pool));
1327 /* Make a record of this modification in the changes table. */
1328 SVN_ERR(add_change(args->root->fs, txn_id,
1329 args->path, svn_fs_base__dag_get_id(parent_path->node),
1330 svn_fs_path_change_modify, FALSE, TRUE, trail,
1331 trail->pool));
1333 return SVN_NO_ERROR;
1337 static svn_error_t *
1338 base_change_node_prop(svn_fs_root_t *root,
1339 const char *path,
1340 const char *name,
1341 const svn_string_t *value,
1342 apr_pool_t *pool)
1344 struct change_node_prop_args args;
1346 if (! root->is_txn_root)
1347 return SVN_FS__NOT_TXN(root);
1349 args.root = root;
1350 args.path = path;
1351 args.name = name;
1352 args.value = value;
1353 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_change_node_prop, &args,
1354 pool));
1356 return SVN_NO_ERROR;
1360 struct things_changed_args
1362 svn_boolean_t *changed_p;
1363 svn_fs_root_t *root1;
1364 svn_fs_root_t *root2;
1365 const char *path1;
1366 const char *path2;
1367 apr_pool_t *pool;
1371 static svn_error_t *
1372 txn_body_props_changed(void *baton, trail_t *trail)
1374 struct things_changed_args *args = baton;
1375 dag_node_t *node1, *node2;
1377 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
1378 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
1379 SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
1380 node1, node2, trail, trail->pool));
1381 return SVN_NO_ERROR;
1385 static svn_error_t *
1386 base_props_changed(svn_boolean_t *changed_p,
1387 svn_fs_root_t *root1,
1388 const char *path1,
1389 svn_fs_root_t *root2,
1390 const char *path2,
1391 apr_pool_t *pool)
1393 struct things_changed_args args;
1395 /* Check that roots are in the same fs. */
1396 if (root1->fs != root2->fs)
1397 return svn_error_create
1398 (SVN_ERR_FS_GENERAL, NULL,
1399 _("Cannot compare property value between two different filesystems"));
1401 args.root1 = root1;
1402 args.root2 = root2;
1403 args.path1 = path1;
1404 args.path2 = path2;
1405 args.changed_p = changed_p;
1406 args.pool = pool;
1408 SVN_ERR(svn_fs_base__retry_txn(root1->fs, txn_body_props_changed,
1409 &args, pool));
1411 return SVN_NO_ERROR;
1416 /* Getting a directory's entries */
1419 struct dir_entries_args
1421 apr_hash_t **table_p;
1422 svn_fs_root_t *root;
1423 const char *path;
1427 /* *(BATON->table_p) will never be NULL on successful return */
1428 static svn_error_t *
1429 txn_body_dir_entries(void *baton,
1430 trail_t *trail)
1432 struct dir_entries_args *args = baton;
1433 dag_node_t *node;
1434 apr_hash_t *entries;
1436 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
1438 /* Get the entries for PARENT_PATH. */
1439 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
1441 /* Potentially initialize the return value to an empty hash. */
1442 *args->table_p = entries ? entries : apr_hash_make(trail->pool);
1443 return SVN_NO_ERROR;
1447 static svn_error_t *
1448 base_dir_entries(apr_hash_t **table_p,
1449 svn_fs_root_t *root,
1450 const char *path,
1451 apr_pool_t *pool)
1453 struct dir_entries_args args;
1454 apr_pool_t *iterpool;
1455 apr_hash_t *table;
1456 svn_fs_t *fs = root->fs;
1457 apr_hash_index_t *hi;
1459 args.table_p = &table;
1460 args.root = root;
1461 args.path = path;
1462 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_dir_entries, &args,
1463 pool));
1465 iterpool = svn_pool_create(pool);
1467 /* Add in the kind data. */
1468 for (hi = apr_hash_first(pool, table); hi; hi = apr_hash_next(hi))
1470 svn_fs_dirent_t *entry;
1471 struct node_kind_args nk_args;
1472 void *val;
1474 svn_pool_clear(iterpool);
1476 /* KEY will be the entry name in ancestor (about which we
1477 simply don't care), VAL the dirent. */
1478 apr_hash_this(hi, NULL, NULL, &val);
1479 entry = val;
1480 nk_args.id = entry->id;
1481 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_node_kind, &nk_args,
1482 iterpool));
1483 entry->kind = nk_args.kind;
1486 svn_pool_destroy(iterpool);
1488 *table_p = table;
1489 return SVN_NO_ERROR;
1494 /* Merges and commits. */
1497 struct deltify_committed_args
1499 svn_fs_t *fs; /* the filesystem */
1500 svn_revnum_t rev; /* revision just committed */
1501 const char *txn_id; /* transaction just committed */
1505 struct txn_deltify_args
1507 /* The target is what we're deltifying. */
1508 const svn_fs_id_t *tgt_id;
1510 /* The base is what we're deltifying against. It's not necessarily
1511 the "next" revision of the node; skip deltas mean we sometimes
1512 deltify against a successor many generations away. */
1513 const svn_fs_id_t *base_id;
1515 /* We only deltify props for directories.
1516 ### Didn't we try removing this horrid little optimization once?
1517 ### What was the result? I would have thought that skip deltas
1518 ### mean directory undeltification is cheap enough now. */
1519 svn_boolean_t is_dir;
1523 static svn_error_t *
1524 txn_body_txn_deltify(void *baton, trail_t *trail)
1526 struct txn_deltify_args *args = baton;
1527 dag_node_t *tgt_node, *base_node;
1529 SVN_ERR(svn_fs_base__dag_get_node(&tgt_node, trail->fs, args->tgt_id,
1530 trail, trail->pool));
1531 SVN_ERR(svn_fs_base__dag_get_node(&base_node, trail->fs, args->base_id,
1532 trail, trail->pool));
1533 SVN_ERR(svn_fs_base__dag_deltify(tgt_node, base_node, args->is_dir,
1534 trail, trail->pool));
1536 return SVN_NO_ERROR;
1540 struct txn_pred_count_args
1542 const svn_fs_id_t *id;
1543 int pred_count;
1547 static svn_error_t *
1548 txn_body_pred_count(void *baton, trail_t *trail)
1550 node_revision_t *noderev;
1551 struct txn_pred_count_args *args = baton;
1553 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, trail->fs,
1554 args->id, trail, trail->pool));
1555 args->pred_count = noderev->predecessor_count;
1556 return SVN_NO_ERROR;
1560 struct txn_pred_id_args
1562 const svn_fs_id_t *id; /* The node id whose predecessor we want. */
1563 const svn_fs_id_t *pred_id; /* The returned predecessor id. */
1564 apr_pool_t *pool; /* The pool in which to allocate pred_id. */
1568 static svn_error_t *
1569 txn_body_pred_id(void *baton, trail_t *trail)
1571 node_revision_t *nr;
1572 struct txn_pred_id_args *args = baton;
1574 SVN_ERR(svn_fs_bdb__get_node_revision(&nr, trail->fs, args->id,
1575 trail, trail->pool));
1576 if (nr->predecessor_id)
1577 args->pred_id = svn_fs_base__id_copy(nr->predecessor_id, args->pool);
1578 else
1579 args->pred_id = NULL;
1581 return SVN_NO_ERROR;
1585 /* Deltify ID's predecessor iff ID is mutable under TXN_ID in FS. If
1586 ID is a mutable directory, recurse. Do this as part of TRAIL. */
1587 static svn_error_t *
1588 deltify_mutable(svn_fs_t *fs,
1589 svn_fs_root_t *root,
1590 const char *path,
1591 const char *txn_id,
1592 apr_pool_t *pool)
1594 const svn_fs_id_t *id;
1595 apr_hash_t *entries = NULL;
1596 svn_node_kind_t kind;
1597 struct txn_deltify_args td_args;
1599 /* Get the ID for PATH under ROOT. */
1600 SVN_ERR(base_node_id(&id, root, path, pool));
1602 /* Check for mutability. Not mutable? Go no further. This is safe
1603 to do because for items in the tree to be mutable, their parent
1604 dirs must also be mutable. Therefore, if a directory is not
1605 mutable under TXN_ID, its children cannot be. */
1606 if (strcmp(svn_fs_base__id_txn_id(id), txn_id))
1607 return SVN_NO_ERROR;
1609 /* Is this a directory? */
1610 SVN_ERR(base_check_path(&kind, root, path, pool));
1612 /* If this is a directory, read its entries. */
1613 if (kind == svn_node_dir)
1614 SVN_ERR(base_dir_entries(&entries, root, path, pool));
1616 /* If there are entries, recurse on 'em. */
1617 if (entries)
1619 apr_pool_t *subpool = svn_pool_create(pool);
1620 apr_hash_index_t *hi;
1622 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1624 /* KEY will be the entry name, VAL the dirent (about
1625 which we really don't care) */
1626 const void *key;
1627 svn_pool_clear(subpool);
1628 apr_hash_this(hi, &key, NULL, NULL);
1629 SVN_ERR(deltify_mutable(fs, root,
1630 svn_path_join(path, key, subpool),
1631 txn_id, subpool));
1634 svn_pool_destroy(subpool);
1637 /* Finally, deltify old data against this node. */
1639 /* Redeltify predecessor node-revisions of the one we added. The
1640 idea is to require at most 2*lg(N) deltas to be applied to get
1641 to any node-revision in a chain of N predecessors. We do this
1642 using a technique derived from skip lists:
1644 - Always redeltify the immediate parent
1646 - If the number of predecessors is divisible by 2,
1647 redeltify the revision two predecessors back
1649 - If the number of predecessors is divisible by 4,
1650 redeltify the revision four predecessors back
1652 ... and so on.
1654 That's the theory, anyway. Unfortunately, if we strictly
1655 follow that theory we get a bunch of overhead up front and no
1656 great benefit until the number of predecessors gets large. So,
1657 stop at redeltifying the parent if the number of predecessors
1658 is less than 32, and also skip the second level (redeltifying
1659 two predecessors back), since that doesn't help much. Also,
1660 don't redeltify the oldest node-revision; it's potentially
1661 expensive and doesn't help retrieve any other revision.
1662 (Retrieving the oldest node-revision will still be fast, just
1663 not as blindingly so.) */
1665 int pred_count, nlevels, lev, count;
1666 const svn_fs_id_t *pred_id;
1667 struct txn_pred_count_args tpc_args;
1668 apr_pool_t *subpools[2];
1669 int active_subpool = 0;
1671 tpc_args.id = id;
1672 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_count, &tpc_args,
1673 pool));
1674 pred_count = tpc_args.pred_count;
1676 /* If nothing to deltify, then we're done. */
1677 if (pred_count == 0)
1678 return SVN_NO_ERROR;
1680 /* Decide how many predecessors to redeltify. To save overhead,
1681 don't redeltify anything but the immediate predecessor if there
1682 are less than 32 predecessors. */
1683 nlevels = 1;
1684 if (pred_count >= 32)
1686 while (pred_count % 2 == 0)
1688 pred_count /= 2;
1689 nlevels++;
1692 /* Don't redeltify the oldest revision. */
1693 if (1 << (nlevels - 1) == pred_count)
1694 nlevels--;
1697 /* Redeltify the desired number of predecessors. */
1698 count = 0;
1699 pred_id = id;
1701 /* We need to use two alternating pools because the id used in the
1702 call to txn_body_pred_id is allocated by the previous inner
1703 loop iteration. If we would clear the pool each iteration we
1704 would free the previous result. */
1705 subpools[0] = svn_pool_create(pool);
1706 subpools[1] = svn_pool_create(pool);
1707 for (lev = 0; lev < nlevels; lev++)
1709 /* To save overhead, skip the second level (that is, never
1710 redeltify the node-revision two predecessors back). */
1711 if (lev == 1)
1712 continue;
1714 /* Note that COUNT is not reset between levels, and neither is
1715 PREDNODE; we just keep counting from where we were up to
1716 where we're supposed to get. */
1717 while (count < (1 << lev))
1719 struct txn_pred_id_args tpi_args;
1721 active_subpool = !active_subpool;
1722 svn_pool_clear(subpools[active_subpool]);
1724 tpi_args.id = pred_id;
1725 tpi_args.pool = subpools[active_subpool];
1726 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id, &tpi_args,
1727 subpools[active_subpool]));
1728 pred_id = tpi_args.pred_id;
1730 if (pred_id == NULL)
1731 return svn_error_create
1732 (SVN_ERR_FS_CORRUPT, 0,
1733 _("Corrupt DB: faulty predecessor count"));
1735 count++;
1738 /* Finally, do the deltification. */
1739 td_args.tgt_id = pred_id;
1740 td_args.base_id = id;
1741 td_args.is_dir = (kind == svn_node_dir);
1742 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_txn_deltify, &td_args,
1743 subpools[active_subpool]));
1746 svn_pool_destroy(subpools[0]);
1747 svn_pool_destroy(subpools[1]);
1750 return SVN_NO_ERROR;
1754 struct get_root_args
1756 svn_fs_root_t *root;
1757 dag_node_t *node;
1761 /* Set ARGS->node to the root node of ARGS->root. */
1762 static svn_error_t *
1763 txn_body_get_root(void *baton, trail_t *trail)
1765 struct get_root_args *args = baton;
1766 SVN_ERR(get_dag(&(args->node), args->root, "", trail, trail->pool));
1767 return SVN_NO_ERROR;
1772 static svn_error_t *
1773 update_ancestry(svn_fs_t *fs,
1774 const svn_fs_id_t *source_id,
1775 const svn_fs_id_t *target_id,
1776 const char *txn_id,
1777 const char *target_path,
1778 int source_pred_count,
1779 trail_t *trail,
1780 apr_pool_t *pool)
1782 node_revision_t *noderev;
1784 /* Set target's predecessor-id to source_id. */
1785 if (strcmp(svn_fs_base__id_txn_id(target_id), txn_id))
1786 return svn_error_createf
1787 (SVN_ERR_FS_NOT_MUTABLE, NULL,
1788 _("Unexpected immutable node at '%s'"), target_path);
1789 SVN_ERR(svn_fs_bdb__get_node_revision(&noderev, fs, target_id,
1790 trail, pool));
1791 noderev->predecessor_id = source_id;
1792 noderev->predecessor_count = source_pred_count;
1793 if (noderev->predecessor_count != -1)
1794 noderev->predecessor_count++;
1795 return svn_fs_bdb__put_node_revision(fs, target_id, noderev, trail, pool);
1799 /* Set the contents of CONFLICT_PATH to PATH, and return an
1800 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1801 at PATH. Perform all allocations in POOL (except the allocation of
1802 CONFLICT_PATH, which should be handled outside this function). */
1803 static svn_error_t *
1804 conflict_err(svn_stringbuf_t *conflict_path,
1805 const char *path)
1807 svn_stringbuf_set(conflict_path, path);
1808 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
1809 _("Conflict at '%s'"), path);
1813 /* Merge changes between ANCESTOR and SOURCE into TARGET as part of
1814 * TRAIL. ANCESTOR and TARGET must be distinct node revisions.
1815 * TARGET_PATH should correspond to TARGET's full path in its
1816 * filesystem, and is used for reporting conflict location.
1818 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1819 * function recursively merges the directories' contents. If any are
1820 * files, this function simply returns an error whenever SOURCE,
1821 * TARGET, and ANCESTOR are all distinct node revisions.
1823 * If there are differences between ANCESTOR and SOURCE that conflict
1824 * with changes between ANCESTOR and TARGET, this function returns an
1825 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1826 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1828 * If there are no conflicting differences, CONFLICT_P is updated to
1829 * the empty string.
1831 * CONFLICT_P must point to a valid svn_stringbuf_t.
1833 * Do any necessary temporary allocation in POOL.
1835 static svn_error_t *
1836 merge(svn_stringbuf_t *conflict_p,
1837 const char *target_path,
1838 dag_node_t *target,
1839 dag_node_t *source,
1840 dag_node_t *ancestor,
1841 const char *txn_id,
1842 apr_int64_t *mergeinfo_increment_out,
1843 trail_t *trail,
1844 apr_pool_t *pool)
1846 const svn_fs_id_t *source_id, *target_id, *ancestor_id;
1847 apr_hash_t *s_entries, *t_entries, *a_entries;
1848 apr_hash_index_t *hi;
1849 apr_pool_t *iterpool;
1850 svn_fs_t *fs;
1851 int pred_count;
1852 apr_int64_t mergeinfo_increment = 0;
1853 base_fs_data_t *bfd = trail->fs->fsap_data;
1855 /* Make sure everyone comes from the same filesystem. */
1856 fs = svn_fs_base__dag_get_fs(ancestor);
1857 if ((fs != svn_fs_base__dag_get_fs(source))
1858 || (fs != svn_fs_base__dag_get_fs(target)))
1860 return svn_error_create
1861 (SVN_ERR_FS_CORRUPT, NULL,
1862 _("Bad merge; ancestor, source, and target not all in same fs"));
1865 /* We have the same fs, now check it. */
1866 SVN_ERR(svn_fs__check_fs(fs, TRUE));
1868 source_id = svn_fs_base__dag_get_id(source);
1869 target_id = svn_fs_base__dag_get_id(target);
1870 ancestor_id = svn_fs_base__dag_get_id(ancestor);
1872 /* It's improper to call this function with ancestor == target. */
1873 if (svn_fs_base__id_eq(ancestor_id, target_id))
1875 svn_string_t *id_str = svn_fs_base__id_unparse(target_id, pool);
1876 return svn_error_createf
1877 (SVN_ERR_FS_GENERAL, NULL,
1878 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1879 target_path, id_str->data);
1882 svn_stringbuf_setempty(conflict_p);
1884 /* Base cases:
1885 * Either no change made in source, or same change as made in target.
1886 * Both mean nothing to merge here.
1888 if (svn_fs_base__id_eq(ancestor_id, source_id)
1889 || (svn_fs_base__id_eq(source_id, target_id)))
1890 return SVN_NO_ERROR;
1892 /* Else proceed, knowing all three are distinct node revisions.
1894 * How to merge from this point:
1896 * if (not all 3 are directories)
1898 * early exit with conflict;
1901 * // Property changes may only be made to up-to-date
1902 * // directories, because once the client commits the prop
1903 * // change, it bumps the directory's revision, and therefore
1904 * // must be able to depend on there being no other changes to
1905 * // that directory in the repository.
1906 * if (target's property list differs from ancestor's)
1907 * conflict;
1909 * For each entry NAME in the directory ANCESTOR:
1911 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1912 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1913 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1915 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1916 * No changes were made to this entry while the transaction was in
1917 * progress, so do nothing to the target.
1919 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1920 * A change was made to this entry while the transaction was in
1921 * process, but the transaction did not touch this entry. Replace
1922 * TARGET-ENTRY with SOURCE-ENTRY.
1924 * Else:
1925 * Changes were made to this entry both within the transaction and
1926 * to the repository while the transaction was in progress. They
1927 * must be merged or declared to be in conflict.
1929 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1930 * double delete; flag a conflict.
1932 * If any of the three entries is of type file, declare a conflict.
1934 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1935 * modification of ANCESTOR-ENTRY (determine by comparing the
1936 * node-id fields), declare a conflict. A replacement is
1937 * incompatible with a modification or other replacement--even
1938 * an identical replacement.
1940 * Direct modifications were made to the directory ANCESTOR-ENTRY
1941 * in both SOURCE and TARGET. Recursively merge these
1942 * modifications.
1944 * For each leftover entry NAME in the directory SOURCE:
1946 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1947 * TARGET are adding exactly the same thing, two additions are not
1948 * auto-mergeable with each other.
1950 * Add NAME to TARGET with the entry from SOURCE.
1952 * Now that we are done merging the changes from SOURCE into the
1953 * directory TARGET, update TARGET's predecessor to be SOURCE.
1956 if ((svn_fs_base__dag_node_kind(source) != svn_node_dir)
1957 || (svn_fs_base__dag_node_kind(target) != svn_node_dir)
1958 || (svn_fs_base__dag_node_kind(ancestor) != svn_node_dir))
1960 return conflict_err(conflict_p, target_path);
1964 /* Possible early merge failure: if target and ancestor have
1965 different property lists, then the merge should fail.
1966 Propchanges can *only* be committed on an up-to-date directory.
1967 ### TODO: see issue #418 about the inelegance of this.
1969 Another possible, similar, early merge failure: if source and
1970 ancestor have different property lists (meaning someone else
1971 changed directory properties while our commit transaction was
1972 happening), the merge should fail. See issue #2751.
1975 node_revision_t *tgt_nr, *anc_nr, *src_nr;
1977 /* Get node revisions for our id's. */
1978 SVN_ERR(svn_fs_bdb__get_node_revision(&tgt_nr, fs, target_id,
1979 trail, pool));
1980 SVN_ERR(svn_fs_bdb__get_node_revision(&anc_nr, fs, ancestor_id,
1981 trail, pool));
1982 SVN_ERR(svn_fs_bdb__get_node_revision(&src_nr, fs, source_id,
1983 trail, pool));
1985 /* Now compare the prop-keys of the skels. Note that just because
1986 the keys are different -doesn't- mean the proplists have
1987 different contents. But merge() isn't concerned with contents;
1988 it doesn't do a brute-force comparison on textual contents, so
1989 it won't do that here either. Checking to see if the propkey
1990 atoms are `equal' is enough. */
1991 if (! svn_fs_base__same_keys(tgt_nr->prop_key, anc_nr->prop_key))
1992 return conflict_err(conflict_p, target_path);
1993 if (! svn_fs_base__same_keys(src_nr->prop_key, anc_nr->prop_key))
1994 return conflict_err(conflict_p, target_path);
1997 /* ### todo: it would be more efficient to simply check for a NULL
1998 entries hash where necessary below than to allocate an empty hash
1999 here, but another day, another day... */
2000 SVN_ERR(svn_fs_base__dag_dir_entries(&s_entries, source, trail, pool));
2001 if (! s_entries)
2002 s_entries = apr_hash_make(pool);
2003 SVN_ERR(svn_fs_base__dag_dir_entries(&t_entries, target, trail, pool));
2004 if (! t_entries)
2005 t_entries = apr_hash_make(pool);
2006 SVN_ERR(svn_fs_base__dag_dir_entries(&a_entries, ancestor, trail, pool));
2007 if (! a_entries)
2008 a_entries = apr_hash_make(pool);
2010 /* for each entry E in a_entries... */
2011 iterpool = svn_pool_create(pool);
2012 for (hi = apr_hash_first(pool, a_entries);
2014 hi = apr_hash_next(hi))
2016 svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
2018 const void *key;
2019 void *val;
2020 apr_ssize_t klen;
2022 svn_pool_clear(iterpool);
2024 /* KEY will be the entry name in ancestor, VAL the dirent */
2025 apr_hash_this(hi, &key, &klen, &val);
2026 a_entry = val;
2028 s_entry = apr_hash_get(s_entries, key, klen);
2029 t_entry = apr_hash_get(t_entries, key, klen);
2031 /* No changes were made to this entry while the transaction was
2032 in progress, so do nothing to the target. */
2033 if (s_entry && svn_fs_base__id_eq(a_entry->id, s_entry->id))
2034 goto end;
2036 /* A change was made to this entry while the transaction was in
2037 process, but the transaction did not touch this entry. */
2038 else if (t_entry && svn_fs_base__id_eq(a_entry->id, t_entry->id))
2040 dag_node_t *t_ent_node;
2041 apr_int64_t mergeinfo_start;
2042 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2043 t_entry->id, trail, iterpool));
2044 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_start,
2045 t_ent_node, trail,
2046 iterpool));
2047 mergeinfo_increment -= mergeinfo_start;
2049 if (s_entry)
2051 dag_node_t *s_ent_node;
2052 apr_int64_t mergeinfo_end;
2053 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2054 s_entry->id, trail,
2055 iterpool));
2056 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2057 &mergeinfo_end,
2058 s_ent_node, trail,
2059 iterpool));
2060 mergeinfo_increment += mergeinfo_end;
2061 SVN_ERR(svn_fs_base__dag_set_entry(target, key, s_entry->id,
2062 txn_id, trail, iterpool));
2064 else
2066 SVN_ERR(svn_fs_base__dag_delete(target, key, txn_id,
2067 trail, iterpool));
2071 /* Changes were made to this entry both within the transaction
2072 and to the repository while the transaction was in progress.
2073 They must be merged or declared to be in conflict. */
2074 else
2076 dag_node_t *s_ent_node, *t_ent_node, *a_ent_node;
2077 const char *new_tpath;
2078 apr_int64_t sub_mergeinfo_increment;
2080 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
2081 double delete; flag a conflict. */
2082 if (s_entry == NULL || t_entry == NULL)
2083 return conflict_err(conflict_p,
2084 svn_path_join(target_path,
2085 a_entry->name,
2086 iterpool));
2088 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
2089 modification of ANCESTOR-ENTRY, declare a conflict. */
2090 if (strcmp(svn_fs_base__id_node_id(s_entry->id),
2091 svn_fs_base__id_node_id(a_entry->id)) != 0
2092 || strcmp(svn_fs_base__id_copy_id(s_entry->id),
2093 svn_fs_base__id_copy_id(a_entry->id)) != 0
2094 || strcmp(svn_fs_base__id_node_id(t_entry->id),
2095 svn_fs_base__id_node_id(a_entry->id)) != 0
2096 || strcmp(svn_fs_base__id_copy_id(t_entry->id),
2097 svn_fs_base__id_copy_id(a_entry->id)) != 0)
2098 return conflict_err(conflict_p,
2099 svn_path_join(target_path,
2100 a_entry->name,
2101 iterpool));
2103 /* Fetch the nodes for our entries. */
2104 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2105 s_entry->id, trail, iterpool));
2106 SVN_ERR(svn_fs_base__dag_get_node(&t_ent_node, fs,
2107 t_entry->id, trail, iterpool));
2108 SVN_ERR(svn_fs_base__dag_get_node(&a_ent_node, fs,
2109 a_entry->id, trail, iterpool));
2111 /* If any of the three entries is of type file, flag a conflict. */
2112 if ((svn_fs_base__dag_node_kind(s_ent_node) == svn_node_file)
2113 || (svn_fs_base__dag_node_kind(t_ent_node) == svn_node_file)
2114 || (svn_fs_base__dag_node_kind(a_ent_node) == svn_node_file))
2115 return conflict_err(conflict_p,
2116 svn_path_join(target_path,
2117 a_entry->name,
2118 iterpool));
2120 /* Direct modifications were made to the directory
2121 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
2122 merge these modifications. */
2123 new_tpath = svn_path_join(target_path, t_entry->name, iterpool);
2124 SVN_ERR(merge(conflict_p, new_tpath,
2125 t_ent_node, s_ent_node, a_ent_node,
2126 txn_id, &sub_mergeinfo_increment, trail, iterpool));
2127 mergeinfo_increment += sub_mergeinfo_increment;
2130 /* We've taken care of any possible implications E could have.
2131 Remove it from source_entries, so it's easy later to loop
2132 over all the source entries that didn't exist in
2133 ancestor_entries. */
2134 end:
2135 apr_hash_set(s_entries, key, klen, NULL);
2138 /* For each entry E in source but not in ancestor */
2139 for (hi = apr_hash_first(pool, s_entries);
2141 hi = apr_hash_next(hi))
2143 svn_fs_dirent_t *s_entry, *t_entry;
2144 const void *key;
2145 void *val;
2146 apr_ssize_t klen;
2147 dag_node_t *s_ent_node;
2148 apr_int64_t mergeinfo_s;
2150 svn_pool_clear(iterpool);
2152 apr_hash_this(hi, &key, &klen, &val);
2153 s_entry = val;
2154 t_entry = apr_hash_get(t_entries, key, klen);
2156 /* If NAME exists in TARGET, declare a conflict. */
2157 if (t_entry)
2158 return conflict_err(conflict_p,
2159 svn_path_join(target_path,
2160 t_entry->name,
2161 iterpool));
2163 SVN_ERR(svn_fs_base__dag_get_node(&s_ent_node, fs,
2164 s_entry->id, trail, iterpool));
2165 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_s,
2166 s_ent_node, trail,
2167 iterpool));
2168 mergeinfo_increment += mergeinfo_s;
2169 SVN_ERR(svn_fs_base__dag_set_entry
2170 (target, s_entry->name, s_entry->id, txn_id, trail, iterpool));
2172 svn_pool_destroy(iterpool);
2174 /* Now that TARGET has absorbed all of the history between ANCESTOR
2175 and SOURCE, we can update its predecessor to point to SOURCE. */
2176 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count, source,
2177 trail, pool));
2178 SVN_ERR(update_ancestry(fs, source_id, target_id, txn_id, target_path,
2179 pred_count, trail, pool));
2181 /* Tweak mergeinfo data if our format supports it. */
2182 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2184 SVN_ERR(svn_fs_base__dag_adjust_mergeinfo_count(target,
2185 mergeinfo_increment,
2186 txn_id, trail, pool));
2189 if (mergeinfo_increment_out)
2190 *mergeinfo_increment_out = mergeinfo_increment;
2192 return SVN_NO_ERROR;
2196 struct merge_args
2198 /* The ancestor for the merge. If this is null, then TXN's base is
2199 used as the ancestor for the merge. */
2200 dag_node_t *ancestor_node;
2202 /* This is the SOURCE node for the merge. It may not be null. */
2203 dag_node_t *source_node;
2205 /* This is the TARGET of the merge. It may not be null. If
2206 ancestor_node above is null, then this txn's base is used as the
2207 ancestor for the merge. */
2208 svn_fs_txn_t *txn;
2210 /* If a conflict results, this is updated to the path in the txn that
2211 conflicted. It must point to a valid svn_stringbuf_t before calling
2212 svn_fs_base__retry_txn, as this determines the pool used to allocate any
2213 required memory. */
2214 svn_stringbuf_t *conflict;
2218 /* Merge changes between an ancestor and BATON->source_node into
2219 BATON->txn. The ancestor is either BATON->ancestor_node, or if
2220 that is null, BATON->txn's base node.
2222 If the merge is successful, BATON->txn's base will become
2223 BATON->source_node, and its root node will have a new ID, a
2224 successor of BATON->source_node. */
2225 static svn_error_t *
2226 txn_body_merge(void *baton, trail_t *trail)
2228 struct merge_args *args = baton;
2229 dag_node_t *source_node, *txn_root_node, *ancestor_node;
2230 const svn_fs_id_t *source_id;
2231 svn_fs_t *fs = args->txn->fs;
2232 const char *txn_id = args->txn->id;
2234 source_node = args->source_node;
2235 ancestor_node = args->ancestor_node;
2236 source_id = svn_fs_base__dag_get_id(source_node);
2238 SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_id,
2239 trail, trail->pool));
2241 if (ancestor_node == NULL)
2243 SVN_ERR(svn_fs_base__dag_txn_base_root(&ancestor_node, fs,
2244 txn_id, trail, trail->pool));
2247 if (svn_fs_base__id_eq(svn_fs_base__dag_get_id(ancestor_node),
2248 svn_fs_base__dag_get_id(txn_root_node)))
2250 /* If no changes have been made in TXN since its current base,
2251 then it can't conflict with any changes since that base. So
2252 we just set *both* its base and root to source, making TXN
2253 in effect a repeat of source. */
2255 /* ### kff todo: this would, of course, be a mighty silly thing
2256 for the caller to do, and we might want to consider whether
2257 this response is really appropriate. */
2259 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2260 trail, trail->pool));
2261 SVN_ERR(svn_fs_base__set_txn_root(fs, txn_id, source_id,
2262 trail, trail->pool));
2264 else
2266 int pred_count;
2268 SVN_ERR(merge(args->conflict, "/", txn_root_node, source_node,
2269 ancestor_node, txn_id, NULL, trail, trail->pool));
2271 SVN_ERR(svn_fs_base__dag_get_predecessor_count(&pred_count,
2272 source_node, trail,
2273 trail->pool));
2275 /* After the merge, txn's new "ancestor" is now really the node
2276 at source_id, so record that fact. Think of this as
2277 ratcheting the txn forward in time, so it can't backslide and
2278 forget the merging work that's already been done. */
2279 SVN_ERR(update_ancestry(fs, source_id,
2280 svn_fs_base__dag_get_id(txn_root_node),
2281 txn_id, "/", pred_count, trail, trail->pool));
2282 SVN_ERR(svn_fs_base__set_txn_base(fs, txn_id, source_id,
2283 trail, trail->pool));
2286 return SVN_NO_ERROR;
2290 /* Verify that there are registered with TRAIL->fs all the locks
2291 necessary to permit all the changes associated with TXN_NAME. */
2292 static svn_error_t *
2293 verify_locks(const char *txn_name,
2294 trail_t *trail,
2295 apr_pool_t *pool)
2297 apr_pool_t *subpool = svn_pool_create(pool);
2298 apr_hash_t *changes;
2299 apr_hash_index_t *hi;
2300 apr_array_header_t *changed_paths;
2301 svn_stringbuf_t *last_recursed = NULL;
2302 int i;
2304 /* Fetch the changes for this transaction. */
2305 SVN_ERR(svn_fs_bdb__changes_fetch(&changes, trail->fs, txn_name,
2306 trail, pool));
2308 /* Make an array of the changed paths, and sort them depth-first-ily. */
2309 changed_paths = apr_array_make(pool, apr_hash_count(changes) + 1,
2310 sizeof(const char *));
2311 for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
2313 const void *key;
2314 apr_hash_this(hi, &key, NULL, NULL);
2315 APR_ARRAY_PUSH(changed_paths, const char *) = key;
2317 qsort(changed_paths->elts, changed_paths->nelts,
2318 changed_paths->elt_size, svn_sort_compare_paths);
2320 /* Now, traverse the array of changed paths, verify locks. Note
2321 that if we need to do a recursive verification a path, we'll skip
2322 over children of that path when we get to them. */
2323 for (i = 0; i < changed_paths->nelts; i++)
2325 const char *path;
2326 svn_fs_path_change_t *change;
2327 svn_boolean_t recurse = TRUE;
2329 svn_pool_clear(subpool);
2330 path = APR_ARRAY_IDX(changed_paths, i, const char *);
2332 /* If this path has already been verified as part of a recursive
2333 check of one of its parents, no need to do it again. */
2334 if (last_recursed
2335 && svn_path_is_child(last_recursed->data, path, subpool))
2336 continue;
2338 /* Fetch the change associated with our path. */
2339 change = apr_hash_get(changes, path, APR_HASH_KEY_STRING);
2341 /* What does it mean to succeed at lock verification for a given
2342 path? For an existing file or directory getting modified
2343 (text, props), it means we hold the lock on the file or
2344 directory. For paths being added or removed, we need to hold
2345 the locks for that path and any children of that path.
2347 WHEW! We have no reliable way to determine the node kind of
2348 deleted items, but fortunately we are going to do a recursive
2349 check on deleted paths regardless of their kind. */
2350 if (change->change_kind == svn_fs_path_change_modify)
2351 recurse = FALSE;
2352 SVN_ERR(svn_fs_base__allow_locked_operation(path, recurse,
2353 trail, subpool));
2355 /* If we just did a recursive check, remember the path we
2356 checked (so children can be skipped). */
2357 if (recurse)
2359 if (! last_recursed)
2360 last_recursed = svn_stringbuf_create(path, pool);
2361 else
2362 svn_stringbuf_set(last_recursed, path);
2365 svn_pool_destroy(subpool);
2366 return SVN_NO_ERROR;
2370 struct commit_args
2372 svn_fs_txn_t *txn;
2373 svn_revnum_t new_rev;
2377 /* Commit ARGS->txn, setting ARGS->new_rev to the resulting new
2378 * revision, if ARGS->txn is up-to-date with respect to the repository.
2380 * Up-to-date means that ARGS->txn's base root is the same as the root
2381 * of the youngest revision. If ARGS->txn is not up-to-date, the
2382 * error SVN_ERR_FS_TXN_OUT_OF_DATE is returned, and the commit fails: no
2383 * new revision is created, and ARGS->new_rev is not touched.
2385 * If the commit succeeds, ARGS->txn is destroyed.
2387 static svn_error_t *
2388 txn_body_commit(void *baton, trail_t *trail)
2390 struct commit_args *args = baton;
2392 svn_fs_txn_t *txn = args->txn;
2393 svn_fs_t *fs = txn->fs;
2394 const char *txn_name = txn->id;
2396 svn_revnum_t youngest_rev;
2397 const svn_fs_id_t *y_rev_root_id;
2398 dag_node_t *txn_base_root_node;
2400 /* Getting the youngest revision locks the revisions table until
2401 this trail is done. */
2402 SVN_ERR(svn_fs_bdb__youngest_rev(&youngest_rev, fs, trail, trail->pool));
2404 /* If the root of the youngest revision is the same as txn's base,
2405 then no further merging is necessary and we can commit. */
2406 SVN_ERR(svn_fs_base__rev_get_root(&y_rev_root_id, fs, youngest_rev,
2407 trail, trail->pool));
2408 SVN_ERR(svn_fs_base__dag_txn_base_root(&txn_base_root_node, fs, txn_name,
2409 trail, trail->pool));
2410 /* ### kff todo: it seems weird to grab the ID for one, and the node
2411 for the other. We can certainly do the comparison we need, but
2412 it would be nice to grab the same type of information from the
2413 start, instead of having to transform one of them. */
2414 if (! svn_fs_base__id_eq(y_rev_root_id,
2415 svn_fs_base__dag_get_id(txn_base_root_node)))
2417 svn_string_t *id_str = svn_fs_base__id_unparse(y_rev_root_id,
2418 trail->pool);
2419 return svn_error_createf
2420 (SVN_ERR_FS_TXN_OUT_OF_DATE, NULL,
2421 _("Transaction '%s' out-of-date with respect to revision '%s'"),
2422 txn_name, id_str->data);
2425 /* Locks may have been added (or stolen) between the calling of
2426 previous svn_fs.h functions and svn_fs_commit_txn(), so we need
2427 to re-examine every changed-path in the txn and re-verify all
2428 discovered locks. */
2429 SVN_ERR(verify_locks(txn_name, trail, trail->pool));
2431 /* Else, commit the txn. */
2432 SVN_ERR(svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
2433 trail->pool));
2435 return SVN_NO_ERROR;
2439 /* Note: it is acceptable for this function to call back into
2440 top-level FS interfaces because it does not itself use trails. */
2441 svn_error_t *
2442 svn_fs_base__commit_txn(const char **conflict_p,
2443 svn_revnum_t *new_rev,
2444 svn_fs_txn_t *txn,
2445 apr_pool_t *pool)
2447 /* How do commits work in Subversion?
2449 * When you're ready to commit, here's what you have:
2451 * 1. A transaction, with a mutable tree hanging off it.
2452 * 2. A base revision, against which TXN_TREE was made.
2453 * 3. A latest revision, which may be newer than the base rev.
2455 * The problem is that if latest != base, then one can't simply
2456 * attach the txn root as the root of the new revision, because that
2457 * would lose all the changes between base and latest. It is also
2458 * not acceptable to insist that base == latest; in a busy
2459 * repository, commits happen too fast to insist that everyone keep
2460 * their entire tree up-to-date at all times. Non-overlapping
2461 * changes should not interfere with each other.
2463 * The solution is to merge the changes between base and latest into
2464 * the txn tree [see the function merge()]. The txn tree is the
2465 * only one of the three trees that is mutable, so it has to be the
2466 * one to adjust.
2468 * You might have to adjust it more than once, if a new latest
2469 * revision gets committed while you were merging in the previous
2470 * one. For example:
2472 * 1. Jane starts txn T, based at revision 6.
2473 * 2. Someone commits (or already committed) revision 7.
2474 * 3. Jane's starts merging the changes between 6 and 7 into T.
2475 * 4. Meanwhile, someone commits revision 8.
2476 * 5. Jane finishes the 6-->7 merge. T could now be committed
2477 * against a latest revision of 7, if only that were still the
2478 * latest. Unfortunately, 8 is now the latest, so...
2479 * 6. Jane starts merging the changes between 7 and 8 into T.
2480 * 7. Meanwhile, no one commits any new revisions. Whew.
2481 * 8. Jane commits T, creating revision 9, whose tree is exactly
2482 * T's tree, except immutable now.
2484 * Lather, rinse, repeat.
2487 svn_error_t *err;
2488 svn_fs_t *fs = txn->fs;
2489 apr_pool_t *subpool = svn_pool_create(pool);
2491 /* Initialize output params. */
2492 *new_rev = SVN_INVALID_REVNUM;
2493 if (conflict_p)
2494 *conflict_p = NULL;
2496 while (1729)
2498 struct get_root_args get_root_args;
2499 struct merge_args merge_args;
2500 struct commit_args commit_args;
2501 svn_revnum_t youngish_rev;
2502 svn_fs_root_t *youngish_root;
2503 dag_node_t *youngish_root_node;
2505 svn_pool_clear(subpool);
2507 /* Get the *current* youngest revision, in one short-lived
2508 Berkeley transaction. (We don't want the revisions table
2509 locked while we do the main merge.) We call it "youngish"
2510 because new revisions might get committed after we've
2511 obtained it. */
2513 SVN_ERR(svn_fs_base__youngest_rev(&youngish_rev, fs, subpool));
2514 SVN_ERR(svn_fs_base__revision_root(&youngish_root, fs, youngish_rev,
2515 subpool));
2517 /* Get the dag node for the youngest revision, also in one
2518 Berkeley transaction. Later we'll use it as the SOURCE
2519 argument to a merge, and if the merge succeeds, this youngest
2520 root node will become the new base root for the svn txn that
2521 was the target of the merge (but note that the youngest rev
2522 may have changed by then -- that's why we're careful to get
2523 this root in its own bdb txn here). */
2524 get_root_args.root = youngish_root;
2525 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root,
2526 &get_root_args, subpool));
2527 youngish_root_node = get_root_args.node;
2529 /* Try to merge. If the merge succeeds, the base root node of
2530 TARGET's txn will become the same as youngish_root_node, so
2531 any future merges will only be between that node and whatever
2532 the root node of the youngest rev is by then. */
2533 merge_args.ancestor_node = NULL;
2534 merge_args.source_node = youngish_root_node;
2535 merge_args.txn = txn;
2536 merge_args.conflict = svn_stringbuf_create("", pool); /* use pool */
2537 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, subpool);
2538 if (err)
2540 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2541 *conflict_p = merge_args.conflict->data;
2542 return err;
2545 /* Try to commit. */
2546 commit_args.txn = txn;
2547 err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
2548 subpool);
2549 if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
2551 /* Did someone else finish committing a new revision while we
2552 were in mid-merge or mid-commit? If so, we'll need to
2553 loop again to merge the new changes in, then try to
2554 commit again. Or if that's not what happened, then just
2555 return the error. */
2556 svn_revnum_t youngest_rev;
2557 svn_error_t *err2 = svn_fs_base__youngest_rev(&youngest_rev, fs,
2558 subpool);
2559 if (err2)
2561 svn_error_clear(err);
2562 return err2; /* err2 is bad, it should not occur */
2564 else if (youngest_rev == youngish_rev)
2565 return err;
2566 else
2567 svn_error_clear(err);
2569 else if (err)
2571 return err;
2573 else
2575 /* Set the return value -- our brand spankin' new revision! */
2576 *new_rev = commit_args.new_rev;
2577 break;
2581 svn_pool_destroy(subpool);
2582 return SVN_NO_ERROR;
2586 /* Note: it is acceptable for this function to call back into
2587 public FS API interfaces because it does not itself use trails. */
2588 static svn_error_t *
2589 base_merge(const char **conflict_p,
2590 svn_fs_root_t *source_root,
2591 const char *source_path,
2592 svn_fs_root_t *target_root,
2593 const char *target_path,
2594 svn_fs_root_t *ancestor_root,
2595 const char *ancestor_path,
2596 apr_pool_t *pool)
2598 dag_node_t *source, *ancestor;
2599 struct get_root_args get_root_args;
2600 struct merge_args merge_args;
2601 svn_fs_txn_t *txn;
2602 svn_error_t *err;
2603 svn_fs_t *fs;
2605 if (! target_root->is_txn_root)
2606 return SVN_FS__NOT_TXN(target_root);
2608 /* Paranoia. */
2609 fs = ancestor_root->fs;
2610 if ((source_root->fs != fs) || (target_root->fs != fs))
2612 return svn_error_create
2613 (SVN_ERR_FS_CORRUPT, NULL,
2614 _("Bad merge; ancestor, source, and target not all in same fs"));
2617 /* ### kff todo: is there any compelling reason to get the nodes in
2618 one db transaction? Right now we don't; txn_body_get_root() gets
2619 one node at a time. This will probably need to change:
2621 Jim Blandy <jimb@zwingli.cygnus.com> writes:
2622 > svn_fs_merge needs to be a single transaction, to protect it against
2623 > people deleting parents of nodes it's working on, etc.
2626 /* Get the ancestor node. */
2627 get_root_args.root = ancestor_root;
2628 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2629 pool));
2630 ancestor = get_root_args.node;
2632 /* Get the source node. */
2633 get_root_args.root = source_root;
2634 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_root, &get_root_args,
2635 pool));
2636 source = get_root_args.node;
2638 /* Open a txn for the txn root into which we're merging. */
2639 SVN_ERR(svn_fs_base__open_txn(&txn, fs, target_root->txn, pool));
2641 /* Merge changes between ANCESTOR and SOURCE into TXN. */
2642 merge_args.source_node = source;
2643 merge_args.ancestor_node = ancestor;
2644 merge_args.txn = txn;
2645 merge_args.conflict = svn_stringbuf_create("", pool);
2646 err = svn_fs_base__retry_txn(fs, txn_body_merge, &merge_args, pool);
2647 if (err)
2649 if ((err->apr_err == SVN_ERR_FS_CONFLICT) && conflict_p)
2650 *conflict_p = merge_args.conflict->data;
2651 return err;
2654 return SVN_NO_ERROR;
2658 struct rev_get_txn_id_args
2660 const char **txn_id;
2661 svn_revnum_t revision;
2665 static svn_error_t *
2666 txn_body_rev_get_txn_id(void *baton, trail_t *trail)
2668 struct rev_get_txn_id_args *args = baton;
2669 return svn_fs_base__rev_get_txn_id(args->txn_id, trail->fs,
2670 args->revision, trail, trail->pool);
2674 svn_error_t *
2675 svn_fs_base__deltify(svn_fs_t *fs,
2676 svn_revnum_t revision,
2677 apr_pool_t *pool)
2679 svn_fs_root_t *root;
2680 const char *txn_id;
2681 struct rev_get_txn_id_args args;
2683 SVN_ERR(svn_fs_base__revision_root(&root, fs, revision, pool));
2685 args.txn_id = &txn_id;
2686 args.revision = revision;
2687 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_rev_get_txn_id, &args, pool));
2689 return deltify_mutable(fs, root, "/", txn_id, pool);
2694 /* Modifying directories */
2697 struct make_dir_args
2699 svn_fs_root_t *root;
2700 const char *path;
2704 static svn_error_t *
2705 txn_body_make_dir(void *baton,
2706 trail_t *trail)
2708 struct make_dir_args *args = baton;
2709 svn_fs_root_t *root = args->root;
2710 const char *path = args->path;
2711 parent_path_t *parent_path;
2712 dag_node_t *sub_dir;
2713 const char *txn_id = root->txn;
2715 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
2716 txn_id, trail, trail->pool));
2718 /* If there's already a sub-directory by that name, complain. This
2719 also catches the case of trying to make a subdirectory named `/'. */
2720 if (parent_path->node)
2721 return SVN_FS__ALREADY_EXISTS(root, path);
2723 /* Check to see if some lock is 'reserving' a file-path or dir-path
2724 at that location, or even some child-path; if so, check that we
2725 can use it. */
2726 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2728 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
2729 trail, trail->pool));
2732 /* Create the subdirectory. */
2733 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
2734 trail, trail->pool));
2735 SVN_ERR(svn_fs_base__dag_make_dir(&sub_dir,
2736 parent_path->parent->node,
2737 parent_path_path(parent_path->parent,
2738 trail->pool),
2739 parent_path->entry,
2740 txn_id,
2741 trail, trail->pool));
2743 /* Make a record of this modification in the changes table. */
2744 SVN_ERR(add_change(root->fs, txn_id, path,
2745 svn_fs_base__dag_get_id(sub_dir),
2746 svn_fs_path_change_add, FALSE, FALSE,
2747 trail, trail->pool));
2749 return SVN_NO_ERROR;
2753 static svn_error_t *
2754 base_make_dir(svn_fs_root_t *root,
2755 const char *path,
2756 apr_pool_t *pool)
2758 struct make_dir_args args;
2760 if (! root->is_txn_root)
2761 return SVN_FS__NOT_TXN(root);
2763 args.root = root;
2764 args.path = path;
2765 return svn_fs_base__retry_txn(root->fs, txn_body_make_dir, &args, pool);
2769 struct delete_args
2771 svn_fs_root_t *root;
2772 const char *path;
2776 /* If this returns SVN_ERR_FS_NO_SUCH_ENTRY, it means that the
2777 basename of PATH is missing from its parent, that is, the final
2778 target of the deletion is missing. */
2779 static svn_error_t *
2780 txn_body_delete(void *baton,
2781 trail_t *trail)
2783 struct delete_args *args = baton;
2784 svn_fs_root_t *root = args->root;
2785 const char *path = args->path;
2786 parent_path_t *parent_path;
2787 const char *txn_id = root->txn;
2788 base_fs_data_t *bfd = trail->fs->fsap_data;
2790 if (! root->is_txn_root)
2791 return SVN_FS__NOT_TXN(root);
2793 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
2794 trail, trail->pool));
2796 /* We can't remove the root of the filesystem. */
2797 if (! parent_path->parent)
2798 return svn_error_create(SVN_ERR_FS_ROOT_DIR, NULL,
2799 _("The root directory cannot be deleted"));
2801 /* Check to see if path (or any child thereof) is locked; if so,
2802 check that we can use the existing lock(s). */
2803 if (root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2805 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
2806 trail, trail->pool));
2809 /* Make the parent directory mutable. */
2810 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
2811 trail, trail->pool));
2813 /* Decrement mergeinfo counts on the parents of this node by the
2814 count it previously carried, if our format supports it. */
2815 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2817 apr_int64_t mergeinfo_count;
2818 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL, &mergeinfo_count,
2819 parent_path->node,
2820 trail, trail->pool));
2821 SVN_ERR(adjust_parent_mergeinfo_counts(parent_path->parent,
2822 -mergeinfo_count, txn_id,
2823 trail, trail->pool));
2826 /* Do the deletion. */
2827 SVN_ERR(svn_fs_base__dag_delete(parent_path->parent->node,
2828 parent_path->entry,
2829 txn_id, trail, trail->pool));
2832 /* Make a record of this modification in the changes table. */
2833 SVN_ERR(add_change(root->fs, txn_id, path,
2834 svn_fs_base__dag_get_id(parent_path->node),
2835 svn_fs_path_change_delete, FALSE, FALSE, trail,
2836 trail->pool));
2838 return SVN_NO_ERROR;
2842 static svn_error_t *
2843 base_delete_node(svn_fs_root_t *root,
2844 const char *path,
2845 apr_pool_t *pool)
2847 struct delete_args args;
2849 args.root = root;
2850 args.path = path;
2851 return svn_fs_base__retry_txn(root->fs, txn_body_delete, &args, pool);
2855 struct copy_args
2857 svn_fs_root_t *from_root;
2858 const char *from_path;
2859 svn_fs_root_t *to_root;
2860 const char *to_path;
2861 svn_boolean_t preserve_history;
2865 static svn_error_t *
2866 txn_body_copy(void *baton,
2867 trail_t *trail)
2869 struct copy_args *args = baton;
2870 svn_fs_root_t *from_root = args->from_root;
2871 const char *from_path = args->from_path;
2872 svn_fs_root_t *to_root = args->to_root;
2873 const char *to_path = args->to_path;
2874 dag_node_t *from_node;
2875 parent_path_t *to_parent_path;
2876 const char *txn_id = to_root->txn;
2878 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2879 SVN_ERR(get_dag(&from_node, from_root, from_path, trail, trail->pool));
2881 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2882 component does not exist, it's not that big a deal. We'll just
2883 make one there. */
2884 SVN_ERR(open_path(&to_parent_path, to_root, to_path,
2885 open_path_last_optional, txn_id, trail, trail->pool));
2887 /* Check to see if to-path (or any child thereof) is locked, or at
2888 least 'reserved', whether it exists or not; if so, check that we
2889 can use the existing lock(s). */
2890 if (to_root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
2892 SVN_ERR(svn_fs_base__allow_locked_operation(to_path, TRUE,
2893 trail, trail->pool));
2896 /* If the destination node already exists as the same node as the
2897 source (in other words, this operation would result in nothing
2898 happening at all), just do nothing an return successfully,
2899 proud that you saved yourself from a tiresome task. */
2900 if ((to_parent_path->node)
2901 && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
2902 svn_fs_base__dag_get_id
2903 (to_parent_path->node)) == 0))
2904 return SVN_NO_ERROR;
2906 if (! from_root->is_txn_root)
2908 svn_fs_path_change_kind_t kind;
2909 dag_node_t *new_node;
2910 apr_int64_t old_mergeinfo_count = 0, mergeinfo_count;
2911 base_fs_data_t *bfd = trail->fs->fsap_data;
2913 /* If TO_PATH already existed prior to the copy, note that this
2914 operation is a replacement, not an addition. */
2915 if (to_parent_path->node)
2916 kind = svn_fs_path_change_replace;
2917 else
2918 kind = svn_fs_path_change_add;
2920 /* Make sure the target node's parents are mutable. */
2921 SVN_ERR(make_path_mutable(to_root, to_parent_path->parent,
2922 to_path, trail, trail->pool));
2924 /* If this is a replacement operation, we need to know the old
2925 node's mergeinfo count. */
2926 if (to_parent_path->node)
2927 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2928 &old_mergeinfo_count,
2929 to_parent_path->node,
2930 trail, trail->pool));
2931 /* Do the copy. */
2932 SVN_ERR(svn_fs_base__dag_copy(to_parent_path->parent->node,
2933 to_parent_path->entry,
2934 from_node,
2935 args->preserve_history,
2936 from_root->rev,
2937 from_path, txn_id, trail, trail->pool));
2939 /* Adjust the mergeinfo counts of the destination's parents if
2940 our format supports it. */
2941 if (bfd->format >= SVN_FS_BASE__MIN_MERGEINFO_FORMAT)
2943 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(NULL,
2944 &mergeinfo_count,
2945 from_node, trail,
2946 trail->pool));
2947 SVN_ERR(adjust_parent_mergeinfo_counts
2948 (to_parent_path->parent,
2949 mergeinfo_count - old_mergeinfo_count,
2950 txn_id, trail, trail->pool));
2953 /* Make a record of this modification in the changes table. */
2954 SVN_ERR(get_dag(&new_node, to_root, to_path, trail, trail->pool));
2955 SVN_ERR(add_change(to_root->fs, txn_id, to_path,
2956 svn_fs_base__dag_get_id(new_node),
2957 kind, FALSE, FALSE, trail, trail->pool));
2959 else
2961 /* See IZ Issue #436 */
2962 /* Copying from transaction roots not currently available.
2964 ### cmpilato todo someday: make this not so. :-) Note that
2965 when copying from mutable trees, you have to make sure that
2966 you aren't creating a cyclic graph filesystem, and a simple
2967 referencing operation won't cut it. Currently, we should not
2968 be able to reach this clause, and the interface reports that
2969 this only works from immutable trees anyway, but JimB has
2970 stated that this requirement need not be necessary in the
2971 future. */
2973 abort();
2976 return SVN_NO_ERROR;
2980 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
2981 Use POOL for temporary allocation only.
2982 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
2983 static svn_error_t *
2984 fs_same_p(svn_boolean_t *same_p,
2985 svn_fs_t *fs1,
2986 svn_fs_t *fs2,
2987 apr_pool_t *pool)
2989 const char *uuid1;
2990 const char *uuid2;
2992 /* Random thought: if fetching UUIDs to compare filesystems is too
2993 expensive, one solution would be to cache the UUID in each fs
2994 object (copying the UUID into fs->pool, of course). */
2996 SVN_ERR(fs1->vtable->get_uuid(fs1, &uuid1, pool));
2997 SVN_ERR(fs2->vtable->get_uuid(fs2, &uuid2, pool));
2999 *same_p = ! strcmp(uuid1, uuid2);
3000 return SVN_NO_ERROR;
3003 static svn_error_t *
3004 copy_helper(svn_fs_root_t *from_root,
3005 const char *from_path,
3006 svn_fs_root_t *to_root,
3007 const char *to_path,
3008 svn_boolean_t preserve_history,
3009 apr_pool_t *pool)
3011 struct copy_args args;
3012 svn_boolean_t same_p;
3014 /* Use an error check, not an assert, because even the caller cannot
3015 guarantee that a filesystem's UUID has not changed "on the fly". */
3016 SVN_ERR(fs_same_p(&same_p, from_root->fs, to_root->fs, pool));
3017 if (! same_p)
3018 return svn_error_createf
3019 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3020 _("Cannot copy between two different filesystems ('%s' and '%s')"),
3021 from_root->fs->path, to_root->fs->path);
3023 if (! to_root->is_txn_root)
3024 return SVN_FS__NOT_TXN(to_root);
3026 if (from_root->is_txn_root)
3027 return svn_error_create
3028 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
3029 _("Copy from mutable tree not currently supported"));
3031 args.from_root = from_root;
3032 args.from_path = from_path;
3033 args.to_root = to_root;
3034 args.to_path = to_path;
3035 args.preserve_history = preserve_history;
3037 return svn_fs_base__retry_txn(to_root->fs, txn_body_copy, &args, pool);
3040 static svn_error_t *
3041 base_copy(svn_fs_root_t *from_root,
3042 const char *from_path,
3043 svn_fs_root_t *to_root,
3044 const char *to_path,
3045 apr_pool_t *pool)
3047 return copy_helper(from_root, from_path, to_root, to_path, TRUE, pool);
3051 static svn_error_t *
3052 base_revision_link(svn_fs_root_t *from_root,
3053 svn_fs_root_t *to_root,
3054 const char *path,
3055 apr_pool_t *pool)
3057 return copy_helper(from_root, path, to_root, path, FALSE, pool);
3061 struct copied_from_args
3063 svn_fs_root_t *root; /* Root for the node whose ancestry we seek. */
3064 const char *path; /* Path for the node whose ancestry we seek. */
3066 svn_revnum_t result_rev; /* Revision, if any, of the ancestor. */
3067 const char *result_path; /* Path, if any, of the ancestor. */
3069 apr_pool_t *pool; /* Allocate `result_path' here. */
3073 static svn_error_t *
3074 txn_body_copied_from(void *baton, trail_t *trail)
3076 struct copied_from_args *args = baton;
3077 const svn_fs_id_t *node_id, *pred_id;
3078 dag_node_t *node;
3079 svn_fs_t *fs = args->root->fs;
3081 /* Clear the return variables. */
3082 args->result_path = NULL;
3083 args->result_rev = SVN_INVALID_REVNUM;
3085 /* Fetch the NODE in question. */
3086 SVN_ERR(get_dag(&node, args->root, args->path, trail, trail->pool));
3087 node_id = svn_fs_base__dag_get_id(node);
3089 /* Check the node's predecessor-ID. If it doesn't have one, it
3090 isn't a copy. */
3091 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
3092 trail, trail->pool));
3093 if (! pred_id)
3094 return SVN_NO_ERROR;
3096 /* If NODE's copy-ID is the same as that of its predecessor... */
3097 if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
3098 svn_fs_base__id_copy_id(pred_id)) != 0)
3100 /* ... then NODE was either the target of a copy operation,
3101 a copied subtree item. We examine the actual copy record
3102 to determine which is the case. */
3103 copy_t *copy;
3104 SVN_ERR(svn_fs_bdb__get_copy(&copy, fs,
3105 svn_fs_base__id_copy_id(node_id),
3106 trail, trail->pool));
3107 if ((copy->kind == copy_kind_real)
3108 && svn_fs_base__id_eq(copy->dst_noderev_id, node_id))
3110 args->result_path = copy->src_path;
3111 SVN_ERR(svn_fs_base__txn_get_revision(&(args->result_rev), fs,
3112 copy->src_txn_id,
3113 trail, trail->pool));
3116 return SVN_NO_ERROR;
3120 static svn_error_t *
3121 base_copied_from(svn_revnum_t *rev_p,
3122 const char **path_p,
3123 svn_fs_root_t *root,
3124 const char *path,
3125 apr_pool_t *pool)
3127 struct copied_from_args args;
3129 args.root = root;
3130 args.path = path;
3131 args.pool = pool;
3133 SVN_ERR(svn_fs_base__retry_txn(root->fs,
3134 txn_body_copied_from, &args, pool));
3136 *rev_p = args.result_rev;
3137 *path_p = args.result_path;
3139 return SVN_NO_ERROR;
3144 /* Files. */
3147 struct make_file_args
3149 svn_fs_root_t *root;
3150 const char *path;
3154 static svn_error_t *
3155 txn_body_make_file(void *baton,
3156 trail_t *trail)
3158 struct make_file_args *args = baton;
3159 svn_fs_root_t *root = args->root;
3160 const char *path = args->path;
3161 parent_path_t *parent_path;
3162 dag_node_t *child;
3163 const char *txn_id = root->txn;
3165 SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
3166 txn_id, trail, trail->pool));
3168 /* If there's already a file by that name, complain.
3169 This also catches the case of trying to make a file named `/'. */
3170 if (parent_path->node)
3171 return SVN_FS__ALREADY_EXISTS(root, path);
3173 /* Check to see if some lock is 'reserving' a file-path or dir-path
3174 at that location, or even some child-path; if so, check that we
3175 can use it. */
3176 if (args->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3178 SVN_ERR(svn_fs_base__allow_locked_operation(path, TRUE,
3179 trail, trail->pool));
3182 /* Create the file. */
3183 SVN_ERR(make_path_mutable(root, parent_path->parent, path,
3184 trail, trail->pool));
3185 SVN_ERR(svn_fs_base__dag_make_file(&child,
3186 parent_path->parent->node,
3187 parent_path_path(parent_path->parent,
3188 trail->pool),
3189 parent_path->entry,
3190 txn_id,
3191 trail, trail->pool));
3193 /* Make a record of this modification in the changes table. */
3194 SVN_ERR(add_change(root->fs, txn_id, path,
3195 svn_fs_base__dag_get_id(child),
3196 svn_fs_path_change_add, TRUE, FALSE,
3197 trail, trail->pool));
3199 return SVN_NO_ERROR;
3203 static svn_error_t *
3204 base_make_file(svn_fs_root_t *root,
3205 const char *path,
3206 apr_pool_t *pool)
3208 struct make_file_args args;
3210 args.root = root;
3211 args.path = path;
3212 return svn_fs_base__retry_txn(root->fs, txn_body_make_file, &args, pool);
3217 struct file_length_args
3219 svn_fs_root_t *root;
3220 const char *path;
3221 svn_filesize_t length; /* OUT parameter */
3224 static svn_error_t *
3225 txn_body_file_length(void *baton,
3226 trail_t *trail)
3228 struct file_length_args *args = baton;
3229 dag_node_t *file;
3231 /* First create a dag_node_t from the root/path pair. */
3232 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3234 /* Now fetch its length */
3235 return svn_fs_base__dag_file_length(&args->length, file,
3236 trail, trail->pool);
3239 static svn_error_t *
3240 base_file_length(svn_filesize_t *length_p,
3241 svn_fs_root_t *root,
3242 const char *path,
3243 apr_pool_t *pool)
3245 struct file_length_args args;
3247 args.root = root;
3248 args.path = path;
3249 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_length, &args,
3250 pool));
3252 *length_p = args.length;
3253 return SVN_NO_ERROR;
3257 struct file_checksum_args
3259 svn_fs_root_t *root;
3260 const char *path;
3261 unsigned char *digest; /* OUT parameter, APR_MD5_DIGESTSIZE bytes long */
3264 static svn_error_t *
3265 txn_body_file_checksum(void *baton,
3266 trail_t *trail)
3268 struct file_checksum_args *args = baton;
3269 dag_node_t *file;
3271 SVN_ERR(get_dag(&file, args->root, args->path, trail, trail->pool));
3272 return svn_fs_base__dag_file_checksum(args->digest, file,
3273 trail, trail->pool);
3276 static svn_error_t *
3277 base_file_md5_checksum(unsigned char digest[],
3278 svn_fs_root_t *root,
3279 const char *path,
3280 apr_pool_t *pool)
3282 struct file_checksum_args args;
3284 args.root = root;
3285 args.path = path;
3286 args.digest = digest;
3287 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_file_checksum, &args,
3288 pool));
3290 return SVN_NO_ERROR;
3294 /* --- Machinery for svn_fs_file_contents() --- */
3297 /* Local baton type for txn_body_get_file_contents. */
3298 typedef struct file_contents_baton_t
3300 /* The file we want to read. */
3301 svn_fs_root_t *root;
3302 const char *path;
3304 /* The dag_node that will be made from the above. */
3305 dag_node_t *node;
3307 /* The pool in which `file_stream' (below) is allocated. */
3308 apr_pool_t *pool;
3310 /* The readable file stream that will be made from the
3311 dag_node. (And returned to the caller.) */
3312 svn_stream_t *file_stream;
3314 } file_contents_baton_t;
3317 /* Main body of svn_fs_file_contents; converts a root/path pair into
3318 a readable file stream (in the context of a db txn). */
3319 static svn_error_t *
3320 txn_body_get_file_contents(void *baton, trail_t *trail)
3322 file_contents_baton_t *fb = (file_contents_baton_t *) baton;
3324 /* First create a dag_node_t from the root/path pair. */
3325 SVN_ERR(get_dag(&(fb->node), fb->root, fb->path, trail, trail->pool));
3327 /* Then create a readable stream from the dag_node_t. */
3328 SVN_ERR(svn_fs_base__dag_get_contents(&(fb->file_stream),
3329 fb->node,
3330 trail, fb->pool));
3331 return SVN_NO_ERROR;
3336 static svn_error_t *
3337 base_file_contents(svn_stream_t **contents,
3338 svn_fs_root_t *root,
3339 const char *path,
3340 apr_pool_t *pool)
3342 file_contents_baton_t *fb = apr_pcalloc(pool, sizeof(*fb));
3343 fb->root = root;
3344 fb->path = path;
3345 fb->pool = pool;
3347 /* Create the readable stream in the context of a db txn. */
3348 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_get_file_contents,
3349 fb, pool));
3351 *contents = fb->file_stream;
3352 return SVN_NO_ERROR;
3355 /* --- End machinery for svn_fs_file_contents() --- */
3359 /* --- Machinery for svn_fs_apply_textdelta() --- */
3362 /* Local baton type for all the helper functions below. */
3363 typedef struct txdelta_baton_t
3365 /* This is the custom-built window consumer given to us by the delta
3366 library; it uniquely knows how to read data from our designated
3367 "source" stream, interpret the window, and write data to our
3368 designated "target" stream (in this case, our repos file.) */
3369 svn_txdelta_window_handler_t interpreter;
3370 void *interpreter_baton;
3372 /* The original file info */
3373 svn_fs_root_t *root;
3374 const char *path;
3376 /* Derived from the file info */
3377 dag_node_t *node;
3379 svn_stream_t *source_stream;
3380 svn_stream_t *target_stream;
3381 svn_stream_t *string_stream;
3382 svn_stringbuf_t *target_string;
3384 /* Hex MD5 digest for the base text against which a delta is to be
3385 applied, and for the resultant fulltext, respectively. Either or
3386 both may be null, in which case ignored. */
3387 const char *base_checksum;
3388 const char *result_checksum;
3390 /* Pool used by db txns */
3391 apr_pool_t *pool;
3393 } txdelta_baton_t;
3396 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits.
3397 * This closes BATON->target_stream.
3399 * Note: If you're confused about how this function relates to another
3400 * of similar name, think of it this way:
3402 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3403 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3405 static svn_error_t *
3406 txn_body_txdelta_finalize_edits(void *baton, trail_t *trail)
3408 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3409 return svn_fs_base__dag_finalize_edits(tb->node,
3410 tb->result_checksum,
3411 tb->root->txn,
3412 trail, trail->pool);
3416 /* ### see comment in window_consumer() regarding this function. */
3418 /* Helper function of generic type `svn_write_fn_t'. Implements a
3419 writable stream which appends to an svn_stringbuf_t. */
3420 static svn_error_t *
3421 write_to_string(void *baton, const char *data, apr_size_t *len)
3423 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3424 svn_stringbuf_appendbytes(tb->target_string, data, *len);
3425 return SVN_NO_ERROR;
3430 /* The main window handler returned by svn_fs_apply_textdelta. */
3431 static svn_error_t *
3432 window_consumer(svn_txdelta_window_t *window, void *baton)
3434 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3436 /* Send the window right through to the custom window interpreter.
3437 In theory, the interpreter will then write more data to
3438 cb->target_string. */
3439 SVN_ERR(tb->interpreter(window, tb->interpreter_baton));
3441 /* ### the write_to_string() callback for the txdelta's output stream
3442 ### should be doing all the flush determination logic, not here.
3443 ### in a drastic case, a window could generate a LOT more than the
3444 ### maximum buffer size. we want to flush to the underlying target
3445 ### stream much sooner (e.g. also in a streamy fashion). also, by
3446 ### moving this logic inside the stream, the stream becomes nice
3447 ### and encapsulated: it holds all the logic about buffering and
3448 ### flushing.
3450 ### further: I believe the buffering should be removed from tree.c
3451 ### the buffering should go into the target_stream itself, which
3452 ### is defined by reps-string.c. Specifically, I think the
3453 ### rep_write_contents() function will handle the buffering and
3454 ### the spill to the underlying DB. by locating it there, then
3455 ### anybody who gets a writable stream for FS content can take
3456 ### advantage of the buffering capability. this will be important
3457 ### when we export an FS API function for writing a fulltext into
3458 ### the FS, rather than forcing that fulltext thru apply_textdelta.
3461 /* Check to see if we need to purge the portion of the contents that
3462 have been written thus far. */
3463 if ((! window) || (tb->target_string->len > WRITE_BUFFER_SIZE))
3465 apr_size_t len = tb->target_string->len;
3466 SVN_ERR(svn_stream_write(tb->target_stream,
3467 tb->target_string->data,
3468 &len));
3469 svn_stringbuf_set(tb->target_string, "");
3472 /* Is the window NULL? If so, we're done. */
3473 if (! window)
3475 /* Close the internal-use stream. ### This used to be inside of
3476 txn_body_fulltext_finalize_edits(), but that invoked a nested
3477 Berkeley DB transaction -- scandalous! */
3478 SVN_ERR(svn_stream_close(tb->target_stream));
3480 /* Tell the dag subsystem that we're finished with our edits. */
3481 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3482 txn_body_txdelta_finalize_edits, tb,
3483 tb->pool));
3486 return SVN_NO_ERROR;
3490 static svn_error_t *
3491 txn_body_apply_textdelta(void *baton, trail_t *trail)
3493 txdelta_baton_t *tb = (txdelta_baton_t *) baton;
3494 parent_path_t *parent_path;
3495 const char *txn_id = tb->root->txn;
3497 /* Call open_path with no flags, as we want this to return an error
3498 if the node for which we are searching doesn't exist. */
3499 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3500 trail, trail->pool));
3502 /* Check to see if path is locked; if so, check that we can use it. */
3503 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3504 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3505 trail, trail->pool));
3507 /* Now, make sure this path is mutable. */
3508 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3509 trail, trail->pool));
3510 tb->node = parent_path->node;
3512 if (tb->base_checksum)
3514 unsigned char digest[APR_MD5_DIGESTSIZE];
3515 const char *hex;
3517 /* Until we finalize the node, its data_key points to the old
3518 contents, in other words, the base text. */
3519 SVN_ERR(svn_fs_base__dag_file_checksum(digest, tb->node,
3520 trail, trail->pool));
3521 hex = svn_md5_digest_to_cstring(digest, trail->pool);
3522 if (hex && (strcmp(tb->base_checksum, hex) != 0))
3523 return svn_error_createf
3524 (SVN_ERR_CHECKSUM_MISMATCH,
3525 NULL,
3526 _("Base checksum mismatch on '%s':\n"
3527 " expected: %s\n"
3528 " actual: %s\n"),
3529 tb->path, tb->base_checksum, hex);
3532 /* Make a readable "source" stream out of the current contents of
3533 ROOT/PATH; obviously, this must done in the context of a db_txn.
3534 The stream is returned in tb->source_stream. */
3535 SVN_ERR(svn_fs_base__dag_get_contents(&(tb->source_stream),
3536 tb->node, trail, tb->pool));
3538 /* Make a writable "target" stream */
3539 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->target_stream), tb->node,
3540 txn_id, trail, tb->pool));
3542 /* Make a writable "string" stream which writes data to
3543 tb->target_string. */
3544 tb->target_string = svn_stringbuf_create("", tb->pool);
3545 tb->string_stream = svn_stream_create(tb, tb->pool);
3546 svn_stream_set_write(tb->string_stream, write_to_string);
3548 /* Now, create a custom window handler that uses our two streams. */
3549 svn_txdelta_apply(tb->source_stream,
3550 tb->string_stream,
3551 NULL,
3552 tb->path,
3553 tb->pool,
3554 &(tb->interpreter),
3555 &(tb->interpreter_baton));
3557 /* Make a record of this modification in the changes table. */
3558 SVN_ERR(add_change(tb->root->fs, txn_id, tb->path,
3559 svn_fs_base__dag_get_id(tb->node),
3560 svn_fs_path_change_modify, TRUE, FALSE, trail,
3561 trail->pool));
3563 return SVN_NO_ERROR;
3567 static svn_error_t *
3568 base_apply_textdelta(svn_txdelta_window_handler_t *contents_p,
3569 void **contents_baton_p,
3570 svn_fs_root_t *root,
3571 const char *path,
3572 const char *base_checksum,
3573 const char *result_checksum,
3574 apr_pool_t *pool)
3576 txdelta_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3578 tb->root = root;
3579 tb->path = path;
3580 tb->pool = pool;
3582 if (base_checksum)
3583 tb->base_checksum = apr_pstrdup(pool, base_checksum);
3584 else
3585 tb->base_checksum = NULL;
3587 if (result_checksum)
3588 tb->result_checksum = apr_pstrdup(pool, result_checksum);
3589 else
3590 tb->result_checksum = NULL;
3592 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_textdelta, tb,
3593 pool));
3595 *contents_p = window_consumer;
3596 *contents_baton_p = tb;
3597 return SVN_NO_ERROR;
3600 /* --- End machinery for svn_fs_apply_textdelta() --- */
3602 /* --- Machinery for svn_fs_apply_text() --- */
3604 /* Baton for svn_fs_apply_text(). */
3605 struct text_baton_t
3607 /* The original file info */
3608 svn_fs_root_t *root;
3609 const char *path;
3611 /* Derived from the file info */
3612 dag_node_t *node;
3614 /* The returned stream that will accept the file's new contents. */
3615 svn_stream_t *stream;
3617 /* The actual fs stream that the returned stream will write to. */
3618 svn_stream_t *file_stream;
3620 /* Hex MD5 digest for the final fulltext written to the file. May
3621 be null, in which case ignored. */
3622 const char *result_checksum;
3624 /* Pool used by db txns */
3625 apr_pool_t *pool;
3629 /* A trail-ready wrapper around svn_fs_base__dag_finalize_edits, but for
3630 * fulltext data, not text deltas. Closes BATON->file_stream.
3632 * Note: If you're confused about how this function relates to another
3633 * of similar name, think of it this way:
3635 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
3636 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
3638 static svn_error_t *
3639 txn_body_fulltext_finalize_edits(void *baton, trail_t *trail)
3641 struct text_baton_t *tb = baton;
3642 return svn_fs_base__dag_finalize_edits(tb->node,
3643 tb->result_checksum,
3644 tb->root->txn,
3645 trail, trail->pool);
3648 /* Write function for the publically returned stream. */
3649 static svn_error_t *
3650 text_stream_writer(void *baton,
3651 const char *data,
3652 apr_size_t *len)
3654 struct text_baton_t *tb = baton;
3656 /* Psst, here's some data. Pass it on to the -real- file stream. */
3657 return svn_stream_write(tb->file_stream, data, len);
3660 /* Close function for the publically returned stream. */
3661 static svn_error_t *
3662 text_stream_closer(void *baton)
3664 struct text_baton_t *tb = baton;
3666 /* Close the internal-use stream. ### This used to be inside of
3667 txn_body_fulltext_finalize_edits(), but that invoked a nested
3668 Berkeley DB transaction -- scandalous! */
3669 SVN_ERR(svn_stream_close(tb->file_stream));
3671 /* Need to tell fs that we're done sending text */
3672 SVN_ERR(svn_fs_base__retry_txn(tb->root->fs,
3673 txn_body_fulltext_finalize_edits, tb,
3674 tb->pool));
3676 return SVN_NO_ERROR;
3680 static svn_error_t *
3681 txn_body_apply_text(void *baton, trail_t *trail)
3683 struct text_baton_t *tb = baton;
3684 parent_path_t *parent_path;
3685 const char *txn_id = tb->root->txn;
3687 /* Call open_path with no flags, as we want this to return an error
3688 if the node for which we are searching doesn't exist. */
3689 SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id,
3690 trail, trail->pool));
3692 /* Check to see if path is locked; if so, check that we can use it. */
3693 if (tb->root->txn_flags & SVN_FS_TXN_CHECK_LOCKS)
3694 SVN_ERR(svn_fs_base__allow_locked_operation(tb->path, FALSE,
3695 trail, trail->pool));
3697 /* Now, make sure this path is mutable. */
3698 SVN_ERR(make_path_mutable(tb->root, parent_path, tb->path,
3699 trail, trail->pool));
3700 tb->node = parent_path->node;
3702 /* Make a writable stream for replacing the file's text. */
3703 SVN_ERR(svn_fs_base__dag_get_edit_stream(&(tb->file_stream), tb->node,
3704 txn_id, trail, tb->pool));
3706 /* Create a 'returnable' stream which writes to the file_stream. */
3707 tb->stream = svn_stream_create(tb, tb->pool);
3708 svn_stream_set_write(tb->stream, text_stream_writer);
3709 svn_stream_set_close(tb->stream, text_stream_closer);
3711 /* Make a record of this modification in the changes table. */
3712 SVN_ERR(add_change(tb->root->fs, txn_id, tb->path,
3713 svn_fs_base__dag_get_id(tb->node),
3714 svn_fs_path_change_modify, TRUE, FALSE, trail,
3715 trail->pool));
3717 return SVN_NO_ERROR;
3721 static svn_error_t *
3722 base_apply_text(svn_stream_t **contents_p,
3723 svn_fs_root_t *root,
3724 const char *path,
3725 const char *result_checksum,
3726 apr_pool_t *pool)
3728 struct text_baton_t *tb = apr_pcalloc(pool, sizeof(*tb));
3730 tb->root = root;
3731 tb->path = path;
3732 tb->pool = pool;
3734 if (result_checksum)
3735 tb->result_checksum = apr_pstrdup(pool, result_checksum);
3736 else
3737 tb->result_checksum = NULL;
3739 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_apply_text, tb, pool));
3741 *contents_p = tb->stream;
3742 return SVN_NO_ERROR;
3745 /* --- End machinery for svn_fs_apply_text() --- */
3748 /* Note: we're sharing the `things_changed_args' struct with
3749 svn_fs_props_changed(). */
3751 static svn_error_t *
3752 txn_body_contents_changed(void *baton, trail_t *trail)
3754 struct things_changed_args *args = baton;
3755 dag_node_t *node1, *node2;
3757 SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
3758 SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
3759 SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
3760 node1, node2, trail, trail->pool));
3761 return SVN_NO_ERROR;
3765 /* Note: it is acceptable for this function to call back into
3766 top-level interfaces because it does not itself use trails. */
3767 static svn_error_t *
3768 base_contents_changed(svn_boolean_t *changed_p,
3769 svn_fs_root_t *root1,
3770 const char *path1,
3771 svn_fs_root_t *root2,
3772 const char *path2,
3773 apr_pool_t *pool)
3775 struct things_changed_args args;
3777 /* Check that roots are in the same fs. */
3778 if (root1->fs != root2->fs)
3779 return svn_error_create
3780 (SVN_ERR_FS_GENERAL, NULL,
3781 _("Cannot compare file contents between two different filesystems"));
3783 /* Check that both paths are files. */
3785 svn_node_kind_t kind;
3787 SVN_ERR(base_check_path(&kind, root1, path1, pool));
3788 if (kind != svn_node_file)
3789 return svn_error_createf
3790 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
3792 SVN_ERR(base_check_path(&kind, root2, path2, pool));
3793 if (kind != svn_node_file)
3794 return svn_error_createf
3795 (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
3798 args.root1 = root1;
3799 args.root2 = root2;
3800 args.path1 = path1;
3801 args.path2 = path2;
3802 args.changed_p = changed_p;
3803 args.pool = pool;
3805 SVN_ERR(svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed,
3806 &args, pool));
3808 return SVN_NO_ERROR;
3813 /* Public interface to computing file text deltas. */
3815 /* Note: it is acceptable for this function to call back into
3816 public FS API interfaces because it does not itself use trails. */
3817 static svn_error_t *
3818 base_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
3819 svn_fs_root_t *source_root,
3820 const char *source_path,
3821 svn_fs_root_t *target_root,
3822 const char *target_path,
3823 apr_pool_t *pool)
3825 svn_stream_t *source, *target;
3826 svn_txdelta_stream_t *delta_stream;
3828 /* Get read functions for the source file contents. */
3829 if (source_root && source_path)
3830 SVN_ERR(base_file_contents(&source, source_root, source_path, pool));
3831 else
3832 source = svn_stream_empty(pool);
3834 /* Get read functions for the target file contents. */
3835 SVN_ERR(base_file_contents(&target, target_root, target_path, pool));
3837 /* Create a delta stream that turns the ancestor into the target. */
3838 svn_txdelta(&delta_stream, source, target, pool);
3840 *stream_p = delta_stream;
3841 return SVN_NO_ERROR;
3846 /* Finding Changes */
3848 struct paths_changed_args
3850 apr_hash_t *changes;
3851 svn_fs_root_t *root;
3855 static svn_error_t *
3856 txn_body_paths_changed(void *baton,
3857 trail_t *trail)
3859 /* WARNING: This is called *without* the protection of a Berkeley DB
3860 transaction. If you modify this function, keep that in mind. */
3862 struct paths_changed_args *args = baton;
3863 const char *txn_id;
3864 svn_fs_t *fs = args->root->fs;
3866 /* Get the transaction ID from ROOT. */
3867 if (! args->root->is_txn_root)
3868 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, args->root->rev,
3869 trail, trail->pool));
3870 else
3871 txn_id = args->root->txn;
3873 return svn_fs_bdb__changes_fetch(&(args->changes), fs, txn_id,
3874 trail, trail->pool);
3878 static svn_error_t *
3879 base_paths_changed(apr_hash_t **changed_paths_p,
3880 svn_fs_root_t *root,
3881 apr_pool_t *pool)
3883 struct paths_changed_args args;
3884 args.root = root;
3885 args.changes = NULL;
3886 SVN_ERR(svn_fs_base__retry(root->fs, txn_body_paths_changed, &args, pool));
3887 *changed_paths_p = args.changes;
3888 return SVN_NO_ERROR;
3893 /* Our coolio opaque history object. */
3894 typedef struct
3896 /* filesystem object */
3897 svn_fs_t *fs;
3899 /* path and revision of historical location */
3900 const char *path;
3901 svn_revnum_t revision;
3903 /* internal-use hints about where to resume the history search. */
3904 const char *path_hint;
3905 svn_revnum_t rev_hint;
3907 /* FALSE until the first call to svn_fs_history_prev(). */
3908 svn_boolean_t is_interesting;
3909 } base_history_data_t;
3912 static svn_fs_history_t *assemble_history(svn_fs_t *fs, const char *path,
3913 svn_revnum_t revision,
3914 svn_boolean_t is_interesting,
3915 const char *path_hint,
3916 svn_revnum_t rev_hint,
3917 apr_pool_t *pool);
3920 static svn_error_t *
3921 base_node_history(svn_fs_history_t **history_p,
3922 svn_fs_root_t *root,
3923 const char *path,
3924 apr_pool_t *pool)
3926 svn_node_kind_t kind;
3928 /* We require a revision root. */
3929 if (root->is_txn_root)
3930 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
3932 /* And we require that the path exist in the root. */
3933 SVN_ERR(base_check_path(&kind, root, path, pool));
3934 if (kind == svn_node_none)
3935 return SVN_FS__NOT_FOUND(root, path);
3937 /* Okay, all seems well. Build our history object and return it. */
3938 *history_p = assemble_history(root->fs,
3939 svn_fs__canonicalize_abspath(path, pool),
3940 root->rev, FALSE, NULL,
3941 SVN_INVALID_REVNUM, pool);
3942 return SVN_NO_ERROR;
3946 /* Examine the PARENT_PATH structure chain to determine how copy IDs
3947 would be doled out in the event that PARENT_PATH was made mutable.
3948 Return the ID of the copy that last affected PARENT_PATH (and the
3949 COPY itself, if we've already fetched it).
3951 static svn_error_t *
3952 examine_copy_inheritance(const char **copy_id,
3953 copy_t **copy,
3954 svn_fs_t *fs,
3955 parent_path_t *parent_path,
3956 trail_t *trail,
3957 apr_pool_t *pool)
3959 /* The default response -- our current copy ID, and no fetched COPY. */
3960 *copy_id = svn_fs_base__id_copy_id
3961 (svn_fs_base__dag_get_id(parent_path->node));
3962 *copy = NULL;
3964 /* If we have no parent (we are looking at the root node), or if
3965 this node is supposed to inherit from itself, return that fact. */
3966 if (! parent_path->parent)
3967 return SVN_NO_ERROR;
3969 /* We could be a branch destination (which would answer our question
3970 altogether)! But then, again, we might just have been modified
3971 in this revision, so all bets are off. */
3972 if (parent_path->copy_inherit == copy_id_inherit_self)
3974 /* A copy ID of "0" means we've never been branched. Therefore,
3975 there are no copies relevant to our history. */
3976 if (((*copy_id)[0] == '0') && ((*copy_id)[1] == '\0'))
3977 return SVN_NO_ERROR;
3979 /* Get the COPY record. If it was a real copy (not an implicit
3980 one), we have our answer. Otherwise, we fall through to the
3981 recursive case. */
3982 SVN_ERR(svn_fs_bdb__get_copy(copy, fs, *copy_id, trail, pool));
3983 if ((*copy)->kind != copy_kind_soft)
3984 return SVN_NO_ERROR;
3987 /* Otherwise, our answer is dependent upon our parent. */
3988 return examine_copy_inheritance(copy_id, copy, fs,
3989 parent_path->parent, trail, pool);
3993 struct history_prev_args
3995 svn_fs_history_t **prev_history_p;
3996 svn_fs_history_t *history;
3997 svn_boolean_t cross_copies;
3998 apr_pool_t *pool;
4002 static svn_error_t *
4003 txn_body_history_prev(void *baton, trail_t *trail)
4005 struct history_prev_args *args = baton;
4006 svn_fs_history_t **prev_history = args->prev_history_p;
4007 svn_fs_history_t *history = args->history;
4008 base_history_data_t *bhd = history->fsap_data;
4009 const char *commit_path, *src_path, *path = bhd->path;
4010 svn_revnum_t commit_rev, src_rev, dst_rev, revision = bhd->revision;
4011 apr_pool_t *retpool = args->pool;
4012 svn_fs_t *fs = bhd->fs;
4013 parent_path_t *parent_path;
4014 dag_node_t *node;
4015 svn_fs_root_t *root;
4016 const svn_fs_id_t *node_id;
4017 const char *end_copy_id = NULL;
4018 struct revision_root_args rr_args;
4019 svn_boolean_t reported = bhd->is_interesting;
4020 const char *txn_id;
4021 copy_t *copy = NULL;
4022 svn_boolean_t retry = FALSE;
4024 /* Initialize our return value. */
4025 *prev_history = NULL;
4027 /* If our last history report left us hints about where to pickup
4028 the chase, then our last report was on the destination of a
4029 copy. If we are crossing copies, start from those locations,
4030 otherwise, we're all done here. */
4031 if (bhd->path_hint && SVN_IS_VALID_REVNUM(bhd->rev_hint))
4033 reported = FALSE;
4034 if (! args->cross_copies)
4035 return SVN_NO_ERROR;
4036 path = bhd->path_hint;
4037 revision = bhd->rev_hint;
4040 /* Construct a ROOT for the current revision. */
4041 rr_args.root_p = &root;
4042 rr_args.rev = revision;
4043 SVN_ERR(txn_body_revision_root(&rr_args, trail));
4045 /* Open PATH/REVISION, and get its node and a bunch of other
4046 goodies. */
4047 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, revision, trail,
4048 trail->pool));
4049 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4050 trail, trail->pool));
4051 node = parent_path->node;
4052 node_id = svn_fs_base__dag_get_id(node);
4053 commit_path = svn_fs_base__dag_get_created_path(node);
4054 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4055 trail, trail->pool));
4057 /* The Subversion filesystem is written in such a way that a given
4058 line of history may have at most one interesting history point
4059 per filesystem revision. Either that node was edited (and
4060 possibly copied), or it was copied but not edited. And a copy
4061 source cannot be from the same revision as its destination. So,
4062 if our history revision matches its node's commit revision, we
4063 know that ... */
4064 if (revision == commit_rev)
4066 if (! reported)
4068 /* ... we either have not yet reported on this revision (and
4069 need now to do so) ... */
4070 *prev_history = assemble_history(fs,
4071 apr_pstrdup(retpool, commit_path),
4072 commit_rev, TRUE, NULL,
4073 SVN_INVALID_REVNUM, retpool);
4074 return SVN_NO_ERROR;
4076 else
4078 /* ... or we *have* reported on this revision, and must now
4079 progress toward this node's predecessor (unless there is
4080 no predecessor, in which case we're all done!). */
4081 const svn_fs_id_t *pred_id;
4083 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id, node,
4084 trail, trail->pool));
4085 if (! pred_id)
4086 return SVN_NO_ERROR;
4088 /* Replace NODE and friends with the information from its
4089 predecessor. */
4090 SVN_ERR(svn_fs_base__dag_get_node(&node, fs, pred_id,
4091 trail, trail->pool));
4092 node_id = svn_fs_base__dag_get_id(node);
4093 commit_path = svn_fs_base__dag_get_created_path(node);
4094 SVN_ERR(svn_fs_base__dag_get_revision(&commit_rev, node,
4095 trail, trail->pool));
4099 /* Calculate a possibly relevant copy ID. */
4100 SVN_ERR(examine_copy_inheritance(&end_copy_id, &copy, fs,
4101 parent_path, trail, trail->pool));
4103 /* Initialize some state variables. */
4104 src_path = NULL;
4105 src_rev = SVN_INVALID_REVNUM;
4106 dst_rev = SVN_INVALID_REVNUM;
4108 /* If our current copy ID (which is either the real copy ID of our
4109 node, or the last copy ID which would affect our node if it were
4110 to be made mutable) diffs at all from that of its predecessor
4111 (which is either a real predecessor, or is the node itself
4112 playing the predecessor role to an imaginary mutable successor),
4113 then we need to report a copy. */
4114 if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
4115 end_copy_id) != 0)
4117 const char *remainder;
4118 dag_node_t *dst_node;
4119 const char *copy_dst;
4121 /* Get the COPY record if we haven't already fetched it. */
4122 if (! copy)
4123 SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, end_copy_id, trail,
4124 trail->pool));
4126 /* Figure out the destination path of the copy operation. */
4127 SVN_ERR(svn_fs_base__dag_get_node(&dst_node, fs,
4128 copy->dst_noderev_id,
4129 trail, trail->pool));
4130 copy_dst = svn_fs_base__dag_get_created_path(dst_node);
4132 /* If our current path was the very destination of the copy,
4133 then our new current path will be the copy source. If our
4134 current path was instead the *child* of the destination of
4135 the copy, then figure out its previous location by taking its
4136 path relative to the copy destination and appending that to
4137 the copy source. Finally, if our current path doesn't meet
4138 one of these other criteria ... ### for now just fallback to
4139 the old copy hunt algorithm. */
4140 if (strcmp(path, copy_dst) == 0)
4141 remainder = "";
4142 else
4143 remainder = svn_path_is_child(copy_dst, path, trail->pool);
4145 if (remainder)
4147 /* If we get here, then our current path is the destination
4148 of, or the child of the destination of, a copy. Fill
4149 in the return values and get outta here. */
4150 SVN_ERR(svn_fs_base__txn_get_revision
4151 (&src_rev, fs, copy->src_txn_id, trail, trail->pool));
4152 SVN_ERR(svn_fs_base__txn_get_revision
4153 (&dst_rev, fs,
4154 svn_fs_base__id_txn_id(copy->dst_noderev_id),
4155 trail, trail->pool));
4156 src_path = svn_path_join(copy->src_path, remainder,
4157 trail->pool);
4158 if (copy->kind == copy_kind_soft)
4159 retry = TRUE;
4163 /* If we calculated a copy source path and revision, and the
4164 copy source revision doesn't pre-date a revision in which we
4165 *know* our node was modified, we'll make a 'copy-style' history
4166 object. */
4167 if (src_path && SVN_IS_VALID_REVNUM(src_rev) && (src_rev >= commit_rev))
4169 /* It's possible for us to find a copy location that is the same
4170 as the history point we've just reported. If that happens,
4171 we simply need to take another trip through this history
4172 search. */
4173 if ((dst_rev == revision) && reported)
4174 retry = TRUE;
4176 *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
4177 dst_rev, retry ? FALSE : TRUE,
4178 src_path, src_rev, retpool);
4180 else
4182 *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
4183 commit_rev, TRUE, NULL,
4184 SVN_INVALID_REVNUM, retpool);
4187 return SVN_NO_ERROR;
4191 static svn_error_t *
4192 base_history_prev(svn_fs_history_t **prev_history_p,
4193 svn_fs_history_t *history,
4194 svn_boolean_t cross_copies,
4195 apr_pool_t *pool)
4197 svn_fs_history_t *prev_history = NULL;
4198 base_history_data_t *bhd = history->fsap_data;
4199 svn_fs_t *fs = bhd->fs;
4201 /* Special case: the root directory changes in every single
4202 revision, no exceptions. And, the root can't be the target (or
4203 child of a target -- duh) of a copy. So, if that's our path,
4204 then we need only decrement our revision by 1, and there you go. */
4205 if (strcmp(bhd->path, "/") == 0)
4207 if (! bhd->is_interesting)
4208 prev_history = assemble_history(fs, "/", bhd->revision,
4209 1, NULL, SVN_INVALID_REVNUM, pool);
4210 else if (bhd->revision > 0)
4211 prev_history = assemble_history(fs, "/", bhd->revision - 1,
4212 1, NULL, SVN_INVALID_REVNUM, pool);
4214 else
4216 struct history_prev_args args;
4217 prev_history = history;
4219 while (1)
4221 /* Get a trail, and get to work. */
4223 args.prev_history_p = &prev_history;
4224 args.history = prev_history;
4225 args.cross_copies = cross_copies;
4226 args.pool = pool;
4227 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
4228 pool));
4229 if (! prev_history)
4230 break;
4231 bhd = prev_history->fsap_data;
4232 if (bhd->is_interesting)
4233 break;
4237 *prev_history_p = prev_history;
4238 return SVN_NO_ERROR;
4242 static svn_error_t *
4243 base_history_location(const char **path,
4244 svn_revnum_t *revision,
4245 svn_fs_history_t *history,
4246 apr_pool_t *pool)
4248 base_history_data_t *bhd = history->fsap_data;
4250 *path = apr_pstrdup(pool, bhd->path);
4251 *revision = bhd->revision;
4252 return SVN_NO_ERROR;
4256 static history_vtable_t history_vtable = {
4257 base_history_prev,
4258 base_history_location
4263 struct closest_copy_args
4265 svn_fs_root_t **root_p;
4266 const char **path_p;
4267 svn_fs_root_t *root;
4268 const char *path;
4269 apr_pool_t *pool;
4273 static svn_error_t *
4274 txn_body_closest_copy(void *baton, trail_t *trail)
4276 struct closest_copy_args *args = baton;
4277 svn_fs_root_t *root = args->root;
4278 const char *path = args->path;
4279 svn_fs_t *fs = root->fs;
4280 parent_path_t *parent_path;
4281 const svn_fs_id_t *node_id;
4282 const char *txn_id, *copy_id;
4283 copy_t *copy = NULL;
4284 svn_fs_root_t *copy_dst_root;
4285 dag_node_t *path_node_in_copy_dst, *copy_dst_node, *copy_dst_root_node;
4286 const char *copy_dst_path;
4287 svn_revnum_t copy_dst_rev, created_rev;
4288 svn_error_t *err;
4290 *(args->path_p) = NULL;
4291 *(args->root_p) = NULL;
4293 /* Get the transaction ID associated with our root. */
4294 if (root->is_txn_root)
4295 txn_id = root->txn;
4296 else
4297 SVN_ERR(svn_fs_base__rev_get_txn_id(&txn_id, fs, root->rev,
4298 trail, trail->pool));
4300 /* Open PATH in ROOT -- it must exist. */
4301 SVN_ERR(open_path(&parent_path, root, path, 0, txn_id,
4302 trail, trail->pool));
4303 node_id = svn_fs_base__dag_get_id(parent_path->node);
4305 /* Now, examine the copy inheritance rules in play should our path
4306 be made mutable in the future (if it isn't already). This will
4307 tell us about the youngest affecting copy. */
4308 SVN_ERR(examine_copy_inheritance(&copy_id, &copy, fs, parent_path,
4309 trail, trail->pool));
4311 /* Easy out: if the copy ID is 0, there's nothing of interest here. */
4312 if (((copy_id)[0] == '0') && ((copy_id)[1] == '\0'))
4313 return SVN_NO_ERROR;
4315 /* Fetch our copy if examine_copy_inheritance() didn't do it for us. */
4316 if (! copy)
4317 SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, copy_id, trail, trail->pool));
4319 /* Figure out the destination path and revision of the copy operation. */
4320 SVN_ERR(svn_fs_base__dag_get_node(&copy_dst_node, fs, copy->dst_noderev_id,
4321 trail, trail->pool));
4322 copy_dst_path = svn_fs_base__dag_get_created_path(copy_dst_node);
4323 SVN_ERR(svn_fs_base__dag_get_revision(&copy_dst_rev, copy_dst_node,
4324 trail, trail->pool));
4326 /* Turn that revision into a revision root. */
4327 SVN_ERR(svn_fs_base__dag_revision_root(&copy_dst_root_node, fs,
4328 copy_dst_rev, trail, args->pool));
4329 copy_dst_root = make_revision_root(fs, copy_dst_rev,
4330 copy_dst_root_node, args->pool);
4332 /* It is possible that this node was created from scratch at some
4333 revision between COPY_DST_REV and the transaction associated with
4334 our ROOT. Make sure that PATH exists as of COPY_DST_REV and is
4335 related to this node-rev. */
4336 err = get_dag(&path_node_in_copy_dst, copy_dst_root, path,
4337 trail, trail->pool);
4338 if (err)
4340 if ((err->apr_err == SVN_ERR_FS_NOT_FOUND)
4341 || (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY))
4343 svn_error_clear(err);
4344 return SVN_NO_ERROR;
4346 return err;
4348 if ((svn_fs_base__dag_node_kind(path_node_in_copy_dst) == svn_node_none)
4349 || (! (svn_fs_base__id_check_related
4350 (node_id, svn_fs_base__dag_get_id(path_node_in_copy_dst)))))
4352 return SVN_NO_ERROR;
4355 /* One final check must be done here. If you copy a directory and
4356 create a new entity somewhere beneath that directory in the same
4357 txn, then we can't claim that the copy affected the new entity.
4358 For example, if you do:
4360 copy dir1 dir2
4361 create dir2/new-thing
4362 commit
4364 then dir2/new-thing was not affected by the copy of dir1 to dir2.
4365 We detect this situation by asking if PATH@COPY_DST_REV's
4366 created-rev is COPY_DST_REV, and that node-revision has no
4367 predecessors, then there is no relevant closest copy.
4369 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node_in_copy_dst,
4370 trail, trail->pool));
4371 if (created_rev == copy_dst_rev)
4373 const svn_fs_id_t *pred_id;
4374 SVN_ERR(svn_fs_base__dag_get_predecessor_id(&pred_id,
4375 path_node_in_copy_dst,
4376 trail, trail->pool));
4377 if (! pred_id)
4378 return SVN_NO_ERROR;
4381 *(args->path_p) = apr_pstrdup(args->pool, copy_dst_path);
4382 *(args->root_p) = copy_dst_root;
4384 return SVN_NO_ERROR;
4388 static svn_error_t *
4389 base_closest_copy(svn_fs_root_t **root_p,
4390 const char **path_p,
4391 svn_fs_root_t *root,
4392 const char *path,
4393 apr_pool_t *pool)
4395 struct closest_copy_args args;
4396 svn_fs_t *fs = root->fs;
4397 svn_fs_root_t *closest_root = NULL;
4398 const char *closest_path = NULL;
4400 args.root_p = &closest_root;
4401 args.path_p = &closest_path;
4402 args.root = root;
4403 args.path = path;
4404 args.pool = pool;
4405 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_closest_copy, &args, pool));
4406 *root_p = closest_root;
4407 *path_p = closest_path;
4408 return SVN_NO_ERROR;
4412 /* Return a new history object (marked as "interesting") for PATH and
4413 REVISION, allocated in POOL, and with its members set to the values
4414 of the parameters provided. Note that PATH and PATH_HINT are not
4415 duped into POOL -- it is the responsibility of the caller to ensure
4416 that this happens. */
4417 static svn_fs_history_t *
4418 assemble_history(svn_fs_t *fs,
4419 const char *path,
4420 svn_revnum_t revision,
4421 svn_boolean_t is_interesting,
4422 const char *path_hint,
4423 svn_revnum_t rev_hint,
4424 apr_pool_t *pool)
4426 svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
4427 base_history_data_t *bhd = apr_pcalloc(pool, sizeof(*bhd));
4428 bhd->path = path;
4429 bhd->revision = revision;
4430 bhd->is_interesting = is_interesting;
4431 bhd->path_hint = path_hint;
4432 bhd->rev_hint = rev_hint;
4433 bhd->fs = fs;
4434 history->vtable = &history_vtable;
4435 history->fsap_data = bhd;
4436 return history;
4440 svn_error_t *
4441 svn_fs_base__get_path_kind(svn_node_kind_t *kind,
4442 const char *path,
4443 trail_t *trail,
4444 apr_pool_t *pool)
4446 svn_revnum_t head_rev;
4447 svn_fs_root_t *root;
4448 dag_node_t *root_dir, *path_node;
4449 svn_error_t *err;
4451 /* Get HEAD revision, */
4452 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4454 /* Then convert it into a root_t, */
4455 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4456 trail, pool));
4457 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4459 /* And get the dag_node for path in the root_t. */
4460 err = get_dag(&path_node, root, path, trail, pool);
4461 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4463 svn_error_clear(err);
4464 *kind = svn_node_none;
4465 return SVN_NO_ERROR;
4467 else if (err)
4468 return err;
4470 *kind = svn_fs_base__dag_node_kind(path_node);
4471 return SVN_NO_ERROR;
4475 svn_error_t *
4476 svn_fs_base__get_path_created_rev(svn_revnum_t *rev,
4477 const char *path,
4478 trail_t *trail,
4479 apr_pool_t *pool)
4481 svn_revnum_t head_rev, created_rev;
4482 svn_fs_root_t *root;
4483 dag_node_t *root_dir, *path_node;
4484 svn_error_t *err;
4486 /* Get HEAD revision, */
4487 SVN_ERR(svn_fs_bdb__youngest_rev(&head_rev, trail->fs, trail, pool));
4489 /* Then convert it into a root_t, */
4490 SVN_ERR(svn_fs_base__dag_revision_root(&root_dir, trail->fs, head_rev,
4491 trail, pool));
4492 root = make_revision_root(trail->fs, head_rev, root_dir, pool);
4494 /* And get the dag_node for path in the root_t. */
4495 err = get_dag(&path_node, root, path, trail, pool);
4496 if (err && (err->apr_err == SVN_ERR_FS_NOT_FOUND))
4498 svn_error_clear(err);
4499 *rev = SVN_INVALID_REVNUM;
4500 return SVN_NO_ERROR;
4502 else if (err)
4503 return err;
4505 /* Find the created_rev of the dag_node. */
4506 SVN_ERR(svn_fs_base__dag_get_revision(&created_rev, path_node,
4507 trail, pool));
4509 *rev = created_rev;
4510 return SVN_NO_ERROR;
4515 /*** Finding the Origin of a Line of History ***/
4517 /* Set *PREV_PATH and *PREV_REV to the path and revision which
4518 represent the location at which PATH in FS was located immediately
4519 prior to REVISION iff there was a copy operation (to PATH or one of
4520 its parent directories) between that previous location and
4521 PATH@REVISION.
4523 If there was no such copy operation in that portion of PATH's
4524 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM.
4526 WARNING: Do *not* call this from inside a trail. */
4527 static svn_error_t *
4528 prev_location(const char **prev_path,
4529 svn_revnum_t *prev_rev,
4530 svn_fs_t *fs,
4531 svn_fs_root_t *root,
4532 const char *path,
4533 apr_pool_t *pool)
4535 const char *copy_path, *copy_src_path, *remainder = "";
4536 svn_fs_root_t *copy_root;
4537 svn_revnum_t copy_src_rev;
4539 /* Ask about the most recent copy which affected PATH@REVISION. If
4540 there was no such copy, we're done. */
4541 SVN_ERR(base_closest_copy(&copy_root, &copy_path, root, path, pool));
4542 if (! copy_root)
4544 *prev_rev = SVN_INVALID_REVNUM;
4545 *prev_path = NULL;
4546 return SVN_NO_ERROR;
4549 /* Ultimately, it's not the path of the closest copy's source that
4550 we care about -- it's our own path's location in the copy source
4551 revision. So we'll tack the relative path that expresses the
4552 difference between the copy destination and our path in the copy
4553 revision onto the copy source path to determine this information.
4555 In other words, if our path is "/branches/my-branch/foo/bar", and
4556 we know that the closest relevant copy was a copy of "/trunk" to
4557 "/branches/my-branch", then that relative path under the copy
4558 destination is "/foo/bar". Tacking that onto the copy source
4559 path tells us that our path was located at "/trunk/foo/bar"
4560 before the copy.
4562 SVN_ERR(base_copied_from(&copy_src_rev, &copy_src_path,
4563 copy_root, copy_path, pool));
4564 if (! strcmp(copy_path, path) == 0)
4565 remainder = svn_path_is_child(copy_path, path, pool);
4566 *prev_path = svn_path_join(copy_src_path, remainder, pool);
4567 *prev_rev = copy_src_rev;
4568 return SVN_NO_ERROR;
4572 struct id_created_rev_args {
4573 svn_revnum_t revision;
4574 const svn_fs_id_t *id;
4575 const char *path;
4579 static svn_error_t *
4580 txn_body_id_created_rev(void *baton, trail_t *trail)
4582 struct id_created_rev_args *args = baton;
4583 dag_node_t *node;
4585 SVN_ERR(svn_fs_base__dag_get_node(&node, trail->fs, args->id,
4586 trail, trail->pool));
4587 SVN_ERR(svn_fs_base__dag_get_revision(&(args->revision), node,
4588 trail, trail->pool));
4589 return SVN_NO_ERROR;
4593 struct get_set_node_origin_args {
4594 const svn_fs_id_t *origin_id;
4595 const char *node_id;
4599 static svn_error_t *
4600 txn_body_get_node_origin(void *baton, trail_t *trail)
4602 struct get_set_node_origin_args *args = baton;
4603 return svn_fs_bdb__get_node_origin(&(args->origin_id), trail->fs,
4604 args->node_id, trail, trail->pool);
4607 static svn_error_t *
4608 txn_body_set_node_origin(void *baton, trail_t *trail)
4610 struct get_set_node_origin_args *args = baton;
4611 return svn_fs_bdb__set_node_origin(trail->fs, args->node_id,
4612 args->origin_id, trail, trail->pool);
4615 static svn_error_t *
4616 base_node_origin_rev(svn_revnum_t *revision,
4617 svn_fs_root_t *root,
4618 const char *path,
4619 apr_pool_t *pool)
4621 svn_fs_t *fs = root->fs;
4622 svn_error_t *err;
4623 struct get_set_node_origin_args args;
4624 const svn_fs_id_t *id, *origin_id;
4625 struct id_created_rev_args icr_args;
4627 /* Verify that our filesystem version supports node origins stuff. */
4628 SVN_ERR(svn_fs_base__test_required_feature_format
4629 (fs, "node-origins", SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT));
4631 SVN_ERR(base_node_id(&id, root, path, pool));
4632 args.node_id = svn_fs_base__id_node_id(id);
4633 err = svn_fs_base__retry_txn(root->fs, txn_body_get_node_origin,
4634 &args, pool);
4636 /* If we got a value for the origin node-revision-ID, that's great.
4637 If we didn't, that's sad but non-fatal -- we'll just figure it
4638 out the hard way, then record it so we don't have suffer again
4639 the next time. */
4640 if (! err)
4642 origin_id = args.origin_id;
4644 else if (err->apr_err == SVN_ERR_FS_NO_SUCH_NODE_ORIGIN)
4646 svn_fs_root_t *curroot = root;
4647 apr_pool_t *subpool = svn_pool_create(pool);
4648 svn_stringbuf_t *lastpath =
4649 svn_stringbuf_create(path, pool);
4650 svn_revnum_t lastrev = SVN_INVALID_REVNUM;
4651 const svn_fs_id_t *pred_id;
4653 svn_error_clear(err);
4654 err = SVN_NO_ERROR;
4656 /* Walk the closest-copy chain back to the first copy in our history.
4658 NOTE: We merely *assume* that this is faster than walking the
4659 predecessor chain, because we *assume* that copies of parent
4660 directories happen less often than modifications to a given item. */
4661 while (1)
4663 svn_revnum_t currev;
4664 const char *curpath = lastpath->data;
4666 /* Get a root pointing to LASTREV. (The first time around,
4667 LASTREV is invalid, but that's cool because CURROOT is
4668 already initialized.) */
4669 if (SVN_IS_VALID_REVNUM(lastrev))
4670 SVN_ERR(svn_fs_base__revision_root(&curroot, fs,
4671 lastrev, subpool));
4673 /* Find the previous location using the closest-copy shortcut. */
4674 SVN_ERR(prev_location(&curpath, &currev, fs, curroot,
4675 curpath, subpool));
4676 if (! curpath)
4677 break;
4679 /* Update our LASTPATH and LASTREV variables (which survive
4680 SUBPOOL). */
4681 svn_stringbuf_set(lastpath, curpath);
4684 /* Walk the predecessor links back to origin. */
4685 SVN_ERR(base_node_id(&pred_id, curroot, lastpath->data, pool));
4686 while (1)
4688 struct txn_pred_id_args pid_args;
4689 svn_pool_clear(subpool);
4690 pid_args.id = pred_id;
4691 pid_args.pool = subpool;
4692 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_pred_id,
4693 &pid_args, subpool));
4694 if (! pid_args.pred_id)
4695 break;
4696 pred_id = pid_args.pred_id;
4699 /* Okay. PRED_ID should hold our origin ID now. Let's remember
4700 this value from now on, shall we? */
4701 args.origin_id = origin_id = svn_fs_base__id_copy(pred_id, pool);
4702 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_set_node_origin,
4703 &args, subpool));
4704 svn_pool_destroy(subpool);
4706 else
4708 return err;
4711 /* Okay. We have an origin node-revision-ID. Let's get a created
4712 revision from it. */
4713 icr_args.id = origin_id;
4714 SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_id_created_rev,
4715 &icr_args, pool));
4716 *revision = icr_args.revision;
4717 return SVN_NO_ERROR;
4722 /* Mergeinfo Queries */
4724 /* Examine directory NODE's immediately children for mergeinfo.
4726 For those which have explicit mergeinfo, add their mergeinfo to
4727 RESULT_CATALOG (allocated in RESULT_CATALOG's pool).
4729 For those which don't, but sit atop trees which contain mergeinfo
4730 somewhere deeper, add them to *CHILDREN_ATOP_MERGEINFO_TREES, a
4731 hash mapping dirent names to dag_node_t * objects, allocated
4732 from that hash's pool.
4734 For those which neither have explicit mergeinfo nor sit atop trees
4735 which contain mergeinfo, ignore them.
4737 Use TRAIL->pool for temporary allocations. */
4739 struct get_mergeinfo_data_and_entries_baton
4741 svn_mergeinfo_catalog_t result_catalog;
4742 apr_hash_t *children_atop_mergeinfo_trees;
4743 dag_node_t *node;
4744 const char *node_path;
4747 static svn_error_t *
4748 txn_body_get_mergeinfo_data_and_entries(void *baton, trail_t *trail)
4750 struct get_mergeinfo_data_and_entries_baton *args = baton;
4751 dag_node_t *node = args->node;
4752 apr_hash_t *entries;
4753 apr_hash_index_t *hi;
4754 apr_pool_t *iterpool = svn_pool_create(trail->pool);
4755 apr_pool_t *result_pool = apr_hash_pool_get(args->result_catalog);
4756 apr_pool_t *children_pool =
4757 apr_hash_pool_get(args->children_atop_mergeinfo_trees);
4759 assert(svn_fs_base__dag_node_kind(node) == svn_node_dir);
4761 SVN_ERR(svn_fs_base__dag_dir_entries(&entries, node, trail, trail->pool));
4762 for (hi = apr_hash_first(NULL, entries); hi; hi = apr_hash_next(hi))
4764 void *val;
4765 svn_fs_dirent_t *dirent;
4766 const svn_fs_id_t *child_id;
4767 dag_node_t *child_node;
4768 svn_boolean_t has_mergeinfo;
4769 apr_int64_t kid_count;
4771 svn_pool_clear(iterpool);
4772 apr_hash_this(hi, NULL, NULL, &val);
4773 dirent = val;
4774 child_id = dirent->id;
4776 /* Get the node for this child. */
4777 SVN_ERR(svn_fs_base__dag_get_node(&child_node, trail->fs, child_id,
4778 trail, iterpool));
4780 /* Query the child node's mergeinfo stats. */
4781 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &kid_count,
4782 child_node, trail,
4783 iterpool));
4785 /* If the child has mergeinfo, add it to the result catalog. */
4786 if (has_mergeinfo)
4788 apr_hash_t *plist;
4789 svn_mergeinfo_t child_mergeinfo;
4790 svn_string_t *pval;
4792 SVN_ERR(svn_fs_base__dag_get_proplist(&plist, child_node,
4793 trail, iterpool));
4794 pval = apr_hash_get(plist, SVN_PROP_MERGEINFO, APR_HASH_KEY_STRING);
4795 if (! pval)
4797 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
4798 iterpool);
4799 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4800 _("Node-revision '%s' claims to have "
4801 "mergeinfo but doesn't"),
4802 id_str->data);
4804 SVN_ERR(svn_mergeinfo_parse(&child_mergeinfo, pval->data,
4805 result_pool));
4806 apr_hash_set(args->result_catalog,
4807 svn_path_join(args->node_path, dirent->name,
4808 result_pool),
4809 APR_HASH_KEY_STRING,
4810 child_mergeinfo);
4813 /* If the child has descendants with mergeinfo -- that is, if
4814 the count of descendants beneath it carrying mergeinfo, not
4815 including itself, is non-zero -- then add it to the
4816 children_atop_mergeinfo_trees hash to be crawled later. */
4817 if ((kid_count - (has_mergeinfo ? 1 : 0)) > 0)
4819 if (svn_fs_base__dag_node_kind(child_node) != svn_node_dir)
4821 svn_string_t *id_str = svn_fs_base__id_unparse(child_id,
4822 iterpool);
4823 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4824 _("Node-revision '%s' claims to sit "
4825 "atop a tree containing mergeinfo "
4826 "but is not a directory"),
4827 id_str->data);
4829 apr_hash_set(args->children_atop_mergeinfo_trees,
4830 apr_pstrdup(children_pool, dirent->name),
4831 APR_HASH_KEY_STRING,
4832 svn_fs_base__dag_dup(child_node, children_pool));
4836 svn_pool_destroy(iterpool);
4837 return SVN_NO_ERROR;
4840 static svn_error_t *
4841 crawl_directory_for_mergeinfo(svn_fs_t *fs,
4842 dag_node_t *node,
4843 const char *node_path,
4844 svn_mergeinfo_catalog_t result_catalog,
4845 apr_pool_t *pool)
4847 struct get_mergeinfo_data_and_entries_baton gmdae_args;
4848 apr_hash_t *children_atop_mergeinfo_trees = apr_hash_make(pool);
4849 apr_hash_index_t *hi;
4850 apr_pool_t *iterpool;
4852 /* Add mergeinfo for immediate children that have it, and fetch
4853 immediate children that *don't* have it but sit atop trees that do. */
4854 gmdae_args.result_catalog = result_catalog;
4855 gmdae_args.children_atop_mergeinfo_trees = children_atop_mergeinfo_trees;
4856 gmdae_args.node = node;
4857 gmdae_args.node_path = node_path;
4858 SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_mergeinfo_data_and_entries,
4859 &gmdae_args, pool));
4861 /* If no children sit atop trees with mergeinfo, we're done.
4862 Otherwise, recurse on those children. */
4864 if (apr_hash_count(children_atop_mergeinfo_trees) == 0)
4865 return SVN_NO_ERROR;
4867 iterpool = svn_pool_create(pool);
4868 for (hi = apr_hash_first(NULL, children_atop_mergeinfo_trees);
4869 hi;
4870 hi = apr_hash_next(hi))
4872 const void *key;
4873 void *val;
4874 svn_pool_clear(iterpool);
4875 apr_hash_this(hi, &key, NULL, &val);
4876 crawl_directory_for_mergeinfo(fs, val,
4877 svn_path_join(node_path, key, iterpool),
4878 result_catalog, iterpool);
4880 svn_pool_destroy(iterpool);
4881 return SVN_NO_ERROR;
4885 /* Helper for get_mergeinfo_for_path() that will append REL_PATH
4886 (which may contain slashes) to each path that exists in the
4887 mergeinfo INPUT, and return a new mergeinfo in *OUTPUT. Deep
4888 copies the values. Perform all allocations in POOL. */
4889 static svn_error_t *
4890 append_to_merged_froms(svn_mergeinfo_t *output,
4891 svn_mergeinfo_t input,
4892 const char *rel_path,
4893 apr_pool_t *pool)
4895 apr_hash_index_t *hi;
4897 *output = apr_hash_make(pool);
4898 for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
4900 const void *key;
4901 void *val;
4902 apr_hash_this(hi, &key, NULL, &val);
4903 apr_hash_set(*output, svn_path_join(key, rel_path, pool),
4904 APR_HASH_KEY_STRING, svn_rangelist_dup(val, pool));
4906 return SVN_NO_ERROR;
4910 /* Calculate the mergeinfo for PATH under revision ROOT using
4911 inheritance type INHERIT. Set *MERGEINFO to the mergeinfo, or to
4912 NULL if there is none. Results are allocated in POOL; TRAIL->pool
4913 is used for temporary allocations. */
4915 struct get_mergeinfo_for_path_baton
4917 svn_mergeinfo_t *mergeinfo;
4918 svn_fs_root_t *root;
4919 const char *path;
4920 svn_mergeinfo_inheritance_t inherit;
4921 apr_pool_t *pool;
4924 static svn_error_t *
4925 txn_body_get_mergeinfo_for_path(void *baton, trail_t *trail)
4927 struct get_mergeinfo_for_path_baton *args = baton;
4928 parent_path_t *parent_path, *nearest_ancestor;
4929 apr_hash_t *proplist;
4930 svn_string_t *mergeinfo_string;
4931 apr_pool_t *iterpool;
4932 dag_node_t *node = NULL;
4934 *(args->mergeinfo) = NULL;
4936 SVN_ERR(open_path(&parent_path, args->root, args->path, 0,
4937 NULL, trail, trail->pool));
4939 /* Init the nearest ancestor. */
4940 nearest_ancestor = parent_path;
4941 if (args->inherit == svn_mergeinfo_nearest_ancestor)
4943 if (! parent_path->parent)
4944 return SVN_NO_ERROR;
4945 nearest_ancestor = parent_path->parent;
4948 iterpool = svn_pool_create(trail->pool);
4949 while (TRUE)
4951 svn_boolean_t has_mergeinfo;
4952 apr_int64_t count;
4954 svn_pool_clear(iterpool);
4956 node = nearest_ancestor->node;
4957 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&has_mergeinfo, &count,
4958 node, trail, iterpool));
4959 if (has_mergeinfo)
4960 break;
4962 /* No need to loop if we're looking for explicit mergeinfo. */
4963 if (args->inherit == svn_mergeinfo_explicit)
4965 svn_pool_destroy(iterpool);
4966 return SVN_NO_ERROR;
4969 nearest_ancestor = nearest_ancestor->parent;
4971 /* Run out? There's no mergeinfo. */
4972 if (! nearest_ancestor)
4974 svn_pool_destroy(iterpool);
4975 return SVN_NO_ERROR;
4978 svn_pool_destroy(iterpool);
4980 SVN_ERR(svn_fs_base__dag_get_proplist(&proplist, node, trail, trail->pool));
4981 mergeinfo_string = apr_hash_get(proplist, SVN_PROP_MERGEINFO,
4982 APR_HASH_KEY_STRING);
4983 if (! mergeinfo_string)
4985 svn_string_t *id_str =
4986 svn_fs_base__id_unparse(svn_fs_base__dag_get_id(node), trail->pool);
4987 return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
4988 _("Node-revision '%s' claims to have "
4989 "mergeinfo but doesn't"), id_str->data);
4992 /* If our nearest ancestor is the very path we inquired about, we
4993 can return the mergeinfo results directly. Otherwise, we're
4994 inheriting the mergeinfo, so we need to a) remove non-inheritable
4995 ranges and b) telescope the merged-from paths. */
4996 if (nearest_ancestor == parent_path)
4998 SVN_ERR(svn_mergeinfo_parse(args->mergeinfo,
4999 mergeinfo_string->data, args->pool));
5001 else
5003 svn_mergeinfo_t tmp_mergeinfo;
5004 SVN_ERR(svn_mergeinfo_parse(&tmp_mergeinfo,
5005 mergeinfo_string->data, trail->pool));
5006 SVN_ERR(svn_mergeinfo_inheritable(&tmp_mergeinfo,
5007 tmp_mergeinfo,
5008 NULL, SVN_INVALID_REVNUM,
5009 SVN_INVALID_REVNUM, trail->pool));
5010 SVN_ERR(append_to_merged_froms(args->mergeinfo,
5011 tmp_mergeinfo,
5012 parent_path_relpath(parent_path,
5013 nearest_ancestor,
5014 trail->pool),
5015 args->pool));
5017 return SVN_NO_ERROR;
5020 /* Set **NODE to the dag node for PATH in ROOT (allocated in POOL),
5021 and query its mergeinfo stats, setting HAS_MERGEINFO and
5022 CHILD_MERGEINFO_COUNT appropriately. */
5024 struct get_node_mergeinfo_stats_baton
5026 dag_node_t *node;
5027 svn_boolean_t has_mergeinfo;
5028 apr_int64_t child_mergeinfo_count;
5029 svn_fs_root_t *root;
5030 const char *path;
5033 static svn_error_t *
5034 txn_body_get_node_mergeinfo_stats(void *baton, trail_t *trail)
5036 struct get_node_mergeinfo_stats_baton *args = baton;
5038 SVN_ERR(get_dag(&(args->node), args->root, args->path,
5039 trail, trail->pool));
5040 SVN_ERR(svn_fs_base__dag_get_mergeinfo_stats(&(args->has_mergeinfo),
5041 &(args->child_mergeinfo_count),
5042 args->node, trail,
5043 trail->pool));
5044 return SVN_NO_ERROR;
5048 /* Get the mergeinfo for a set of paths, returned in
5049 *MERGEINFO_CATALOG. Returned values are allocated in POOL, while
5050 temporary values are allocated in a sub-pool. */
5051 static svn_error_t *
5052 get_mergeinfos_for_paths(svn_fs_root_t *root,
5053 svn_mergeinfo_catalog_t *mergeinfo_catalog,
5054 const apr_array_header_t *paths,
5055 svn_mergeinfo_inheritance_t inherit,
5056 svn_boolean_t include_descendants,
5057 apr_pool_t *pool)
5059 svn_mergeinfo_catalog_t result_catalog = apr_hash_make(pool);
5060 apr_pool_t *iterpool = svn_pool_create(pool);
5061 int i;
5063 for (i = 0; i < paths->nelts; i++)
5065 svn_mergeinfo_t path_mergeinfo;
5066 struct get_mergeinfo_for_path_baton gmfp_args;
5067 const char *path = APR_ARRAY_IDX(paths, i, const char *);
5069 svn_pool_clear(iterpool);
5071 path = svn_fs__canonicalize_abspath(path, iterpool);
5073 /* Get the mergeinfo for PATH itself. */
5074 gmfp_args.mergeinfo = &path_mergeinfo;
5075 gmfp_args.root = root;
5076 gmfp_args.path = path;
5077 gmfp_args.inherit = inherit;
5078 gmfp_args.pool = pool;
5079 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5080 txn_body_get_mergeinfo_for_path,
5081 &gmfp_args, iterpool));
5082 if (path_mergeinfo)
5083 apr_hash_set(result_catalog, apr_pstrdup(pool, path),
5084 APR_HASH_KEY_STRING,
5085 path_mergeinfo);
5087 /* If we're including descendants, do so. */
5088 if (include_descendants)
5090 svn_boolean_t do_crawl;
5091 struct get_node_mergeinfo_stats_baton gnms_args;
5093 /* Query the node and its mergeinfo stats. */
5094 gnms_args.root = root;
5095 gnms_args.path = path;
5096 SVN_ERR(svn_fs_base__retry_txn(root->fs,
5097 txn_body_get_node_mergeinfo_stats,
5098 &gnms_args, iterpool));
5100 /* Determine if there's anything worth crawling here. */
5101 if (svn_fs_base__dag_node_kind(gnms_args.node) != svn_node_dir)
5102 do_crawl = FALSE;
5103 else
5104 do_crawl = ((gnms_args.child_mergeinfo_count > 1)
5105 || ((gnms_args.child_mergeinfo_count == 1)
5106 && (! gnms_args.has_mergeinfo)));
5108 /* If it's worth crawling, crawl. */
5109 if (do_crawl)
5110 SVN_ERR(crawl_directory_for_mergeinfo(root->fs, gnms_args.node,
5111 path, result_catalog,
5112 iterpool));
5115 svn_pool_destroy(iterpool);
5117 *mergeinfo_catalog = result_catalog;
5118 return SVN_NO_ERROR;
5122 /* Implements svn_fs_get_mergeinfo. */
5123 static svn_error_t *
5124 base_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
5125 svn_fs_root_t *root,
5126 const apr_array_header_t *paths,
5127 svn_mergeinfo_inheritance_t inherit,
5128 svn_boolean_t include_descendants,
5129 apr_pool_t *pool)
5131 /* Verify that our filesystem version supports mergeinfo stuff. */
5132 SVN_ERR(svn_fs_base__test_required_feature_format
5133 (root->fs, "mergeinfo", SVN_FS_BASE__MIN_MERGEINFO_FORMAT));
5135 /* We require a revision root. */
5136 if (root->is_txn_root)
5137 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
5139 /* Retrieve a path -> mergeinfo mapping. */
5140 return get_mergeinfos_for_paths(root, catalog, paths,
5141 inherit, include_descendants,
5142 pool);
5146 /* Creating root objects. */
5149 static root_vtable_t root_vtable = {
5150 base_paths_changed,
5151 base_check_path,
5152 base_node_history,
5153 base_node_id,
5154 base_node_created_rev,
5155 base_node_origin_rev,
5156 base_node_created_path,
5157 base_delete_node,
5158 base_copied_from,
5159 base_closest_copy,
5160 base_node_prop,
5161 base_node_proplist,
5162 base_change_node_prop,
5163 base_props_changed,
5164 base_dir_entries,
5165 base_make_dir,
5166 base_copy,
5167 base_revision_link,
5168 base_file_length,
5169 base_file_md5_checksum,
5170 base_file_contents,
5171 base_make_file,
5172 base_apply_textdelta,
5173 base_apply_text,
5174 base_contents_changed,
5175 base_get_file_delta_stream,
5176 base_merge,
5177 base_get_mergeinfo,
5181 /* Construct a new root object in FS, allocated from POOL. */
5182 static svn_fs_root_t *
5183 make_root(svn_fs_t *fs,
5184 apr_pool_t *pool)
5186 /* We create a subpool for each root object to allow us to implement
5187 svn_fs_close_root. */
5188 apr_pool_t *subpool = svn_pool_create(pool);
5189 svn_fs_root_t *root = apr_pcalloc(subpool, sizeof(*root));
5190 base_root_data_t *brd = apr_palloc(subpool, sizeof(*brd));
5192 root->fs = fs;
5193 root->pool = subpool;
5195 /* Init the node ID cache. */
5196 brd->node_cache = apr_hash_make(pool);
5197 brd->node_cache_idx = 0;
5198 root->vtable = &root_vtable;
5199 root->fsap_data = brd;
5201 return root;
5205 /* Construct a root object referring to the root of REVISION in FS,
5206 whose root directory is ROOT_DIR. Create the new root in POOL. */
5207 static svn_fs_root_t *
5208 make_revision_root(svn_fs_t *fs,
5209 svn_revnum_t rev,
5210 dag_node_t *root_dir,
5211 apr_pool_t *pool)
5213 svn_fs_root_t *root = make_root(fs, pool);
5214 base_root_data_t *brd = root->fsap_data;
5216 root->is_txn_root = FALSE;
5217 root->rev = rev;
5218 brd->root_dir = root_dir;
5220 return root;
5224 /* Construct a root object referring to the root of the transaction
5225 named TXN and based on revision BASE_REV in FS. FLAGS represents
5226 the behavior of the transaction. Create the new root in POOL. */
5227 static svn_fs_root_t *
5228 make_txn_root(svn_fs_t *fs,
5229 const char *txn,
5230 svn_revnum_t base_rev,
5231 apr_uint32_t flags,
5232 apr_pool_t *pool)
5234 svn_fs_root_t *root = make_root(fs, pool);
5235 root->is_txn_root = TRUE;
5236 root->txn = apr_pstrdup(root->pool, txn);
5237 root->txn_flags = flags;
5238 root->rev = base_rev;
5240 return root;