1 /** 2 * @copyright 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 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 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 19 * under the License. 20 * ==================================================================== 21 * @endcopyright 22 * 23 * @file svn_branch.h 24 * @brief Operating on a branched version history 25 * 26 * @since New in ???. 27 */ 28 29 /* Transactions 30 * 31 * A 'txn' contains a set of changes to the branches/elements. 32 * 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. 37 * 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 40 * element 3. 41 * 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. 45 * 46 * We allow this non-ordering because we want the changes to different 47 * elements to be totally independent. 48 * 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.). 52 * 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. 57 * 58 * So there are three kinds of operations: 59 * 60 * - query involving paths 61 * => requires a flat tree state to query, not an in-progress txn 62 * 63 * - query, not involving paths 64 * => accepts a txn-in-progress or a flat tree 65 * 66 * - modify (not involving paths) 67 * => requires a txn 68 * 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. 74 */ 75 76 77 #ifndef SVN_BRANCH_H 78 #define SVN_BRANCH_H 79 80 #include <apr_pools.h> 81 82 #include "svn_types.h" 83 #include "svn_error.h" 84 #include "svn_io.h" /* for svn_stream_t */ 85 #include "svn_delta.h" 86 87 #include "private/svn_element.h" 88 89 #ifdef __cplusplus 90 extern "C" { 91 #endif /* __cplusplus */ 92 93 94 /* ### */ 95 #define SVN_BRANCH__ERR 123456 96 97 /** Element Identifier (EID). 98 * 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. 101 * 102 * By definition, an element keeps the same EID for its whole lifetime, even 103 * if deleted from all branches and later 'resurrected'. 104 * 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. 109 * 110 * ### In most places the code currently says 'int', verbatim. 111 */ 112 typedef int svn_branch__eid_t; 113 114 typedef struct svn_branch__el_rev_id_t svn_branch__el_rev_id_t; 115 116 typedef struct svn_branch__rev_bid_eid_t svn_branch__rev_bid_eid_t; 117 118 typedef struct svn_branch__rev_bid_t svn_branch__rev_bid_t; 119 120 typedef struct svn_branch__state_t svn_branch__state_t; 121 122 /* Per-repository branching info. 123 */ 124 typedef struct svn_branch__repos_t svn_branch__repos_t; 125 126 /* Methods (conceptually public, but called indirectly) for a transaction. 127 */ 128 typedef struct svn_branch__txn_vtable_t svn_branch__txn_vtable_t; 129 130 /* Private data for a transaction. 131 */ 132 typedef struct svn_branch__txn_priv_t svn_branch__txn_priv_t; 133 134 /* A container for all the branching metadata for a specific revision (or 135 * an uncommitted transaction). 136 */ 137 typedef struct svn_branch__txn_t 138 { 139 /* Methods (conceptually public, but called indirectly). */ 140 svn_branch__txn_vtable_t *vtable; 141 142 /* Private data. */ 143 svn_branch__txn_priv_t *priv; 144 145 /* Public data. */ 146 147 /* The repository in which this revision exists. */ 148 svn_branch__repos_t *repos; 149 150 /* If committed, the revision number; else SVN_INVALID_REVNUM. */ 151 svn_revnum_t rev; 152 153 /* If committed, the previous revision number, else the revision number 154 on which this transaction is based. */ 155 svn_revnum_t base_rev; 156 157 } svn_branch__txn_t; 158 159 /* Create a new branch txn object. 160 */ 161 svn_branch__txn_t * 162 svn_branch__txn_create(const svn_branch__txn_vtable_t *vtable, 163 svn_cancel_func_t cancel_func, 164 void *cancel_baton, 165 apr_pool_t *result_pool); 166 167 /* Return all the branches in TXN. 168 * 169 * These branches are available for reading. (Some of them may also be 170 * mutable.) 171 * 172 * ### Rename to 'list_branches' & return only their ids? 173 * 174 * Return an empty array if there are none. 175 */ 176 apr_array_header_t * 177 svn_branch__txn_get_branches(const svn_branch__txn_t *txn, 178 apr_pool_t *result_pool); 179 180 /* Return the branch whose id is BRANCH_ID in TXN. 181 * 182 * Return NULL if not found. 183 * 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. 187 * 188 * See also: svn_branch__get_id(). 189 */ 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); 194 195 svn_error_t * 196 svn_branch__txn_get_num_new_eids(const svn_branch__txn_t *txn, 197 int *num_new_eids_p, 198 apr_pool_t *scratch_pool); 199 200 /* Assign a new txn-scope element id in TXN. 201 */ 202 svn_error_t * 203 svn_branch__txn_new_eid(svn_branch__txn_t *txn, 204 int *new_eid_p, 205 apr_pool_t *scratch_pool); 206 207 /** Open for writing, either a new branch or an existing branch. 208 * 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. 211 * 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. 215 * 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. 219 * 220 * ### TODO: Take a 'history' parameter; 'none' is a valid option. 221 * 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. 225 * 226 * This method returns a mutable 'branch state' object which is a part of 227 * the txn. 228 * 229 * ### When opening ('finding') an existing branch, ROOT_EID should match 230 * it. (Should we check, and throw an error if not?) 231 */ 232 svn_error_t * 233 svn_branch__txn_open_branch(svn_branch__txn_t *txn, 234 svn_branch__state_t **new_branch_p, 235 const char *branch_id, 236 int root_eid, 237 svn_branch__rev_bid_eid_t *tree_ref, 238 apr_pool_t *result_pool, 239 apr_pool_t *scratch_pool); 240 241 /** Register a sequence point. 242 * 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 246 * a sequence point. 247 * 248 * The other edit operations -- add, alter, delete, etc. -- result in a 249 * state that is not a sequence point. 250 * 251 * The new transaction begins at a sequence point. Completion of editing 252 * (svn_branch__txn_complete()) also creates a sequence point. 253 */ 254 svn_error_t * 255 svn_branch__txn_sequence_point(svn_branch__txn_t *txn, 256 apr_pool_t *scratch_pool); 257 258 /** Finalize this transaction. 259 * 260 * Notify that the edit has been completed successfully. 261 */ 262 svn_error_t * 263 svn_branch__txn_complete(svn_branch__txn_t *txn, 264 apr_pool_t *scratch_pool); 265 266 /** Abandon this transaction. 267 * 268 * Notify that editing this transaction was not successful. 269 */ 270 svn_error_t * 271 svn_branch__txn_abort(svn_branch__txn_t *txn, 272 apr_pool_t *scratch_pool); 273 274 /* Change txn-local EIDs (negative integers) in TXN to revision EIDs, by 275 * assigning a new revision-EID (positive integer) for each one. 276 * 277 * Rewrite TXN->first_eid and TXN->next_eid accordingly. 278 */ 279 svn_error_t * 280 svn_branch__txn_finalize_eids(svn_branch__txn_t *txn, 281 apr_pool_t *scratch_pool); 282 283 /* Often, branches have the same root element. For example, 284 * branching /trunk to /branches/br1 results in: 285 * 286 * branch 1: (root-EID=100) 287 * EID 100 => /trunk 288 * ... 289 * branch 2: (root-EID=100) 290 * EID 100 => /branches/br1 291 * ... 292 * 293 * However, the root element of one branch may correspond to a non-root 294 * element of another branch. 295 * 296 * Continuing the same example, branching from the trunk subtree 297 * /trunk/D (which is not itself a branch root) results in: 298 * 299 * branch 3: (root-EID=104) 300 * EID 100 => (nil) 301 * ... 302 * EID 104 => /branches/branch-of-trunk-subtree-D 303 * ... 304 */ 305 306 /* Methods (conceptually public, but called indirectly) for a branch state. 307 */ 308 typedef struct svn_branch__state_vtable_t svn_branch__state_vtable_t; 309 310 /* Private data for a branch state. 311 */ 312 typedef struct svn_branch__state_priv_t svn_branch__state_priv_t; 313 314 /* A branch state. 315 * 316 * A branch state object describes one version of one branch. 317 */ 318 struct svn_branch__state_t 319 { 320 /* Methods (conceptually public, but called indirectly). */ 321 svn_branch__state_vtable_t *vtable; 322 323 /* Private data. */ 324 svn_branch__state_priv_t *priv; 325 326 /* Public data. */ 327 328 /* The branch identifier (starting with 'B') */ 329 const char *bid; 330 331 /* The revision to which this branch state belongs */ 332 /* ### Later we should remove this and let a single state be sharable 333 by multiple txns. */ 334 svn_branch__txn_t *txn; 335 336 }; 337 338 /* Create a new branch state object. 339 */ 340 svn_branch__state_t * 341 svn_branch__state_create(const svn_branch__state_vtable_t *vtable, 342 svn_cancel_func_t cancel_func, 343 void *cancel_baton, 344 apr_pool_t *result_pool); 345 346 /* Get the full id of branch BRANCH. 347 * 348 * Branch id format: 349 * B<top-level-branch-num>[.<1st-level-eid>[.<2nd-level-eid>[...]]] 350 * 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. 354 * 355 * See also: svn_branch__txn_get_branch_by_id(). 356 */ 357 const char * 358 svn_branch__get_id(const svn_branch__state_t *branch, 359 apr_pool_t *result_pool); 360 361 /* Return the element id of the root element of BRANCH. 362 */ 363 int 364 svn_branch__root_eid(const svn_branch__state_t *branch); 365 366 /* Return the id of the branch nested in OUTER_BID at element OUTER_EID. 367 * 368 * For a top-level branch, OUTER_BID is null and OUTER_EID is the 369 * top-level branch number. 370 * 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 373 * element id.) 374 */ 375 const char * 376 svn_branch__id_nest(const char *outer_bid, 377 int outer_eid, 378 apr_pool_t *result_pool); 379 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. 382 * 383 * For a top-level branch, set *OUTER_BID to NULL and *OUTER_EID to the 384 * top-level branch number. 385 * 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 388 * element id.) 389 */ 390 void 391 svn_branch__id_unnest(const char **outer_bid, 392 int *outer_eid, 393 const char *bid, 394 apr_pool_t *result_pool); 395 396 /* Remove the branch with id BID from the list of branches in TXN. 397 */ 398 svn_error_t * 399 svn_branch__txn_delete_branch(svn_branch__txn_t *txn, 400 const char *bid, 401 apr_pool_t *scratch_pool); 402 403 /* Branch-Element-Revision */ 404 struct svn_branch__el_rev_id_t 405 { 406 /* The branch state that applies to REV. */ 407 svn_branch__state_t *branch; 408 /* Element. */ 409 int eid; 410 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. 411 ### Do we need this if BRANCH refers to a particular branch-revision? */ 412 svn_revnum_t rev; 413 414 }; 415 416 /* Revision-branch-element id. */ 417 struct svn_branch__rev_bid_eid_t 418 { 419 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */ 420 svn_revnum_t rev; 421 /* The branch id in revision REV. */ 422 const char *bid; 423 /* Element id. */ 424 int eid; 425 426 }; 427 428 /* Revision-branch id. */ 429 struct svn_branch__rev_bid_t 430 { 431 /* Revision. SVN_INVALID_REVNUM means 'in this transaction', not 'head'. */ 432 svn_revnum_t rev; 433 /* The branch id in revision REV. */ 434 const char *bid; 435 436 }; 437 438 /* Return a new el_rev_id object constructed with *shallow* copies of BRANCH, 439 * EID and REV, allocated in RESULT_POOL. 440 */ 441 svn_branch__el_rev_id_t * 442 svn_branch__el_rev_id_create(svn_branch__state_t *branch, 443 int eid, 444 svn_revnum_t rev, 445 apr_pool_t *result_pool); 446 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); 452 453 /* Return a new id object constructed with deep copies of REV, BRANCH_ID 454 * and EID, allocated in RESULT_POOL. 455 */ 456 svn_branch__rev_bid_eid_t * 457 svn_branch__rev_bid_eid_create(svn_revnum_t rev, 458 const char *branch_id, 459 int eid, 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); 465 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); 474 475 svn_boolean_t 476 svn_branch__rev_bid_equal(const svn_branch__rev_bid_t *id1, 477 const svn_branch__rev_bid_t *id2); 478 479 typedef struct svn_branch__history_t 480 { 481 /* The immediate parents of this state in the branch/merge graph. 482 Hash of (BID -> svn_branch__rev_bid_t). */ 483 apr_hash_t *parents; 484 } svn_branch__history_t; 485 486 svn_branch__history_t * 487 svn_branch__history_create_empty(apr_pool_t *result_pool); 488 489 svn_branch__history_t * 490 svn_branch__history_create(apr_hash_t *parents, 491 apr_pool_t *result_pool); 492 493 svn_branch__history_t * 494 svn_branch__history_dup(const svn_branch__history_t *old, 495 apr_pool_t *result_pool); 496 497 /* Return the mapping of elements in branch BRANCH. 498 */ 499 svn_error_t * 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); 503 504 /* In BRANCH, get element EID (parent, name, payload). 505 * 506 * If element EID is not present, return null. 507 */ 508 svn_error_t * 509 svn_branch__state_get_element(const svn_branch__state_t *branch, 510 svn_element__content_t **element_p, 511 int eid, 512 apr_pool_t *result_pool); 513 514 /** Equivalent to 515 * alter_one(..., element->parent_eid, element->name, element->payload), 516 * or, if @a element is null, to 517 * delete_one(...). 518 */ 519 svn_error_t * 520 svn_branch__state_set_element(svn_branch__state_t *branch, 521 int eid, 522 const svn_element__content_t *element, 523 apr_pool_t *result_pool); 524 525 /** Specify that the element of @a branch identified by @a eid shall not 526 * be present. 527 * 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. 532 * 533 * The element @a eid must not be the root element of @a branch. 534 * 535 * ### Options for Out-Of-Date Checking on Rebase 536 * 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. 540 * 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 548 * by the sender. 549 * 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. 552 * 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. 558 * 559 * It would also be possible to let the caller specify, at some suitable 560 * granularity, which option to use. 561 */ 562 svn_error_t * 563 svn_branch__state_delete_one(svn_branch__state_t *branch, 564 svn_branch__eid_t eid, 565 apr_pool_t *scratch_pool); 566 567 /** Specify the tree position and payload of the element of @a branch 568 * identified by @a eid. 569 * 570 * Set the element's parent EID, name and payload to @a new_parent_eid, 571 * @a new_name and @a new_payload respectively. 572 * 573 * This may create a new element or alter an existing element. 574 * 575 * If the element ... we can describe the effect as ... 576 * 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. 581 * 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'. 585 * 586 * Duplicate @a new_name and @a new_payload into the branch's pool. 587 */ 588 svn_error_t * 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); 595 596 svn_error_t * 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); 602 603 /* Purge orphaned elements in BRANCH. 604 */ 605 svn_error_t * 606 svn_branch__state_purge(svn_branch__state_t *branch, 607 apr_pool_t *scratch_pool); 608 609 /* Get the merge history of BRANCH. 610 */ 611 svn_error_t * 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); 615 616 /* Set the merge history of BRANCH. 617 */ 618 svn_error_t * 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); 622 623 /* Return the branch-relative path of element EID in BRANCH. 624 * 625 * If the element EID does not currently exist in BRANCH, return NULL. 626 * 627 * ### TODO: Clarify sequencing requirements. 628 */ 629 const char * 630 svn_branch__get_path_by_eid(const svn_branch__state_t *branch, 631 int eid, 632 apr_pool_t *result_pool); 633 634 /* Return the EID for the branch-relative path PATH in BRANCH. 635 * 636 * If no element of BRANCH is at this path, return -1. 637 * 638 * ### TODO: Clarify sequencing requirements. 639 */ 640 int 641 svn_branch__get_eid_by_path(const svn_branch__state_t *branch, 642 const char *path, 643 apr_pool_t *scratch_pool); 644 645 /* Get the default branching metadata for r0 of a new repository. 646 */ 647 svn_string_t * 648 svn_branch__get_default_r0_metadata(apr_pool_t *result_pool); 649 650 /* Create a new txn object *TXN_P, initialized with info 651 * parsed from STREAM, allocated in RESULT_POOL. 652 */ 653 svn_error_t * 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); 659 660 /* Write to STREAM a parseable representation of TXN. 661 */ 662 svn_error_t * 663 svn_branch__txn_serialize(svn_branch__txn_t *txn, 664 svn_stream_t *stream, 665 apr_pool_t *scratch_pool); 666 667 /* Write to STREAM a parseable representation of BRANCH. 668 */ 669 svn_error_t * 670 svn_branch__state_serialize(svn_stream_t *stream, 671 svn_branch__state_t *branch, 672 apr_pool_t *scratch_pool); 673 674 675 #ifdef __cplusplus 676 } 677 #endif /* __cplusplus */ 678 679 #endif /* SVN_BRANCH_H */ 680