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. */
36 #include <apr_pools.h>
39 #include "svn_private_config.h"
40 #include "svn_pools.h"
41 #include "svn_error.h"
44 #include "svn_mergeinfo.h"
55 #include "private/svn_mergeinfo_private.h"
56 #include "private/svn_fs_util.h"
57 #include "../libsvn_fs/fs-loader.h"
60 /* ### I believe this constant will become internal to reps-strings.c.
61 ### see the comment in window_consumer() for more information. */
63 /* ### the comment also seems to need tweaking: the log file stuff
64 ### is no longer an issue... */
65 /* Data written to the filesystem through the svn_fs_apply_textdelta()
66 interface is cached in memory until the end of the data stream, or
67 until a size trigger is hit. Define that trigger here (in bytes).
68 Setting the value to 0 will result in no filesystem buffering at
69 all. The value only really matters when dealing with file contents
70 bigger than the value itself. Above that point, large values here
71 allow the filesystem to buffer more data in memory before flushing
72 to the database, which increases memory usage but greatly decreases
73 the amount of disk access (and log-file generation) in database.
74 Smaller values will limit your overall memory consumption, but can
75 drastically hurt throughput by necessitating more write operations
76 to the database (which also generates more log-files). */
77 #define WRITE_BUFFER_SIZE 512000
79 /* The maximum number of cache items to maintain in the node cache. */
80 #define TXN_NODE_CACHE_MAX_KEYS 32
81 #define REV_NODE_CACHE_MAX_KEYS 128
85 /* The root structures.
87 Why do they contain different data? Well, transactions are mutable
88 enough that it isn't safe to cache the DAG node for the root
89 directory or the hash of copyfrom data: somebody else might modify
90 them concurrently on disk! (Why is the DAG node cache safer than
91 the root DAG node? When cloning transaction DAG nodes in and out
92 of the cache, all of the possibly-mutable data from the
93 node_revision_t inside the dag_node_t is dropped.) Additionally,
94 revisions are immutable enough that their DAG node cache can be
95 kept in the FS object and shared among multiple revision root
100 /* A dag node for the revision's root directory. */
101 dag_node_t
*root_dir
;
103 /* Cache structure for mapping const char * PATH to const char
104 *COPYFROM_STRING, so that paths_changed can remember all the
105 copyfrom information in the changes file.
106 COPYFROM_STRING has the format "REV PATH", or is the empty string if
107 the path was added without history. */
108 apr_hash_t
*copyfrom_cache
;
110 } fs_rev_root_data_t
;
114 /* Dummy entry for circular LRU cache, and associated hash table. */
115 dag_node_cache_t txn_node_list
;
116 apr_hash_t
*txn_node_cache
;
117 } fs_txn_root_data_t
;
119 /* Declared here to resolve the circular dependencies. */
120 static svn_error_t
* get_dag(dag_node_t
**dag_node_p
, svn_fs_root_t
*root
,
121 const char *path
, apr_pool_t
*pool
);
123 static svn_fs_root_t
*make_revision_root(svn_fs_t
*fs
, svn_revnum_t rev
,
124 dag_node_t
*root_dir
,
127 static svn_fs_root_t
*make_txn_root(svn_fs_t
*fs
, const char *txn
,
128 svn_revnum_t base_rev
, apr_uint32_t flags
,
132 /*** Node Caching ***/
135 locate_cache(dag_node_cache_t
**node_list
,
136 apr_hash_t
**node_cache
,
142 if (root
->is_txn_root
)
144 fs_txn_root_data_t
*frd
= root
->fsap_data
;
145 *node_list
= &frd
->txn_node_list
;
146 *node_cache
= frd
->txn_node_cache
;
151 fs_fs_data_t
*ffd
= root
->fs
->fsap_data
;
152 *node_list
= &ffd
->rev_node_list
;
153 *node_cache
= ffd
->rev_node_cache
;
154 *key
= apr_psprintf(pool
, "%ld%s",
159 /* Return NODE for PATH from ROOT's node cache, or NULL if the node
162 dag_node_cache_get(svn_fs_root_t
*root
,
166 dag_node_cache_t
*item
, *node_list
;
167 apr_hash_t
*node_cache
;
170 /* Assert valid input. */
171 assert(*path
== '/');
173 locate_cache(&node_list
, &node_cache
, &key
,
176 /* Look in the cache for our desired item. */
177 item
= apr_hash_get(node_cache
, key
, APR_HASH_KEY_STRING
);
178 if (item
&& item
->node
)
180 /* Move this cache item to the front of the LRU list. */
181 item
->prev
->next
= item
->next
;
182 item
->next
->prev
= item
->prev
;
183 item
->prev
= node_list
;
184 item
->next
= node_list
->next
;
185 item
->prev
->next
= item
;
186 item
->next
->prev
= item
;
188 /* Return the cached node. */
189 return svn_fs_fs__dag_dup(item
->node
, pool
);
196 /* Add the NODE for PATH to ROOT's node cache. */
198 dag_node_cache_set(svn_fs_root_t
*root
,
201 apr_pool_t
*temp_pool
)
203 dag_node_cache_t
*item
, *node_list
;
204 apr_hash_t
*node_cache
;
207 int max_keys
= root
->is_txn_root
208 ? TXN_NODE_CACHE_MAX_KEYS
: REV_NODE_CACHE_MAX_KEYS
;
210 /* The pool passed to this function can *only* be used for
211 short-term calculations, not for the actual cache value!
213 To ensure that our cache values live as long as the svn_fs_root_t
214 in which they are ultimately stored, and to allow us to free()
215 them individually without harming the rest, they are each
216 allocated from a subpool of ROOT's pool. We'll keep one subpool
217 around for each cache slot -- as we start expiring stuff
218 to make room for more entries, we'll re-use the expired thing's
221 /* Assert valid input and state. */
222 assert(*path
== '/');
224 locate_cache(&node_list
, &node_cache
, &key
,
225 root
, path
, temp_pool
);
227 /* If we have an existing entry for this path, reuse it. */
228 item
= apr_hash_get(node_cache
, key
, APR_HASH_KEY_STRING
);
230 /* Otherwise, if the cache is full, reuse the tail of the LRU list. */
231 if (!item
&& apr_hash_count(node_cache
) == max_keys
)
232 item
= node_list
->prev
;
236 /* Remove the existing item from the cache and reuse its pool. */
237 item
->prev
->next
= item
->next
;
238 item
->next
->prev
= item
->prev
;
239 apr_hash_set(node_cache
, item
->key
, APR_HASH_KEY_STRING
, NULL
);
241 svn_pool_clear(pool
);
245 /* Allocate a new pool. */
246 apr_pool_t
*parent_pool
= root
->is_txn_root
? root
->pool
: root
->fs
->pool
;
247 pool
= svn_pool_create(parent_pool
);
250 /* Create and fill in the cache item. */
251 item
= apr_palloc(pool
, sizeof(*item
));
252 item
->key
= apr_pstrdup(pool
, key
);
253 item
->node
= svn_fs_fs__dag_dup(node
, pool
);
256 /* Link it into the head of the LRU list and hash table. */
257 item
->prev
= node_list
;
258 item
->next
= node_list
->next
;
259 item
->prev
->next
= item
;
260 item
->next
->prev
= item
;
261 apr_hash_set(node_cache
, item
->key
, APR_HASH_KEY_STRING
, item
);
265 /* Invalidate cache entries for PATH and any of its children. */
267 dag_node_cache_invalidate(svn_fs_root_t
*root
,
270 fs_txn_root_data_t
*frd
;
271 apr_size_t len
= strlen(path
);
273 dag_node_cache_t
*item
;
275 assert(root
->is_txn_root
);
277 frd
= root
->fsap_data
;
279 for (item
= frd
->txn_node_list
.next
;
280 item
!= &frd
->txn_node_list
;
284 if (strncmp(key
, path
, len
) == 0 && (key
[len
] == '/' || !key
[len
]))
291 /* Creating transaction and revision root nodes. */
294 svn_fs_fs__txn_root(svn_fs_root_t
**root_p
,
299 apr_uint32_t flags
= 0;
300 apr_hash_t
*txnprops
;
302 /* Look for the temporary txn props representing 'flags'. */
303 SVN_ERR(svn_fs_fs__txn_proplist(&txnprops
, txn
, pool
));
306 if (apr_hash_get(txnprops
, SVN_FS__PROP_TXN_CHECK_OOD
,
307 APR_HASH_KEY_STRING
))
308 flags
|= SVN_FS_TXN_CHECK_OOD
;
310 if (apr_hash_get(txnprops
, SVN_FS__PROP_TXN_CHECK_LOCKS
,
311 APR_HASH_KEY_STRING
))
312 flags
|= SVN_FS_TXN_CHECK_LOCKS
;
315 root
= make_txn_root(txn
->fs
, txn
->id
, txn
->base_rev
, flags
, pool
);
324 svn_fs_fs__revision_root(svn_fs_root_t
**root_p
,
329 dag_node_t
*root_dir
;
331 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
333 SVN_ERR(svn_fs_fs__dag_revision_root(&root_dir
, fs
, rev
, pool
));
335 *root_p
= make_revision_root(fs
, rev
, root_dir
, pool
);
342 /* Getting dag nodes for roots. */
345 /* Set *NODE_P to a freshly opened dag node referring to the root
346 directory of ROOT, allocating from POOL. */
348 root_node(dag_node_t
**node_p
,
352 if (root
->is_txn_root
)
354 /* It's a transaction root. Open a fresh copy. */
355 return svn_fs_fs__dag_txn_root(node_p
, root
->fs
, root
->txn
, pool
);
359 /* It's a revision root, so we already have its root directory
361 fs_rev_root_data_t
*frd
= root
->fsap_data
;
362 *node_p
= svn_fs_fs__dag_dup(frd
->root_dir
, pool
);
368 /* Set *NODE_P to a mutable root directory for ROOT, cloning if
369 necessary, allocating in POOL. ROOT must be a transaction root.
370 Use ERROR_PATH in error messages. */
372 mutable_root_node(dag_node_t
**node_p
,
374 const char *error_path
,
377 if (root
->is_txn_root
)
378 return svn_fs_fs__dag_clone_root(node_p
, root
->fs
, root
->txn
, pool
);
380 /* If it's not a transaction root, we can't change its contents. */
381 return SVN_FS__ERR_NOT_MUTABLE(root
->fs
, root
->rev
, error_path
);
386 /* Traversing directory paths. */
388 typedef enum copy_id_inherit_t
390 copy_id_inherit_unknown
= 0,
391 copy_id_inherit_self
,
392 copy_id_inherit_parent
,
397 /* A linked list representing the path from a node up to a root
398 directory. We use this for cloning, and for operations that need
399 to deal with both a node and its parent directory. For example, a
400 `delete' operation needs to know that the node actually exists, but
401 also needs to change the parent directory. */
402 typedef struct parent_path_t
405 /* A node along the path. This could be the final node, one of its
406 parents, or the root. Every parent path ends with an element for
407 the root directory. */
410 /* The name NODE has in its parent directory. This is zero for the
411 root directory, which (obviously) has no name in its parent. */
414 /* The parent of NODE, or zero if NODE is the root directory. */
415 struct parent_path_t
*parent
;
417 /* The copy ID inheritence style. */
418 copy_id_inherit_t copy_inherit
;
420 /* If copy ID inheritence style is copy_id_inherit_new, this is the
421 path which should be implicitly copied; otherwise, this is NULL. */
422 const char *copy_src_path
;
426 /* Return a text string describing the absolute path of parent_path
427 PARENT_PATH. It will be allocated in POOL. */
429 parent_path_path(parent_path_t
*parent_path
,
432 const char *path_so_far
= "/";
433 if (parent_path
->parent
)
434 path_so_far
= parent_path_path(parent_path
->parent
, pool
);
435 return parent_path
->entry
436 ? svn_path_join(path_so_far
, parent_path
->entry
, pool
)
441 /* Return the FS path for the parent path chain object CHILD relative
442 to its ANCESTOR in the same chain, allocated in POOL. */
444 parent_path_relpath(parent_path_t
*child
,
445 parent_path_t
*ancestor
,
448 const char *path_so_far
= "";
449 parent_path_t
*this_node
= child
;
450 while (this_node
!= ancestor
)
452 assert(this_node
!= NULL
);
453 path_so_far
= svn_path_join(this_node
->entry
, path_so_far
, pool
);
454 this_node
= this_node
->parent
;
461 /* Choose a copy ID inheritance method *INHERIT_P to be used in the
462 event that immutable node CHILD in FS needs to be made mutable. If
463 the inheritance method is copy_id_inherit_new, also return a
464 *COPY_SRC_PATH on which to base the new copy ID (else return NULL
465 for that path). CHILD must have a parent (it cannot be the root
466 node). TXN_ID is the transaction in which these items might be
467 mutable. Allocations are taken from POOL. */
469 get_copy_inheritance(copy_id_inherit_t
*inherit_p
,
470 const char **copy_src_path
,
472 parent_path_t
*child
,
476 const svn_fs_id_t
*child_id
, *parent_id
, *copyroot_id
;
477 const char *child_copy_id
, *parent_copy_id
;
478 const char *id_path
= NULL
;
479 svn_fs_root_t
*copyroot_root
;
480 dag_node_t
*copyroot_node
;
481 svn_revnum_t copyroot_rev
;
482 const char *copyroot_path
;
484 /* Make some assertions about the function input. */
485 assert(child
&& child
->parent
&& txn_id
);
487 /* Initialize some convenience variables. */
488 child_id
= svn_fs_fs__dag_get_id(child
->node
);
489 parent_id
= svn_fs_fs__dag_get_id(child
->parent
->node
);
490 child_copy_id
= svn_fs_fs__id_copy_id(child_id
);
491 parent_copy_id
= svn_fs_fs__id_copy_id(parent_id
);
493 /* If this child is already mutable, we have nothing to do. */
494 if (svn_fs_fs__id_txn_id(child_id
))
496 *inherit_p
= copy_id_inherit_self
;
497 *copy_src_path
= NULL
;
501 /* From this point on, we'll assume that the child will just take
502 its copy ID from its parent. */
503 *inherit_p
= copy_id_inherit_parent
;
504 *copy_src_path
= NULL
;
506 /* Special case: if the child's copy ID is '0', use the parent's
508 if (strcmp(child_copy_id
, "0") == 0)
511 /* Compare the copy IDs of the child and its parent. If they are
512 the same, then the child is already on the same branch as the
513 parent, and should use the same mutability copy ID that the
515 if (svn_fs_fs__key_compare(child_copy_id
, parent_copy_id
) == 0)
518 /* If the child is on the same branch that the parent is on, the
519 child should just use the same copy ID that the parent would use.
520 Else, the child needs to generate a new copy ID to use should it
521 need to be made mutable. We will claim that child is on the same
522 branch as its parent if the child itself is not a branch point,
523 or if it is a branch point that we are accessing via its original
524 copy destination path. */
525 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev
, ©root_path
,
527 SVN_ERR(svn_fs_fs__revision_root(©root_root
, fs
, copyroot_rev
, pool
));
528 SVN_ERR(get_dag(©root_node
, copyroot_root
, copyroot_path
, pool
));
529 copyroot_id
= svn_fs_fs__dag_get_id(copyroot_node
);
531 if (svn_fs_fs__id_compare(copyroot_id
, child_id
) == -1)
534 /* Determine if we are looking at the child via its original path or
535 as a subtree item of a copied tree. */
536 id_path
= svn_fs_fs__dag_get_created_path(child
->node
);
537 if (strcmp(id_path
, parent_path_path(child
, pool
)) == 0)
539 *inherit_p
= copy_id_inherit_self
;
543 /* We are pretty sure that the child node is an unedited nested
544 branched node. When it needs to be made mutable, it should claim
546 *inherit_p
= copy_id_inherit_new
;
547 *copy_src_path
= id_path
;
551 /* Allocate a new parent_path_t node from POOL, referring to NODE,
552 ENTRY, PARENT, and COPY_ID. */
553 static parent_path_t
*
554 make_parent_path(dag_node_t
*node
,
556 parent_path_t
*parent
,
559 parent_path_t
*parent_path
= apr_pcalloc(pool
, sizeof(*parent_path
));
560 parent_path
->node
= node
;
561 parent_path
->entry
= entry
;
562 parent_path
->parent
= parent
;
563 parent_path
->copy_inherit
= copy_id_inherit_unknown
;
564 parent_path
->copy_src_path
= NULL
;
569 /* Flags for open_path. */
570 typedef enum open_path_flags_t
{
572 /* The last component of the PATH need not exist. (All parent
573 directories must exist, as usual.) If the last component doesn't
574 exist, simply leave the `node' member of the bottom parent_path
576 open_path_last_optional
= 1
581 /* Open the node identified by PATH in ROOT, allocating in POOL. Set
582 *PARENT_PATH_P to a path from the node up to ROOT. The resulting
583 **PARENT_PATH_P value is guaranteed to contain at least one
584 *element, for the root directory.
586 If resulting *PARENT_PATH_P will eventually be made mutable and
587 modified, or if copy ID inheritance information is otherwise
588 needed, TXN_ID should be the ID of the mutability transaction. If
589 TXN_ID is NULL, no copy ID in heritance information will be
590 calculated for the *PARENT_PATH_P chain.
592 If FLAGS & open_path_last_optional is zero, return the error
593 SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist. If
594 non-zero, require all the parent directories to exist as normal,
595 but if the final path component doesn't exist, simply return a path
596 whose bottom `node' member is zero. This option is useful for
597 callers that create new nodes --- we find the parent directory for
598 them, and tell them whether the entry exists already.
600 NOTE: Public interfaces which only *read* from the filesystem
601 should not call this function directly, but should instead use
605 open_path(parent_path_t
**parent_path_p
,
612 svn_fs_t
*fs
= root
->fs
;
613 const svn_fs_id_t
*id
;
614 dag_node_t
*here
; /* The directory we're currently looking at. */
615 parent_path_t
*parent_path
; /* The path from HERE up to the root. */
616 const char *rest
; /* The portion of PATH we haven't traversed yet. */
617 const char *canon_path
= svn_fs__canonicalize_abspath(path
, pool
);
618 const char *path_so_far
= "/";
620 /* Make a parent_path item for the root node, using its own current
622 SVN_ERR(root_node(&here
, root
, pool
));
623 id
= svn_fs_fs__dag_get_id(here
);
624 parent_path
= make_parent_path(here
, 0, 0, pool
);
625 parent_path
->copy_inherit
= copy_id_inherit_self
;
627 rest
= canon_path
+ 1; /* skip the leading '/', it saves in iteration */
629 /* Whenever we are at the top of this loop:
630 - HERE is our current directory,
631 - ID is the node revision ID of HERE,
632 - REST is the path we're going to find in HERE, and
633 - PARENT_PATH includes HERE and all its parents. */
640 /* Parse out the next entry from the path. */
641 entry
= svn_fs__next_entry_name(&next
, rest
, pool
);
643 /* Calculate the path traversed thus far. */
644 path_so_far
= svn_path_join(path_so_far
, entry
, pool
);
648 /* Given the behavior of svn_fs__next_entry_name(), this
649 happens when the path either starts or ends with a slash.
650 In either case, we stay put: the current directory stays
651 the same, and we add nothing to the parent path. */
656 copy_id_inherit_t inherit
;
657 const char *copy_path
= NULL
;
658 svn_error_t
*err
= SVN_NO_ERROR
;
659 dag_node_t
*cached_node
;
661 /* If we found a directory entry, follow it. First, we
662 check our node cache, and, failing that, we hit the DAG
664 cached_node
= dag_node_cache_get(root
, path_so_far
, pool
);
668 err
= svn_fs_fs__dag_open(&child
, here
, entry
, pool
);
670 /* "file not found" requires special handling. */
671 if (err
&& err
->apr_err
== SVN_ERR_FS_NOT_FOUND
)
673 /* If this was the last path component, and the caller
674 said it was optional, then don't return an error;
675 just put a NULL node pointer in the path. */
677 svn_error_clear(err
);
679 if ((flags
& open_path_last_optional
)
680 && (! next
|| *next
== '\0'))
682 parent_path
= make_parent_path(NULL
, entry
, parent_path
,
688 /* Build a better error message than svn_fs_fs__dag_open
689 can provide, giving the root and full path name. */
690 return SVN_FS__NOT_FOUND(root
, path
);
694 /* Other errors we return normally. */
697 /* Now, make a parent_path item for CHILD. */
698 parent_path
= make_parent_path(child
, entry
, parent_path
, pool
);
701 SVN_ERR(get_copy_inheritance(&inherit
, ©_path
,
702 fs
, parent_path
, txn_id
, pool
));
703 parent_path
->copy_inherit
= inherit
;
704 parent_path
->copy_src_path
= apr_pstrdup(pool
, copy_path
);
707 /* Cache the node we found (if it wasn't already cached). */
709 dag_node_cache_set(root
, path_so_far
, child
, pool
);
712 /* Are we finished traversing the path? */
716 /* The path isn't finished yet; we'd better be in a directory. */
717 if (svn_fs_fs__dag_node_kind(child
) != svn_node_dir
)
718 SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs
, path_so_far
),
719 apr_psprintf(pool
, _("Failure opening '%s'"), path
));
725 *parent_path_p
= parent_path
;
730 /* Make the node referred to by PARENT_PATH mutable, if it isn't
731 already, allocating from POOL. ROOT must be the root from which
732 PARENT_PATH descends. Clone any parent directories as needed.
733 Adjust the dag nodes in PARENT_PATH to refer to the clones. Use
734 ERROR_PATH in error messages. */
736 make_path_mutable(svn_fs_root_t
*root
,
737 parent_path_t
*parent_path
,
738 const char *error_path
,
742 const char *txn_id
= root
->txn
;
744 /* Is the node mutable already? */
745 if (svn_fs_fs__dag_check_mutable(parent_path
->node
))
748 /* Are we trying to clone the root, or somebody's child node? */
749 if (parent_path
->parent
)
751 const svn_fs_id_t
*parent_id
, *child_id
, *copyroot_id
;
752 const char *copy_id
= NULL
;
753 copy_id_inherit_t inherit
= parent_path
->copy_inherit
;
754 const char *clone_path
, *copyroot_path
;
755 svn_revnum_t copyroot_rev
;
756 svn_boolean_t is_parent_copyroot
= FALSE
;
757 svn_fs_root_t
*copyroot_root
;
758 dag_node_t
*copyroot_node
;
760 /* We're trying to clone somebody's child. Make sure our parent
762 SVN_ERR(make_path_mutable(root
, parent_path
->parent
,
767 case copy_id_inherit_parent
:
768 parent_id
= svn_fs_fs__dag_get_id(parent_path
->parent
->node
);
769 copy_id
= svn_fs_fs__id_copy_id(parent_id
);
772 case copy_id_inherit_new
:
773 SVN_ERR(svn_fs_fs__reserve_copy_id(©_id
, root
->fs
, txn_id
,
777 case copy_id_inherit_self
:
781 case copy_id_inherit_unknown
:
783 abort(); /* uh-oh -- somebody didn't calculate copy-ID
787 /* Determine what copyroot our new child node should use. */
788 SVN_ERR(svn_fs_fs__dag_get_copyroot(©root_rev
, ©root_path
,
789 parent_path
->node
, pool
));
790 SVN_ERR(svn_fs_fs__revision_root(©root_root
, root
->fs
,
791 copyroot_rev
, pool
));
792 SVN_ERR(get_dag(©root_node
, copyroot_root
, copyroot_path
, pool
));
794 child_id
= svn_fs_fs__dag_get_id(parent_path
->node
);
795 copyroot_id
= svn_fs_fs__dag_get_id(copyroot_node
);
796 if (strcmp(svn_fs_fs__id_node_id(child_id
),
797 svn_fs_fs__id_node_id(copyroot_id
)) != 0)
798 is_parent_copyroot
= TRUE
;
800 /* Now make this node mutable. */
801 clone_path
= parent_path_path(parent_path
->parent
, pool
);
802 SVN_ERR(svn_fs_fs__dag_clone_child(&clone
,
803 parent_path
->parent
->node
,
810 /* Update the path cache. */
811 dag_node_cache_set(root
, parent_path_path(parent_path
, pool
), clone
,
816 /* We're trying to clone the root directory. */
817 SVN_ERR(mutable_root_node(&clone
, root
, error_path
, pool
));
820 /* Update the PARENT_PATH link to refer to the clone. */
821 parent_path
->node
= clone
;
827 /* Open the node identified by PATH in ROOT. Set DAG_NODE_P to the
828 *node we find, allocated in POOL. Return the error
829 *SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
831 get_dag(dag_node_t
**dag_node_p
,
836 parent_path_t
*parent_path
;
837 dag_node_t
*node
= NULL
;
839 /* Canonicalize the input PATH. */
840 path
= svn_fs__canonicalize_abspath(path
, pool
);
842 /* If ROOT is a revision root, we'll look for the DAG in our cache. */
843 node
= dag_node_cache_get(root
, path
, pool
);
846 /* Call open_path with no flags, as we want this to return an error
847 if the node for which we are searching doesn't exist. */
848 SVN_ERR(open_path(&parent_path
, root
, path
, 0, NULL
, pool
));
849 node
= parent_path
->node
;
851 /* No need to cache our find -- open_path() will do that for us. */
860 /* Populating the `changes' table. */
862 /* Add a change to the changes table in FS, keyed on transaction id
863 TXN_ID, and indicated that a change of kind CHANGE_KIND occurred on
864 PATH (whose node revision id is--or was, in the case of a
865 deletion--NODEREV_ID), and optionally that TEXT_MODs or PROP_MODs
866 occurred. If the change resulted from a copy, COPYFROM_REV and
867 COPYFROM_PATH specify under which revision and path the node was
868 copied from. If this was not part of a copy, COPYFROM_REV should
869 be SVN_INVALID_REVNUM. Do all this as part of POOL. */
871 add_change(svn_fs_t
*fs
,
874 const svn_fs_id_t
*noderev_id
,
875 svn_fs_path_change_kind_t change_kind
,
876 svn_boolean_t text_mod
,
877 svn_boolean_t prop_mod
,
878 svn_revnum_t copyfrom_rev
,
879 const char *copyfrom_path
,
882 SVN_ERR(svn_fs_fs__add_change(fs
, txn_id
,
883 svn_fs__canonicalize_abspath(path
, pool
),
884 noderev_id
, change_kind
, text_mod
, prop_mod
,
885 copyfrom_rev
, copyfrom_path
,
893 /* Generic node operations. */
895 /* Get the id of a node referenced by path PATH in ROOT. Return the
896 id in *ID_P allocated in POOL. */
898 fs_node_id(const svn_fs_id_t
**id_p
,
903 if ((! root
->is_txn_root
)
904 && (path
[0] == '\0' || ((path
[0] == '/') && (path
[1] == '\0'))))
906 /* Optimize the case where we don't need any db access at all.
907 The root directory ("" or "/") node is stored in the
908 svn_fs_root_t object, and never changes when it's a revision
909 root, so we can just reach in and grab it directly. */
910 fs_rev_root_data_t
*frd
= root
->fsap_data
;
911 *id_p
= svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd
->root_dir
), pool
);
917 SVN_ERR(get_dag(&node
, root
, path
, pool
));
918 *id_p
= svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node
), pool
);
925 svn_fs_fs__node_created_rev(svn_revnum_t
*revision
,
932 SVN_ERR(get_dag(&node
, root
, path
, pool
));
933 SVN_ERR(svn_fs_fs__dag_get_revision(revision
, node
, pool
));
939 /* Set *CREATED_PATH to the path at which PATH under ROOT was created.
940 Return a string allocated in POOL. */
942 fs_node_created_path(const char **created_path
,
949 SVN_ERR(get_dag(&node
, root
, path
, pool
));
950 *created_path
= svn_fs_fs__dag_get_created_path(node
);
956 /* Set *KIND_P to the type of node located at PATH under ROOT.
957 Perform temporary allocations in POOL. */
959 node_kind(svn_node_kind_t
*kind_p
,
964 const svn_fs_id_t
*node_id
;
967 /* Get the node id. */
968 SVN_ERR(fs_node_id(&node_id
, root
, path
, pool
));
970 /* Use the node id to get the real kind. */
971 SVN_ERR(svn_fs_fs__dag_get_node(&node
, root
->fs
, node_id
, pool
));
972 *kind_p
= svn_fs_fs__dag_node_kind(node
);
978 /* Set *KIND_P to the type of node present at PATH under ROOT. If
979 PATH does not exist under ROOT, set *KIND_P to svn_node_none. Use
980 POOL for temporary allocation. */
982 svn_fs_fs__check_path(svn_node_kind_t
*kind_p
,
987 svn_error_t
*err
= node_kind(kind_p
, root
, path
, pool
);
989 ((err
->apr_err
== SVN_ERR_FS_NOT_FOUND
)
990 || (err
->apr_err
== SVN_ERR_FS_NOT_DIRECTORY
)))
992 svn_error_clear(err
);
993 *kind_p
= svn_node_none
;
1002 /* Set *VALUE_P to the value of the property named PROPNAME of PATH in
1003 ROOT. If the node has no property by that name, set *VALUE_P to
1004 zero. Allocate the result in POOL. */
1005 static svn_error_t
*
1006 fs_node_prop(svn_string_t
**value_p
,
1007 svn_fs_root_t
*root
,
1009 const char *propname
,
1013 apr_hash_t
*proplist
;
1015 SVN_ERR(get_dag(&node
, root
, path
, pool
));
1016 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist
, node
, pool
));
1019 *value_p
= apr_hash_get(proplist
, propname
, APR_HASH_KEY_STRING
);
1021 return SVN_NO_ERROR
;
1025 /* Set *TABLE_P to the entire property list of PATH under ROOT, as an
1026 APR hash table allocated in POOL. The resulting property table
1027 maps property names to pointers to svn_string_t objects containing
1028 the property value. */
1029 static svn_error_t
*
1030 fs_node_proplist(apr_hash_t
**table_p
,
1031 svn_fs_root_t
*root
,
1038 SVN_ERR(get_dag(&node
, root
, path
, pool
));
1039 SVN_ERR(svn_fs_fs__dag_get_proplist(&table
, node
, pool
));
1040 *table_p
= table
? table
: apr_hash_make(pool
);
1042 return SVN_NO_ERROR
;
1046 static svn_error_t
*
1047 increment_mergeinfo_up_tree(parent_path_t
*pp
,
1048 apr_int64_t increment
,
1051 for (; pp
; pp
= pp
->parent
)
1052 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(pp
->node
,
1056 return SVN_NO_ERROR
;
1059 /* Change, add, or delete a node's property value. The affected node
1060 is PATH under ROOT, the property value to modify is NAME, and VALUE
1061 points to either a string value to set the new contents to, or NULL
1062 if the property should be deleted. Perform temporary allocations
1064 static svn_error_t
*
1065 fs_change_node_prop(svn_fs_root_t
*root
,
1068 const svn_string_t
*value
,
1071 parent_path_t
*parent_path
;
1072 apr_hash_t
*proplist
;
1075 if (! root
->is_txn_root
)
1076 return SVN_FS__NOT_TXN(root
);
1079 SVN_ERR(open_path(&parent_path
, root
, path
, 0, txn_id
, pool
));
1081 /* Check (non-recursively) to see if path is locked; if so, check
1082 that we can use it. */
1083 if (root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
1084 SVN_ERR(svn_fs_fs__allow_locked_operation(path
, root
->fs
, FALSE
, FALSE
,
1087 SVN_ERR(make_path_mutable(root
, parent_path
, path
, pool
));
1088 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist
, parent_path
->node
, pool
));
1090 /* If there's no proplist, but we're just deleting a property, exit now. */
1091 if ((! proplist
) && (! value
))
1092 return SVN_NO_ERROR
;
1094 /* Now, if there's no proplist, we know we need to make one. */
1096 proplist
= apr_hash_make(pool
);
1098 if (svn_fs_fs__fs_supports_mergeinfo(root
->fs
)
1099 && strcmp (name
, SVN_PROP_MERGEINFO
) == 0)
1101 apr_int64_t increment
= 0;
1102 svn_boolean_t had_mergeinfo
;
1103 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&had_mergeinfo
, parent_path
->node
,
1106 if (value
&& !had_mergeinfo
)
1108 else if (!value
&& had_mergeinfo
)
1113 SVN_ERR(increment_mergeinfo_up_tree(parent_path
, increment
, pool
));
1114 SVN_ERR(svn_fs_fs__dag_set_has_mergeinfo(parent_path
->node
,
1115 (value
!= NULL
), pool
));
1119 /* Set the property. */
1120 apr_hash_set(proplist
, name
, APR_HASH_KEY_STRING
, value
);
1122 /* Overwrite the node's proplist. */
1123 SVN_ERR(svn_fs_fs__dag_set_proplist(parent_path
->node
, proplist
,
1126 /* Make a record of this modification in the changes table. */
1127 SVN_ERR(add_change(root
->fs
, txn_id
, path
,
1128 svn_fs_fs__dag_get_id(parent_path
->node
),
1129 svn_fs_path_change_modify
, FALSE
, TRUE
, SVN_INVALID_REVNUM
,
1132 return SVN_NO_ERROR
;
1136 /* Determine if the properties of two path/root combinations are
1137 different. Set *CHANGED_P to TRUE if the properties at PATH1 under
1138 ROOT1 differ from those at PATH2 under ROOT2, or FALSE otherwise.
1139 Both roots must be in the same filesystem. */
1140 static svn_error_t
*
1141 fs_props_changed(svn_boolean_t
*changed_p
,
1142 svn_fs_root_t
*root1
,
1144 svn_fs_root_t
*root2
,
1148 dag_node_t
*node1
, *node2
;
1150 /* Check that roots are in the same fs. */
1151 if (root1
->fs
!= root2
->fs
)
1152 return svn_error_create
1153 (SVN_ERR_FS_GENERAL
, NULL
,
1154 _("Cannot compare property value between two different filesystems"));
1156 SVN_ERR(get_dag(&node1
, root1
, path1
, pool
));
1157 SVN_ERR(get_dag(&node2
, root2
, path2
, pool
));
1158 SVN_ERR(svn_fs_fs__dag_things_different(changed_p
, NULL
,
1159 node1
, node2
, pool
));
1161 return SVN_NO_ERROR
;
1166 /* Merges and commits. */
1168 /* Set ARGS->node to the root node of ARGS->root. */
1169 static svn_error_t
*
1170 get_root(dag_node_t
**node
, svn_fs_root_t
*root
, apr_pool_t
*pool
)
1172 SVN_ERR(get_dag(node
, root
, "", pool
));
1173 return SVN_NO_ERROR
;
1177 static svn_error_t
*
1178 update_ancestry(svn_fs_t
*fs
,
1179 const svn_fs_id_t
*source_id
,
1180 const svn_fs_id_t
*target_id
,
1181 const char *target_path
,
1182 int source_pred_count
,
1185 node_revision_t
*noderev
;
1187 if (svn_fs_fs__id_txn_id(target_id
) == NULL
)
1188 return svn_error_createf
1189 (SVN_ERR_FS_NOT_MUTABLE
, NULL
,
1190 _("Unexpected immutable node at '%s'"), target_path
);
1192 SVN_ERR(svn_fs_fs__get_node_revision(&noderev
, fs
, target_id
, pool
));
1193 noderev
->predecessor_id
= source_id
;
1194 noderev
->predecessor_count
= source_pred_count
;
1195 if (noderev
->predecessor_count
!= -1)
1196 noderev
->predecessor_count
++;
1197 SVN_ERR(svn_fs_fs__put_node_revision(fs
, target_id
, noderev
, FALSE
, pool
));
1199 return SVN_NO_ERROR
;
1203 /* Set the contents of CONFLICT_PATH to PATH, and return an
1204 SVN_ERR_FS_CONFLICT error that indicates that there was a conflict
1205 at PATH. Perform all allocations in POOL (except the allocation of
1206 CONFLICT_PATH, which should be handled outside this function). */
1207 static svn_error_t
*
1208 conflict_err(svn_stringbuf_t
*conflict_path
,
1211 svn_stringbuf_set(conflict_path
, path
);
1212 return svn_error_createf(SVN_ERR_FS_CONFLICT
, NULL
,
1213 _("Conflict at '%s'"), path
);
1217 /* Merge changes between ANCESTOR and SOURCE into TARGET. ANCESTOR
1218 * and TARGET must be distinct node revisions. TARGET_PATH should
1219 * correspond to TARGET's full path in its filesystem, and is used for
1220 * reporting conflict location.
1222 * SOURCE, TARGET, and ANCESTOR are generally directories; this
1223 * function recursively merges the directories' contents. If any are
1224 * files, this function simply returns an error whenever SOURCE,
1225 * TARGET, and ANCESTOR are all distinct node revisions.
1227 * If there are differences between ANCESTOR and SOURCE that conflict
1228 * with changes between ANCESTOR and TARGET, this function returns an
1229 * SVN_ERR_FS_CONFLICT error, and updates CONFLICT_P to the name of the
1230 * conflicting node in TARGET, with TARGET_PATH prepended as a path.
1232 * If there are no conflicting differences, CONFLICT_P is updated to
1235 * CONFLICT_P must point to a valid svn_stringbuf_t.
1237 * Do any necessary temporary allocation in POOL.
1239 static svn_error_t
*
1240 merge(svn_stringbuf_t
*conflict_p
,
1241 const char *target_path
,
1244 dag_node_t
*ancestor
,
1246 apr_int64_t
*mergeinfo_increment_out
,
1249 const svn_fs_id_t
*source_id
, *target_id
, *ancestor_id
;
1250 apr_hash_t
*s_entries
, *t_entries
, *a_entries
;
1251 apr_hash_index_t
*hi
;
1253 apr_pool_t
*iterpool
;
1255 apr_int64_t mergeinfo_increment
= 0;
1257 /* Make sure everyone comes from the same filesystem. */
1258 fs
= svn_fs_fs__dag_get_fs(ancestor
);
1259 if ((fs
!= svn_fs_fs__dag_get_fs(source
))
1260 || (fs
!= svn_fs_fs__dag_get_fs(target
)))
1262 return svn_error_create
1263 (SVN_ERR_FS_CORRUPT
, NULL
,
1264 _("Bad merge; ancestor, source, and target not all in same fs"));
1267 /* We have the same fs, now check it. */
1268 SVN_ERR(svn_fs__check_fs(fs
, TRUE
));
1270 source_id
= svn_fs_fs__dag_get_id(source
);
1271 target_id
= svn_fs_fs__dag_get_id(target
);
1272 ancestor_id
= svn_fs_fs__dag_get_id(ancestor
);
1274 /* It's improper to call this function with ancestor == target. */
1275 if (svn_fs_fs__id_eq(ancestor_id
, target_id
))
1277 svn_string_t
*id_str
= svn_fs_fs__id_unparse(target_id
, pool
);
1278 return svn_error_createf
1279 (SVN_ERR_FS_GENERAL
, NULL
,
1280 _("Bad merge; target '%s' has id '%s', same as ancestor"),
1281 target_path
, id_str
->data
);
1284 svn_stringbuf_setempty(conflict_p
);
1287 * Either no change made in source, or same change as made in target.
1288 * Both mean nothing to merge here.
1290 if (svn_fs_fs__id_eq(ancestor_id
, source_id
)
1291 || (svn_fs_fs__id_eq(source_id
, target_id
)))
1292 return SVN_NO_ERROR
;
1294 /* Else proceed, knowing all three are distinct node revisions.
1296 * How to merge from this point:
1298 * if (not all 3 are directories)
1300 * early exit with conflict;
1303 * // Property changes may only be made to up-to-date
1304 * // directories, because once the client commits the prop
1305 * // change, it bumps the directory's revision, and therefore
1306 * // must be able to depend on there being no other changes to
1307 * // that directory in the repository.
1308 * if (target's property list differs from ancestor's)
1311 * For each entry NAME in the directory ANCESTOR:
1313 * Let ANCESTOR-ENTRY, SOURCE-ENTRY, and TARGET-ENTRY be the IDs of
1314 * the name within ANCESTOR, SOURCE, and TARGET respectively.
1315 * (Possibly null if NAME does not exist in SOURCE or TARGET.)
1317 * If ANCESTOR-ENTRY == SOURCE-ENTRY, then:
1318 * No changes were made to this entry while the transaction was in
1319 * progress, so do nothing to the target.
1321 * Else if ANCESTOR-ENTRY == TARGET-ENTRY, then:
1322 * A change was made to this entry while the transaction was in
1323 * process, but the transaction did not touch this entry. Replace
1324 * TARGET-ENTRY with SOURCE-ENTRY.
1327 * Changes were made to this entry both within the transaction and
1328 * to the repository while the transaction was in progress. They
1329 * must be merged or declared to be in conflict.
1331 * If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1332 * double delete; flag a conflict.
1334 * If any of the three entries is of type file, declare a conflict.
1336 * If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1337 * modification of ANCESTOR-ENTRY (determine by comparing the
1338 * node-id fields), declare a conflict. A replacement is
1339 * incompatible with a modification or other replacement--even
1340 * an identical replacement.
1342 * Direct modifications were made to the directory ANCESTOR-ENTRY
1343 * in both SOURCE and TARGET. Recursively merge these
1346 * For each leftover entry NAME in the directory SOURCE:
1348 * If NAME exists in TARGET, declare a conflict. Even if SOURCE and
1349 * TARGET are adding exactly the same thing, two additions are not
1350 * auto-mergeable with each other.
1352 * Add NAME to TARGET with the entry from SOURCE.
1354 * Now that we are done merging the changes from SOURCE into the
1355 * directory TARGET, update TARGET's predecessor to be SOURCE.
1358 if ((svn_fs_fs__dag_node_kind(source
) != svn_node_dir
)
1359 || (svn_fs_fs__dag_node_kind(target
) != svn_node_dir
)
1360 || (svn_fs_fs__dag_node_kind(ancestor
) != svn_node_dir
))
1362 return conflict_err(conflict_p
, target_path
);
1366 /* Possible early merge failure: if target and ancestor have
1367 different property lists, then the merge should fail.
1368 Propchanges can *only* be committed on an up-to-date directory.
1369 ### TODO: see issue #418 about the inelegance of this.
1371 Another possible, similar, early merge failure: if source and
1372 ancestor have different property lists (meaning someone else
1373 changed directory properties while our commit transaction was
1374 happening), the merge should fail. See issue #2751.
1377 node_revision_t
*tgt_nr
, *anc_nr
, *src_nr
;
1379 /* Get node revisions for our id's. */
1380 SVN_ERR(svn_fs_fs__get_node_revision(&tgt_nr
, fs
, target_id
, pool
));
1381 SVN_ERR(svn_fs_fs__get_node_revision(&anc_nr
, fs
, ancestor_id
, pool
));
1382 SVN_ERR(svn_fs_fs__get_node_revision(&src_nr
, fs
, source_id
, pool
));
1384 /* Now compare the prop-keys of the skels. Note that just because
1385 the keys are different -doesn't- mean the proplists have
1386 different contents. But merge() isn't concerned with contents;
1387 it doesn't do a brute-force comparison on textual contents, so
1388 it won't do that here either. Checking to see if the propkey
1389 atoms are `equal' is enough. */
1390 if (! svn_fs_fs__noderev_same_rep_key(tgt_nr
->prop_rep
, anc_nr
->prop_rep
))
1391 return conflict_err(conflict_p
, target_path
);
1392 if (! svn_fs_fs__noderev_same_rep_key(src_nr
->prop_rep
, anc_nr
->prop_rep
))
1393 return conflict_err(conflict_p
, target_path
);
1396 /* ### todo: it would be more efficient to simply check for a NULL
1397 entries hash where necessary below than to allocate an empty hash
1398 here, but another day, another day... */
1399 SVN_ERR(svn_fs_fs__dag_dir_entries(&s_entries
, source
, pool
, pool
));
1400 SVN_ERR(svn_fs_fs__dag_dir_entries(&t_entries
, target
, pool
, pool
));
1401 SVN_ERR(svn_fs_fs__dag_dir_entries(&a_entries
, ancestor
, pool
, pool
));
1403 /* for each entry E in a_entries... */
1404 iterpool
= svn_pool_create(pool
);
1405 for (hi
= apr_hash_first(pool
, a_entries
);
1407 hi
= apr_hash_next(hi
))
1409 svn_fs_dirent_t
*s_entry
, *t_entry
, *a_entry
;
1415 svn_pool_clear(iterpool
);
1417 /* KEY will be the entry name in ancestor, VAL the dirent */
1418 apr_hash_this(hi
, &key
, &klen
, &val
);
1421 s_entry
= apr_hash_get(s_entries
, key
, klen
);
1422 t_entry
= apr_hash_get(t_entries
, key
, klen
);
1424 /* No changes were made to this entry while the transaction was
1425 in progress, so do nothing to the target. */
1426 if (s_entry
&& svn_fs_fs__id_eq(a_entry
->id
, s_entry
->id
))
1429 /* A change was made to this entry while the transaction was in
1430 process, but the transaction did not touch this entry. */
1431 else if (t_entry
&& svn_fs_fs__id_eq(a_entry
->id
, t_entry
->id
))
1433 dag_node_t
*t_ent_node
;
1434 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node
, fs
,
1435 t_entry
->id
, iterpool
));
1436 if (svn_fs_fs__fs_supports_mergeinfo(fs
))
1438 apr_int64_t mergeinfo_start
;
1439 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start
,
1442 mergeinfo_increment
-= mergeinfo_start
;
1447 dag_node_t
*s_ent_node
;
1448 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node
, fs
,
1449 s_entry
->id
, iterpool
));
1451 if (svn_fs_fs__fs_supports_mergeinfo(fs
))
1453 apr_int64_t mergeinfo_end
;
1454 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end
,
1457 mergeinfo_increment
+= mergeinfo_end
;
1460 SVN_ERR(svn_fs_fs__dag_set_entry(target
, key
,
1468 SVN_ERR(svn_fs_fs__dag_delete(target
, key
, txn_id
, iterpool
));
1472 /* Changes were made to this entry both within the transaction
1473 and to the repository while the transaction was in progress.
1474 They must be merged or declared to be in conflict. */
1477 dag_node_t
*s_ent_node
, *t_ent_node
, *a_ent_node
;
1478 const char *new_tpath
;
1479 apr_int64_t sub_mergeinfo_increment
;
1481 /* If SOURCE-ENTRY and TARGET-ENTRY are both null, that's a
1482 double delete; flag a conflict. */
1483 if (s_entry
== NULL
|| t_entry
== NULL
)
1484 return conflict_err(conflict_p
,
1485 svn_path_join(target_path
,
1489 /* If any of the three entries is of type file, flag a conflict. */
1490 if (s_entry
->kind
== svn_node_file
1491 || t_entry
->kind
== svn_node_file
1492 || a_entry
->kind
== svn_node_file
)
1493 return conflict_err(conflict_p
,
1494 svn_path_join(target_path
,
1498 /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
1499 modification of ANCESTOR-ENTRY, declare a conflict. */
1500 if (strcmp(svn_fs_fs__id_node_id(s_entry
->id
),
1501 svn_fs_fs__id_node_id(a_entry
->id
)) != 0
1502 || strcmp(svn_fs_fs__id_copy_id(s_entry
->id
),
1503 svn_fs_fs__id_copy_id(a_entry
->id
)) != 0
1504 || strcmp(svn_fs_fs__id_node_id(t_entry
->id
),
1505 svn_fs_fs__id_node_id(a_entry
->id
)) != 0
1506 || strcmp(svn_fs_fs__id_copy_id(t_entry
->id
),
1507 svn_fs_fs__id_copy_id(a_entry
->id
)) != 0)
1508 return conflict_err(conflict_p
,
1509 svn_path_join(target_path
,
1513 /* Direct modifications were made to the directory
1514 ANCESTOR-ENTRY in both SOURCE and TARGET. Recursively
1515 merge these modifications. */
1516 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node
, fs
,
1517 s_entry
->id
, iterpool
));
1518 SVN_ERR(svn_fs_fs__dag_get_node(&t_ent_node
, fs
,
1519 t_entry
->id
, iterpool
));
1520 SVN_ERR(svn_fs_fs__dag_get_node(&a_ent_node
, fs
,
1521 a_entry
->id
, iterpool
));
1522 new_tpath
= svn_path_join(target_path
, t_entry
->name
, iterpool
);
1523 SVN_ERR(merge(conflict_p
, new_tpath
,
1524 t_ent_node
, s_ent_node
, a_ent_node
,
1526 &sub_mergeinfo_increment
,
1528 if (svn_fs_fs__fs_supports_mergeinfo(fs
))
1529 mergeinfo_increment
+= sub_mergeinfo_increment
;
1532 /* We've taken care of any possible implications E could have.
1533 Remove it from source_entries, so it's easy later to loop
1534 over all the source entries that didn't exist in
1535 ancestor_entries. */
1537 apr_hash_set(s_entries
, key
, klen
, NULL
);
1540 /* For each entry E in source but not in ancestor */
1541 for (hi
= apr_hash_first(pool
, s_entries
);
1543 hi
= apr_hash_next(hi
))
1545 svn_fs_dirent_t
*s_entry
, *t_entry
;
1549 dag_node_t
*s_ent_node
;
1551 svn_pool_clear(iterpool
);
1553 apr_hash_this(hi
, &key
, &klen
, &val
);
1555 t_entry
= apr_hash_get(t_entries
, key
, klen
);
1557 /* If NAME exists in TARGET, declare a conflict. */
1559 return conflict_err(conflict_p
,
1560 svn_path_join(target_path
,
1564 SVN_ERR(svn_fs_fs__dag_get_node(&s_ent_node
, fs
,
1565 s_entry
->id
, iterpool
));
1566 if (svn_fs_fs__fs_supports_mergeinfo(fs
))
1568 apr_int64_t mergeinfo_s
;
1569 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_s
,
1572 mergeinfo_increment
+= mergeinfo_s
;
1575 SVN_ERR(svn_fs_fs__dag_set_entry
1576 (target
, s_entry
->name
, s_entry
->id
, s_entry
->kind
,
1579 svn_pool_destroy(iterpool
);
1581 SVN_ERR(svn_fs_fs__dag_get_predecessor_count(&pred_count
, source
, pool
));
1582 SVN_ERR(update_ancestry(fs
, source_id
, target_id
, target_path
,
1585 if (svn_fs_fs__fs_supports_mergeinfo(fs
))
1586 SVN_ERR(svn_fs_fs__dag_increment_mergeinfo_count(target
,
1587 mergeinfo_increment
,
1590 if (mergeinfo_increment_out
)
1591 *mergeinfo_increment_out
= mergeinfo_increment
;
1593 return SVN_NO_ERROR
;
1596 /* Merge changes between an ancestor and BATON->source_node into
1597 BATON->txn. The ancestor is either BATON->ancestor_node, or if
1598 that is null, BATON->txn's base node.
1600 If the merge is successful, BATON->txn's base will become
1601 BATON->source_node, and its root node will have a new ID, a
1602 successor of BATON->source_node. */
1603 static svn_error_t
*
1604 merge_changes(dag_node_t
*ancestor_node
,
1605 dag_node_t
*source_node
,
1607 svn_stringbuf_t
*conflict
,
1610 dag_node_t
*txn_root_node
;
1611 const svn_fs_id_t
*source_id
;
1612 svn_fs_t
*fs
= txn
->fs
;
1613 const char *txn_id
= txn
->id
;
1615 source_id
= svn_fs_fs__dag_get_id(source_node
);
1617 SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node
, fs
, txn_id
, pool
));
1619 if (ancestor_node
== NULL
)
1621 SVN_ERR(svn_fs_fs__dag_txn_base_root(&ancestor_node
, fs
,
1625 if (svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(ancestor_node
),
1626 svn_fs_fs__dag_get_id(txn_root_node
)))
1628 /* If no changes have been made in TXN since its current base,
1629 then it can't conflict with any changes since that base. So
1630 we just set *both* its base and root to source, making TXN
1631 in effect a repeat of source. */
1633 /* ### kff todo: this would, of course, be a mighty silly thing
1634 for the caller to do, and we might want to consider whether
1635 this response is really appropriate. */
1639 SVN_ERR(merge(conflict
, "/", txn_root_node
,
1640 source_node
, ancestor_node
, txn_id
, NULL
, pool
));
1642 return SVN_NO_ERROR
;
1647 svn_fs_fs__commit_txn(const char **conflict_p
,
1648 svn_revnum_t
*new_rev_p
,
1652 /* How do commits work in Subversion?
1654 * When you're ready to commit, here's what you have:
1656 * 1. A transaction, with a mutable tree hanging off it.
1657 * 2. A base revision, against which TXN_TREE was made.
1658 * 3. A latest revision, which may be newer than the base rev.
1660 * The problem is that if latest != base, then one can't simply
1661 * attach the txn root as the root of the new revision, because that
1662 * would lose all the changes between base and latest. It is also
1663 * not acceptable to insist that base == latest; in a busy
1664 * repository, commits happen too fast to insist that everyone keep
1665 * their entire tree up-to-date at all times. Non-overlapping
1666 * changes should not interfere with each other.
1668 * The solution is to merge the changes between base and latest into
1669 * the txn tree [see the function merge()]. The txn tree is the
1670 * only one of the three trees that is mutable, so it has to be the
1673 * You might have to adjust it more than once, if a new latest
1674 * revision gets committed while you were merging in the previous
1677 * 1. Jane starts txn T, based at revision 6.
1678 * 2. Someone commits (or already committed) revision 7.
1679 * 3. Jane's starts merging the changes between 6 and 7 into T.
1680 * 4. Meanwhile, someone commits revision 8.
1681 * 5. Jane finishes the 6-->7 merge. T could now be committed
1682 * against a latest revision of 7, if only that were still the
1683 * latest. Unfortunately, 8 is now the latest, so...
1684 * 6. Jane starts merging the changes between 7 and 8 into T.
1685 * 7. Meanwhile, no one commits any new revisions. Whew.
1686 * 8. Jane commits T, creating revision 9, whose tree is exactly
1687 * T's tree, except immutable now.
1689 * Lather, rinse, repeat.
1693 svn_revnum_t new_rev
;
1694 svn_fs_t
*fs
= txn
->fs
;
1696 /* Initialize output params. */
1697 new_rev
= SVN_INVALID_REVNUM
;
1703 svn_revnum_t youngish_rev
;
1704 svn_fs_root_t
*youngish_root
;
1705 dag_node_t
*youngish_root_node
;
1706 svn_stringbuf_t
*conflict
= svn_stringbuf_create("", pool
);
1708 /* Get the *current* youngest revision, in one short-lived
1709 Berkeley transaction. (We don't want the revisions table
1710 locked while we do the main merge.) We call it "youngish"
1711 because new revisions might get committed after we've
1714 SVN_ERR(svn_fs_fs__youngest_rev(&youngish_rev
, fs
, pool
));
1715 SVN_ERR(svn_fs_fs__revision_root(&youngish_root
, fs
, youngish_rev
,
1718 /* Get the dag node for the youngest revision, also in one
1719 Berkeley transaction. Later we'll use it as the SOURCE
1720 argument to a merge, and if the merge succeeds, this youngest
1721 root node will become the new base root for the svn txn that
1722 was the target of the merge (but note that the youngest rev
1723 may have changed by then -- that's why we're careful to get
1724 this root in its own bdb txn here). */
1725 SVN_ERR(get_root(&youngish_root_node
, youngish_root
, pool
));
1727 /* Try to merge. If the merge succeeds, the base root node of
1728 TARGET's txn will become the same as youngish_root_node, so
1729 any future merges will only be between that node and whatever
1730 the root node of the youngest rev is by then. */
1731 err
= merge_changes(NULL
, youngish_root_node
, txn
, conflict
, pool
);
1734 if ((err
->apr_err
== SVN_ERR_FS_CONFLICT
) && conflict_p
)
1735 *conflict_p
= conflict
->data
;
1738 txn
->base_rev
= youngish_rev
;
1740 /* Try to commit. */
1741 err
= svn_fs_fs__commit(&new_rev
, fs
, txn
, pool
);
1742 if (err
&& (err
->apr_err
== SVN_ERR_FS_TXN_OUT_OF_DATE
))
1744 /* Did someone else finish committing a new revision while we
1745 were in mid-merge or mid-commit? If so, we'll need to
1746 loop again to merge the new changes in, then try to
1747 commit again. Or if that's not what happened, then just
1748 return the error. */
1749 svn_revnum_t youngest_rev
;
1750 SVN_ERR(svn_fs_fs__youngest_rev(&youngest_rev
, fs
, pool
));
1751 if (youngest_rev
== youngish_rev
)
1754 svn_error_clear(err
);
1762 /* Set the return value -- our brand spankin' new revision! */
1763 *new_rev_p
= new_rev
;
1764 return SVN_NO_ERROR
;
1772 /* Merge changes between two nodes into a third node. Given nodes
1773 SOURCE_PATH under SOURCE_ROOT, TARGET_PATH under TARGET_ROOT and
1774 ANCESTOR_PATH under ANCESTOR_ROOT, modify target to contain all the
1775 changes between the ancestor and source. If there are conflicts,
1776 return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a textual
1777 description of the offending changes. Perform any temporary
1778 allocations in POOL. */
1779 static svn_error_t
*
1780 fs_merge(const char **conflict_p
,
1781 svn_fs_root_t
*source_root
,
1782 const char *source_path
,
1783 svn_fs_root_t
*target_root
,
1784 const char *target_path
,
1785 svn_fs_root_t
*ancestor_root
,
1786 const char *ancestor_path
,
1789 dag_node_t
*source
, *ancestor
;
1792 svn_stringbuf_t
*conflict
= svn_stringbuf_create("", pool
);
1794 if (! target_root
->is_txn_root
)
1795 return SVN_FS__NOT_TXN(target_root
);
1798 if ((source_root
->fs
!= ancestor_root
->fs
)
1799 || (target_root
->fs
!= ancestor_root
->fs
))
1801 return svn_error_create
1802 (SVN_ERR_FS_CORRUPT
, NULL
,
1803 _("Bad merge; ancestor, source, and target not all in same fs"));
1806 /* ### kff todo: is there any compelling reason to get the nodes in
1807 one db transaction? Right now we don't; txn_body_get_root() gets
1808 one node at a time. This will probably need to change:
1810 Jim Blandy <jimb@zwingli.cygnus.com> writes:
1811 > svn_fs_merge needs to be a single transaction, to protect it against
1812 > people deleting parents of nodes it's working on, etc.
1815 /* Get the ancestor node. */
1816 SVN_ERR(get_root(&ancestor
, ancestor_root
, pool
));
1818 /* Get the source node. */
1819 SVN_ERR(get_root(&source
, source_root
, pool
));
1821 /* Open a txn for the txn root into which we're merging. */
1822 SVN_ERR(svn_fs_fs__open_txn(&txn
, ancestor_root
->fs
, target_root
->txn
,
1825 /* Merge changes between ANCESTOR and SOURCE into TXN. */
1826 err
= merge_changes(ancestor
, source
, txn
, conflict
, pool
);
1829 if ((err
->apr_err
== SVN_ERR_FS_CONFLICT
) && conflict_p
)
1830 *conflict_p
= conflict
->data
;
1834 return SVN_NO_ERROR
;
1838 svn_fs_fs__deltify(svn_fs_t
*fs
,
1839 svn_revnum_t revision
,
1842 /* Deltify is a no-op for fs_fs. */
1844 return SVN_NO_ERROR
;
1851 /* Set *TABLE_P to a newly allocated APR hash table containing the
1852 entries of the directory at PATH in ROOT. The keys of the table
1853 are entry names, as byte strings, excluding the final null
1854 character; the table's values are pointers to svn_fs_dirent_t
1855 structures. Allocate the table and its contents in POOL. */
1856 static svn_error_t
*
1857 fs_dir_entries(apr_hash_t
**table_p
,
1858 svn_fs_root_t
*root
,
1864 /* Get the entries for this path in the caller's pool. */
1865 SVN_ERR(get_dag(&node
, root
, path
, pool
));
1866 SVN_ERR(svn_fs_fs__dag_dir_entries(table_p
, node
, pool
, pool
));
1867 return SVN_NO_ERROR
;
1871 /* Create a new directory named PATH in ROOT. The new directory has
1872 no entries, and no properties. ROOT must be the root of a
1873 transaction, not a revision. Do any necessary temporary allocation
1875 static svn_error_t
*
1876 fs_make_dir(svn_fs_root_t
*root
,
1880 parent_path_t
*parent_path
;
1881 dag_node_t
*sub_dir
;
1882 const char *txn_id
= root
->txn
;
1884 SVN_ERR(open_path(&parent_path
, root
, path
, open_path_last_optional
,
1887 /* Check (recursively) to see if some lock is 'reserving' a path at
1888 that location, or even some child-path; if so, check that we can
1890 if (root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
1891 SVN_ERR(svn_fs_fs__allow_locked_operation(path
, root
->fs
, TRUE
, FALSE
,
1894 /* If there's already a sub-directory by that name, complain. This
1895 also catches the case of trying to make a subdirectory named `/'. */
1896 if (parent_path
->node
)
1897 return SVN_FS__ALREADY_EXISTS(root
, path
);
1899 /* Create the subdirectory. */
1900 SVN_ERR(make_path_mutable(root
, parent_path
->parent
, path
, pool
));
1901 SVN_ERR(svn_fs_fs__dag_make_dir(&sub_dir
,
1902 parent_path
->parent
->node
,
1903 parent_path_path(parent_path
->parent
,
1909 /* Add this directory to the path cache. */
1910 dag_node_cache_set(root
, parent_path_path(parent_path
, pool
), sub_dir
, pool
);
1912 /* Make a record of this modification in the changes table. */
1913 SVN_ERR(add_change(root
->fs
, txn_id
, path
, svn_fs_fs__dag_get_id(sub_dir
),
1914 svn_fs_path_change_add
, FALSE
, FALSE
, SVN_INVALID_REVNUM
,
1917 return SVN_NO_ERROR
;
1921 /* Delete the node at PATH under ROOT. ROOT must be a transaction
1922 root. Perform temporary allocations in POOL. */
1923 static svn_error_t
*
1924 fs_delete_node(svn_fs_root_t
*root
,
1928 parent_path_t
*parent_path
;
1929 const char *txn_id
= root
->txn
;
1930 apr_int64_t mergeinfo_count
;
1932 if (! root
->is_txn_root
)
1933 return SVN_FS__NOT_TXN(root
);
1935 SVN_ERR(open_path(&parent_path
, root
, path
, 0, txn_id
, pool
));
1937 /* We can't remove the root of the filesystem. */
1938 if (! parent_path
->parent
)
1939 return svn_error_create(SVN_ERR_FS_ROOT_DIR
, NULL
,
1940 _("The root directory cannot be deleted"));
1942 /* Check to see if path (or any child thereof) is locked; if so,
1943 check that we can use the existing lock(s). */
1944 if (root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
1945 SVN_ERR(svn_fs_fs__allow_locked_operation(path
, root
->fs
, TRUE
, FALSE
,
1948 /* Make the parent directory mutable, and do the deletion. */
1949 SVN_ERR(make_path_mutable(root
, parent_path
->parent
, path
, pool
));
1950 if (svn_fs_fs__fs_supports_mergeinfo(root
->fs
))
1951 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_count
,
1954 SVN_ERR(svn_fs_fs__dag_delete(parent_path
->parent
->node
,
1958 /* Remove this node and any children from the path cache. */
1959 dag_node_cache_invalidate(root
, parent_path_path(parent_path
, pool
));
1961 /* Update mergeinfo counts for parents */
1962 if (svn_fs_fs__fs_supports_mergeinfo(root
->fs
) && mergeinfo_count
> 0)
1963 SVN_ERR(increment_mergeinfo_up_tree(parent_path
->parent
,
1967 /* Make a record of this modification in the changes table. */
1968 SVN_ERR(add_change(root
->fs
, txn_id
, path
,
1969 svn_fs_fs__dag_get_id(parent_path
->node
),
1970 svn_fs_path_change_delete
, FALSE
, FALSE
,
1971 SVN_INVALID_REVNUM
, NULL
, pool
));
1973 return SVN_NO_ERROR
;
1977 /* Set *SAME_P to TRUE if FS1 and FS2 have the same UUID, else set to FALSE.
1978 Use POOL for temporary allocation only.
1979 Note: this code is duplicated between libsvn_fs_fs and libsvn_fs_base. */
1980 static svn_error_t
*
1981 fs_same_p(svn_boolean_t
*same_p
,
1989 /* Random thought: if fetching UUIDs to compare filesystems is too
1990 expensive, one solution would be to cache the UUID in each fs
1991 object (copying the UUID into fs->pool, of course). */
1993 SVN_ERR(fs1
->vtable
->get_uuid(fs1
, &uuid1
, pool
));
1994 SVN_ERR(fs2
->vtable
->get_uuid(fs2
, &uuid2
, pool
));
1996 *same_p
= ! strcmp(uuid1
, uuid2
);
1997 return SVN_NO_ERROR
;
2000 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
2001 TO_ROOT. If PRESERVE_HISTORY is set, then the copy is recorded in
2002 the copies table. Perform temporary allocations in POOL. */
2003 static svn_error_t
*
2004 copy_helper(svn_fs_root_t
*from_root
,
2005 const char *from_path
,
2006 svn_fs_root_t
*to_root
,
2007 const char *to_path
,
2008 svn_boolean_t preserve_history
,
2011 dag_node_t
*from_node
;
2012 parent_path_t
*to_parent_path
;
2013 const char *txn_id
= to_root
->txn
;
2014 svn_boolean_t same_p
;
2016 /* Use an error check, not an assert, because even the caller cannot
2017 guarantee that a filesystem's UUID has not changed "on the fly". */
2018 SVN_ERR(fs_same_p(&same_p
, from_root
->fs
, to_root
->fs
, pool
));
2020 return svn_error_createf
2021 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
2022 _("Cannot copy between two different filesystems ('%s' and '%s')"),
2023 from_root
->fs
->path
, to_root
->fs
->path
);
2025 if (from_root
->is_txn_root
)
2026 return svn_error_create
2027 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
2028 _("Copy from mutable tree not currently supported"));
2030 /* Get the NODE for FROM_PATH in FROM_ROOT.*/
2031 SVN_ERR(get_dag(&from_node
, from_root
, from_path
, pool
));
2033 /* Build up the parent path from TO_PATH in TO_ROOT. If the last
2034 component does not exist, it's not that big a deal. We'll just
2036 SVN_ERR(open_path(&to_parent_path
, to_root
, to_path
,
2037 open_path_last_optional
, txn_id
, pool
));
2039 /* Check to see if path (or any child thereof) is locked; if so,
2040 check that we can use the existing lock(s). */
2041 if (to_root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
2042 SVN_ERR(svn_fs_fs__allow_locked_operation(to_path
, to_root
->fs
,
2043 TRUE
, FALSE
, pool
));
2045 /* If the destination node already exists as the same node as the
2046 source (in other words, this operation would result in nothing
2047 happening at all), just do nothing an return successfully,
2048 proud that you saved yourself from a tiresome task. */
2049 if (to_parent_path
->node
&&
2050 svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(from_node
),
2051 svn_fs_fs__dag_get_id(to_parent_path
->node
)))
2052 return SVN_NO_ERROR
;
2054 if (! from_root
->is_txn_root
)
2056 svn_fs_path_change_kind_t kind
;
2057 dag_node_t
*new_node
;
2058 const char *from_canonpath
;
2059 apr_int64_t mergeinfo_start
;
2060 apr_int64_t mergeinfo_end
;
2062 /* If TO_PATH already existed prior to the copy, note that this
2063 operation is a replacement, not an addition. */
2064 if (to_parent_path
->node
)
2066 kind
= svn_fs_path_change_replace
;
2067 if (svn_fs_fs__fs_supports_mergeinfo(to_root
->fs
))
2068 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start
,
2069 to_parent_path
->node
,
2074 kind
= svn_fs_path_change_add
;
2075 mergeinfo_start
= 0;
2078 if (svn_fs_fs__fs_supports_mergeinfo(to_root
->fs
))
2079 SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_end
,
2082 /* Make sure the target node's parents are mutable. */
2083 SVN_ERR(make_path_mutable(to_root
, to_parent_path
->parent
,
2086 /* Canonicalize the copyfrom path. */
2087 from_canonpath
= svn_fs__canonicalize_abspath(from_path
, pool
);
2089 SVN_ERR(svn_fs_fs__dag_copy(to_parent_path
->parent
->node
,
2090 to_parent_path
->entry
,
2097 if (kind
== svn_fs_path_change_replace
)
2098 dag_node_cache_invalidate(to_root
, parent_path_path(to_parent_path
,
2101 if (svn_fs_fs__fs_supports_mergeinfo(to_root
->fs
)
2102 && mergeinfo_start
!= mergeinfo_end
)
2103 SVN_ERR(increment_mergeinfo_up_tree(to_parent_path
->parent
,
2104 mergeinfo_end
- mergeinfo_start
,
2107 /* Make a record of this modification in the changes table. */
2108 SVN_ERR(get_dag(&new_node
, to_root
, to_path
, pool
));
2109 SVN_ERR(add_change(to_root
->fs
, txn_id
, to_path
,
2110 svn_fs_fs__dag_get_id(new_node
), kind
, FALSE
, FALSE
,
2111 from_root
->rev
, from_canonpath
, pool
));
2115 /* See IZ Issue #436 */
2116 /* Copying from transaction roots not currently available.
2118 ### cmpilato todo someday: make this not so. :-) Note that
2119 when copying from mutable trees, you have to make sure that
2120 you aren't creating a cyclic graph filesystem, and a simple
2121 referencing operation won't cut it. Currently, we should not
2122 be able to reach this clause, and the interface reports that
2123 this only works from immutable trees anyway, but JimB has
2124 stated that this requirement need not be necessary in the
2130 return SVN_NO_ERROR
;
2134 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2135 If FROM_PATH is a directory, copy it recursively. Temporary
2136 allocations are from POOL.*/
2137 static svn_error_t
*
2138 fs_copy(svn_fs_root_t
*from_root
,
2139 const char *from_path
,
2140 svn_fs_root_t
*to_root
,
2141 const char *to_path
,
2144 return copy_helper(from_root
, from_path
, to_root
, to_path
, TRUE
, pool
);
2148 /* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT.
2149 If FROM_PATH is a directory, copy it recursively. No history is
2150 preserved. Temporary allocations are from POOL. */
2151 static svn_error_t
*
2152 fs_revision_link(svn_fs_root_t
*from_root
,
2153 svn_fs_root_t
*to_root
,
2157 if (! to_root
->is_txn_root
)
2158 return SVN_FS__NOT_TXN(to_root
);
2160 return copy_helper(from_root
, path
, to_root
, path
, FALSE
, pool
);
2164 /* Discover the copy ancestry of PATH under ROOT. Return a relevant
2165 ancestor/revision combination in *PATH_P and *REV_P. Temporary
2166 allocations are in POOL. */
2167 static svn_error_t
*
2168 fs_copied_from(svn_revnum_t
*rev_p
,
2169 const char **path_p
,
2170 svn_fs_root_t
*root
,
2175 const char *copyfrom_path
, *copyfrom_str
= NULL
;
2176 svn_revnum_t copyfrom_rev
;
2177 char *str
, *last_str
, *buf
;
2179 /* Check to see if there is a cached version of this copyfrom
2181 if (! root
->is_txn_root
) {
2182 fs_rev_root_data_t
*frd
= root
->fsap_data
;
2183 copyfrom_str
= apr_hash_get(frd
->copyfrom_cache
, path
, APR_HASH_KEY_STRING
);
2188 if (strlen(copyfrom_str
) == 0)
2190 /* We have a cached entry that says there is no copyfrom
2192 copyfrom_rev
= SVN_INVALID_REVNUM
;
2193 copyfrom_path
= NULL
;
2197 /* Parse the copyfrom string for our cached entry. */
2198 buf
= apr_pstrdup(pool
, copyfrom_str
);
2199 str
= apr_strtok(buf
, " ", &last_str
);
2200 copyfrom_rev
= atol(str
);
2201 copyfrom_path
= last_str
;
2206 /* There is no cached entry, look it up the old-fashioned
2208 SVN_ERR(get_dag(&node
, root
, path
, pool
));
2209 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(©from_rev
, node
, pool
));
2210 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©from_path
, node
, pool
));
2213 *rev_p
= copyfrom_rev
;
2214 *path_p
= copyfrom_path
;
2216 return SVN_NO_ERROR
;
2223 /* Create the empty file PATH under ROOT. Temporary allocations are
2225 static svn_error_t
*
2226 fs_make_file(svn_fs_root_t
*root
,
2230 parent_path_t
*parent_path
;
2232 const char *txn_id
= root
->txn
;
2234 SVN_ERR(open_path(&parent_path
, root
, path
, open_path_last_optional
,
2237 /* If there's already a file by that name, complain.
2238 This also catches the case of trying to make a file named `/'. */
2239 if (parent_path
->node
)
2240 return SVN_FS__ALREADY_EXISTS(root
, path
);
2242 /* Check (non-recursively) to see if path is locked; if so, check
2243 that we can use it. */
2244 if (root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
2245 SVN_ERR(svn_fs_fs__allow_locked_operation(path
, root
->fs
, FALSE
, FALSE
,
2248 /* Create the file. */
2249 SVN_ERR(make_path_mutable(root
, parent_path
->parent
, path
, pool
));
2250 SVN_ERR(svn_fs_fs__dag_make_file(&child
,
2251 parent_path
->parent
->node
,
2252 parent_path_path(parent_path
->parent
,
2258 /* Add this file to the path cache. */
2259 dag_node_cache_set(root
, parent_path_path(parent_path
, pool
), child
, pool
);
2261 /* Make a record of this modification in the changes table. */
2262 SVN_ERR(add_change(root
->fs
, txn_id
, path
, svn_fs_fs__dag_get_id(child
),
2263 svn_fs_path_change_add
, TRUE
, FALSE
, SVN_INVALID_REVNUM
,
2266 return SVN_NO_ERROR
;
2270 /* Set *LENGTH_P to the size of the file PATH under ROOT. Temporary
2271 allocations are in POOL. */
2272 static svn_error_t
*
2273 fs_file_length(svn_filesize_t
*length_p
,
2274 svn_fs_root_t
*root
,
2280 /* First create a dag_node_t from the root/path pair. */
2281 SVN_ERR(get_dag(&file
, root
, path
, pool
));
2283 /* Now fetch its length */
2284 SVN_ERR(svn_fs_fs__dag_file_length(length_p
, file
, pool
));
2286 return SVN_NO_ERROR
;
2290 /* Set DIGEST to the MD5 checksum of PATH under ROOT. Temporary
2291 allocations are from POOL. */
2292 static svn_error_t
*
2293 fs_file_md5_checksum(unsigned char digest
[],
2294 svn_fs_root_t
*root
,
2300 SVN_ERR(get_dag(&file
, root
, path
, pool
));
2301 return svn_fs_fs__dag_file_checksum(digest
, file
, pool
);
2305 /* --- Machinery for svn_fs_file_contents() --- */
2307 /* Set *CONTENTS to a readable stream that will return the contents of
2308 PATH under ROOT. The stream is allocated in POOL. */
2309 static svn_error_t
*
2310 fs_file_contents(svn_stream_t
**contents
,
2311 svn_fs_root_t
*root
,
2316 svn_stream_t
*file_stream
;
2318 /* First create a dag_node_t from the root/path pair. */
2319 SVN_ERR(get_dag(&node
, root
, path
, pool
));
2321 /* Then create a readable stream from the dag_node_t. */
2322 SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream
, node
, pool
));
2324 *contents
= file_stream
;
2325 return SVN_NO_ERROR
;
2328 /* --- End machinery for svn_fs_file_contents() --- */
2332 /* --- Machinery for svn_fs_apply_textdelta() --- */
2335 /* Local baton type for all the helper functions below. */
2336 typedef struct txdelta_baton_t
2338 /* This is the custom-built window consumer given to us by the delta
2339 library; it uniquely knows how to read data from our designated
2340 "source" stream, interpret the window, and write data to our
2341 designated "target" stream (in this case, our repos file.) */
2342 svn_txdelta_window_handler_t interpreter
;
2343 void *interpreter_baton
;
2345 /* The original file info */
2346 svn_fs_root_t
*root
;
2349 /* Derived from the file info */
2352 svn_stream_t
*source_stream
;
2353 svn_stream_t
*target_stream
;
2354 svn_stream_t
*string_stream
;
2355 svn_stringbuf_t
*target_string
;
2357 /* Hex MD5 digest for the base text against which a delta is to be
2358 applied, and for the resultant fulltext, respectively. Either or
2359 both may be null, in which case ignored. */
2360 const char *base_checksum
;
2361 const char *result_checksum
;
2363 /* Pool used by db txns */
2369 /* ### see comment in window_consumer() regarding this function. */
2371 /* Helper function of generic type `svn_write_fn_t'. Implements a
2372 writable stream which appends to an svn_stringbuf_t. */
2373 static svn_error_t
*
2374 write_to_string(void *baton
, const char *data
, apr_size_t
*len
)
2376 txdelta_baton_t
*tb
= (txdelta_baton_t
*) baton
;
2377 svn_stringbuf_appendbytes(tb
->target_string
, data
, *len
);
2378 return SVN_NO_ERROR
;
2383 /* The main window handler returned by svn_fs_apply_textdelta. */
2384 static svn_error_t
*
2385 window_consumer(svn_txdelta_window_t
*window
, void *baton
)
2387 txdelta_baton_t
*tb
= (txdelta_baton_t
*) baton
;
2389 /* Send the window right through to the custom window interpreter.
2390 In theory, the interpreter will then write more data to
2391 cb->target_string. */
2392 SVN_ERR(tb
->interpreter(window
, tb
->interpreter_baton
));
2394 /* ### the write_to_string() callback for the txdelta's output stream
2395 ### should be doing all the flush determination logic, not here.
2396 ### in a drastic case, a window could generate a LOT more than the
2397 ### maximum buffer size. we want to flush to the underlying target
2398 ### stream much sooner (e.g. also in a streamy fashion). also, by
2399 ### moving this logic inside the stream, the stream becomes nice
2400 ### and encapsulated: it holds all the logic about buffering and
2403 ### further: I believe the buffering should be removed from tree.c
2404 ### the buffering should go into the target_stream itself, which
2405 ### is defined by reps-string.c. Specifically, I think the
2406 ### rep_write_contents() function will handle the buffering and
2407 ### the spill to the underlying DB. by locating it there, then
2408 ### anybody who gets a writable stream for FS content can take
2409 ### advantage of the buffering capability. this will be important
2410 ### when we export an FS API function for writing a fulltext into
2411 ### the FS, rather than forcing that fulltext thru apply_textdelta.
2414 /* Check to see if we need to purge the portion of the contents that
2415 have been written thus far. */
2416 if ((! window
) || (tb
->target_string
->len
> WRITE_BUFFER_SIZE
))
2418 apr_size_t len
= tb
->target_string
->len
;
2419 SVN_ERR(svn_stream_write(tb
->target_stream
,
2420 tb
->target_string
->data
,
2422 svn_stringbuf_set(tb
->target_string
, "");
2425 /* Is the window NULL? If so, we're done. */
2428 /* Close the internal-use stream. ### This used to be inside of
2429 txn_body_fulltext_finalize_edits(), but that invoked a nested
2430 Berkeley DB transaction -- scandalous! */
2431 SVN_ERR(svn_stream_close(tb
->target_stream
));
2433 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb
->node
, tb
->result_checksum
,
2437 return SVN_NO_ERROR
;
2440 /* Helper function for fs_apply_textdelta. BATON is of type
2442 static svn_error_t
*
2443 apply_textdelta(void *baton
, apr_pool_t
*pool
)
2445 txdelta_baton_t
*tb
= (txdelta_baton_t
*) baton
;
2446 parent_path_t
*parent_path
;
2447 const char *txn_id
= tb
->root
->txn
;
2449 /* Call open_path with no flags, as we want this to return an error
2450 if the node for which we are searching doesn't exist. */
2451 SVN_ERR(open_path(&parent_path
, tb
->root
, tb
->path
, 0, txn_id
, pool
));
2453 /* Check (non-recursively) to see if path is locked; if so, check
2454 that we can use it. */
2455 if (tb
->root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
2456 SVN_ERR(svn_fs_fs__allow_locked_operation(tb
->path
, tb
->root
->fs
,
2457 FALSE
, FALSE
, pool
));
2459 /* Now, make sure this path is mutable. */
2460 SVN_ERR(make_path_mutable(tb
->root
, parent_path
, tb
->path
, pool
));
2461 tb
->node
= parent_path
->node
;
2463 if (tb
->base_checksum
)
2465 unsigned char digest
[APR_MD5_DIGESTSIZE
];
2468 /* Until we finalize the node, its data_key points to the old
2469 contents, in other words, the base text. */
2470 SVN_ERR(svn_fs_fs__dag_file_checksum(digest
, tb
->node
, pool
));
2471 hex
= svn_md5_digest_to_cstring(digest
, pool
);
2472 if (hex
&& (strcmp(tb
->base_checksum
, hex
) != 0))
2473 return svn_error_createf
2474 (SVN_ERR_CHECKSUM_MISMATCH
,
2476 _("Base checksum mismatch on '%s':\n"
2479 tb
->path
, tb
->base_checksum
, hex
);
2482 /* Make a readable "source" stream out of the current contents of
2483 ROOT/PATH; obviously, this must done in the context of a db_txn.
2484 The stream is returned in tb->source_stream. */
2485 SVN_ERR(svn_fs_fs__dag_get_contents(&(tb
->source_stream
),
2486 tb
->node
, tb
->pool
));
2488 /* Make a writable "target" stream */
2489 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb
->target_stream
), tb
->node
,
2492 /* Make a writable "string" stream which writes data to
2493 tb->target_string. */
2494 tb
->target_string
= svn_stringbuf_create("", tb
->pool
);
2495 tb
->string_stream
= svn_stream_create(tb
, tb
->pool
);
2496 svn_stream_set_write(tb
->string_stream
, write_to_string
);
2498 /* Now, create a custom window handler that uses our two streams. */
2499 svn_txdelta_apply(tb
->source_stream
,
2505 &(tb
->interpreter_baton
));
2507 /* Make a record of this modification in the changes table. */
2508 SVN_ERR(add_change(tb
->root
->fs
, txn_id
, tb
->path
,
2509 svn_fs_fs__dag_get_id(tb
->node
),
2510 svn_fs_path_change_modify
, TRUE
, FALSE
, SVN_INVALID_REVNUM
,
2513 return SVN_NO_ERROR
;
2517 /* Set *CONTENTS_P and *CONTENTS_BATON_P to a window handler and baton
2518 that will accept text delta windows to modify the contents of PATH
2519 under ROOT. Allocations are in POOL. */
2520 static svn_error_t
*
2521 fs_apply_textdelta(svn_txdelta_window_handler_t
*contents_p
,
2522 void **contents_baton_p
,
2523 svn_fs_root_t
*root
,
2525 const char *base_checksum
,
2526 const char *result_checksum
,
2529 txdelta_baton_t
*tb
= apr_pcalloc(pool
, sizeof(*tb
));
2536 tb
->base_checksum
= apr_pstrdup(pool
, base_checksum
);
2538 tb
->base_checksum
= NULL
;
2540 if (result_checksum
)
2541 tb
->result_checksum
= apr_pstrdup(pool
, result_checksum
);
2543 tb
->result_checksum
= NULL
;
2546 SVN_ERR(apply_textdelta(tb
, pool
));
2548 *contents_p
= window_consumer
;
2549 *contents_baton_p
= tb
;
2550 return SVN_NO_ERROR
;
2553 /* --- End machinery for svn_fs_apply_textdelta() --- */
2555 /* --- Machinery for svn_fs_apply_text() --- */
2557 /* Baton for svn_fs_apply_text(). */
2560 /* The original file info */
2561 svn_fs_root_t
*root
;
2564 /* Derived from the file info */
2567 /* The returned stream that will accept the file's new contents. */
2568 svn_stream_t
*stream
;
2570 /* The actual fs stream that the returned stream will write to. */
2571 svn_stream_t
*file_stream
;
2573 /* Hex MD5 digest for the final fulltext written to the file. May
2574 be null, in which case ignored. */
2575 const char *result_checksum
;
2577 /* Pool used by db txns */
2582 /* A wrapper around svn_fs_fs__dag_finalize_edits, but for
2583 * fulltext data, not text deltas. Closes BATON->file_stream.
2585 * Note: If you're confused about how this function relates to another
2586 * of similar name, think of it this way:
2588 * svn_fs_apply_textdelta() ==> ... ==> txn_body_txdelta_finalize_edits()
2589 * svn_fs_apply_text() ==> ... ==> txn_body_fulltext_finalize_edits()
2592 /* Write function for the publically returned stream. */
2593 static svn_error_t
*
2594 text_stream_writer(void *baton
,
2598 struct text_baton_t
*tb
= baton
;
2600 /* Psst, here's some data. Pass it on to the -real- file stream. */
2601 return svn_stream_write(tb
->file_stream
, data
, len
);
2604 /* Close function for the publically returned stream. */
2605 static svn_error_t
*
2606 text_stream_closer(void *baton
)
2608 struct text_baton_t
*tb
= baton
;
2610 /* Close the internal-use stream. ### This used to be inside of
2611 txn_body_fulltext_finalize_edits(), but that invoked a nested
2612 Berkeley DB transaction -- scandalous! */
2613 SVN_ERR(svn_stream_close(tb
->file_stream
));
2615 /* Need to tell fs that we're done sending text */
2616 SVN_ERR(svn_fs_fs__dag_finalize_edits(tb
->node
, tb
->result_checksum
,
2619 return SVN_NO_ERROR
;
2623 /* Helper function for fs_apply_text. BATON is of type
2625 static svn_error_t
*
2626 apply_text(void *baton
, apr_pool_t
*pool
)
2628 struct text_baton_t
*tb
= baton
;
2629 parent_path_t
*parent_path
;
2630 const char *txn_id
= tb
->root
->txn
;
2632 /* Call open_path with no flags, as we want this to return an error
2633 if the node for which we are searching doesn't exist. */
2634 SVN_ERR(open_path(&parent_path
, tb
->root
, tb
->path
, 0, txn_id
, pool
));
2636 /* Check (non-recursively) to see if path is locked; if so, check
2637 that we can use it. */
2638 if (tb
->root
->txn_flags
& SVN_FS_TXN_CHECK_LOCKS
)
2639 SVN_ERR(svn_fs_fs__allow_locked_operation(tb
->path
, tb
->root
->fs
,
2640 FALSE
, FALSE
, pool
));
2642 /* Now, make sure this path is mutable. */
2643 SVN_ERR(make_path_mutable(tb
->root
, parent_path
, tb
->path
, pool
));
2644 tb
->node
= parent_path
->node
;
2646 /* Make a writable stream for replacing the file's text. */
2647 SVN_ERR(svn_fs_fs__dag_get_edit_stream(&(tb
->file_stream
), tb
->node
,
2650 /* Create a 'returnable' stream which writes to the file_stream. */
2651 tb
->stream
= svn_stream_create(tb
, tb
->pool
);
2652 svn_stream_set_write(tb
->stream
, text_stream_writer
);
2653 svn_stream_set_close(tb
->stream
, text_stream_closer
);
2655 /* Make a record of this modification in the changes table. */
2656 SVN_ERR(add_change(tb
->root
->fs
, txn_id
, tb
->path
,
2657 svn_fs_fs__dag_get_id(tb
->node
),
2658 svn_fs_path_change_modify
, TRUE
, FALSE
, SVN_INVALID_REVNUM
,
2661 return SVN_NO_ERROR
;
2665 /* Return a writable stream that will set the contents of PATH under
2666 ROOT. RESULT_CHECKSUM is the MD5 checksum of the final result.
2667 Temporary allocations are in POOL. */
2668 static svn_error_t
*
2669 fs_apply_text(svn_stream_t
**contents_p
,
2670 svn_fs_root_t
*root
,
2672 const char *result_checksum
,
2675 struct text_baton_t
*tb
= apr_pcalloc(pool
, sizeof(*tb
));
2681 if (result_checksum
)
2682 tb
->result_checksum
= apr_pstrdup(pool
, result_checksum
);
2684 tb
->result_checksum
= NULL
;
2686 SVN_ERR(apply_text(tb
, pool
));
2688 *contents_p
= tb
->stream
;
2689 return SVN_NO_ERROR
;
2692 /* --- End machinery for svn_fs_apply_text() --- */
2695 /* Check if the contents of PATH1 under ROOT1 are different from the
2696 contents of PATH2 under ROOT2. If they are different set
2697 *CHANGED_P to TRUE, otherwise set it to FALSE. */
2698 static svn_error_t
*
2699 fs_contents_changed(svn_boolean_t
*changed_p
,
2700 svn_fs_root_t
*root1
,
2702 svn_fs_root_t
*root2
,
2706 dag_node_t
*node1
, *node2
;
2708 /* Check that roots are in the same fs. */
2709 if (root1
->fs
!= root2
->fs
)
2710 return svn_error_create
2711 (SVN_ERR_FS_GENERAL
, NULL
,
2712 _("Cannot compare file contents between two different filesystems"));
2714 /* Check that both paths are files. */
2716 svn_node_kind_t kind
;
2718 SVN_ERR(svn_fs_fs__check_path(&kind
, root1
, path1
, pool
));
2719 if (kind
!= svn_node_file
)
2720 return svn_error_createf
2721 (SVN_ERR_FS_GENERAL
, NULL
, _("'%s' is not a file"), path1
);
2723 SVN_ERR(svn_fs_fs__check_path(&kind
, root2
, path2
, pool
));
2724 if (kind
!= svn_node_file
)
2725 return svn_error_createf
2726 (SVN_ERR_FS_GENERAL
, NULL
, _("'%s' is not a file"), path2
);
2729 SVN_ERR(get_dag(&node1
, root1
, path1
, pool
));
2730 SVN_ERR(get_dag(&node2
, root2
, path2
, pool
));
2731 SVN_ERR(svn_fs_fs__dag_things_different(NULL
, changed_p
,
2732 node1
, node2
, pool
));
2734 return SVN_NO_ERROR
;
2739 /* Public interface to computing file text deltas. */
2741 static svn_error_t
*
2742 fs_get_file_delta_stream(svn_txdelta_stream_t
**stream_p
,
2743 svn_fs_root_t
*source_root
,
2744 const char *source_path
,
2745 svn_fs_root_t
*target_root
,
2746 const char *target_path
,
2749 dag_node_t
*source_node
, *target_node
;
2751 if (source_root
&& source_path
)
2752 SVN_ERR(get_dag(&source_node
, source_root
, source_path
, pool
));
2755 SVN_ERR(get_dag(&target_node
, target_root
, target_path
, pool
));
2757 /* Create a delta stream that turns the source into the target. */
2758 SVN_ERR(svn_fs_fs__dag_get_file_delta_stream(stream_p
, source_node
,
2759 target_node
, pool
));
2761 return SVN_NO_ERROR
;
2766 /* Finding Changes */
2768 /* Set *CHANGED_PATHS_P to a newly allocated hash containing
2769 descriptions of the paths changed under ROOT. The hash is keyed
2770 with const char * paths an dhas svn_fs_path_change_t * values. Use
2771 POOL for all allocations. */
2772 static svn_error_t
*
2773 fs_paths_changed(apr_hash_t
**changed_paths_p
,
2774 svn_fs_root_t
*root
,
2777 if (root
->is_txn_root
)
2778 return svn_fs_fs__txn_changes_fetch(changed_paths_p
, root
->fs
, root
->txn
,
2782 fs_rev_root_data_t
*frd
= root
->fsap_data
;
2783 return svn_fs_fs__paths_changed(changed_paths_p
, root
->fs
, root
->rev
,
2784 frd
->copyfrom_cache
, pool
);
2790 /* Our coolio opaque history object. */
2793 /* filesystem object */
2796 /* path and revision of historical location */
2798 svn_revnum_t revision
;
2800 /* internal-use hints about where to resume the history search. */
2801 const char *path_hint
;
2802 svn_revnum_t rev_hint
;
2804 /* FALSE until the first call to svn_fs_history_prev(). */
2805 svn_boolean_t is_interesting
;
2806 } fs_history_data_t
;
2808 static svn_fs_history_t
*
2809 assemble_history(svn_fs_t
*fs
,
2811 svn_revnum_t revision
,
2812 svn_boolean_t is_interesting
,
2813 const char *path_hint
,
2814 svn_revnum_t rev_hint
,
2818 /* Set *HISTORY_P to an opaque node history object which represents
2819 PATH under ROOT. ROOT must be a revision root. Use POOL for all
2821 static svn_error_t
*
2822 fs_node_history(svn_fs_history_t
**history_p
,
2823 svn_fs_root_t
*root
,
2827 svn_node_kind_t kind
;
2829 /* We require a revision root. */
2830 if (root
->is_txn_root
)
2831 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT
, NULL
, NULL
);
2833 /* And we require that the path exist in the root. */
2834 SVN_ERR(svn_fs_fs__check_path(&kind
, root
, path
, pool
));
2835 if (kind
== svn_node_none
)
2836 return SVN_FS__NOT_FOUND(root
, path
);
2838 /* Okay, all seems well. Build our history object and return it. */
2839 *history_p
= assemble_history(root
->fs
,
2840 svn_fs__canonicalize_abspath(path
, pool
),
2841 root
->rev
, FALSE
, NULL
,
2842 SVN_INVALID_REVNUM
, pool
);
2843 return SVN_NO_ERROR
;
2846 /* Find the youngest copyroot for path PARENT_PATH or its parents in
2847 filesystem FS, and store the copyroot in *REV_P and *PATH_P.
2848 Perform all allocations in POOL. */
2849 static svn_error_t
*
2850 find_youngest_copyroot(svn_revnum_t
*rev_p
,
2851 const char **path_p
,
2853 parent_path_t
*parent_path
,
2856 svn_revnum_t rev_mine
, rev_parent
= -1;
2857 const char *path_mine
, *path_parent
;
2859 /* First find our parent's youngest copyroot. */
2860 if (parent_path
->parent
)
2861 SVN_ERR(find_youngest_copyroot(&rev_parent
, &path_parent
, fs
,
2862 parent_path
->parent
, pool
));
2864 /* Find our copyroot. */
2865 SVN_ERR(svn_fs_fs__dag_get_copyroot(&rev_mine
, &path_mine
,
2866 parent_path
->node
, pool
));
2868 /* If a parent and child were copied to in the same revision, prefer
2869 the child copy target, since it is the copy relevant to the
2870 history of the child. */
2871 if (rev_mine
>= rev_parent
)
2874 *path_p
= path_mine
;
2878 *rev_p
= rev_parent
;
2879 *path_p
= path_parent
;
2882 return SVN_NO_ERROR
;
2886 static svn_error_t
*fs_closest_copy(svn_fs_root_t
**root_p
,
2887 const char **path_p
,
2888 svn_fs_root_t
*root
,
2892 svn_fs_t
*fs
= root
->fs
;
2893 parent_path_t
*parent_path
, *copy_dst_parent_path
;
2894 svn_revnum_t copy_dst_rev
, created_rev
;
2895 const char *copy_dst_path
;
2896 svn_fs_root_t
*copy_dst_root
;
2897 dag_node_t
*copy_dst_node
;
2898 svn_node_kind_t kind
;
2900 /* Initialize return values. */
2904 SVN_ERR(open_path(&parent_path
, root
, path
, 0, NULL
, pool
));
2906 /* Find the youngest copyroot in the path of this node-rev, which
2907 will indicate the target of the innermost copy affecting the
2909 SVN_ERR(find_youngest_copyroot(©_dst_rev
, ©_dst_path
,
2910 fs
, parent_path
, pool
));
2911 if (copy_dst_rev
== 0) /* There are no copies affecting this node-rev. */
2912 return SVN_NO_ERROR
;
2914 /* It is possible that this node was created from scratch at some
2915 revision between COPY_DST_REV and REV. Make sure that PATH
2916 exists as of COPY_DST_REV and is related to this node-rev. */
2917 SVN_ERR(svn_fs_fs__revision_root(©_dst_root
, fs
, copy_dst_rev
, pool
));
2918 SVN_ERR(svn_fs_fs__check_path(&kind
, copy_dst_root
, path
, pool
));
2919 if (kind
== svn_node_none
)
2920 return SVN_NO_ERROR
;
2921 SVN_ERR(open_path(©_dst_parent_path
, copy_dst_root
, path
,
2923 copy_dst_node
= copy_dst_parent_path
->node
;
2924 if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node
),
2925 svn_fs_fs__dag_get_id(parent_path
->node
)))
2926 return SVN_NO_ERROR
;
2928 /* One final check must be done here. If you copy a directory and
2929 create a new entity somewhere beneath that directory in the same
2930 txn, then we can't claim that the copy affected the new entity.
2931 For example, if you do:
2934 create dir2/new-thing
2937 then dir2/new-thing was not affected by the copy of dir1 to dir2.
2938 We detect this situation by asking if PATH@COPY_DST_REV's
2939 created-rev is COPY_DST_REV, and that node-revision has no
2940 predecessors, then there is no relevant closest copy.
2942 SVN_ERR(svn_fs_fs__dag_get_revision(&created_rev
, copy_dst_node
, pool
));
2943 if (created_rev
== copy_dst_rev
)
2945 const svn_fs_id_t
*pred
;
2946 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred
, copy_dst_node
, pool
));
2948 return SVN_NO_ERROR
;
2951 /* The copy destination checks out. Return it. */
2952 *root_p
= copy_dst_root
;
2953 *path_p
= copy_dst_path
;
2954 return SVN_NO_ERROR
;
2958 /* Set *PREV_PATH and *PREV_REV to the path and revision which
2959 represent the location at which PATH in FS was located immediately
2960 prior to REVISION iff there was a copy operation (to PATH or one of
2961 its parent directories) between that previous location and
2964 If there was no such copy operation in that portion of PATH's
2965 history, set *PREV_PATH to NULL and *PREV_REV to SVN_INVALID_REVNUM. */
2966 static svn_error_t
*
2967 prev_location(const char **prev_path
,
2968 svn_revnum_t
*prev_rev
,
2970 svn_fs_root_t
*root
,
2974 const char *copy_path
, *copy_src_path
, *remainder
= "";
2975 svn_fs_root_t
*copy_root
;
2976 svn_revnum_t copy_src_rev
;
2978 /* Ask about the most recent copy which affected PATH@REVISION. If
2979 there was no such copy, we're done. */
2980 SVN_ERR(fs_closest_copy(©_root
, ©_path
, root
, path
, pool
));
2983 *prev_rev
= SVN_INVALID_REVNUM
;
2985 return SVN_NO_ERROR
;
2988 /* Ultimately, it's not the path of the closest copy's source that
2989 we care about -- it's our own path's location in the copy source
2990 revision. So we'll tack the relative path that expresses the
2991 difference between the copy destination and our path in the copy
2992 revision onto the copy source path to determine this information.
2994 In other words, if our path is "/branches/my-branch/foo/bar", and
2995 we know that the closest relevant copy was a copy of "/trunk" to
2996 "/branches/my-branch", then that relative path under the copy
2997 destination is "/foo/bar". Tacking that onto the copy source
2998 path tells us that our path was located at "/trunk/foo/bar"
3001 SVN_ERR(fs_copied_from(©_src_rev
, ©_src_path
,
3002 copy_root
, copy_path
, pool
));
3003 if (strcmp(copy_path
, path
) != 0)
3004 remainder
= svn_path_is_child(copy_path
, path
, pool
);
3005 *prev_path
= svn_path_join(copy_src_path
, remainder
, pool
);
3006 *prev_rev
= copy_src_rev
;
3007 return SVN_NO_ERROR
;
3011 static svn_error_t
*
3012 fs_node_origin_rev(svn_revnum_t
*revision
,
3013 svn_fs_root_t
*root
,
3017 svn_fs_t
*fs
= root
->fs
;
3018 const svn_fs_id_t
*given_noderev_id
, *cached_origin_id
;
3019 const char *node_id
, *dash
;
3021 path
= svn_fs__canonicalize_abspath(path
, pool
);
3023 /* Check the cache first. */
3024 SVN_ERR(fs_node_id(&given_noderev_id
, root
, path
, pool
));
3025 node_id
= svn_fs_fs__id_node_id(given_noderev_id
);
3027 /* Is it a brand new uncommitted node? */
3028 if (node_id
[0] == '_')
3030 *revision
= SVN_INVALID_REVNUM
;
3031 return SVN_NO_ERROR
;
3034 /* Maybe this is a new-style node ID that just has the revision
3035 sitting right in it. */
3036 dash
= strchr(node_id
, '-');
3037 if (dash
&& *(dash
+1))
3039 *revision
= SVN_STR_TO_REV(dash
+ 1);
3040 return SVN_NO_ERROR
;
3043 /* OK, it's an old-style ID? Maybe it's cached. */
3044 SVN_ERR(svn_fs_fs__get_node_origin(&cached_origin_id
,
3048 if (cached_origin_id
!= NULL
)
3050 *revision
= svn_fs_fs__id_rev(cached_origin_id
);
3051 return SVN_NO_ERROR
;
3055 /* Ah well, the answer isn't in the ID itself or in the cache.
3056 Let's actually calculate it, then. */
3057 svn_fs_root_t
*curroot
= root
;
3058 apr_pool_t
*subpool
= svn_pool_create(pool
);
3059 apr_pool_t
*predidpool
= svn_pool_create(pool
);
3060 svn_stringbuf_t
*lastpath
= svn_stringbuf_create(path
, pool
);
3061 svn_revnum_t lastrev
= SVN_INVALID_REVNUM
;
3063 const svn_fs_id_t
*pred_id
;
3065 /* Walk the closest-copy chain back to the first copy in our history.
3067 NOTE: We merely *assume* that this is faster than walking the
3068 predecessor chain, because we *assume* that copies of parent
3069 directories happen less often than modifications to a given item. */
3072 svn_revnum_t currev
;
3073 const char *curpath
= lastpath
->data
;
3075 svn_pool_clear(subpool
);
3077 /* Get a root pointing to LASTREV. (The first time around,
3078 LASTREV is invalid, but that's cool because CURROOT is
3079 already initialized.) */
3080 if (SVN_IS_VALID_REVNUM(lastrev
))
3081 SVN_ERR(svn_fs_fs__revision_root(&curroot
, fs
, lastrev
, subpool
));
3083 /* Find the previous location using the closest-copy shortcut. */
3084 SVN_ERR(prev_location(&curpath
, &currev
, fs
, curroot
, curpath
,
3089 /* Update our LASTPATH and LASTREV variables (which survive
3091 svn_stringbuf_set(lastpath
, curpath
);
3095 /* Walk the predecessor links back to origin. */
3096 SVN_ERR(fs_node_id(&pred_id
, curroot
, lastpath
->data
, predidpool
));
3099 svn_pool_clear(subpool
);
3100 SVN_ERR(svn_fs_fs__dag_get_node(&node
, fs
, pred_id
, subpool
));
3101 svn_pool_clear(predidpool
);
3102 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id
, node
,
3106 /* When we get here, NODE should be the first node-revision in our
3108 SVN_ERR(svn_fs_fs__dag_get_revision(revision
, node
, pool
));
3110 /* Wow, I don't want to have to do all that again. Let's cache
3112 if (node_id
[0] != '_')
3113 SVN_ERR(svn_fs_fs__set_node_origin(fs
, node_id
,
3114 svn_fs_fs__dag_get_id(node
), pool
));
3116 svn_pool_destroy(subpool
);
3117 svn_pool_destroy(predidpool
);
3118 return SVN_NO_ERROR
;
3123 struct history_prev_args
3125 svn_fs_history_t
**prev_history_p
;
3126 svn_fs_history_t
*history
;
3127 svn_boolean_t cross_copies
;
3132 static svn_error_t
*
3133 history_prev(void *baton
, apr_pool_t
*pool
)
3135 struct history_prev_args
*args
= baton
;
3136 svn_fs_history_t
**prev_history
= args
->prev_history_p
;
3137 svn_fs_history_t
*history
= args
->history
;
3138 fs_history_data_t
*fhd
= history
->fsap_data
;
3139 const char *commit_path
, *src_path
, *path
= fhd
->path
;
3140 svn_revnum_t commit_rev
, src_rev
, dst_rev
;
3141 svn_revnum_t revision
= fhd
->revision
;
3142 apr_pool_t
*retpool
= args
->pool
;
3143 svn_fs_t
*fs
= fhd
->fs
;
3144 parent_path_t
*parent_path
;
3146 svn_fs_root_t
*root
;
3147 const svn_fs_id_t
*node_id
;
3148 svn_boolean_t reported
= fhd
->is_interesting
;
3149 svn_boolean_t retry
= FALSE
;
3150 svn_revnum_t copyroot_rev
;
3151 const char *copyroot_path
;
3153 /* Initialize our return value. */
3154 *prev_history
= NULL
;
3156 /* If our last history report left us hints about where to pickup
3157 the chase, then our last report was on the destination of a
3158 copy. If we are crossing copies, start from those locations,
3159 otherwise, we're all done here. */
3160 if (fhd
->path_hint
&& SVN_IS_VALID_REVNUM(fhd
->rev_hint
))
3163 if (! args
->cross_copies
)
3164 return SVN_NO_ERROR
;
3165 path
= fhd
->path_hint
;
3166 revision
= fhd
->rev_hint
;
3169 /* Construct a ROOT for the current revision. */
3170 SVN_ERR(svn_fs_fs__revision_root(&root
, fs
, revision
, pool
));
3172 /* Open PATH/REVISION, and get its node and a bunch of other
3174 SVN_ERR(open_path(&parent_path
, root
, path
, 0, NULL
, pool
));
3175 node
= parent_path
->node
;
3176 node_id
= svn_fs_fs__dag_get_id(node
);
3177 commit_path
= svn_fs_fs__dag_get_created_path(node
);
3178 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev
, node
, pool
));
3180 /* The Subversion filesystem is written in such a way that a given
3181 line of history may have at most one interesting history point
3182 per filesystem revision. Either that node was edited (and
3183 possibly copied), or it was copied but not edited. And a copy
3184 source cannot be from the same revision as its destination. So,
3185 if our history revision matches its node's commit revision, we
3187 if (revision
== commit_rev
)
3191 /* ... we either have not yet reported on this revision (and
3192 need now to do so) ... */
3193 *prev_history
= assemble_history(fs
,
3194 apr_pstrdup(retpool
, commit_path
),
3195 commit_rev
, TRUE
, NULL
,
3196 SVN_INVALID_REVNUM
, retpool
);
3197 return SVN_NO_ERROR
;
3201 /* ... or we *have* reported on this revision, and must now
3202 progress toward this node's predecessor (unless there is
3203 no predecessor, in which case we're all done!). */
3204 const svn_fs_id_t
*pred_id
;
3206 SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id
, node
, pool
));
3208 return SVN_NO_ERROR
;
3210 /* Replace NODE and friends with the information from its
3212 SVN_ERR(svn_fs_fs__dag_get_node(&node
, fs
, pred_id
, pool
));
3213 node_id
= svn_fs_fs__dag_get_id(node
);
3214 commit_path
= svn_fs_fs__dag_get_created_path(node
);
3215 SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev
, node
, pool
));
3219 /* Find the youngest copyroot in the path of this node, including
3221 SVN_ERR(find_youngest_copyroot(©root_rev
, ©root_path
, fs
,
3222 parent_path
, pool
));
3224 /* Initialize some state variables. */
3226 src_rev
= SVN_INVALID_REVNUM
;
3227 dst_rev
= SVN_INVALID_REVNUM
;
3229 if (copyroot_rev
> commit_rev
)
3231 const char *remainder
;
3232 const char *copy_dst
, *copy_src
;
3233 svn_fs_root_t
*copyroot_root
;
3235 SVN_ERR(svn_fs_fs__revision_root(©root_root
, fs
, copyroot_rev
,
3237 SVN_ERR(get_dag(&node
, copyroot_root
, copyroot_path
, pool
));
3238 copy_dst
= svn_fs_fs__dag_get_created_path(node
);
3240 /* If our current path was the very destination of the copy,
3241 then our new current path will be the copy source. If our
3242 current path was instead the *child* of the destination of
3243 the copy, then figure out its previous location by taking its
3244 path relative to the copy destination and appending that to
3245 the copy source. Finally, if our current path doesn't meet
3246 one of these other criteria ... ### for now just fallback to
3247 the old copy hunt algorithm. */
3248 if (strcmp(path
, copy_dst
) == 0)
3251 remainder
= svn_path_is_child(copy_dst
, path
, pool
);
3255 /* If we get here, then our current path is the destination
3256 of, or the child of the destination of, a copy. Fill
3257 in the return values and get outta here. */
3258 SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&src_rev
, node
, pool
));
3259 SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(©_src
, node
, pool
));
3261 dst_rev
= copyroot_rev
;
3262 src_path
= svn_path_join(copy_src
, remainder
, pool
);
3266 /* If we calculated a copy source path and revision, we'll make a
3267 'copy-style' history object. */
3268 if (src_path
&& SVN_IS_VALID_REVNUM(src_rev
))
3270 /* It's possible for us to find a copy location that is the same
3271 as the history point we've just reported. If that happens,
3272 we simply need to take another trip through this history
3274 if ((dst_rev
== revision
) && reported
)
3277 *prev_history
= assemble_history(fs
, apr_pstrdup(retpool
, path
),
3278 dst_rev
, retry
? FALSE
: TRUE
,
3279 src_path
, src_rev
, retpool
);
3283 *prev_history
= assemble_history(fs
, apr_pstrdup(retpool
, commit_path
),
3284 commit_rev
, TRUE
, NULL
,
3285 SVN_INVALID_REVNUM
, retpool
);
3288 return SVN_NO_ERROR
;
3292 /* Implement svn_fs_history_prev, set *PREV_HISTORY_P to a new
3293 svn_fs_history_t object that represents the predecessory of
3294 HISTORY. If CROSS_COPIES is true, *PREV_HISTORY_P may be related
3295 only through a copy operation. Perform all allocations in POOL. */
3296 static svn_error_t
*
3297 fs_history_prev(svn_fs_history_t
**prev_history_p
,
3298 svn_fs_history_t
*history
,
3299 svn_boolean_t cross_copies
,
3302 svn_fs_history_t
*prev_history
= NULL
;
3303 fs_history_data_t
*fhd
= history
->fsap_data
;
3304 svn_fs_t
*fs
= fhd
->fs
;
3306 /* Special case: the root directory changes in every single
3307 revision, no exceptions. And, the root can't be the target (or
3308 child of a target -- duh) of a copy. So, if that's our path,
3309 then we need only decrement our revision by 1, and there you go. */
3310 if (strcmp(fhd
->path
, "/") == 0)
3312 if (! fhd
->is_interesting
)
3313 prev_history
= assemble_history(fs
, "/", fhd
->revision
,
3314 1, NULL
, SVN_INVALID_REVNUM
, pool
);
3315 else if (fhd
->revision
> 0)
3316 prev_history
= assemble_history(fs
, "/", fhd
->revision
- 1,
3317 1, NULL
, SVN_INVALID_REVNUM
, pool
);
3321 struct history_prev_args args
;
3322 prev_history
= history
;
3326 args
.prev_history_p
= &prev_history
;
3327 args
.history
= prev_history
;
3328 args
.cross_copies
= cross_copies
;
3330 SVN_ERR(history_prev(&args
, pool
));
3334 fhd
= prev_history
->fsap_data
;
3335 if (fhd
->is_interesting
)
3340 *prev_history_p
= prev_history
;
3341 return SVN_NO_ERROR
;
3345 /* Set *PATH and *REVISION to the path and revision for the HISTORY
3346 object. Use POOL for all allocations. */
3347 static svn_error_t
*
3348 fs_history_location(const char **path
,
3349 svn_revnum_t
*revision
,
3350 svn_fs_history_t
*history
,
3353 fs_history_data_t
*fhd
= history
->fsap_data
;
3355 *path
= apr_pstrdup(pool
, fhd
->path
);
3356 *revision
= fhd
->revision
;
3357 return SVN_NO_ERROR
;
3360 static history_vtable_t history_vtable
= {
3365 /* Return a new history object (marked as "interesting") for PATH and
3366 REVISION, allocated in POOL, and with its members set to the values
3367 of the parameters provided. Note that PATH and PATH_HINT are not
3368 duped into POOL -- it is the responsibility of the caller to ensure
3369 that this happens. */
3370 static svn_fs_history_t
*
3371 assemble_history(svn_fs_t
*fs
,
3373 svn_revnum_t revision
,
3374 svn_boolean_t is_interesting
,
3375 const char *path_hint
,
3376 svn_revnum_t rev_hint
,
3379 svn_fs_history_t
*history
= apr_pcalloc(pool
, sizeof(*history
));
3380 fs_history_data_t
*fhd
= apr_pcalloc(pool
, sizeof(*fhd
));
3382 fhd
->revision
= revision
;
3383 fhd
->is_interesting
= is_interesting
;
3384 fhd
->path_hint
= path_hint
;
3385 fhd
->rev_hint
= rev_hint
;
3388 history
->vtable
= &history_vtable
;
3389 history
->fsap_data
= fhd
;
3394 /* mergeinfo queries */
3396 /* DIR_DAG is a directory DAG node which has mergeinfo in its
3397 descendants. This function iterates over its children. For each
3398 child with immediate mergeinfo, it adds its mergeinfo to
3399 RESULT_CATALOG. appropriate arguments. For each child with
3400 descendants with mergeinfo, it recurses. Note that it does *not*
3401 call the action on the path for DIR_DAG itself.
3403 POOL is used for temporary allocations, including the mergeinfo
3404 hashes passed to actions; RESULT_POOL is used for the mergeinfo added
3407 static svn_error_t
*
3408 crawl_directory_dag_for_mergeinfo(svn_fs_root_t
*root
,
3409 const char *this_path
,
3410 dag_node_t
*dir_dag
,
3411 svn_mergeinfo_catalog_t result_catalog
,
3413 apr_pool_t
*result_pool
)
3415 apr_hash_t
*entries
;
3416 apr_hash_index_t
*hi
;
3417 apr_pool_t
*iterpool
= svn_pool_create(pool
);
3419 SVN_ERR(svn_fs_fs__dag_dir_entries(&entries
, dir_dag
, pool
, pool
));
3421 for (hi
= apr_hash_first(pool
, entries
);
3423 hi
= apr_hash_next(hi
))
3426 svn_fs_dirent_t
*dirent
;
3427 const char *kid_path
;
3428 dag_node_t
*kid_dag
;
3429 svn_boolean_t has_mergeinfo
, go_down
;
3431 svn_pool_clear(iterpool
);
3433 apr_hash_this(hi
, NULL
, NULL
, &val
);
3435 kid_path
= svn_path_join(this_path
, dirent
->name
, iterpool
);
3436 SVN_ERR(get_dag(&kid_dag
, root
, kid_path
, iterpool
));
3438 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo
, kid_dag
, iterpool
));
3439 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down
, kid_dag
,
3444 /* Save this partisular node's mergeinfo. */
3445 apr_hash_t
*proplist
;
3446 svn_mergeinfo_t kid_mergeinfo
;
3447 svn_string_t
*mergeinfo_string
;
3449 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist
, kid_dag
, iterpool
));
3450 mergeinfo_string
= apr_hash_get(proplist
, SVN_PROP_MERGEINFO
,
3451 APR_HASH_KEY_STRING
);
3452 if (!mergeinfo_string
)
3454 svn_string_t
*idstr
= svn_fs_fs__id_unparse(dirent
->id
, iterpool
);
3455 return svn_error_createf
3456 (SVN_ERR_FS_CORRUPT
, NULL
,
3457 _("Node-revision #'%s' claims to have mergeinfo but doesn't"),
3461 SVN_ERR(svn_mergeinfo_parse(&kid_mergeinfo
,
3462 mergeinfo_string
->data
,
3465 apr_hash_set(result_catalog
,
3466 apr_pstrdup(result_pool
, kid_path
),
3467 APR_HASH_KEY_STRING
,
3472 SVN_ERR(crawl_directory_dag_for_mergeinfo(root
,
3480 svn_pool_destroy(iterpool
);
3481 return SVN_NO_ERROR
;
3485 /* Helper for get_mergeinfo_for_path() that will append PATH_PIECE
3486 (which may contain slashes) to each path that exists in the
3487 mergeinfo INPUT, and return a new mergeinfo in *OUTPUT. Deep
3488 copies the values. Perform all allocations in POOL. */
3489 static svn_error_t
*
3490 append_to_merged_froms(svn_mergeinfo_t
*output
,
3491 svn_mergeinfo_t input
,
3492 const char *path_piece
,
3495 apr_hash_index_t
*hi
;
3496 *output
= apr_hash_make(pool
);
3498 for (hi
= apr_hash_first(pool
, input
); hi
; hi
= apr_hash_next(hi
))
3504 apr_hash_this(hi
, &key
, NULL
, &val
);
3505 newpath
= svn_path_join((const char *) key
, path_piece
, pool
);
3506 apr_hash_set(*output
, newpath
, APR_HASH_KEY_STRING
,
3507 svn_rangelist_dup((apr_array_header_t
*) val
, pool
));
3510 return SVN_NO_ERROR
;
3513 /* Calculates the mergeinfo for PATH under REV_ROOT using inheritance
3514 type INHERIT. Returns it in *MERGEINFO, or NULL if there is none.
3515 The result is allocated in RESULT_POOL; POOL is used for temporary
3518 static svn_error_t
*
3519 get_mergeinfo_for_path(svn_mergeinfo_t
*mergeinfo
,
3520 svn_fs_root_t
*rev_root
,
3522 svn_mergeinfo_inheritance_t inherit
,
3524 apr_pool_t
*result_pool
)
3526 parent_path_t
*parent_path
, *nearest_ancestor
;
3527 apr_hash_t
*proplist
;
3528 svn_string_t
*mergeinfo_string
;
3529 apr_pool_t
*iterpool
= svn_pool_create(pool
);
3533 path
= svn_fs__canonicalize_abspath(path
, pool
);
3535 SVN_ERR(open_path(&parent_path
, rev_root
, path
, 0, NULL
, pool
));
3537 if (inherit
== svn_mergeinfo_nearest_ancestor
&& ! parent_path
->parent
)
3538 return SVN_NO_ERROR
;
3540 if (inherit
== svn_mergeinfo_nearest_ancestor
)
3541 nearest_ancestor
= parent_path
->parent
;
3543 nearest_ancestor
= parent_path
;
3547 svn_boolean_t has_mergeinfo
;
3549 svn_pool_clear(iterpool
);
3551 SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo
,
3552 nearest_ancestor
->node
, iterpool
));
3556 /* No need to loop if we're looking for explicit mergeinfo. */
3557 if (inherit
== svn_mergeinfo_explicit
)
3559 svn_pool_destroy(iterpool
);
3560 return SVN_NO_ERROR
;
3563 nearest_ancestor
= nearest_ancestor
->parent
;
3565 /* Run out? There's no mergeinfo. */
3566 if (!nearest_ancestor
)
3568 svn_pool_destroy(iterpool
);
3569 return SVN_NO_ERROR
;
3572 svn_pool_destroy(iterpool
);
3574 SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist
, nearest_ancestor
->node
, pool
));
3575 mergeinfo_string
= apr_hash_get(proplist
, SVN_PROP_MERGEINFO
,
3576 APR_HASH_KEY_STRING
);
3577 if (!mergeinfo_string
)
3578 return svn_error_createf
3579 (SVN_ERR_FS_CORRUPT
, NULL
,
3580 _("Node-revision '%s@%ld' claims to have mergeinfo but doesn't"),
3581 parent_path_path(nearest_ancestor
, pool
), rev_root
->rev
);
3583 if (nearest_ancestor
== parent_path
)
3585 /* We can return this directly. */
3586 SVN_ERR(svn_mergeinfo_parse(mergeinfo
,
3587 mergeinfo_string
->data
,
3589 return SVN_NO_ERROR
;
3593 svn_mergeinfo_t temp_mergeinfo
;
3595 /* We're inheriting this, so we need to (a) remove
3596 non-inheritable ranges and (b) add the rest of the path to
3597 the merged-from paths.
3600 SVN_ERR(svn_mergeinfo_parse(&temp_mergeinfo
,
3601 mergeinfo_string
->data
,
3603 SVN_ERR(svn_mergeinfo_inheritable(&temp_mergeinfo
,
3605 NULL
, SVN_INVALID_REVNUM
,
3606 SVN_INVALID_REVNUM
, pool
));
3608 SVN_ERR(append_to_merged_froms(mergeinfo
,
3610 parent_path_relpath(parent_path
,
3614 return SVN_NO_ERROR
;
3618 /* Adds mergeinfo for each descendant of PATH (but not PATH itself)
3619 under ROOT to RESULT_CATALOG. Returned values are allocated in
3620 RESULT_POOL; temporary values in POOL. */
3621 static svn_error_t
*
3622 add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog
,
3623 svn_fs_root_t
*root
,
3626 apr_pool_t
*result_pool
)
3628 dag_node_t
*this_dag
;
3629 svn_boolean_t go_down
;
3631 SVN_ERR(get_dag(&this_dag
, root
, path
, pool
));
3632 SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down
,
3636 SVN_ERR(crawl_directory_dag_for_mergeinfo(root
,
3642 return SVN_NO_ERROR
;
3646 /* Get the mergeinfo for a set of paths, returned in
3647 *MERGEINFO_CATALOG. Returned values are allocated in POOL, while
3648 temporary values are allocated in a sub-pool. */
3649 static svn_error_t
*
3650 get_mergeinfos_for_paths(svn_fs_root_t
*root
,
3651 svn_mergeinfo_catalog_t
*mergeinfo_catalog
,
3652 const apr_array_header_t
*paths
,
3653 svn_mergeinfo_inheritance_t inherit
,
3654 svn_boolean_t include_descendants
,
3657 svn_mergeinfo_catalog_t result_catalog
= apr_hash_make(pool
);
3658 apr_pool_t
*iterpool
= svn_pool_create(pool
);
3661 for (i
= 0; i
< paths
->nelts
; i
++)
3663 svn_mergeinfo_t path_mergeinfo
;
3664 const char *path
= APR_ARRAY_IDX(paths
, i
, const char *);
3666 svn_pool_clear(iterpool
);
3668 SVN_ERR(get_mergeinfo_for_path(&path_mergeinfo
, root
, path
,
3669 inherit
, iterpool
, pool
));
3671 apr_hash_set(result_catalog
, path
, APR_HASH_KEY_STRING
,
3673 if (include_descendants
)
3674 SVN_ERR(add_descendant_mergeinfo(result_catalog
, root
, path
, iterpool
,
3677 svn_pool_destroy(iterpool
);
3679 *mergeinfo_catalog
= result_catalog
;
3680 return SVN_NO_ERROR
;
3684 /* Implements svn_fs_get_mergeinfo. */
3685 static svn_error_t
*
3686 fs_get_mergeinfo(svn_mergeinfo_catalog_t
*catalog
,
3687 svn_fs_root_t
*root
,
3688 const apr_array_header_t
*paths
,
3689 svn_mergeinfo_inheritance_t inherit
,
3690 svn_boolean_t include_descendants
,
3693 fs_fs_data_t
*ffd
= root
->fs
->fsap_data
;
3695 /* We require a revision root. */
3696 if (root
->is_txn_root
)
3697 return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT
, NULL
, NULL
);
3699 /* We have to actually be able to find the mergeinfo metadata! */
3700 if (! svn_fs_fs__fs_supports_mergeinfo(root
->fs
))
3701 return svn_error_createf
3702 (SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
3703 _("Querying mergeinfo requires version %d of the FSFS filesystem "
3704 "schema; filesystem '%s' uses only version %d"),
3705 SVN_FS_FS__MIN_MERGEINFO_FORMAT
, root
->fs
->path
, ffd
->format
);
3707 /* Retrieve a path -> mergeinfo hash mapping. */
3708 return get_mergeinfos_for_paths(root
, catalog
, paths
,
3709 inherit
, include_descendants
,
3713 /* The vtable associated with root objects. */
3714 static root_vtable_t root_vtable
= {
3716 svn_fs_fs__check_path
,
3719 svn_fs_fs__node_created_rev
,
3721 fs_node_created_path
,
3727 fs_change_node_prop
,
3734 fs_file_md5_checksum
,
3739 fs_contents_changed
,
3740 fs_get_file_delta_stream
,
3745 /* Construct a new root object in FS, allocated from POOL. */
3746 static svn_fs_root_t
*
3747 make_root(svn_fs_t
*fs
,
3750 /* We create a subpool for each root object to allow us to implement
3751 svn_fs_close_root. */
3752 apr_pool_t
*subpool
= svn_pool_create(pool
);
3753 svn_fs_root_t
*root
= apr_pcalloc(subpool
, sizeof(*root
));
3756 root
->pool
= subpool
;
3757 root
->vtable
= &root_vtable
;
3763 /* Construct a root object referring to the root of REVISION in FS,
3764 whose root directory is ROOT_DIR. Create the new root in POOL. */
3765 static svn_fs_root_t
*
3766 make_revision_root(svn_fs_t
*fs
,
3768 dag_node_t
*root_dir
,
3771 svn_fs_root_t
*root
= make_root(fs
, pool
);
3772 fs_rev_root_data_t
*frd
= apr_pcalloc(root
->pool
, sizeof(*frd
));
3774 root
->is_txn_root
= FALSE
;
3777 frd
->root_dir
= root_dir
;
3778 frd
->copyfrom_cache
= apr_hash_make(root
->pool
);
3780 root
->fsap_data
= frd
;
3786 /* Construct a root object referring to the root of the transaction
3787 named TXN and based on revision BASE_REV in FS, with FLAGS to
3788 describe transaction's behavior. Create the new root in POOL. */
3789 static svn_fs_root_t
*
3790 make_txn_root(svn_fs_t
*fs
,
3792 svn_revnum_t base_rev
,
3796 svn_fs_root_t
*root
= make_root(fs
, pool
);
3797 fs_txn_root_data_t
*frd
= apr_pcalloc(root
->pool
, sizeof(*frd
));
3799 root
->is_txn_root
= TRUE
;
3800 root
->txn
= apr_pstrdup(root
->pool
, txn
);
3801 root
->txn_flags
= flags
;
3802 root
->rev
= base_rev
;
3804 frd
->txn_node_cache
= apr_hash_make(root
->pool
);
3805 frd
->txn_node_list
.prev
= &frd
->txn_node_list
;
3806 frd
->txn_node_list
.next
= &frd
->txn_node_list
;
3808 root
->fsap_data
= frd
;