2 * branch.c : Element-Based Branching and Move Tracking.
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_element.h"
34 #include "private/svn_branch.h"
35 #include "private/svn_branch_impl.h"
36 #include "private/svn_sorts_private.h"
38 #include "svn_private_config.h"
41 /* Is EID allocated (no matter whether an element with this id exists)? */
42 #define EID_IS_ALLOCATED(branch, eid) \
43 ((eid) >= (branch)->txn->priv->first_eid \
44 && (eid) < (branch)->txn->priv->next_eid)
46 #define IS_BRANCH_ROOT_EID(branch, eid) \
47 ((eid) == (branch)->priv->element_tree->root_eid)
49 /* Is BRANCH1 the same branch as BRANCH2? Compare by full branch-ids; don't
50 require identical branch objects. */
51 #define BRANCH_IS_SAME_BRANCH(branch1, branch2, scratch_pool) \
52 (strcmp(svn_branch__get_id(branch1, scratch_pool), \
53 svn_branch__get_id(branch2, scratch_pool)) == 0)
55 struct svn_branch__txn_priv_t
58 apr_array_header_t *branches;
60 /* The range of element ids assigned. */
61 /* EIDs local to the txn are negative, assigned by decrementing FIRST_EID
63 int first_eid, next_eid;
67 struct svn_branch__state_priv_t
69 /* EID -> svn_element__content_t mapping. */
70 svn_element__tree_t *element_tree;
72 /* Merge history for this branch state. */
73 svn_branch__history_t *history;
75 svn_boolean_t is_flat;
79 static svn_branch__state_t *
80 branch_state_create(const char *bid,
82 svn_branch__txn_t *txn,
83 apr_pool_t *result_pool);
86 branch_instantiate_elements(svn_branch__state_t *to_branch,
87 const svn_element__tree_t *elements,
88 apr_pool_t *scratch_pool);
91 svn_branch__map_add_subtree(svn_branch__state_t *to_branch,
93 svn_branch__eid_t new_parent_eid,
95 svn_element__tree_t *new_subtree,
96 apr_pool_t *scratch_pool);
100 branch_state_pool_get(svn_branch__state_t *branch)
102 return apr_hash_pool_get(branch->priv->element_tree->e_map);
105 /* ### Layering: we didn't want to look at the whole repos in here, but
106 copying seems to require it. */
108 svn_branch__repos_get_branch_by_id(svn_branch__state_t **branch_p,
109 const svn_branch__repos_t *repos,
111 const char *branch_id,
112 apr_pool_t *scratch_pool);
116 branch_in_rev_or_txn(svn_branch__state_t **src_branch,
117 const svn_branch__rev_bid_eid_t *src_el_rev,
118 svn_branch__txn_t *txn,
119 apr_pool_t *result_pool)
121 if (SVN_IS_VALID_REVNUM(src_el_rev->rev))
123 SVN_ERR(svn_branch__repos_get_branch_by_id(src_branch,
132 = svn_branch__txn_get_branch_by_id(txn, src_el_rev->bid, result_pool);
138 /* An #svn_branch__txn_t method. */
139 static apr_array_header_t *
140 branch_txn_get_branches(const svn_branch__txn_t *txn,
141 apr_pool_t *result_pool)
143 return apr_array_copy(result_pool, txn->priv->branches);
146 /* An #svn_branch__txn_t method. */
148 branch_txn_delete_branch(svn_branch__txn_t *txn,
150 apr_pool_t *scratch_pool)
154 for (i = 0; i < txn->priv->branches->nelts; i++)
156 svn_branch__state_t *b = APR_ARRAY_IDX(txn->priv->branches, i, void *);
158 if (strcmp(b->bid, bid) == 0)
160 svn_sort__array_delete(txn->priv->branches, i, 1);
167 /* An #svn_branch__txn_t method. */
169 branch_txn_get_num_new_eids(const svn_branch__txn_t *txn,
171 apr_pool_t *scratch_pool)
174 *num_new_eids_p = -1 - txn->priv->first_eid;
178 /* An #svn_branch__txn_t method. */
180 branch_txn_new_eid(svn_branch__txn_t *txn,
181 svn_branch__eid_t *eid_p,
182 apr_pool_t *scratch_pool)
184 int eid = (txn->priv->first_eid < 0) ? txn->priv->first_eid - 1 : -2;
186 txn->priv->first_eid = eid;
192 /* An #svn_branch__txn_t method. */
194 branch_txn_open_branch(svn_branch__txn_t *txn,
195 svn_branch__state_t **new_branch_p,
196 const char *branch_id,
198 svn_branch__rev_bid_eid_t *tree_ref,
199 apr_pool_t *result_pool,
200 apr_pool_t *scratch_pool)
202 svn_branch__state_t *new_branch;
204 /* if the branch already exists, just return it, else create it */
206 = svn_branch__txn_get_branch_by_id(txn, branch_id, scratch_pool);
209 SVN_ERR_ASSERT(root_eid == svn_branch__root_eid(new_branch));
213 SVN_ERR_ASSERT_NO_RETURN(root_eid != -1);
215 new_branch = branch_state_create(branch_id, root_eid, txn,
216 txn->priv->branches->pool);
217 APR_ARRAY_PUSH(txn->priv->branches, void *) = new_branch;
222 svn_branch__state_t *from_branch;
223 svn_element__tree_t *tree;
225 SVN_ERR(branch_in_rev_or_txn(&from_branch, tree_ref, txn, scratch_pool));
226 /* Source branch must exist */
229 return svn_error_createf(SVN_BRANCH__ERR, NULL,
230 _("Cannot branch from r%ld %s e%d: "
231 "branch does not exist"),
232 tree_ref->rev, tree_ref->bid, tree_ref->eid);
235 SVN_ERR_ASSERT(from_branch->priv->is_flat);
237 SVN_ERR(svn_branch__state_get_elements(from_branch, &tree,
239 tree = svn_element__tree_get_subtree_at_eid(tree, tree_ref->eid,
241 /* Source element must exist */
244 return svn_error_createf(SVN_BRANCH__ERR, NULL,
245 _("Cannot branch from r%ld %s e%d: "
246 "element does not exist"),
247 tree_ref->rev, tree_ref->bid, tree_ref->eid);
250 /* Populate the tree from the 'from' source */
251 SVN_ERR(branch_instantiate_elements(new_branch, tree, scratch_pool));
255 *new_branch_p = new_branch;
259 /* An #svn_branch__txn_t method. */
261 branch_txn_sequence_point(svn_branch__txn_t *txn,
262 apr_pool_t *scratch_pool)
266 /* purge elements in each branch */
267 for (i = 0; i < txn->priv->branches->nelts; i++)
269 svn_branch__state_t *b
270 = APR_ARRAY_IDX(txn->priv->branches, i, void *);
272 SVN_ERR(svn_branch__state_purge(b, scratch_pool));
278 /* An #svn_branch__txn_t method. */
280 branch_txn_complete(svn_branch__txn_t *txn,
281 apr_pool_t *scratch_pool)
286 /* An #svn_branch__txn_t method. */
288 branch_txn_abort(svn_branch__txn_t *txn,
289 apr_pool_t *scratch_pool)
295 * ========================================================================
297 * ========================================================================
301 svn_branch__txn_get_branches(const svn_branch__txn_t *txn,
302 apr_pool_t *result_pool)
304 apr_array_header_t *branches
305 = txn->vtable->get_branches(txn,
311 svn_branch__txn_delete_branch(svn_branch__txn_t *txn,
313 apr_pool_t *scratch_pool)
315 SVN_ERR(txn->vtable->delete_branch(txn,
322 svn_branch__txn_get_num_new_eids(const svn_branch__txn_t *txn,
324 apr_pool_t *scratch_pool)
326 SVN_ERR(txn->vtable->get_num_new_eids(txn,
333 svn_branch__txn_new_eid(svn_branch__txn_t *txn,
335 apr_pool_t *scratch_pool)
337 SVN_ERR(txn->vtable->new_eid(txn,
344 svn_branch__txn_open_branch(svn_branch__txn_t *txn,
345 svn_branch__state_t **new_branch_p,
346 const char *branch_id,
348 svn_branch__rev_bid_eid_t *tree_ref,
349 apr_pool_t *result_pool,
350 apr_pool_t *scratch_pool)
352 SVN_ERR(txn->vtable->open_branch(txn,
355 root_eid, tree_ref, result_pool,
361 svn_branch__txn_finalize_eids(svn_branch__txn_t *txn,
362 apr_pool_t *scratch_pool)
364 SVN_ERR(txn->vtable->finalize_eids(txn,
370 svn_branch__txn_serialize(svn_branch__txn_t *txn,
371 svn_stream_t *stream,
372 apr_pool_t *scratch_pool)
374 SVN_ERR(txn->vtable->serialize(txn,
381 svn_branch__txn_sequence_point(svn_branch__txn_t *txn,
382 apr_pool_t *scratch_pool)
384 SVN_ERR(txn->vtable->sequence_point(txn,
390 svn_branch__txn_complete(svn_branch__txn_t *txn,
391 apr_pool_t *scratch_pool)
393 SVN_ERR(txn->vtable->complete(txn,
399 svn_branch__txn_abort(svn_branch__txn_t *txn,
400 apr_pool_t *scratch_pool)
402 SVN_ERR(txn->vtable->abort(txn,
408 svn_branch__txn_create(const svn_branch__txn_vtable_t *vtable,
409 svn_cancel_func_t cancel_func,
411 apr_pool_t *result_pool)
413 svn_branch__txn_t *txn = apr_pcalloc(result_pool, sizeof(*txn));
415 txn->vtable = apr_pmemdup(result_pool, vtable, sizeof(*vtable));
417 txn->vtable->vpriv.cancel_func = cancel_func;
418 txn->vtable->vpriv.cancel_baton = cancel_baton;
420 #ifdef ENABLE_ORDERING_CHECK
421 txn->vtable->vpriv.within_callback = FALSE;
422 txn->vtable->vpriv.finished = FALSE;
423 txn->vtable->vpriv.state_pool = result_pool;
430 * ========================================================================
435 branch_finalize_bid(const char *bid,
437 apr_pool_t *result_pool)
439 const char *outer_bid;
442 svn_branch__id_unnest(&outer_bid, &outer_eid, bid, result_pool);
446 outer_bid = branch_finalize_bid(outer_bid, mapping_offset, result_pool);
451 outer_eid = mapping_offset - outer_eid;
454 return svn_branch__id_nest(outer_bid, outer_eid, result_pool);
457 /* Change txn-local EIDs (negative integers) in BRANCH to revision EIDs, by
458 * assigning a new revision-EID (positive integer) for each one.
461 branch_finalize_eids(svn_branch__state_t *branch,
463 apr_pool_t *scratch_pool)
465 apr_hash_index_t *hi;
467 branch->bid = branch_finalize_bid(branch->bid, mapping_offset,
468 branch_state_pool_get(branch));
469 if (branch->priv->element_tree->root_eid < -1)
471 branch->priv->element_tree->root_eid
472 = mapping_offset - branch->priv->element_tree->root_eid;
475 for (hi = apr_hash_first(scratch_pool, branch->priv->element_tree->e_map);
476 hi; hi = apr_hash_next(hi))
478 int old_eid = svn_eid__hash_this_key(hi);
479 svn_element__content_t *element = apr_hash_this_val(hi);
483 int new_eid = mapping_offset - old_eid;
485 svn_element__tree_set(branch->priv->element_tree, old_eid, NULL);
486 svn_element__tree_set(branch->priv->element_tree, new_eid, element);
488 if (element->parent_eid < -1)
490 element->parent_eid = mapping_offset - element->parent_eid;
496 /* An #svn_branch__txn_t method. */
498 branch_txn_finalize_eids(svn_branch__txn_t *txn,
499 apr_pool_t *scratch_pool)
501 int n_txn_eids = (-1) - txn->priv->first_eid;
503 apr_array_header_t *branches = branch_txn_get_branches(txn, scratch_pool);
506 if (txn->priv->first_eid == 0)
509 /* mapping from txn-local (negative) EID to committed (positive) EID is:
510 txn_local_eid == -2 => committed_eid := (txn.next_eid + 0)
511 txn_local_eid == -3 => committed_eid := (txn.next_eid + 1) ... */
512 mapping_offset = txn->priv->next_eid - 2;
514 for (i = 0; i < branches->nelts; i++)
516 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *);
518 SVN_ERR(branch_finalize_eids(b, mapping_offset, scratch_pool));
521 txn->priv->next_eid += n_txn_eids;
522 txn->priv->first_eid = 0;
527 * ========================================================================
531 branch_txn_serialize(svn_branch__txn_t *txn,
532 svn_stream_t *stream,
533 apr_pool_t *scratch_pool)
535 apr_array_header_t *branches = branch_txn_get_branches(txn, scratch_pool);
538 SVN_ERR(svn_stream_printf(stream, scratch_pool,
542 txn->priv->first_eid, txn->priv->next_eid,
545 for (i = 0; i < branches->nelts; i++)
547 svn_branch__state_t *branch = APR_ARRAY_IDX(branches, i, void *);
549 SVN_ERR(svn_branch__state_serialize(stream, branch, scratch_pool));
555 * ========================================================================
558 svn_branch__state_t *
559 svn_branch__txn_get_branch_by_id(const svn_branch__txn_t *txn,
560 const char *branch_id,
561 apr_pool_t *scratch_pool)
563 apr_array_header_t *branches = svn_branch__txn_get_branches(txn, scratch_pool);
565 svn_branch__state_t *branch = NULL;
567 for (i = 0; i < branches->nelts; i++)
569 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *);
571 if (strcmp(svn_branch__get_id(b, scratch_pool), branch_id) == 0)
581 * ========================================================================
584 /* Create a new branch txn object.
586 * It will have no branches.
588 static svn_branch__txn_t *
589 branch_txn_create(svn_branch__repos_t *repos,
591 svn_revnum_t base_rev,
592 apr_pool_t *result_pool)
594 static const svn_branch__txn_vtable_t vtable = {
596 branch_txn_get_branches,
597 branch_txn_delete_branch,
598 branch_txn_get_num_new_eids,
600 branch_txn_open_branch,
601 branch_txn_finalize_eids,
602 branch_txn_serialize,
603 branch_txn_sequence_point,
607 svn_branch__txn_t *txn
608 = svn_branch__txn_create(&vtable, NULL, NULL, result_pool);
610 txn->priv = apr_pcalloc(result_pool, sizeof(*txn->priv));
613 txn->base_rev = base_rev;
614 txn->priv->branches = apr_array_make(result_pool, 0, sizeof(void *));
619 * ========================================================================
623 branch_validate_element(const svn_branch__state_t *branch,
625 const svn_element__content_t *element);
627 /* Assert BRANCH satisfies all its invariants.
630 assert_branch_state_invariants(const svn_branch__state_t *branch,
631 apr_pool_t *scratch_pool)
633 apr_hash_index_t *hi;
637 assert(branch->priv->element_tree);
638 assert(branch->priv->element_tree->e_map);
640 /* Validate elements in the map */
641 for (hi = apr_hash_first(scratch_pool, branch->priv->element_tree->e_map);
642 hi; hi = apr_hash_next(hi))
644 branch_validate_element(branch, svn_eid__hash_this_key(hi),
645 apr_hash_this_val(hi));
649 /* An #svn_branch__state_t method. */
651 branch_state_copy_one(svn_branch__state_t *branch,
652 const svn_branch__rev_bid_eid_t *src_el_rev,
653 svn_branch__eid_t eid,
654 svn_branch__eid_t new_parent_eid,
655 const char *new_name,
656 const svn_element__payload_t *new_payload,
657 apr_pool_t *scratch_pool)
659 /* New payload shall be the same as the source if NEW_PAYLOAD is null. */
660 /* ### if (! new_payload)
662 new_payload = branch_map_get(branch, eid)->payload;
671 * Adjust TO_BRANCH and its subbranches (recursively), to reflect a copy
672 * of a subtree from FROM_EL_REV to TO_PARENT_EID:TO_NAME.
674 * FROM_EL_REV must be an existing element. (It may be a branch root.)
677 * If FROM_EL_REV is the root of a subbranch and/or contains nested
678 * subbranches, also copy them ...
679 * ### What shall we do with a subbranch? Make plain copies of its raw
680 * elements; make a subbranch by branching the source subbranch?
682 * TO_PARENT_EID must be a directory element in TO_BRANCH, and TO_NAME a
683 * non-existing path in it.
686 copy_subtree(const svn_branch__el_rev_id_t *from_el_rev,
687 svn_branch__state_t *to_branch,
688 svn_branch__eid_t to_parent_eid,
690 apr_pool_t *scratch_pool)
692 svn_element__tree_t *new_subtree;
694 SVN_ERR_ASSERT(from_el_rev->branch->priv->is_flat);
696 SVN_ERR(svn_branch__state_get_elements(from_el_rev->branch, &new_subtree,
698 new_subtree = svn_element__tree_get_subtree_at_eid(new_subtree,
702 /* copy the subtree, assigning new EIDs */
703 SVN_ERR(svn_branch__map_add_subtree(to_branch, -1 /*to_eid*/,
704 to_parent_eid, to_name,
711 /* An #svn_branch__state_t method. */
713 branch_state_copy_tree(svn_branch__state_t *to_branch,
714 const svn_branch__rev_bid_eid_t *src_el_rev,
715 svn_branch__eid_t new_parent_eid,
716 const char *new_name,
717 apr_pool_t *scratch_pool)
719 svn_branch__txn_t *txn = to_branch->txn;
720 svn_branch__state_t *src_branch;
721 svn_branch__el_rev_id_t *from_el_rev;
723 SVN_ERR(branch_in_rev_or_txn(&src_branch, src_el_rev, txn, scratch_pool));
724 from_el_rev = svn_branch__el_rev_id_create(src_branch, src_el_rev->eid,
725 src_el_rev->rev, scratch_pool);
726 SVN_ERR(copy_subtree(from_el_rev,
727 to_branch, new_parent_eid, new_name,
734 svn_branch__get_id(const svn_branch__state_t *branch,
735 apr_pool_t *result_pool)
741 svn_branch__root_eid(const svn_branch__state_t *branch)
743 svn_element__tree_t *elements;
745 svn_error_clear(svn_branch__state_get_elements(branch, &elements,
746 NULL/*scratch_pool*/));
747 return elements->root_eid;
750 svn_branch__el_rev_id_t *
751 svn_branch__el_rev_id_create(svn_branch__state_t *branch,
754 apr_pool_t *result_pool)
756 svn_branch__el_rev_id_t *id = apr_palloc(result_pool, sizeof(*id));
764 svn_branch__el_rev_id_t *
765 svn_branch__el_rev_id_dup(const svn_branch__el_rev_id_t *old_id,
766 apr_pool_t *result_pool)
771 return svn_branch__el_rev_id_create(old_id->branch,
777 svn_branch__rev_bid_eid_t *
778 svn_branch__rev_bid_eid_create(svn_revnum_t rev,
779 const char *branch_id,
781 apr_pool_t *result_pool)
783 svn_branch__rev_bid_eid_t *id = apr_palloc(result_pool, sizeof(*id));
785 id->bid = apr_pstrdup(result_pool, branch_id);
791 svn_branch__rev_bid_eid_t *
792 svn_branch__rev_bid_eid_dup(const svn_branch__rev_bid_eid_t *old_id,
793 apr_pool_t *result_pool)
795 svn_branch__rev_bid_eid_t *id;
800 id = apr_pmemdup(result_pool, old_id, sizeof(*id));
801 id->bid = apr_pstrdup(result_pool, old_id->bid);
805 svn_branch__rev_bid_t *
806 svn_branch__rev_bid_create(svn_revnum_t rev,
807 const char *branch_id,
808 apr_pool_t *result_pool)
810 svn_branch__rev_bid_t *id = apr_palloc(result_pool, sizeof(*id));
812 id->bid = apr_pstrdup(result_pool, branch_id);
817 svn_branch__rev_bid_t *
818 svn_branch__rev_bid_dup(const svn_branch__rev_bid_t *old_id,
819 apr_pool_t *result_pool)
821 svn_branch__rev_bid_t *id;
826 id = apr_pmemdup(result_pool, old_id, sizeof(*id));
827 id->bid = apr_pstrdup(result_pool, old_id->bid);
832 svn_branch__rev_bid_equal(const svn_branch__rev_bid_t *id1,
833 const svn_branch__rev_bid_t *id2)
835 return (id1->rev == id2->rev
836 && strcmp(id1->bid, id2->bid) == 0);
839 svn_branch__history_t *
840 svn_branch__history_create_empty(apr_pool_t *result_pool)
842 svn_branch__history_t *history
843 = svn_branch__history_create(NULL, result_pool);
848 svn_branch__history_t *
849 svn_branch__history_create(apr_hash_t *parents,
850 apr_pool_t *result_pool)
852 svn_branch__history_t *history
853 = apr_pcalloc(result_pool, sizeof(*history));
855 history->parents = apr_hash_make(result_pool);
858 apr_hash_index_t *hi;
860 for (hi = apr_hash_first(result_pool, parents);
861 hi; hi = apr_hash_next(hi))
863 const char *bid = apr_hash_this_key(hi);
864 svn_branch__rev_bid_t *val = apr_hash_this_val(hi);
866 svn_hash_sets(history->parents,
867 apr_pstrdup(result_pool, bid),
868 svn_branch__rev_bid_dup(val, result_pool));
874 svn_branch__history_t *
875 svn_branch__history_dup(const svn_branch__history_t *old,
876 apr_pool_t *result_pool)
878 svn_branch__history_t *history = NULL;
883 = svn_branch__history_create(old->parents, result_pool);
890 * ========================================================================
892 * ========================================================================
895 /* Validate that ELEMENT is suitable for a mapping of BRANCH:EID.
896 * ELEMENT->payload may be null.
899 branch_validate_element(const svn_branch__state_t *branch,
901 const svn_element__content_t *element)
903 SVN_ERR_ASSERT_NO_RETURN(element);
905 /* Parent EID must be valid and different from this element's EID, or -1
906 iff this is the branch root element. */
907 SVN_ERR_ASSERT_NO_RETURN(
908 IS_BRANCH_ROOT_EID(branch, eid)
909 ? (element->parent_eid == -1)
910 : (element->parent_eid != eid
911 && EID_IS_ALLOCATED(branch, element->parent_eid)));
913 /* Element name must be given, and empty iff EID is the branch root. */
914 SVN_ERR_ASSERT_NO_RETURN(
916 && IS_BRANCH_ROOT_EID(branch, eid) == (*element->name == '\0'));
918 SVN_ERR_ASSERT_NO_RETURN(svn_element__payload_invariants(element->payload));
919 if (element->payload->is_subbranch_root)
921 /* a subbranch root element must not be the branch root element */
922 SVN_ERR_ASSERT_NO_RETURN(! IS_BRANCH_ROOT_EID(branch, eid));
927 branch_state_get_elements(const svn_branch__state_t *branch,
928 svn_element__tree_t **element_tree_p,
929 apr_pool_t *result_pool)
931 *element_tree_p = branch->priv->element_tree;
935 static svn_element__content_t *
936 branch_get_element(const svn_branch__state_t *branch,
939 svn_element__content_t *element;
941 element = svn_element__tree_get(branch->priv->element_tree, eid);
944 branch_validate_element(branch, eid, element);
949 branch_state_get_element(const svn_branch__state_t *branch,
950 svn_element__content_t **element_p,
952 apr_pool_t *result_pool)
954 *element_p = branch_get_element(branch, eid);
958 /* In BRANCH, set element EID to ELEMENT.
960 * If ELEMENT is null, delete element EID.
962 * Assume ELEMENT is already allocated with sufficient lifetime.
965 branch_map_set(svn_branch__state_t *branch,
967 const svn_element__content_t *element)
969 apr_pool_t *map_pool = apr_hash_pool_get(branch->priv->element_tree->e_map);
971 SVN_ERR_ASSERT_NO_RETURN(EID_IS_ALLOCATED(branch, eid));
973 branch_validate_element(branch, eid, element);
975 svn_element__tree_set(branch->priv->element_tree, eid, element);
976 branch->priv->is_flat = FALSE;
977 assert_branch_state_invariants(branch, map_pool);
980 /* An #svn_branch__state_t method. */
982 branch_state_set_element(svn_branch__state_t *branch,
983 svn_branch__eid_t eid,
984 const svn_element__content_t *element,
985 apr_pool_t *scratch_pool)
987 apr_pool_t *map_pool = apr_hash_pool_get(branch->priv->element_tree->e_map);
989 /* EID must be a valid element id */
990 SVN_ERR_ASSERT(EID_IS_ALLOCATED(branch, eid));
994 element = svn_element__content_dup(element, map_pool);
996 /* NEW_PAYLOAD must be specified, either in full or by reference */
997 SVN_ERR_ASSERT(element->payload);
999 if ((element->parent_eid == -1) != IS_BRANCH_ROOT_EID(branch, eid)
1000 || (*element->name == '\0') != IS_BRANCH_ROOT_EID(branch, eid))
1002 return svn_error_createf(SVN_BRANCH__ERR, NULL,
1003 _("Cannot set e%d to (parent=e%d, name='%s'): "
1004 "branch root is e%d"),
1005 eid, element->parent_eid, element->name,
1006 branch->priv->element_tree->root_eid);
1010 /* Insert the new version */
1011 branch_map_set(branch, eid, element);
1012 return SVN_NO_ERROR;
1015 /* An #svn_branch__state_t method. */
1016 static svn_error_t *
1017 branch_state_purge(svn_branch__state_t *branch,
1018 apr_pool_t *scratch_pool)
1020 svn_element__tree_purge_orphans(branch->priv->element_tree->e_map,
1021 branch->priv->element_tree->root_eid,
1023 branch->priv->is_flat = TRUE;
1024 return SVN_NO_ERROR;
1027 /* An #svn_branch__state_t method. */
1028 static svn_error_t *
1029 branch_state_get_history(svn_branch__state_t *branch,
1030 svn_branch__history_t **history_p,
1031 apr_pool_t *result_pool)
1036 = svn_branch__history_dup(branch->priv->history, result_pool);
1038 return SVN_NO_ERROR;
1041 /* An #svn_branch__state_t method. */
1042 static svn_error_t *
1043 branch_state_set_history(svn_branch__state_t *branch,
1044 const svn_branch__history_t *history,
1045 apr_pool_t *scratch_pool)
1047 apr_pool_t *branch_pool = branch_state_pool_get(branch);
1049 branch->priv->history
1050 = svn_branch__history_dup(history, branch_pool);
1051 return SVN_NO_ERROR;
1055 svn_branch__get_path_by_eid(const svn_branch__state_t *branch,
1057 apr_pool_t *result_pool)
1059 svn_element__tree_t *elements;
1061 SVN_ERR_ASSERT_NO_RETURN(EID_IS_ALLOCATED(branch, eid));
1062 /*SVN_ERR_ASSERT_NO_RETURN(branch->priv->is_flat);*/
1064 svn_error_clear(svn_branch__state_get_elements(branch, &elements, result_pool));
1065 return svn_element__tree_get_path_by_eid(elements, eid, result_pool);
1069 svn_branch__get_eid_by_path(const svn_branch__state_t *branch,
1071 apr_pool_t *scratch_pool)
1073 svn_element__tree_t *elements;
1074 apr_hash_index_t *hi;
1076 /*SVN_ERR_ASSERT_NO_RETURN(branch->priv->is_flat);*/
1078 /* ### This is a crude, linear search */
1079 svn_error_clear(svn_branch__state_get_elements(branch, &elements, scratch_pool));
1080 for (hi = apr_hash_first(scratch_pool, elements->e_map);
1081 hi; hi = apr_hash_next(hi))
1083 int eid = svn_eid__hash_this_key(hi);
1084 const char *this_path = svn_element__tree_get_path_by_eid(elements, eid,
1089 /* Mapping is not complete; this element is in effect not present. */
1092 if (strcmp(path, this_path) == 0)
1101 /* Create a copy of NEW_SUBTREE in TO_BRANCH.
1103 * For each non-root element in NEW_SUBTREE, create a new element with
1104 * a new EID, no matter what EID is used to represent it in NEW_SUBTREE.
1106 * For the new subtree root element, if TO_EID is -1, generate a new EID,
1107 * otherwise alter (if it exists) or instantiate the element TO_EID.
1109 * Set the new subtree root element's parent to NEW_PARENT_EID and name to
1112 static svn_error_t *
1113 svn_branch__map_add_subtree(svn_branch__state_t *to_branch,
1115 svn_branch__eid_t new_parent_eid,
1116 const char *new_name,
1117 svn_element__tree_t *new_subtree,
1118 apr_pool_t *scratch_pool)
1120 apr_hash_index_t *hi;
1121 svn_element__content_t *new_root_content;
1123 /* Get a new EID for the root element, if not given. */
1126 SVN_ERR(svn_branch__txn_new_eid(to_branch->txn, &to_eid,
1130 /* Create the new subtree root element */
1131 new_root_content = svn_element__tree_get(new_subtree, new_subtree->root_eid);
1132 new_root_content = svn_element__content_create(new_parent_eid, new_name,
1133 new_root_content->payload,
1135 SVN_ERR(branch_state_set_element(to_branch, to_eid, new_root_content,
1138 /* Process its immediate children */
1139 for (hi = apr_hash_first(scratch_pool, new_subtree->e_map);
1140 hi; hi = apr_hash_next(hi))
1142 int this_from_eid = svn_eid__hash_this_key(hi);
1143 svn_element__content_t *from_element = apr_hash_this_val(hi);
1145 if (from_element->parent_eid == new_subtree->root_eid)
1147 svn_element__tree_t *this_subtree;
1149 /* Recurse. (We don't try to check whether it's a directory node,
1150 as we might not have the node kind in the map.) */
1152 = svn_element__tree_create(new_subtree->e_map, this_from_eid,
1154 SVN_ERR(svn_branch__map_add_subtree(to_branch, -1 /*to_eid*/,
1155 to_eid, from_element->name,
1156 this_subtree, scratch_pool));
1160 return SVN_NO_ERROR;
1163 /* Instantiate elements in a branch.
1165 * In TO_BRANCH, instantiate (or alter, if existing) each element of
1166 * ELEMENTS, each with its given tree structure (parent, name) and payload.
1168 static svn_error_t *
1169 branch_instantiate_elements(svn_branch__state_t *to_branch,
1170 const svn_element__tree_t *elements,
1171 apr_pool_t *scratch_pool)
1173 apr_hash_index_t *hi;
1175 for (hi = apr_hash_first(scratch_pool, elements->e_map);
1176 hi; hi = apr_hash_next(hi))
1178 int this_eid = svn_eid__hash_this_key(hi);
1179 svn_element__content_t *this_element = apr_hash_this_val(hi);
1181 branch_map_set(to_branch, this_eid,
1182 svn_element__content_dup(
1184 apr_hash_pool_get(to_branch->priv->element_tree->e_map)));
1187 return SVN_NO_ERROR;
1191 * ========================================================================
1192 * Branch State Object
1193 * ========================================================================
1197 svn_branch__state_get_elements(const svn_branch__state_t *branch,
1198 svn_element__tree_t **element_tree_p,
1199 apr_pool_t *result_pool)
1201 SVN_ERR(branch->vtable->get_elements(branch,
1204 return SVN_NO_ERROR;
1208 svn_branch__state_get_element(const svn_branch__state_t *branch,
1209 svn_element__content_t **element_p,
1211 apr_pool_t *result_pool)
1213 SVN_ERR(branch->vtable->get_element(branch,
1214 element_p, eid, result_pool));
1215 return SVN_NO_ERROR;
1219 svn_branch__state_set_element(svn_branch__state_t *branch,
1221 const svn_element__content_t *element,
1222 apr_pool_t *scratch_pool)
1224 SVN_ERR(branch->vtable->set_element(branch,
1227 return SVN_NO_ERROR;
1231 svn_branch__state_alter_one(svn_branch__state_t *branch,
1232 svn_branch__eid_t eid,
1233 svn_branch__eid_t new_parent_eid,
1234 const char *new_name,
1235 const svn_element__payload_t *new_payload,
1236 apr_pool_t *scratch_pool)
1238 svn_element__content_t *element
1239 = svn_element__content_create(new_parent_eid, new_name, new_payload,
1242 SVN_ERR(svn_branch__state_set_element(branch, eid, element, scratch_pool));
1243 return SVN_NO_ERROR;
1247 svn_branch__state_copy_tree(svn_branch__state_t *branch,
1248 const svn_branch__rev_bid_eid_t *src_el_rev,
1249 svn_branch__eid_t new_parent_eid,
1250 const char *new_name,
1251 apr_pool_t *scratch_pool)
1253 SVN_ERR(branch->vtable->copy_tree(branch,
1254 src_el_rev, new_parent_eid, new_name,
1256 return SVN_NO_ERROR;
1260 svn_branch__state_delete_one(svn_branch__state_t *branch,
1261 svn_branch__eid_t eid,
1262 apr_pool_t *scratch_pool)
1264 SVN_ERR(svn_branch__state_set_element(branch, eid, NULL, scratch_pool));
1265 return SVN_NO_ERROR;
1269 svn_branch__state_purge(svn_branch__state_t *branch,
1270 apr_pool_t *scratch_pool)
1272 SVN_ERR(branch->vtable->purge(branch,
1274 return SVN_NO_ERROR;
1278 svn_branch__state_get_history(svn_branch__state_t *branch,
1279 svn_branch__history_t **history_p,
1280 apr_pool_t *result_pool)
1282 SVN_ERR(branch->vtable->get_history(branch,
1285 SVN_ERR_ASSERT(*history_p);
1286 return SVN_NO_ERROR;
1290 svn_branch__state_set_history(svn_branch__state_t *branch,
1291 const svn_branch__history_t *history,
1292 apr_pool_t *scratch_pool)
1294 SVN_ERR_ASSERT(history);
1295 SVN_ERR(branch->vtable->set_history(branch,
1298 return SVN_NO_ERROR;
1301 svn_branch__state_t *
1302 svn_branch__state_create(const svn_branch__state_vtable_t *vtable,
1303 svn_cancel_func_t cancel_func,
1305 apr_pool_t *result_pool)
1307 svn_branch__state_t *b = apr_pcalloc(result_pool, sizeof(*b));
1309 b->vtable = apr_pmemdup(result_pool, vtable, sizeof(*vtable));
1311 b->vtable->vpriv.cancel_func = cancel_func;
1312 b->vtable->vpriv.cancel_baton = cancel_baton;
1314 #ifdef ENABLE_ORDERING_CHECK
1315 b->vtable->vpriv.within_callback = FALSE;
1316 b->vtable->vpriv.finished = FALSE;
1317 b->vtable->vpriv.state_pool = result_pool;
1323 /* Create a new branch state object.
1325 * It will have no elements (not even a root element).
1327 static svn_branch__state_t *
1328 branch_state_create(const char *bid,
1330 svn_branch__txn_t *txn,
1331 apr_pool_t *result_pool)
1333 static const svn_branch__state_vtable_t vtable = {
1335 branch_state_get_elements,
1336 branch_state_get_element,
1337 branch_state_set_element,
1338 branch_state_copy_one,
1339 branch_state_copy_tree,
1341 branch_state_get_history,
1342 branch_state_set_history,
1344 svn_branch__state_t *b
1345 = svn_branch__state_create(&vtable, NULL, NULL, result_pool);
1347 b->priv = apr_pcalloc(result_pool, sizeof(*b->priv));
1348 b->bid = apr_pstrdup(result_pool, bid);
1350 b->priv->element_tree = svn_element__tree_create(NULL, root_eid, result_pool);
1351 assert_branch_state_invariants(b, result_pool);
1352 b->priv->is_flat = TRUE;
1353 b->priv->history = svn_branch__history_create_empty(result_pool);
1358 * ========================================================================
1359 * Parsing and Serializing
1360 * ========================================================================
1364 svn_branch__get_default_r0_metadata(apr_pool_t *result_pool)
1366 static const char *default_repos_info
1367 = "r0: eids 0 1 branches 1\n"
1368 "B0 root-eid 0 num-eids 1\n"
1369 "history: parents 0\n"
1370 "e0: normal -1 .\n";
1372 return svn_string_create(default_repos_info, result_pool);
1376 static svn_error_t *
1377 parse_branch_line(char *bid_p,
1380 svn_stream_t *stream,
1381 apr_pool_t *result_pool,
1382 apr_pool_t *scratch_pool)
1384 svn_stringbuf_t *line;
1389 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
1390 SVN_ERR_ASSERT(!eof);
1392 n = sscanf(line->data, "%s root-eid %d num-eids %d",
1393 bid_p, root_eid_p, num_eids_p);
1394 SVN_ERR_ASSERT(n == 3);
1396 return SVN_NO_ERROR;
1399 /* Parse the history metadata for BRANCH.
1401 static svn_error_t *
1402 history_parse(svn_branch__history_t **history_p,
1403 svn_stream_t *stream,
1404 apr_pool_t *result_pool,
1405 apr_pool_t *scratch_pool)
1407 svn_branch__history_t *history
1408 = svn_branch__history_create_empty(result_pool);
1409 svn_stringbuf_t *line;
1416 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
1417 SVN_ERR_ASSERT(!eof);
1419 n = sscanf(line->data, "history: parents %d",
1421 SVN_ERR_ASSERT(n == 1);
1423 for (i = 0; i < num_parents; i++)
1428 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
1429 SVN_ERR_ASSERT(!eof);
1431 n = sscanf(line->data, "parent: r%ld.%99s",
1433 SVN_ERR_ASSERT(n == 2);
1435 svn_hash_sets(history->parents,
1436 apr_pstrdup(result_pool, bid),
1437 svn_branch__rev_bid_create(rev, bid, result_pool));
1441 *history_p = history;
1442 return SVN_NO_ERROR;
1445 /* Parse the mapping for one element.
1447 static svn_error_t *
1448 parse_element_line(int *eid_p,
1449 svn_boolean_t *is_subbranch_p,
1451 const char **name_p,
1452 svn_stream_t *stream,
1453 apr_pool_t *result_pool,
1454 apr_pool_t *scratch_pool)
1456 svn_stringbuf_t *line;
1463 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
1464 SVN_ERR_ASSERT(!eof);
1466 n = sscanf(line->data, "e%d: %9s %d%n",
1468 kind, parent_eid_p, &offset);
1469 SVN_ERR_ASSERT(n >= 3); /* C std is unclear on whether '%n' counts */
1470 SVN_ERR_ASSERT(line->data[offset] == ' ');
1472 *name_p = apr_pstrdup(result_pool, line->data + offset + 1);
1473 *is_subbranch_p = (strcmp(kind, "subbranch") == 0);
1475 if (strcmp(*name_p, "(null)") == 0)
1477 else if (strcmp(*name_p, ".") == 0)
1480 return SVN_NO_ERROR;
1484 svn_branch__id_nest(const char *outer_bid,
1486 apr_pool_t *result_pool)
1489 return apr_psprintf(result_pool, "B%d", outer_eid);
1491 return apr_psprintf(result_pool, "%s.%d", outer_bid, outer_eid);
1495 svn_branch__id_unnest(const char **outer_bid,
1498 apr_pool_t *result_pool)
1500 char *last_dot = strrchr(bid, '.');
1502 if (last_dot) /* BID looks like "B3.11" or "B3.11.22" etc. */
1504 *outer_bid = apr_pstrndup(result_pool, bid, last_dot - bid);
1505 *outer_eid = atoi(last_dot + 1);
1507 else /* looks like "B0" or B22" (with no dot) */
1510 *outer_eid = atoi(bid + 1);
1514 /* Create a new branch *NEW_BRANCH, initialized
1515 * with info parsed from STREAM, allocated in RESULT_POOL.
1517 static svn_error_t *
1518 svn_branch__state_parse(svn_branch__state_t **new_branch,
1519 svn_branch__txn_t *txn,
1520 svn_stream_t *stream,
1521 apr_pool_t *result_pool,
1522 apr_pool_t *scratch_pool)
1525 int root_eid, num_eids;
1526 svn_branch__state_t *branch_state;
1529 SVN_ERR(parse_branch_line(bid, &root_eid, &num_eids,
1530 stream, scratch_pool, scratch_pool));
1532 branch_state = branch_state_create(bid, root_eid, txn,
1535 /* Read in the merge history. */
1536 SVN_ERR(history_parse(&branch_state->priv->history,
1537 stream, result_pool, scratch_pool));
1539 /* Read in the structure. Set the payload of each normal element to a
1540 (branch-relative) reference. */
1541 for (i = 0; i < num_eids; i++)
1543 int eid, this_parent_eid;
1544 const char *this_name;
1545 svn_boolean_t is_subbranch;
1547 SVN_ERR(parse_element_line(&eid,
1548 &is_subbranch, &this_parent_eid, &this_name,
1549 stream, scratch_pool, scratch_pool));
1553 svn_element__payload_t *payload;
1554 svn_element__content_t *element;
1558 payload = svn_element__payload_create_ref(txn->rev, bid, eid,
1564 = svn_element__payload_create_subbranch(result_pool);
1566 element = svn_element__content_create(this_parent_eid,
1569 SVN_ERR(branch_state_set_element(branch_state, eid, element,
1574 branch_state->priv->is_flat = TRUE;
1575 *new_branch = branch_state;
1576 return SVN_NO_ERROR;
1580 svn_branch__txn_parse(svn_branch__txn_t **txn_p,
1581 svn_branch__repos_t *repos,
1582 svn_stream_t *stream,
1583 apr_pool_t *result_pool,
1584 apr_pool_t *scratch_pool)
1586 svn_branch__txn_t *txn;
1588 int first_eid, next_eid;
1590 svn_stringbuf_t *line;
1595 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool));
1596 SVN_ERR_ASSERT(! eof);
1597 n = sscanf(line->data, "r%ld: eids %d %d "
1600 &first_eid, &next_eid,
1602 SVN_ERR_ASSERT(n == 4);
1604 txn = branch_txn_create(repos, rev, rev - 1, result_pool);
1605 txn->priv->first_eid = first_eid;
1606 txn->priv->next_eid = next_eid;
1608 /* parse the branches */
1609 for (j = 0; j < num_branches; j++)
1611 svn_branch__state_t *branch;
1613 SVN_ERR(svn_branch__state_parse(&branch, txn, stream,
1614 result_pool, scratch_pool));
1615 APR_ARRAY_PUSH(txn->priv->branches, void *) = branch;
1619 return SVN_NO_ERROR;
1622 /* Serialize the history metadata for BRANCH.
1624 static svn_error_t *
1625 history_serialize(svn_stream_t *stream,
1626 svn_branch__history_t *history,
1627 apr_pool_t *scratch_pool)
1629 apr_array_header_t *ancestors_sorted;
1632 /* Write entries in sorted order for stability -- so that for example
1633 we can test parse-then-serialize by expecting identical output. */
1634 ancestors_sorted = svn_sort__hash(history->parents,
1635 svn_sort_compare_items_lexically,
1637 SVN_ERR(svn_stream_printf(stream, scratch_pool,
1638 "history: parents %d\n",
1639 ancestors_sorted->nelts));
1640 for (i = 0; i < ancestors_sorted->nelts; i++)
1642 svn_sort__item_t *item
1643 = &APR_ARRAY_IDX(ancestors_sorted, i, svn_sort__item_t);
1644 svn_branch__rev_bid_t *rev_bid = item->value;
1646 SVN_ERR(svn_stream_printf(stream, scratch_pool,
1647 "parent: r%ld.%s\n",
1648 rev_bid->rev, rev_bid->bid));
1651 return SVN_NO_ERROR;
1654 /* Write to STREAM a parseable representation of BRANCH.
1657 svn_branch__state_serialize(svn_stream_t *stream,
1658 svn_branch__state_t *branch,
1659 apr_pool_t *scratch_pool)
1661 svn_eid__hash_iter_t *ei;
1663 SVN_ERR_ASSERT(branch->priv->is_flat);
1665 SVN_ERR(svn_stream_printf(stream, scratch_pool,
1666 "%s root-eid %d num-eids %d\n",
1667 svn_branch__get_id(branch, scratch_pool),
1668 branch->priv->element_tree->root_eid,
1669 apr_hash_count(branch->priv->element_tree->e_map)));
1671 SVN_ERR(history_serialize(stream, branch->priv->history,
1674 for (SVN_EID__HASH_ITER_SORTED_BY_EID(ei, branch->priv->element_tree->e_map,
1678 svn_element__content_t *element = branch_get_element(branch, eid);
1682 SVN_ERR_ASSERT(element);
1683 parent_eid = element->parent_eid;
1684 name = element->name[0] ? element->name : ".";
1685 SVN_ERR(svn_stream_printf(stream, scratch_pool,
1688 element ? ((! element->payload->is_subbranch_root)
1689 ? "normal" : "subbranch")
1693 return SVN_NO_ERROR;
1697 * ========================================================================