3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
24 * @brief Operating on a branched version history
31 * A 'txn' contains a set of changes to the branches/elements.
33 * To make changes you say, for example, "for element 5: I want the parent
34 * element to be 3 now, and its name to be 'bar', and its content to be
35 * {props=... text=...}". That sets up a move and/or rename and/or
36 * content-change (or possibly a no-op for all three aspects) for element 5.
38 * Before or after (or at the same time, if we make a parallelizable
39 * implementation) we can make edits to the other elements, including
42 * So at the time of the edit method 'change e5: let its parent be e3'
43 * we might or might not have even created e3, if that happens to be an
44 * element that we wish to create rather than one that already existed.
46 * We allow this non-ordering because we want the changes to different
47 * elements to be totally independent.
49 * So at any given 'moment' in time during specifying the changes to a
50 * txn, the txn state is not necessarily one that maps directly to a
51 * flat tree (single-rooted, no cycles, no clashes of paths, etc.).
53 * Once we've finished specifying the edits, then the txn state will be
54 * converted to a flat tree, and that's the final result. But we can't
55 * query an arbitrary txn (potentially in the middle of making changes
56 * to it) by path, because the paths are not fully defined yet.
58 * So there are three kinds of operations:
60 * - query involving paths
61 * => requires a flat tree state to query, not an in-progress txn
63 * - query, not involving paths
64 * => accepts a txn-in-progress or a flat tree
66 * - modify (not involving paths)
69 * Currently, a txn is represented by 'svn_branch__txn_t', with
70 * 'svn_branch__state_t' for the individual branches in it. A flat tree is
71 * represented by 'svn_branch__subtree_t'. But there is currently not a
72 * clean separation; there is some overlap and some warts such as the
73 * 'svn_branch__txn_sequence_point' method.
80 #include <apr_pools.h>
82 #include "svn_types.h"
83 #include "svn_error.h"
84 #include "svn_io.h" /* for svn_stream_t */
85 #include "svn_delta.h"
87 #include "private/svn_element.h"
91 #endif /* __cplusplus */
95 #define SVN_BRANCH__ERR 123456
97 /** Element Identifier (EID).
99 * An element may appear in any or all branches, and its EID is the same in
100 * each branch in which the element appears.
102 * By definition, an element keeps the same EID for its whole lifetime, even
103 * if deleted from all branches and later 'resurrected'.
105 * In principle, an EID is an arbitrary token and has no intrinsic
106 * relationships (except equality) to other EIDs. The current implementation
107 * uses integers and allocates them sequentially from a central counter, but
108 * the implementation may be changed.
110 * ### In most places the code currently says 'int', verbatim.
112 typedef int svn_branch__eid_t;
114 typedef struct svn_branch__el_rev_id_t svn_branch__el_rev_id_t;
116 typedef struct svn_branch__rev_bid_eid_t svn_branch__rev_bid_eid_t;
118 typedef struct svn_branch__rev_bid_t svn_branch__rev_bid_t;
120 typedef struct svn_branch__state_t svn_branch__state_t;
122 /* Per-repository branching info.
124 typedef struct svn_branch__repos_t svn_branch__repos_t;
126 /* Methods (conceptually public, but called indirectly) for a transaction.
128 typedef struct svn_branch__txn_vtable_t svn_branch__txn_vtable_t;
130 /* Private data for a transaction.
132 typedef struct svn_branch__txn_priv_t svn_branch__txn_priv_t;
134 /* A container for all the branching metadata for a specific revision (or
135 * an uncommitted transaction).
137 typedef struct svn_branch__txn_t
139 /* Methods (conceptually public, but called indirectly). */
140 svn_branch__txn_vtable_t *vtable;
143 svn_branch__txn_priv_t *priv;
147 /* The repository in which this revision exists. */
148 svn_branch__repos_t *repos;
150 /* If committed, the revision number; else SVN_INVALID_REVNUM. */
153 /* If committed, the previous revision number, else the revision number
154 on which this transaction is based. */
155 svn_revnum_t base_rev;
159 /* Create a new branch txn object.
162 svn_branch__txn_create(const svn_branch__txn_vtable_t *vtable,
163 svn_cancel_func_t cancel_func,
165 apr_pool_t *result_pool);
167 /* Return all the branches in TXN.
169 * These branches are available for reading. (Some of them may also be
172 * ### Rename to 'list_branches' & return only their ids?
174 * Return an empty array if there are none.
177 svn_branch__txn_get_branches(const svn_branch__txn_t *txn,
178 apr_pool_t *result_pool);
180 /* Return the branch whose id is BRANCH_ID in TXN.
182 * Return NULL if not found.
184 * Note: a branch id is, in behavioural terms, an arbitrary token. In the
185 * current implementation it is constructed from the hierarchy of subbranch
186 * root EIDs leading to the branch, but that may be changed in future.
188 * See also: svn_branch__get_id().
190 svn_branch__state_t *
191 svn_branch__txn_get_branch_by_id(const svn_branch__txn_t *txn,
192 const char *branch_id,
193 apr_pool_t *scratch_pool);
196 svn_branch__txn_get_num_new_eids(const svn_branch__txn_t *txn,
198 apr_pool_t *scratch_pool);
200 /* Assign a new txn-scope element id in TXN.
203 svn_branch__txn_new_eid(svn_branch__txn_t *txn,
205 apr_pool_t *scratch_pool);
207 /** Open for writing, either a new branch or an existing branch.
209 * When creating a new branch, declare its root element id to be ROOT_EID. Do
210 * not instantiate the root element, nor any other elements.
212 * TREE_REF specifies the initial tree content, by reference to a committed
213 * tree. It overwrites any existing tree, even if the branch was already
214 * mutable in the txn.
216 * If TREE_REF is null, then the initial tree is empty for a new branch
217 * (not already present in the txn), or the branch's current tree if the
218 * branch was already present (readable or mutable) in the txn.
220 * ### TODO: Take a 'history' parameter; 'none' is a valid option.
222 * We use a common 'open subbranch' method for both 'find' and 'add'
223 * cases, according to the principle that 'editing' a txn should dictate
224 * the new state without reference to the old state.
226 * This method returns a mutable 'branch state' object which is a part of
229 * ### When opening ('finding') an existing branch, ROOT_EID should match
230 * it. (Should we check, and throw an error if not?)
233 svn_branch__txn_open_branch(svn_branch__txn_t *txn,
234 svn_branch__state_t **new_branch_p,
235 const char *branch_id,
237 svn_branch__rev_bid_eid_t *tree_ref,
238 apr_pool_t *result_pool,
239 apr_pool_t *scratch_pool);
241 /** Register a sequence point.
243 * At a sequence point, elements are arranged in a tree hierarchy: each
244 * element has exactly one parent element, except the root, and so on.
245 * Translation between paths and element addressing is defined only at
248 * The other edit operations -- add, alter, delete, etc. -- result in a
249 * state that is not a sequence point.
251 * The new transaction begins at a sequence point. Completion of editing
252 * (svn_branch__txn_complete()) also creates a sequence point.
255 svn_branch__txn_sequence_point(svn_branch__txn_t *txn,
256 apr_pool_t *scratch_pool);
258 /** Finalize this transaction.
260 * Notify that the edit has been completed successfully.
263 svn_branch__txn_complete(svn_branch__txn_t *txn,
264 apr_pool_t *scratch_pool);
266 /** Abandon this transaction.
268 * Notify that editing this transaction was not successful.
271 svn_branch__txn_abort(svn_branch__txn_t *txn,
272 apr_pool_t *scratch_pool);
274 /* Change txn-local EIDs (negative integers) in TXN to revision EIDs, by
275 * assigning a new revision-EID (positive integer) for each one.
277 * Rewrite TXN->first_eid and TXN->next_eid accordingly.
280 svn_branch__txn_finalize_eids(svn_branch__txn_t *txn,
281 apr_pool_t *scratch_pool);
283 /* Often, branches have the same root element. For example,
284 * branching /trunk to /branches/br1 results in:
286 * branch 1: (root-EID=100)
289 * branch 2: (root-EID=100)
290 * EID 100 => /branches/br1
293 * However, the root element of one branch may correspond to a non-root
294 * element of another branch.
296 * Continuing the same example, branching from the trunk subtree
297 * /trunk/D (which is not itself a branch root) results in:
299 * branch 3: (root-EID=104)
302 * EID 104 => /branches/branch-of-trunk-subtree-D
306 /* Methods (conceptually public, but called indirectly) for a branch state.
308 typedef struct svn_branch__state_vtable_t svn_branch__state_vtable_t;
310 /* Private data for a branch state.
312 typedef struct svn_branch__state_priv_t svn_branch__state_priv_t;
316 * A branch state object describes one version of one branch.
318 struct svn_branch__state_t
320 /* Methods (conceptually public, but called indirectly). */
321 svn_branch__state_vtable_t *vtable;
324 svn_branch__state_priv_t *priv;
328 /* The branch identifier (starting with 'B') */
331 /* The revision to which this branch state belongs */
332 /* ### Later we should remove this and let a single state be sharable
334 svn_branch__txn_t *txn;
338 /* Create a new branch state object.
340 svn_branch__state_t *
341 svn_branch__state_create(const svn_branch__state_vtable_t *vtable,
342 svn_cancel_func_t cancel_func,
344 apr_pool_t *result_pool);
346 /* Get the full id of branch BRANCH.
349 * B<top-level-branch-num>[.<1st-level-eid>[.<2nd-level-eid>[...]]]
351 * Note: a branch id is, in behavioural terms, an arbitrary token. In the
352 * current implementation it is constructed from the hierarchy of subbranch
353 * root EIDs leading to the branch, but that may be changed in future.
355 * See also: svn_branch__txn_get_branch_by_id().
358 svn_branch__get_id(const svn_branch__state_t *branch,
359 apr_pool_t *result_pool);
361 /* Return the element id of the root element of BRANCH.
364 svn_branch__root_eid(const svn_branch__state_t *branch);
366 /* Return the id of the branch nested in OUTER_BID at element OUTER_EID.
368 * For a top-level branch, OUTER_BID is null and OUTER_EID is the
369 * top-level branch number.
371 * (Such branches need not exist. This works purely with ids, making use
372 * of the fact that nested branch ids are predictable based on the nesting
376 svn_branch__id_nest(const char *outer_bid,
378 apr_pool_t *result_pool);
380 /* Given a nested branch id BID, set *OUTER_BID to the outer branch's id
381 * and *OUTER_EID to the nesting element in the outer branch.
383 * For a top-level branch, set *OUTER_BID to NULL and *OUTER_EID to the
384 * top-level branch number.
386 * (Such branches need not exist. This works purely with ids, making use
387 * of the fact that nested branch ids are predictable based on the nesting
391 svn_branch__id_unnest(const char **outer_bid,
394 apr_pool_t *result_pool);
396 /* Remove the branch with id BID from the list of branches in TXN.
399 svn_branch__txn_delete_branch(svn_branch__txn_t *txn,
401 apr_pool_t *scratch_pool);
403 /* Branch-Element-Revision */
404 struct svn_branch__el_rev_id_t
406 /* The branch state that applies to REV. */
407 svn_branch__state_t *branch;
410 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'.
411 ### Do we need this if BRANCH refers to a particular branch-revision? */
416 /* Revision-branch-element id. */
417 struct svn_branch__rev_bid_eid_t
419 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */
421 /* The branch id in revision REV. */
428 /* Revision-branch id. */
429 struct svn_branch__rev_bid_t
431 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */
433 /* The branch id in revision REV. */
438 /* Return a new el_rev_id object constructed with *shallow* copies of BRANCH,
439 * EID and REV, allocated in RESULT_POOL.
441 svn_branch__el_rev_id_t *
442 svn_branch__el_rev_id_create(svn_branch__state_t *branch,
445 apr_pool_t *result_pool);
447 /* Return a new id object constructed with a deep copy of OLD_ID,
448 * allocated in RESULT_POOL. */
449 svn_branch__el_rev_id_t *
450 svn_branch__el_rev_id_dup(const svn_branch__el_rev_id_t *old_id,
451 apr_pool_t *result_pool);
453 /* Return a new id object constructed with deep copies of REV, BRANCH_ID
454 * and EID, allocated in RESULT_POOL.
456 svn_branch__rev_bid_eid_t *
457 svn_branch__rev_bid_eid_create(svn_revnum_t rev,
458 const char *branch_id,
460 apr_pool_t *result_pool);
461 svn_branch__rev_bid_t *
462 svn_branch__rev_bid_create(svn_revnum_t rev,
463 const char *branch_id,
464 apr_pool_t *result_pool);
466 /* Return a new id object constructed with a deep copy of OLD_ID,
467 * allocated in RESULT_POOL. */
468 svn_branch__rev_bid_eid_t *
469 svn_branch__rev_bid_eid_dup(const svn_branch__rev_bid_eid_t *old_id,
470 apr_pool_t *result_pool);
471 svn_branch__rev_bid_t *
472 svn_branch__rev_bid_dup(const svn_branch__rev_bid_t *old_id,
473 apr_pool_t *result_pool);
476 svn_branch__rev_bid_equal(const svn_branch__rev_bid_t *id1,
477 const svn_branch__rev_bid_t *id2);
479 typedef struct svn_branch__history_t
481 /* The immediate parents of this state in the branch/merge graph.
482 Hash of (BID -> svn_branch__rev_bid_t). */
484 } svn_branch__history_t;
486 svn_branch__history_t *
487 svn_branch__history_create_empty(apr_pool_t *result_pool);
489 svn_branch__history_t *
490 svn_branch__history_create(apr_hash_t *parents,
491 apr_pool_t *result_pool);
493 svn_branch__history_t *
494 svn_branch__history_dup(const svn_branch__history_t *old,
495 apr_pool_t *result_pool);
497 /* Return the mapping of elements in branch BRANCH.
500 svn_branch__state_get_elements(const svn_branch__state_t *branch,
501 svn_element__tree_t **element_tree_p,
502 apr_pool_t *result_pool);
504 /* In BRANCH, get element EID (parent, name, payload).
506 * If element EID is not present, return null.
509 svn_branch__state_get_element(const svn_branch__state_t *branch,
510 svn_element__content_t **element_p,
512 apr_pool_t *result_pool);
515 * alter_one(..., element->parent_eid, element->name, element->payload),
516 * or, if @a element is null, to
520 svn_branch__state_set_element(svn_branch__state_t *branch,
522 const svn_element__content_t *element,
523 apr_pool_t *result_pool);
525 /** Specify that the element of @a branch identified by @a eid shall not
528 * The delete is not explicitly recursive. However, as an effect of the
529 * final 'flattening' of a branch state into a single tree, each element
530 * in the final state that still has this element as its parent will also
531 * be deleted, recursively.
533 * The element @a eid must not be the root element of @a branch.
535 * ### Options for Out-Of-Date Checking on Rebase
537 * We may want to specify what kind of OOD check takes place. The
538 * following two options differ in what happens to an element that is
539 * added, on the other side, as a child of this deleted element.
541 * Rebase option 1: The rebase checks for changes in the whole subtree,
542 * excluding any portions of the subtree for which an explicit delete or
543 * move-away has been issued. The check includes checking that the other
544 * side has not added any child. In other words, the deletion is
545 * interpreted as an action affecting a subtree (dynamically rooted at
546 * this element), rather than as an action affecting a single element or
547 * a fixed set of elements that was explicitly or implicitly specified
550 * To delete a mixed-rev subtree, the client sends an explicit delete for
551 * each subtree that has a different base revision from its parent.
553 * Rebase option 2: The rebase checks for changes to this element only.
554 * The sender can send an explicit delete for each existing child element
555 * that it requires to be checked as well. However, there is no way for
556 * the sender to specify whether a child element added by the other side
557 * should be considered an out-of-date error or silently deleted.
559 * It would also be possible to let the caller specify, at some suitable
560 * granularity, which option to use.
563 svn_branch__state_delete_one(svn_branch__state_t *branch,
564 svn_branch__eid_t eid,
565 apr_pool_t *scratch_pool);
567 /** Specify the tree position and payload of the element of @a branch
568 * identified by @a eid.
570 * Set the element's parent EID, name and payload to @a new_parent_eid,
571 * @a new_name and @a new_payload respectively.
573 * This may create a new element or alter an existing element.
575 * If the element ... we can describe the effect as ...
577 * exists in the branch => altering it;
578 * previously existed in the branch => resurrecting it;
579 * only existed in other branches => branching it;
580 * never existed anywhere => creating or adding it.
582 * However, these are imprecise descriptions and not mutually exclusive.
583 * For example, if it existed previously in this branch and another, then
584 * we may describe the result as 'resurrecting' and/or as 'branching'.
586 * Duplicate @a new_name and @a new_payload into the branch's pool.
589 svn_branch__state_alter_one(svn_branch__state_t *branch,
590 svn_branch__eid_t eid,
591 svn_branch__eid_t new_parent_eid,
592 const char *new_name,
593 const svn_element__payload_t *new_payload,
594 apr_pool_t *scratch_pool);
597 svn_branch__state_copy_tree(svn_branch__state_t *branch,
598 const svn_branch__rev_bid_eid_t *src_el_rev,
599 svn_branch__eid_t new_parent_eid,
600 const char *new_name,
601 apr_pool_t *scratch_pool);
603 /* Purge orphaned elements in BRANCH.
606 svn_branch__state_purge(svn_branch__state_t *branch,
607 apr_pool_t *scratch_pool);
609 /* Get the merge history of BRANCH.
612 svn_branch__state_get_history(svn_branch__state_t *branch,
613 svn_branch__history_t **merge_history_p,
614 apr_pool_t *result_pool);
616 /* Set the merge history of BRANCH.
619 svn_branch__state_set_history(svn_branch__state_t *branch,
620 const svn_branch__history_t *merge_history,
621 apr_pool_t *scratch_pool);
623 /* Return the branch-relative path of element EID in BRANCH.
625 * If the element EID does not currently exist in BRANCH, return NULL.
627 * ### TODO: Clarify sequencing requirements.
630 svn_branch__get_path_by_eid(const svn_branch__state_t *branch,
632 apr_pool_t *result_pool);
634 /* Return the EID for the branch-relative path PATH in BRANCH.
636 * If no element of BRANCH is at this path, return -1.
638 * ### TODO: Clarify sequencing requirements.
641 svn_branch__get_eid_by_path(const svn_branch__state_t *branch,
643 apr_pool_t *scratch_pool);
645 /* Get the default branching metadata for r0 of a new repository.
648 svn_branch__get_default_r0_metadata(apr_pool_t *result_pool);
650 /* Create a new txn object *TXN_P, initialized with info
651 * parsed from STREAM, allocated in RESULT_POOL.
654 svn_branch__txn_parse(svn_branch__txn_t **txn_p,
655 svn_branch__repos_t *repos,
656 svn_stream_t *stream,
657 apr_pool_t *result_pool,
658 apr_pool_t *scratch_pool);
660 /* Write to STREAM a parseable representation of TXN.
663 svn_branch__txn_serialize(svn_branch__txn_t *txn,
664 svn_stream_t *stream,
665 apr_pool_t *scratch_pool);
667 /* Write to STREAM a parseable representation of BRANCH.
670 svn_branch__state_serialize(svn_stream_t *stream,
671 svn_branch__state_t *branch,
672 apr_pool_t *scratch_pool);
677 #endif /* __cplusplus */
679 #endif /* SVN_BRANCH_H */