2 * branch_nested.c : Nested Branches
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
26 #include "svn_types.h"
27 #include "svn_error.h"
28 #include "svn_dirent_uri.h"
31 #include "svn_pools.h"
33 #include "private/svn_branch_nested.h"
34 #include "private/svn_branch_impl.h"
35 #include "private/svn_branch_repos.h"
37 #include "svn_private_config.h"
41 svn_branch__get_outer_branch_and_eid(svn_branch__state_t **outer_branch_p,
43 const svn_branch__state_t *branch,
44 apr_pool_t *scratch_pool)
46 const char *outer_bid;
48 svn_branch__id_unnest(&outer_bid, outer_eid_p, branch->bid, scratch_pool);
49 *outer_branch_p = NULL;
53 = svn_branch__txn_get_branch_by_id(branch->txn, outer_bid,
59 svn_branch__get_root_rrpath(const svn_branch__state_t *branch,
60 apr_pool_t *result_pool)
62 svn_branch__state_t *outer_branch;
64 const char *root_rrpath;
66 svn_branch__get_outer_branch_and_eid(&outer_branch, &outer_eid, branch,
71 = svn_branch__get_rrpath_by_eid(outer_branch, outer_eid, result_pool);
78 SVN_ERR_ASSERT_NO_RETURN(root_rrpath);
83 svn_branch__get_rrpath_by_eid(const svn_branch__state_t *branch,
85 apr_pool_t *result_pool)
87 const char *path = svn_branch__get_path_by_eid(branch, eid, result_pool);
88 const char *rrpath = NULL;
92 rrpath = svn_relpath_join(svn_branch__get_root_rrpath(branch, result_pool),
99 svn_branch__get_subbranch_at_eid(svn_branch__state_t *branch,
100 svn_branch__state_t **subbranch_p,
102 apr_pool_t *scratch_pool)
104 svn_element__content_t *element;
106 SVN_ERR(svn_branch__state_get_element(branch, &element, eid, scratch_pool));
107 if (element && element->payload->is_subbranch_root)
109 const char *branch_id = svn_branch__get_id(branch, scratch_pool);
110 const char *subbranch_id = svn_branch__id_nest(branch_id, eid,
113 *subbranch_p = svn_branch__txn_get_branch_by_id(branch->txn, subbranch_id,
123 /* Set *SUBBRANCH_EIDS_P an array of EIDs of the subbranch-root elements in
127 svn_branch__get_immediate_subbranch_eids(svn_branch__state_t *branch,
128 apr_array_header_t **subbranch_eids_p,
129 apr_pool_t *result_pool,
130 apr_pool_t *scratch_pool)
132 apr_array_header_t *subbranch_eids
133 = apr_array_make(result_pool, 0, sizeof(int));
134 svn_element__tree_t *elements;
135 apr_hash_index_t *hi;
137 SVN_ERR(svn_branch__state_get_elements(branch, &elements, scratch_pool));
138 for (hi = apr_hash_first(scratch_pool, elements->e_map);
139 hi; hi = apr_hash_next(hi))
141 int eid = svn_eid__hash_this_key(hi);
142 svn_element__content_t *element = apr_hash_this_val(hi);
144 if (element->payload->is_subbranch_root)
146 APR_ARRAY_PUSH(subbranch_eids, int) = eid;
149 *subbranch_eids_p = subbranch_eids;
154 svn_branch__get_immediate_subbranches(svn_branch__state_t *branch,
155 apr_array_header_t **subbranches_p,
156 apr_pool_t *result_pool,
157 apr_pool_t *scratch_pool)
159 apr_array_header_t *subbranch_eids;
160 apr_array_header_t *subbranches
161 = apr_array_make(result_pool, 0, sizeof(void *));
162 const char *branch_id = svn_branch__get_id(branch, scratch_pool);
165 SVN_ERR(svn_branch__get_immediate_subbranch_eids(branch, &subbranch_eids,
166 scratch_pool, scratch_pool));
167 for (i = 0; i < subbranch_eids->nelts; i++)
169 int eid = APR_ARRAY_IDX(subbranch_eids, i, int);
170 const char *subbranch_id
171 = svn_branch__id_nest(branch_id, eid, scratch_pool);
172 svn_branch__state_t *subbranch
173 = svn_branch__txn_get_branch_by_id(branch->txn, subbranch_id,
176 SVN_ERR_ASSERT_NO_RETURN(subbranch);
177 APR_ARRAY_PUSH(subbranches, void *) = subbranch;
179 *subbranches_p = subbranches;
183 svn_branch__subtree_t *
184 svn_branch__subtree_create(apr_hash_t *e_map,
186 apr_pool_t *result_pool)
188 svn_branch__subtree_t *subtree = apr_pcalloc(result_pool, sizeof(*subtree));
190 subtree->tree = svn_element__tree_create(e_map, root_eid, result_pool);
191 subtree->subbranches = apr_hash_make(result_pool);
196 svn_branch__get_subtree(svn_branch__state_t *branch,
197 svn_branch__subtree_t **subtree_p,
199 apr_pool_t *result_pool)
201 svn_element__tree_t *element_tree;
202 svn_branch__subtree_t *new_subtree;
203 apr_array_header_t *subbranch_eids;
205 apr_pool_t *iterpool = result_pool; /* ### not a proper iterpool */
207 SVN_ERR(svn_branch__state_get_elements(branch, &element_tree, result_pool));
208 element_tree = svn_element__tree_get_subtree_at_eid(element_tree, eid,
211 = svn_branch__subtree_create(element_tree->e_map, eid, result_pool);
213 /* Add subbranches */
214 SVN_ERR(svn_branch__get_immediate_subbranch_eids(branch, &subbranch_eids,
215 result_pool, result_pool));
216 for (i = 0; i < subbranch_eids->nelts; i++)
218 int outer_eid = APR_ARRAY_IDX(subbranch_eids, i, int);
219 const char *subbranch_relpath_in_subtree;
221 subbranch_relpath_in_subtree
222 = svn_element__tree_get_path_by_eid(new_subtree->tree, outer_eid,
225 /* Is it pathwise at or below EID? If so, add it into the subtree. */
226 if (subbranch_relpath_in_subtree)
228 svn_branch__state_t *subbranch;
229 svn_branch__subtree_t *this_subtree;
231 SVN_ERR(svn_branch__get_subbranch_at_eid(branch, &subbranch,
232 outer_eid, iterpool));
235 SVN_ERR(svn_branch__get_subtree(subbranch, &this_subtree,
236 svn_branch__root_eid(subbranch),
238 svn_eid__hash_set(new_subtree->subbranches, outer_eid,
243 *subtree_p = new_subtree;
247 svn_branch__subtree_t *
248 svn_branch__subtree_get_subbranch_at_eid(svn_branch__subtree_t *subtree,
250 apr_pool_t *result_pool)
252 subtree = svn_eid__hash_get(subtree->subbranches, eid);
257 /* Instantiate ELEMENTS in TO_BRANCH.
260 branch_instantiate_elements(svn_branch__state_t *to_branch,
261 const svn_element__tree_t *elements,
262 apr_pool_t *scratch_pool)
264 apr_hash_index_t *hi;
266 for (hi = apr_hash_first(scratch_pool, elements->e_map);
267 hi; hi = apr_hash_next(hi))
269 int this_eid = svn_eid__hash_this_key(hi);
270 svn_element__content_t *this_element = apr_hash_this_val(hi);
272 SVN_ERR(svn_branch__state_set_element(to_branch, this_eid,
273 this_element, scratch_pool));
280 svn_branch__instantiate_elements_r(svn_branch__state_t *to_branch,
281 svn_branch__subtree_t elements,
282 apr_pool_t *scratch_pool)
284 SVN_ERR(branch_instantiate_elements(to_branch, elements.tree,
287 /* branch any subbranches */
289 apr_hash_index_t *hi;
291 for (hi = apr_hash_first(scratch_pool, elements.subbranches);
292 hi; hi = apr_hash_next(hi))
294 int this_outer_eid = svn_eid__hash_this_key(hi);
295 svn_branch__subtree_t *this_subtree = apr_hash_this_val(hi);
296 const char *new_branch_id;
297 svn_branch__state_t *new_branch;
298 /*### svn_branch__history_t *history;*/
300 /* branch this subbranch into NEW_BRANCH (recursing) */
301 new_branch_id = svn_branch__id_nest(to_branch->bid, this_outer_eid,
303 SVN_ERR(svn_branch__txn_open_branch(to_branch->txn, &new_branch,
305 this_subtree->tree->root_eid,
307 scratch_pool, scratch_pool));
308 /*### SVN_ERR(svn_branch__state_set_history(new_branch, history,
311 SVN_ERR(svn_branch__instantiate_elements_r(new_branch, *this_subtree,
320 * ========================================================================
324 svn_branch__find_nested_branch_element_by_relpath(
325 svn_branch__state_t **branch_p,
327 svn_branch__state_t *root_branch,
329 apr_pool_t *scratch_pool)
331 /* The path we're looking for is (path-wise) in this branch. See if it
332 is also in a sub-branch. */
333 /* Loop invariants: RELPATH is the path we're looking for, relative to
334 ROOT_BRANCH which is the current level of nesting that we've descended
338 apr_array_header_t *subbranch_eids;
340 svn_boolean_t found = FALSE;
342 SVN_ERR(svn_branch__get_immediate_subbranch_eids(
343 root_branch, &subbranch_eids, scratch_pool, scratch_pool));
344 for (i = 0; i < subbranch_eids->nelts; i++)
346 int outer_eid = APR_ARRAY_IDX(subbranch_eids, i, int);
347 const char *relpath_to_subbranch;
348 const char *relpath_in_subbranch;
350 /* Check whether the RELPATH we're looking for is within this
351 subbranch at OUTER_EID. If it is, recurse in the subbranch. */
353 = svn_branch__get_path_by_eid(root_branch, outer_eid, scratch_pool);
355 = svn_relpath_skip_ancestor(relpath_to_subbranch, relpath);
356 if (relpath_in_subbranch)
358 svn_branch__state_t *subbranch;
360 SVN_ERR(svn_branch__get_subbranch_at_eid(
361 root_branch, &subbranch, outer_eid, scratch_pool));
362 /* If the branch hierarchy is not 'flat' then we might find
363 there is no actual branch where the subbranch-root element
364 says there should be one. In that case, ignore it. */
367 root_branch = subbranch;
368 relpath = relpath_in_subbranch;
380 *branch_p = root_branch;
382 *eid_p = svn_branch__get_eid_by_path(root_branch, relpath, scratch_pool);
387 svn_branch__repos_find_el_rev_by_path_rev(svn_branch__el_rev_id_t **el_rev_p,
388 const svn_branch__repos_t *repos,
390 const char *branch_id,
392 apr_pool_t *result_pool,
393 apr_pool_t *scratch_pool)
395 svn_branch__el_rev_id_t *el_rev = apr_palloc(result_pool, sizeof(*el_rev));
396 svn_branch__state_t *branch;
398 SVN_ERR(svn_branch__repos_get_branch_by_id(&branch,
399 repos, revnum, branch_id,
401 el_rev->rev = revnum;
402 SVN_ERR(svn_branch__find_nested_branch_element_by_relpath(&el_rev->branch,
407 /* Any relpath must at least be within the originally given branch */
408 SVN_ERR_ASSERT_NO_RETURN(el_rev->branch);
413 /* Set *BRANCH_P to the branch found in the repository of TXN, at the
414 * location (in a revision or in this txn) SRC_EL_REV.
416 * Return an error if REVNUM or BRANCH_ID is not found.
419 branch_in_rev_or_txn(svn_branch__state_t **branch_p,
420 const svn_branch__rev_bid_eid_t *src_el_rev,
421 svn_branch__txn_t *txn,
422 apr_pool_t *result_pool)
424 if (SVN_IS_VALID_REVNUM(src_el_rev->rev))
426 SVN_ERR(svn_branch__repos_get_branch_by_id(branch_p,
435 = svn_branch__txn_get_branch_by_id(
436 txn, src_el_rev->bid, result_pool);
438 return svn_error_createf(SVN_BRANCH__ERR, NULL,
439 _("Branch %s not found"),
446 struct svn_branch__txn_priv_t
448 /* The underlying branch-txn that supports only non-nested branching. */
449 svn_branch__txn_t *wrapped_txn;
453 /* Implements nested branching.
454 * An #svn_branch__txn_t method. */
455 static apr_array_header_t *
456 nested_branch_txn_get_branches(const svn_branch__txn_t *txn,
457 apr_pool_t *result_pool)
459 /* Just forwarding: nothing more is needed. */
460 apr_array_header_t *branches
461 = svn_branch__txn_get_branches(txn->priv->wrapped_txn,
467 /* An #svn_branch__txn_t method. */
469 nested_branch_txn_delete_branch(svn_branch__txn_t *txn,
471 apr_pool_t *scratch_pool)
473 /* Just forwarding: nothing more is needed. */
474 SVN_ERR(svn_branch__txn_delete_branch(txn->priv->wrapped_txn,
480 /* Implements nested branching.
481 * An #svn_branch__txn_t method. */
483 nested_branch_txn_get_num_new_eids(const svn_branch__txn_t *txn,
485 apr_pool_t *scratch_pool)
487 /* Just forwarding: nothing more is needed. */
488 SVN_ERR(svn_branch__txn_get_num_new_eids(txn->priv->wrapped_txn,
494 /* Implements nested branching.
495 * An #svn_branch__txn_t method. */
497 nested_branch_txn_new_eid(svn_branch__txn_t *txn,
498 svn_branch__eid_t *eid_p,
499 apr_pool_t *scratch_pool)
501 /* Just forwarding: nothing more is needed. */
502 SVN_ERR(svn_branch__txn_new_eid(txn->priv->wrapped_txn,
508 /* Implements nested branching.
509 * An #svn_branch__txn_t method. */
511 nested_branch_txn_open_branch(svn_branch__txn_t *txn,
512 svn_branch__state_t **new_branch_p,
513 const char *new_branch_id,
515 svn_branch__rev_bid_eid_t *tree_ref,
516 apr_pool_t *result_pool,
517 apr_pool_t *scratch_pool)
519 svn_branch__state_t *new_branch;
521 SVN_ERR(svn_branch__txn_open_branch(txn->priv->wrapped_txn,
523 new_branch_id, root_eid, tree_ref,
527 /* Recursively branch any nested branches */
530 svn_branch__state_t *from_branch;
531 svn_branch__subtree_t *from_subtree;
533 /* (The way we're doing it here also redundantly re-instantiates all the
534 elements in NEW_BRANCH.) */
535 SVN_ERR(branch_in_rev_or_txn(&from_branch, tree_ref,
536 txn->priv->wrapped_txn, scratch_pool));
537 SVN_ERR(svn_branch__get_subtree(from_branch, &from_subtree,
538 tree_ref->eid, scratch_pool));
539 SVN_ERR(svn_branch__instantiate_elements_r(new_branch, *from_subtree,
544 *new_branch_p = new_branch;
548 /* Implements nested branching.
549 * An #svn_branch__txn_t method. */
551 nested_branch_txn_finalize_eids(svn_branch__txn_t *txn,
552 apr_pool_t *scratch_pool)
554 /* Just forwarding: nothing more is needed. */
555 SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->wrapped_txn,
560 /* Implements nested branching.
561 * An #svn_branch__txn_t method. */
563 nested_branch_txn_serialize(svn_branch__txn_t *txn,
564 svn_stream_t *stream,
565 apr_pool_t *scratch_pool)
567 /* Just forwarding: nothing more is needed. */
568 SVN_ERR(svn_branch__txn_serialize(txn->priv->wrapped_txn,
574 /* Implements nested branching.
575 * An #svn_branch__txn_t method. */
577 nested_branch_txn_sequence_point(svn_branch__txn_t *txn,
578 apr_pool_t *scratch_pool)
580 svn_branch__txn_t *wrapped_txn = txn->priv->wrapped_txn;
581 apr_array_header_t *branches;
584 /* first, purge elements in each branch */
585 SVN_ERR(svn_branch__txn_sequence_point(wrapped_txn, scratch_pool));
587 /* second, purge branches that are no longer nested */
588 branches = svn_branch__txn_get_branches(wrapped_txn, scratch_pool);
589 for (i = 0; i < branches->nelts; i++)
591 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *);
592 svn_branch__state_t *outer_branch;
595 svn_branch__get_outer_branch_and_eid(&outer_branch, &outer_eid,
599 svn_element__content_t *element;
601 SVN_ERR(svn_branch__state_get_element(outer_branch, &element,
602 outer_eid, scratch_pool));
604 SVN_ERR(svn_branch__txn_delete_branch(wrapped_txn, b->bid,
611 /* An #svn_branch__txn_t method. */
613 nested_branch_txn_complete(svn_branch__txn_t *txn,
614 apr_pool_t *scratch_pool)
616 /* Just forwarding: nothing more is needed. */
617 SVN_ERR(svn_branch__txn_complete(txn->priv->wrapped_txn,
622 /* An #svn_branch__txn_t method. */
624 nested_branch_txn_abort(svn_branch__txn_t *txn,
625 apr_pool_t *scratch_pool)
627 /* Just forwarding: nothing more is needed. */
628 SVN_ERR(svn_branch__txn_abort(txn->priv->wrapped_txn,
634 svn_branch__nested_txn_create(svn_branch__txn_t *wrapped_txn,
635 apr_pool_t *result_pool)
637 static const svn_branch__txn_vtable_t vtable = {
639 nested_branch_txn_get_branches,
640 nested_branch_txn_delete_branch,
641 nested_branch_txn_get_num_new_eids,
642 nested_branch_txn_new_eid,
643 nested_branch_txn_open_branch,
644 nested_branch_txn_finalize_eids,
645 nested_branch_txn_serialize,
646 nested_branch_txn_sequence_point,
647 nested_branch_txn_complete,
648 nested_branch_txn_abort,
650 svn_branch__txn_t *txn
651 = svn_branch__txn_create(&vtable, NULL, NULL, result_pool);
653 txn->priv = apr_pcalloc(result_pool, sizeof(*txn->priv));
654 txn->priv->wrapped_txn = wrapped_txn;
655 txn->repos = wrapped_txn->repos;
656 txn->rev = wrapped_txn->rev;
657 txn->base_rev = wrapped_txn->base_rev;