branch.c revision 333347
1/* 2 * branch.c : Element-Based Branching and Move Tracking. 3 * 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 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 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 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include <assert.h> 25 26#include "svn_types.h" 27#include "svn_error.h" 28#include "svn_dirent_uri.h" 29#include "svn_hash.h" 30#include "svn_iter.h" 31#include "svn_pools.h" 32 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" 37 38#include "svn_private_config.h" 39 40 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) 45 46#define IS_BRANCH_ROOT_EID(branch, eid) \ 47 ((eid) == (branch)->priv->element_tree->root_eid) 48 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) 54 55struct svn_branch__txn_priv_t 56{ 57 /* All branches. */ 58 apr_array_header_t *branches; 59 60 /* The range of element ids assigned. */ 61 /* EIDs local to the txn are negative, assigned by decrementing FIRST_EID 62 * (skipping -1). */ 63 int first_eid, next_eid; 64 65}; 66 67struct svn_branch__state_priv_t 68{ 69 /* EID -> svn_element__content_t mapping. */ 70 svn_element__tree_t *element_tree; 71 72 /* Merge history for this branch state. */ 73 svn_branch__history_t *history; 74 75 svn_boolean_t is_flat; 76 77}; 78 79static svn_branch__state_t * 80branch_state_create(const char *bid, 81 int root_eid, 82 svn_branch__txn_t *txn, 83 apr_pool_t *result_pool); 84 85static svn_error_t * 86branch_instantiate_elements(svn_branch__state_t *to_branch, 87 const svn_element__tree_t *elements, 88 apr_pool_t *scratch_pool); 89 90static svn_error_t * 91svn_branch__map_add_subtree(svn_branch__state_t *to_branch, 92 int to_eid, 93 svn_branch__eid_t new_parent_eid, 94 const char *new_name, 95 svn_element__tree_t *new_subtree, 96 apr_pool_t *scratch_pool); 97 98/* */ 99static apr_pool_t * 100branch_state_pool_get(svn_branch__state_t *branch) 101{ 102 return apr_hash_pool_get(branch->priv->element_tree->e_map); 103} 104 105/* ### Layering: we didn't want to look at the whole repos in here, but 106 copying seems to require it. */ 107svn_error_t * 108svn_branch__repos_get_branch_by_id(svn_branch__state_t **branch_p, 109 const svn_branch__repos_t *repos, 110 svn_revnum_t revnum, 111 const char *branch_id, 112 apr_pool_t *scratch_pool); 113 114/* */ 115static svn_error_t * 116branch_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) 120{ 121 if (SVN_IS_VALID_REVNUM(src_el_rev->rev)) 122 { 123 SVN_ERR(svn_branch__repos_get_branch_by_id(src_branch, 124 txn->repos, 125 src_el_rev->rev, 126 src_el_rev->bid, 127 result_pool)); 128 } 129 else 130 { 131 *src_branch 132 = svn_branch__txn_get_branch_by_id(txn, src_el_rev->bid, result_pool); 133 } 134 135 return SVN_NO_ERROR; 136} 137 138/* An #svn_branch__txn_t method. */ 139static apr_array_header_t * 140branch_txn_get_branches(const svn_branch__txn_t *txn, 141 apr_pool_t *result_pool) 142{ 143 return apr_array_copy(result_pool, txn->priv->branches); 144} 145 146/* An #svn_branch__txn_t method. */ 147static svn_error_t * 148branch_txn_delete_branch(svn_branch__txn_t *txn, 149 const char *bid, 150 apr_pool_t *scratch_pool) 151{ 152 int i; 153 154 for (i = 0; i < txn->priv->branches->nelts; i++) 155 { 156 svn_branch__state_t *b = APR_ARRAY_IDX(txn->priv->branches, i, void *); 157 158 if (strcmp(b->bid, bid) == 0) 159 { 160 svn_sort__array_delete(txn->priv->branches, i, 1); 161 break; 162 } 163 } 164 return SVN_NO_ERROR; 165} 166 167/* An #svn_branch__txn_t method. */ 168static svn_error_t * 169branch_txn_get_num_new_eids(const svn_branch__txn_t *txn, 170 int *num_new_eids_p, 171 apr_pool_t *scratch_pool) 172{ 173 if (num_new_eids_p) 174 *num_new_eids_p = -1 - txn->priv->first_eid; 175 return SVN_NO_ERROR; 176} 177 178/* An #svn_branch__txn_t method. */ 179static svn_error_t * 180branch_txn_new_eid(svn_branch__txn_t *txn, 181 svn_branch__eid_t *eid_p, 182 apr_pool_t *scratch_pool) 183{ 184 int eid = (txn->priv->first_eid < 0) ? txn->priv->first_eid - 1 : -2; 185 186 txn->priv->first_eid = eid; 187 if (eid_p) 188 *eid_p = eid; 189 return SVN_NO_ERROR; 190} 191 192/* An #svn_branch__txn_t method. */ 193static svn_error_t * 194branch_txn_open_branch(svn_branch__txn_t *txn, 195 svn_branch__state_t **new_branch_p, 196 const char *branch_id, 197 int root_eid, 198 svn_branch__rev_bid_eid_t *tree_ref, 199 apr_pool_t *result_pool, 200 apr_pool_t *scratch_pool) 201{ 202 svn_branch__state_t *new_branch; 203 204 /* if the branch already exists, just return it, else create it */ 205 new_branch 206 = svn_branch__txn_get_branch_by_id(txn, branch_id, scratch_pool); 207 if (new_branch) 208 { 209 SVN_ERR_ASSERT(root_eid == svn_branch__root_eid(new_branch)); 210 } 211 else 212 { 213 SVN_ERR_ASSERT_NO_RETURN(root_eid != -1); 214 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; 218 } 219 220 if (tree_ref) 221 { 222 svn_branch__state_t *from_branch; 223 svn_element__tree_t *tree; 224 225 SVN_ERR(branch_in_rev_or_txn(&from_branch, tree_ref, txn, scratch_pool)); 226 /* Source branch must exist */ 227 if (! from_branch) 228 { 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); 233 } 234 235 SVN_ERR_ASSERT(from_branch->priv->is_flat); 236 237 SVN_ERR(svn_branch__state_get_elements(from_branch, &tree, 238 scratch_pool)); 239 tree = svn_element__tree_get_subtree_at_eid(tree, tree_ref->eid, 240 scratch_pool); 241 /* Source element must exist */ 242 if (! tree) 243 { 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); 248 } 249 250 /* Populate the tree from the 'from' source */ 251 SVN_ERR(branch_instantiate_elements(new_branch, tree, scratch_pool)); 252 } 253 254 if (new_branch_p) 255 *new_branch_p = new_branch; 256 return SVN_NO_ERROR; 257} 258 259/* An #svn_branch__txn_t method. */ 260static svn_error_t * 261branch_txn_sequence_point(svn_branch__txn_t *txn, 262 apr_pool_t *scratch_pool) 263{ 264 int i; 265 266 /* purge elements in each branch */ 267 for (i = 0; i < txn->priv->branches->nelts; i++) 268 { 269 svn_branch__state_t *b 270 = APR_ARRAY_IDX(txn->priv->branches, i, void *); 271 272 SVN_ERR(svn_branch__state_purge(b, scratch_pool)); 273 } 274 275 return SVN_NO_ERROR; 276} 277 278/* An #svn_branch__txn_t method. */ 279static svn_error_t * 280branch_txn_complete(svn_branch__txn_t *txn, 281 apr_pool_t *scratch_pool) 282{ 283 return SVN_NO_ERROR; 284} 285 286/* An #svn_branch__txn_t method. */ 287static svn_error_t * 288branch_txn_abort(svn_branch__txn_t *txn, 289 apr_pool_t *scratch_pool) 290{ 291 return SVN_NO_ERROR; 292} 293 294/* 295 * ======================================================================== 296 * Branch Txn Object 297 * ======================================================================== 298 */ 299 300apr_array_header_t * 301svn_branch__txn_get_branches(const svn_branch__txn_t *txn, 302 apr_pool_t *result_pool) 303{ 304 apr_array_header_t *branches 305 = txn->vtable->get_branches(txn, 306 result_pool); 307 return branches; 308} 309 310svn_error_t * 311svn_branch__txn_delete_branch(svn_branch__txn_t *txn, 312 const char *bid, 313 apr_pool_t *scratch_pool) 314{ 315 SVN_ERR(txn->vtable->delete_branch(txn, 316 bid, 317 scratch_pool)); 318 return SVN_NO_ERROR; 319} 320 321svn_error_t * 322svn_branch__txn_get_num_new_eids(const svn_branch__txn_t *txn, 323 int *num_new_eids_p, 324 apr_pool_t *scratch_pool) 325{ 326 SVN_ERR(txn->vtable->get_num_new_eids(txn, 327 num_new_eids_p, 328 scratch_pool)); 329 return SVN_NO_ERROR; 330} 331 332svn_error_t * 333svn_branch__txn_new_eid(svn_branch__txn_t *txn, 334 int *new_eid_p, 335 apr_pool_t *scratch_pool) 336{ 337 SVN_ERR(txn->vtable->new_eid(txn, 338 new_eid_p, 339 scratch_pool)); 340 return SVN_NO_ERROR; 341} 342 343svn_error_t * 344svn_branch__txn_open_branch(svn_branch__txn_t *txn, 345 svn_branch__state_t **new_branch_p, 346 const char *branch_id, 347 int root_eid, 348 svn_branch__rev_bid_eid_t *tree_ref, 349 apr_pool_t *result_pool, 350 apr_pool_t *scratch_pool) 351{ 352 SVN_ERR(txn->vtable->open_branch(txn, 353 new_branch_p, 354 branch_id, 355 root_eid, tree_ref, result_pool, 356 scratch_pool)); 357 return SVN_NO_ERROR; 358} 359 360svn_error_t * 361svn_branch__txn_finalize_eids(svn_branch__txn_t *txn, 362 apr_pool_t *scratch_pool) 363{ 364 SVN_ERR(txn->vtable->finalize_eids(txn, 365 scratch_pool)); 366 return SVN_NO_ERROR; 367} 368 369svn_error_t * 370svn_branch__txn_serialize(svn_branch__txn_t *txn, 371 svn_stream_t *stream, 372 apr_pool_t *scratch_pool) 373{ 374 SVN_ERR(txn->vtable->serialize(txn, 375 stream, 376 scratch_pool)); 377 return SVN_NO_ERROR; 378} 379 380svn_error_t * 381svn_branch__txn_sequence_point(svn_branch__txn_t *txn, 382 apr_pool_t *scratch_pool) 383{ 384 SVN_ERR(txn->vtable->sequence_point(txn, 385 scratch_pool)); 386 return SVN_NO_ERROR; 387} 388 389svn_error_t * 390svn_branch__txn_complete(svn_branch__txn_t *txn, 391 apr_pool_t *scratch_pool) 392{ 393 SVN_ERR(txn->vtable->complete(txn, 394 scratch_pool)); 395 return SVN_NO_ERROR; 396} 397 398svn_error_t * 399svn_branch__txn_abort(svn_branch__txn_t *txn, 400 apr_pool_t *scratch_pool) 401{ 402 SVN_ERR(txn->vtable->abort(txn, 403 scratch_pool)); 404 return SVN_NO_ERROR; 405} 406 407svn_branch__txn_t * 408svn_branch__txn_create(const svn_branch__txn_vtable_t *vtable, 409 svn_cancel_func_t cancel_func, 410 void *cancel_baton, 411 apr_pool_t *result_pool) 412{ 413 svn_branch__txn_t *txn = apr_pcalloc(result_pool, sizeof(*txn)); 414 415 txn->vtable = apr_pmemdup(result_pool, vtable, sizeof(*vtable)); 416 417 txn->vtable->vpriv.cancel_func = cancel_func; 418 txn->vtable->vpriv.cancel_baton = cancel_baton; 419 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; 424#endif 425 426 return txn; 427} 428 429/* 430 * ======================================================================== 431 */ 432 433/* */ 434static const char * 435branch_finalize_bid(const char *bid, 436 int mapping_offset, 437 apr_pool_t *result_pool) 438{ 439 const char *outer_bid; 440 int outer_eid; 441 442 svn_branch__id_unnest(&outer_bid, &outer_eid, bid, result_pool); 443 444 if (outer_bid) 445 { 446 outer_bid = branch_finalize_bid(outer_bid, mapping_offset, result_pool); 447 } 448 449 if (outer_eid < -1) 450 { 451 outer_eid = mapping_offset - outer_eid; 452 } 453 454 return svn_branch__id_nest(outer_bid, outer_eid, result_pool); 455} 456 457/* Change txn-local EIDs (negative integers) in BRANCH to revision EIDs, by 458 * assigning a new revision-EID (positive integer) for each one. 459 */ 460static svn_error_t * 461branch_finalize_eids(svn_branch__state_t *branch, 462 int mapping_offset, 463 apr_pool_t *scratch_pool) 464{ 465 apr_hash_index_t *hi; 466 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) 470 { 471 branch->priv->element_tree->root_eid 472 = mapping_offset - branch->priv->element_tree->root_eid; 473 } 474 475 for (hi = apr_hash_first(scratch_pool, branch->priv->element_tree->e_map); 476 hi; hi = apr_hash_next(hi)) 477 { 478 int old_eid = svn_eid__hash_this_key(hi); 479 svn_element__content_t *element = apr_hash_this_val(hi); 480 481 if (old_eid < -1) 482 { 483 int new_eid = mapping_offset - old_eid; 484 485 svn_element__tree_set(branch->priv->element_tree, old_eid, NULL); 486 svn_element__tree_set(branch->priv->element_tree, new_eid, element); 487 } 488 if (element->parent_eid < -1) 489 { 490 element->parent_eid = mapping_offset - element->parent_eid; 491 } 492 } 493 return SVN_NO_ERROR; 494} 495 496/* An #svn_branch__txn_t method. */ 497static svn_error_t * 498branch_txn_finalize_eids(svn_branch__txn_t *txn, 499 apr_pool_t *scratch_pool) 500{ 501 int n_txn_eids = (-1) - txn->priv->first_eid; 502 int mapping_offset; 503 apr_array_header_t *branches = branch_txn_get_branches(txn, scratch_pool); 504 int i; 505 506 if (txn->priv->first_eid == 0) 507 return SVN_NO_ERROR; 508 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; 513 514 for (i = 0; i < branches->nelts; i++) 515 { 516 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *); 517 518 SVN_ERR(branch_finalize_eids(b, mapping_offset, scratch_pool)); 519 } 520 521 txn->priv->next_eid += n_txn_eids; 522 txn->priv->first_eid = 0; 523 return SVN_NO_ERROR; 524} 525 526/* 527 * ======================================================================== 528 */ 529 530static svn_error_t * 531branch_txn_serialize(svn_branch__txn_t *txn, 532 svn_stream_t *stream, 533 apr_pool_t *scratch_pool) 534{ 535 apr_array_header_t *branches = branch_txn_get_branches(txn, scratch_pool); 536 int i; 537 538 SVN_ERR(svn_stream_printf(stream, scratch_pool, 539 "r%ld: eids %d %d " 540 "branches %d\n", 541 txn->rev, 542 txn->priv->first_eid, txn->priv->next_eid, 543 branches->nelts)); 544 545 for (i = 0; i < branches->nelts; i++) 546 { 547 svn_branch__state_t *branch = APR_ARRAY_IDX(branches, i, void *); 548 549 SVN_ERR(svn_branch__state_serialize(stream, branch, scratch_pool)); 550 } 551 return SVN_NO_ERROR; 552} 553 554/* 555 * ======================================================================== 556 */ 557 558svn_branch__state_t * 559svn_branch__txn_get_branch_by_id(const svn_branch__txn_t *txn, 560 const char *branch_id, 561 apr_pool_t *scratch_pool) 562{ 563 apr_array_header_t *branches = svn_branch__txn_get_branches(txn, scratch_pool); 564 int i; 565 svn_branch__state_t *branch = NULL; 566 567 for (i = 0; i < branches->nelts; i++) 568 { 569 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *); 570 571 if (strcmp(svn_branch__get_id(b, scratch_pool), branch_id) == 0) 572 { 573 branch = b; 574 break; 575 } 576 } 577 return branch; 578} 579 580/* 581 * ======================================================================== 582 */ 583 584/* Create a new branch txn object. 585 * 586 * It will have no branches. 587 */ 588static svn_branch__txn_t * 589branch_txn_create(svn_branch__repos_t *repos, 590 svn_revnum_t rev, 591 svn_revnum_t base_rev, 592 apr_pool_t *result_pool) 593{ 594 static const svn_branch__txn_vtable_t vtable = { 595 {0}, 596 branch_txn_get_branches, 597 branch_txn_delete_branch, 598 branch_txn_get_num_new_eids, 599 branch_txn_new_eid, 600 branch_txn_open_branch, 601 branch_txn_finalize_eids, 602 branch_txn_serialize, 603 branch_txn_sequence_point, 604 branch_txn_complete, 605 branch_txn_abort, 606 }; 607 svn_branch__txn_t *txn 608 = svn_branch__txn_create(&vtable, NULL, NULL, result_pool); 609 610 txn->priv = apr_pcalloc(result_pool, sizeof(*txn->priv)); 611 txn->repos = repos; 612 txn->rev = rev; 613 txn->base_rev = base_rev; 614 txn->priv->branches = apr_array_make(result_pool, 0, sizeof(void *)); 615 return txn; 616} 617 618/* 619 * ======================================================================== 620 */ 621 622static void 623branch_validate_element(const svn_branch__state_t *branch, 624 int eid, 625 const svn_element__content_t *element); 626 627/* Assert BRANCH satisfies all its invariants. 628 */ 629static void 630assert_branch_state_invariants(const svn_branch__state_t *branch, 631 apr_pool_t *scratch_pool) 632{ 633 apr_hash_index_t *hi; 634 635 assert(branch->bid); 636 assert(branch->txn); 637 assert(branch->priv->element_tree); 638 assert(branch->priv->element_tree->e_map); 639 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)) 643 { 644 branch_validate_element(branch, svn_eid__hash_this_key(hi), 645 apr_hash_this_val(hi)); 646 } 647} 648 649/* An #svn_branch__state_t method. */ 650static svn_error_t * 651branch_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) 658{ 659 /* New payload shall be the same as the source if NEW_PAYLOAD is null. */ 660 /* ### if (! new_payload) 661 { 662 new_payload = branch_map_get(branch, eid)->payload; 663 } 664 */ 665 666 return SVN_NO_ERROR; 667} 668 669/* Copy a subtree. 670 * 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. 673 * 674 * FROM_EL_REV must be an existing element. (It may be a branch root.) 675 * 676 * ### TODO: 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? 681 * 682 * TO_PARENT_EID must be a directory element in TO_BRANCH, and TO_NAME a 683 * non-existing path in it. 684 */ 685static svn_error_t * 686copy_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, 689 const char *to_name, 690 apr_pool_t *scratch_pool) 691{ 692 svn_element__tree_t *new_subtree; 693 694 SVN_ERR_ASSERT(from_el_rev->branch->priv->is_flat); 695 696 SVN_ERR(svn_branch__state_get_elements(from_el_rev->branch, &new_subtree, 697 scratch_pool)); 698 new_subtree = svn_element__tree_get_subtree_at_eid(new_subtree, 699 from_el_rev->eid, 700 scratch_pool); 701 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, 705 new_subtree, 706 scratch_pool)); 707 708 return SVN_NO_ERROR; 709} 710 711/* An #svn_branch__state_t method. */ 712static svn_error_t * 713branch_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) 718{ 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; 722 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, 728 scratch_pool)); 729 730 return SVN_NO_ERROR; 731} 732 733const char * 734svn_branch__get_id(const svn_branch__state_t *branch, 735 apr_pool_t *result_pool) 736{ 737 return branch->bid; 738} 739 740int 741svn_branch__root_eid(const svn_branch__state_t *branch) 742{ 743 svn_element__tree_t *elements; 744 745 svn_error_clear(svn_branch__state_get_elements(branch, &elements, 746 NULL/*scratch_pool*/)); 747 return elements->root_eid; 748} 749 750svn_branch__el_rev_id_t * 751svn_branch__el_rev_id_create(svn_branch__state_t *branch, 752 int eid, 753 svn_revnum_t rev, 754 apr_pool_t *result_pool) 755{ 756 svn_branch__el_rev_id_t *id = apr_palloc(result_pool, sizeof(*id)); 757 758 id->branch = branch; 759 id->eid = eid; 760 id->rev = rev; 761 return id; 762} 763 764svn_branch__el_rev_id_t * 765svn_branch__el_rev_id_dup(const svn_branch__el_rev_id_t *old_id, 766 apr_pool_t *result_pool) 767{ 768 if (! old_id) 769 return NULL; 770 771 return svn_branch__el_rev_id_create(old_id->branch, 772 old_id->eid, 773 old_id->rev, 774 result_pool); 775} 776 777svn_branch__rev_bid_eid_t * 778svn_branch__rev_bid_eid_create(svn_revnum_t rev, 779 const char *branch_id, 780 int eid, 781 apr_pool_t *result_pool) 782{ 783 svn_branch__rev_bid_eid_t *id = apr_palloc(result_pool, sizeof(*id)); 784 785 id->bid = apr_pstrdup(result_pool, branch_id); 786 id->eid = eid; 787 id->rev = rev; 788 return id; 789} 790 791svn_branch__rev_bid_eid_t * 792svn_branch__rev_bid_eid_dup(const svn_branch__rev_bid_eid_t *old_id, 793 apr_pool_t *result_pool) 794{ 795 svn_branch__rev_bid_eid_t *id; 796 797 if (! old_id) 798 return NULL; 799 800 id = apr_pmemdup(result_pool, old_id, sizeof(*id)); 801 id->bid = apr_pstrdup(result_pool, old_id->bid); 802 return id; 803} 804 805svn_branch__rev_bid_t * 806svn_branch__rev_bid_create(svn_revnum_t rev, 807 const char *branch_id, 808 apr_pool_t *result_pool) 809{ 810 svn_branch__rev_bid_t *id = apr_palloc(result_pool, sizeof(*id)); 811 812 id->bid = apr_pstrdup(result_pool, branch_id); 813 id->rev = rev; 814 return id; 815} 816 817svn_branch__rev_bid_t * 818svn_branch__rev_bid_dup(const svn_branch__rev_bid_t *old_id, 819 apr_pool_t *result_pool) 820{ 821 svn_branch__rev_bid_t *id; 822 823 if (! old_id) 824 return NULL; 825 826 id = apr_pmemdup(result_pool, old_id, sizeof(*id)); 827 id->bid = apr_pstrdup(result_pool, old_id->bid); 828 return id; 829} 830 831svn_boolean_t 832svn_branch__rev_bid_equal(const svn_branch__rev_bid_t *id1, 833 const svn_branch__rev_bid_t *id2) 834{ 835 return (id1->rev == id2->rev 836 && strcmp(id1->bid, id2->bid) == 0); 837} 838 839svn_branch__history_t * 840svn_branch__history_create_empty(apr_pool_t *result_pool) 841{ 842 svn_branch__history_t *history 843 = svn_branch__history_create(NULL, result_pool); 844 845 return history; 846} 847 848svn_branch__history_t * 849svn_branch__history_create(apr_hash_t *parents, 850 apr_pool_t *result_pool) 851{ 852 svn_branch__history_t *history 853 = apr_pcalloc(result_pool, sizeof(*history)); 854 855 history->parents = apr_hash_make(result_pool); 856 if (parents) 857 { 858 apr_hash_index_t *hi; 859 860 for (hi = apr_hash_first(result_pool, parents); 861 hi; hi = apr_hash_next(hi)) 862 { 863 const char *bid = apr_hash_this_key(hi); 864 svn_branch__rev_bid_t *val = apr_hash_this_val(hi); 865 866 svn_hash_sets(history->parents, 867 apr_pstrdup(result_pool, bid), 868 svn_branch__rev_bid_dup(val, result_pool)); 869 } 870 } 871 return history; 872} 873 874svn_branch__history_t * 875svn_branch__history_dup(const svn_branch__history_t *old, 876 apr_pool_t *result_pool) 877{ 878 svn_branch__history_t *history = NULL; 879 880 if (old) 881 { 882 history 883 = svn_branch__history_create(old->parents, result_pool); 884 } 885 return history; 886} 887 888 889/* 890 * ======================================================================== 891 * Branch mappings 892 * ======================================================================== 893 */ 894 895/* Validate that ELEMENT is suitable for a mapping of BRANCH:EID. 896 * ELEMENT->payload may be null. 897 */ 898static void 899branch_validate_element(const svn_branch__state_t *branch, 900 int eid, 901 const svn_element__content_t *element) 902{ 903 SVN_ERR_ASSERT_NO_RETURN(element); 904 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))); 912 913 /* Element name must be given, and empty iff EID is the branch root. */ 914 SVN_ERR_ASSERT_NO_RETURN( 915 element->name 916 && IS_BRANCH_ROOT_EID(branch, eid) == (*element->name == '\0')); 917 918 SVN_ERR_ASSERT_NO_RETURN(svn_element__payload_invariants(element->payload)); 919 if (element->payload->is_subbranch_root) 920 { 921 /* a subbranch root element must not be the branch root element */ 922 SVN_ERR_ASSERT_NO_RETURN(! IS_BRANCH_ROOT_EID(branch, eid)); 923 } 924} 925 926static svn_error_t * 927branch_state_get_elements(const svn_branch__state_t *branch, 928 svn_element__tree_t **element_tree_p, 929 apr_pool_t *result_pool) 930{ 931 *element_tree_p = branch->priv->element_tree; 932 return SVN_NO_ERROR; 933} 934 935static svn_element__content_t * 936branch_get_element(const svn_branch__state_t *branch, 937 int eid) 938{ 939 svn_element__content_t *element; 940 941 element = svn_element__tree_get(branch->priv->element_tree, eid); 942 943 if (element) 944 branch_validate_element(branch, eid, element); 945 return element; 946} 947 948static svn_error_t * 949branch_state_get_element(const svn_branch__state_t *branch, 950 svn_element__content_t **element_p, 951 int eid, 952 apr_pool_t *result_pool) 953{ 954 *element_p = branch_get_element(branch, eid); 955 return SVN_NO_ERROR; 956} 957 958/* In BRANCH, set element EID to ELEMENT. 959 * 960 * If ELEMENT is null, delete element EID. 961 * 962 * Assume ELEMENT is already allocated with sufficient lifetime. 963 */ 964static void 965branch_map_set(svn_branch__state_t *branch, 966 int eid, 967 const svn_element__content_t *element) 968{ 969 apr_pool_t *map_pool = apr_hash_pool_get(branch->priv->element_tree->e_map); 970 971 SVN_ERR_ASSERT_NO_RETURN(EID_IS_ALLOCATED(branch, eid)); 972 if (element) 973 branch_validate_element(branch, eid, element); 974 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); 978} 979 980/* An #svn_branch__state_t method. */ 981static svn_error_t * 982branch_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) 986{ 987 apr_pool_t *map_pool = apr_hash_pool_get(branch->priv->element_tree->e_map); 988 989 /* EID must be a valid element id */ 990 SVN_ERR_ASSERT(EID_IS_ALLOCATED(branch, eid)); 991 992 if (element) 993 { 994 element = svn_element__content_dup(element, map_pool); 995 996 /* NEW_PAYLOAD must be specified, either in full or by reference */ 997 SVN_ERR_ASSERT(element->payload); 998 999 if ((element->parent_eid == -1) != IS_BRANCH_ROOT_EID(branch, eid) 1000 || (*element->name == '\0') != IS_BRANCH_ROOT_EID(branch, eid)) 1001 { 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); 1007 } 1008 } 1009 1010 /* Insert the new version */ 1011 branch_map_set(branch, eid, element); 1012 return SVN_NO_ERROR; 1013} 1014 1015/* An #svn_branch__state_t method. */ 1016static svn_error_t * 1017branch_state_purge(svn_branch__state_t *branch, 1018 apr_pool_t *scratch_pool) 1019{ 1020 svn_element__tree_purge_orphans(branch->priv->element_tree->e_map, 1021 branch->priv->element_tree->root_eid, 1022 scratch_pool); 1023 branch->priv->is_flat = TRUE; 1024 return SVN_NO_ERROR; 1025} 1026 1027/* An #svn_branch__state_t method. */ 1028static svn_error_t * 1029branch_state_get_history(svn_branch__state_t *branch, 1030 svn_branch__history_t **history_p, 1031 apr_pool_t *result_pool) 1032{ 1033 if (history_p) 1034 { 1035 *history_p 1036 = svn_branch__history_dup(branch->priv->history, result_pool); 1037 } 1038 return SVN_NO_ERROR; 1039} 1040 1041/* An #svn_branch__state_t method. */ 1042static svn_error_t * 1043branch_state_set_history(svn_branch__state_t *branch, 1044 const svn_branch__history_t *history, 1045 apr_pool_t *scratch_pool) 1046{ 1047 apr_pool_t *branch_pool = branch_state_pool_get(branch); 1048 1049 branch->priv->history 1050 = svn_branch__history_dup(history, branch_pool); 1051 return SVN_NO_ERROR; 1052} 1053 1054const char * 1055svn_branch__get_path_by_eid(const svn_branch__state_t *branch, 1056 int eid, 1057 apr_pool_t *result_pool) 1058{ 1059 svn_element__tree_t *elements; 1060 1061 SVN_ERR_ASSERT_NO_RETURN(EID_IS_ALLOCATED(branch, eid)); 1062 /*SVN_ERR_ASSERT_NO_RETURN(branch->priv->is_flat);*/ 1063 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); 1066} 1067 1068int 1069svn_branch__get_eid_by_path(const svn_branch__state_t *branch, 1070 const char *path, 1071 apr_pool_t *scratch_pool) 1072{ 1073 svn_element__tree_t *elements; 1074 apr_hash_index_t *hi; 1075 1076 /*SVN_ERR_ASSERT_NO_RETURN(branch->priv->is_flat);*/ 1077 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)) 1082 { 1083 int eid = svn_eid__hash_this_key(hi); 1084 const char *this_path = svn_element__tree_get_path_by_eid(elements, eid, 1085 scratch_pool); 1086 1087 if (! this_path) 1088 { 1089 /* Mapping is not complete; this element is in effect not present. */ 1090 continue; 1091 } 1092 if (strcmp(path, this_path) == 0) 1093 { 1094 return eid; 1095 } 1096 } 1097 1098 return -1; 1099} 1100 1101/* Create a copy of NEW_SUBTREE in TO_BRANCH. 1102 * 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. 1105 * 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. 1108 * 1109 * Set the new subtree root element's parent to NEW_PARENT_EID and name to 1110 * NEW_NAME. 1111 */ 1112static svn_error_t * 1113svn_branch__map_add_subtree(svn_branch__state_t *to_branch, 1114 int to_eid, 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) 1119{ 1120 apr_hash_index_t *hi; 1121 svn_element__content_t *new_root_content; 1122 1123 /* Get a new EID for the root element, if not given. */ 1124 if (to_eid == -1) 1125 { 1126 SVN_ERR(svn_branch__txn_new_eid(to_branch->txn, &to_eid, 1127 scratch_pool)); 1128 } 1129 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, 1134 scratch_pool); 1135 SVN_ERR(branch_state_set_element(to_branch, to_eid, new_root_content, 1136 scratch_pool)); 1137 1138 /* Process its immediate children */ 1139 for (hi = apr_hash_first(scratch_pool, new_subtree->e_map); 1140 hi; hi = apr_hash_next(hi)) 1141 { 1142 int this_from_eid = svn_eid__hash_this_key(hi); 1143 svn_element__content_t *from_element = apr_hash_this_val(hi); 1144 1145 if (from_element->parent_eid == new_subtree->root_eid) 1146 { 1147 svn_element__tree_t *this_subtree; 1148 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.) */ 1151 this_subtree 1152 = svn_element__tree_create(new_subtree->e_map, this_from_eid, 1153 scratch_pool); 1154 SVN_ERR(svn_branch__map_add_subtree(to_branch, -1 /*to_eid*/, 1155 to_eid, from_element->name, 1156 this_subtree, scratch_pool)); 1157 } 1158 } 1159 1160 return SVN_NO_ERROR; 1161} 1162 1163/* Instantiate elements in a branch. 1164 * 1165 * In TO_BRANCH, instantiate (or alter, if existing) each element of 1166 * ELEMENTS, each with its given tree structure (parent, name) and payload. 1167 */ 1168static svn_error_t * 1169branch_instantiate_elements(svn_branch__state_t *to_branch, 1170 const svn_element__tree_t *elements, 1171 apr_pool_t *scratch_pool) 1172{ 1173 apr_hash_index_t *hi; 1174 1175 for (hi = apr_hash_first(scratch_pool, elements->e_map); 1176 hi; hi = apr_hash_next(hi)) 1177 { 1178 int this_eid = svn_eid__hash_this_key(hi); 1179 svn_element__content_t *this_element = apr_hash_this_val(hi); 1180 1181 branch_map_set(to_branch, this_eid, 1182 svn_element__content_dup( 1183 this_element, 1184 apr_hash_pool_get(to_branch->priv->element_tree->e_map))); 1185 } 1186 1187 return SVN_NO_ERROR; 1188} 1189 1190/* 1191 * ======================================================================== 1192 * Branch State Object 1193 * ======================================================================== 1194 */ 1195 1196svn_error_t * 1197svn_branch__state_get_elements(const svn_branch__state_t *branch, 1198 svn_element__tree_t **element_tree_p, 1199 apr_pool_t *result_pool) 1200{ 1201 SVN_ERR(branch->vtable->get_elements(branch, 1202 element_tree_p, 1203 result_pool)); 1204 return SVN_NO_ERROR; 1205} 1206 1207svn_error_t * 1208svn_branch__state_get_element(const svn_branch__state_t *branch, 1209 svn_element__content_t **element_p, 1210 int eid, 1211 apr_pool_t *result_pool) 1212{ 1213 SVN_ERR(branch->vtable->get_element(branch, 1214 element_p, eid, result_pool)); 1215 return SVN_NO_ERROR; 1216} 1217 1218svn_error_t * 1219svn_branch__state_set_element(svn_branch__state_t *branch, 1220 int eid, 1221 const svn_element__content_t *element, 1222 apr_pool_t *scratch_pool) 1223{ 1224 SVN_ERR(branch->vtable->set_element(branch, 1225 eid, element, 1226 scratch_pool)); 1227 return SVN_NO_ERROR; 1228} 1229 1230svn_error_t * 1231svn_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) 1237{ 1238 svn_element__content_t *element 1239 = svn_element__content_create(new_parent_eid, new_name, new_payload, 1240 scratch_pool); 1241 1242 SVN_ERR(svn_branch__state_set_element(branch, eid, element, scratch_pool)); 1243 return SVN_NO_ERROR; 1244} 1245 1246svn_error_t * 1247svn_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) 1252{ 1253 SVN_ERR(branch->vtable->copy_tree(branch, 1254 src_el_rev, new_parent_eid, new_name, 1255 scratch_pool)); 1256 return SVN_NO_ERROR; 1257} 1258 1259svn_error_t * 1260svn_branch__state_delete_one(svn_branch__state_t *branch, 1261 svn_branch__eid_t eid, 1262 apr_pool_t *scratch_pool) 1263{ 1264 SVN_ERR(svn_branch__state_set_element(branch, eid, NULL, scratch_pool)); 1265 return SVN_NO_ERROR; 1266} 1267 1268svn_error_t * 1269svn_branch__state_purge(svn_branch__state_t *branch, 1270 apr_pool_t *scratch_pool) 1271{ 1272 SVN_ERR(branch->vtable->purge(branch, 1273 scratch_pool)); 1274 return SVN_NO_ERROR; 1275} 1276 1277svn_error_t * 1278svn_branch__state_get_history(svn_branch__state_t *branch, 1279 svn_branch__history_t **history_p, 1280 apr_pool_t *result_pool) 1281{ 1282 SVN_ERR(branch->vtable->get_history(branch, 1283 history_p, 1284 result_pool)); 1285 SVN_ERR_ASSERT(*history_p); 1286 return SVN_NO_ERROR; 1287} 1288 1289svn_error_t * 1290svn_branch__state_set_history(svn_branch__state_t *branch, 1291 const svn_branch__history_t *history, 1292 apr_pool_t *scratch_pool) 1293{ 1294 SVN_ERR_ASSERT(history); 1295 SVN_ERR(branch->vtable->set_history(branch, 1296 history, 1297 scratch_pool)); 1298 return SVN_NO_ERROR; 1299} 1300 1301svn_branch__state_t * 1302svn_branch__state_create(const svn_branch__state_vtable_t *vtable, 1303 svn_cancel_func_t cancel_func, 1304 void *cancel_baton, 1305 apr_pool_t *result_pool) 1306{ 1307 svn_branch__state_t *b = apr_pcalloc(result_pool, sizeof(*b)); 1308 1309 b->vtable = apr_pmemdup(result_pool, vtable, sizeof(*vtable)); 1310 1311 b->vtable->vpriv.cancel_func = cancel_func; 1312 b->vtable->vpriv.cancel_baton = cancel_baton; 1313 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; 1318#endif 1319 1320 return b; 1321} 1322 1323/* Create a new branch state object. 1324 * 1325 * It will have no elements (not even a root element). 1326 */ 1327static svn_branch__state_t * 1328branch_state_create(const char *bid, 1329 int root_eid, 1330 svn_branch__txn_t *txn, 1331 apr_pool_t *result_pool) 1332{ 1333 static const svn_branch__state_vtable_t vtable = { 1334 {0}, 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, 1340 branch_state_purge, 1341 branch_state_get_history, 1342 branch_state_set_history, 1343 }; 1344 svn_branch__state_t *b 1345 = svn_branch__state_create(&vtable, NULL, NULL, result_pool); 1346 1347 b->priv = apr_pcalloc(result_pool, sizeof(*b->priv)); 1348 b->bid = apr_pstrdup(result_pool, bid); 1349 b->txn = txn; 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); 1354 return b; 1355} 1356 1357/* 1358 * ======================================================================== 1359 * Parsing and Serializing 1360 * ======================================================================== 1361 */ 1362 1363svn_string_t * 1364svn_branch__get_default_r0_metadata(apr_pool_t *result_pool) 1365{ 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"; 1371 1372 return svn_string_create(default_repos_info, result_pool); 1373} 1374 1375/* */ 1376static svn_error_t * 1377parse_branch_line(char *bid_p, 1378 int *root_eid_p, 1379 int *num_eids_p, 1380 svn_stream_t *stream, 1381 apr_pool_t *result_pool, 1382 apr_pool_t *scratch_pool) 1383{ 1384 svn_stringbuf_t *line; 1385 svn_boolean_t eof; 1386 int n; 1387 1388 /* Read a line */ 1389 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); 1390 SVN_ERR_ASSERT(!eof); 1391 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); 1395 1396 return SVN_NO_ERROR; 1397} 1398 1399/* Parse the history metadata for BRANCH. 1400 */ 1401static svn_error_t * 1402history_parse(svn_branch__history_t **history_p, 1403 svn_stream_t *stream, 1404 apr_pool_t *result_pool, 1405 apr_pool_t *scratch_pool) 1406{ 1407 svn_branch__history_t *history 1408 = svn_branch__history_create_empty(result_pool); 1409 svn_stringbuf_t *line; 1410 svn_boolean_t eof; 1411 int n; 1412 int num_parents; 1413 int i; 1414 1415 /* Read a line */ 1416 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); 1417 SVN_ERR_ASSERT(!eof); 1418 1419 n = sscanf(line->data, "history: parents %d", 1420 &num_parents); 1421 SVN_ERR_ASSERT(n == 1); 1422 1423 for (i = 0; i < num_parents; i++) 1424 { 1425 svn_revnum_t rev; 1426 char bid[100]; 1427 1428 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); 1429 SVN_ERR_ASSERT(!eof); 1430 1431 n = sscanf(line->data, "parent: r%ld.%99s", 1432 &rev, bid); 1433 SVN_ERR_ASSERT(n == 2); 1434 1435 svn_hash_sets(history->parents, 1436 apr_pstrdup(result_pool, bid), 1437 svn_branch__rev_bid_create(rev, bid, result_pool)); 1438 } 1439 1440 if (history_p) 1441 *history_p = history; 1442 return SVN_NO_ERROR; 1443} 1444 1445/* Parse the mapping for one element. 1446 */ 1447static svn_error_t * 1448parse_element_line(int *eid_p, 1449 svn_boolean_t *is_subbranch_p, 1450 int *parent_eid_p, 1451 const char **name_p, 1452 svn_stream_t *stream, 1453 apr_pool_t *result_pool, 1454 apr_pool_t *scratch_pool) 1455{ 1456 svn_stringbuf_t *line; 1457 svn_boolean_t eof; 1458 char kind[10]; 1459 int n; 1460 int offset; 1461 1462 /* Read a line */ 1463 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, scratch_pool)); 1464 SVN_ERR_ASSERT(!eof); 1465 1466 n = sscanf(line->data, "e%d: %9s %d%n", 1467 eid_p, 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] == ' '); 1471 1472 *name_p = apr_pstrdup(result_pool, line->data + offset + 1); 1473 *is_subbranch_p = (strcmp(kind, "subbranch") == 0); 1474 1475 if (strcmp(*name_p, "(null)") == 0) 1476 *name_p = NULL; 1477 else if (strcmp(*name_p, ".") == 0) 1478 *name_p = ""; 1479 1480 return SVN_NO_ERROR; 1481} 1482 1483const char * 1484svn_branch__id_nest(const char *outer_bid, 1485 int outer_eid, 1486 apr_pool_t *result_pool) 1487{ 1488 if (!outer_bid) 1489 return apr_psprintf(result_pool, "B%d", outer_eid); 1490 1491 return apr_psprintf(result_pool, "%s.%d", outer_bid, outer_eid); 1492} 1493 1494void 1495svn_branch__id_unnest(const char **outer_bid, 1496 int *outer_eid, 1497 const char *bid, 1498 apr_pool_t *result_pool) 1499{ 1500 char *last_dot = strrchr(bid, '.'); 1501 1502 if (last_dot) /* BID looks like "B3.11" or "B3.11.22" etc. */ 1503 { 1504 *outer_bid = apr_pstrndup(result_pool, bid, last_dot - bid); 1505 *outer_eid = atoi(last_dot + 1); 1506 } 1507 else /* looks like "B0" or B22" (with no dot) */ 1508 { 1509 *outer_bid = NULL; 1510 *outer_eid = atoi(bid + 1); 1511 } 1512} 1513 1514/* Create a new branch *NEW_BRANCH, initialized 1515 * with info parsed from STREAM, allocated in RESULT_POOL. 1516 */ 1517static svn_error_t * 1518svn_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) 1523{ 1524 char bid[1000]; 1525 int root_eid, num_eids; 1526 svn_branch__state_t *branch_state; 1527 int i; 1528 1529 SVN_ERR(parse_branch_line(bid, &root_eid, &num_eids, 1530 stream, scratch_pool, scratch_pool)); 1531 1532 branch_state = branch_state_create(bid, root_eid, txn, 1533 result_pool); 1534 1535 /* Read in the merge history. */ 1536 SVN_ERR(history_parse(&branch_state->priv->history, 1537 stream, result_pool, scratch_pool)); 1538 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++) 1542 { 1543 int eid, this_parent_eid; 1544 const char *this_name; 1545 svn_boolean_t is_subbranch; 1546 1547 SVN_ERR(parse_element_line(&eid, 1548 &is_subbranch, &this_parent_eid, &this_name, 1549 stream, scratch_pool, scratch_pool)); 1550 1551 if (this_name) 1552 { 1553 svn_element__payload_t *payload; 1554 svn_element__content_t *element; 1555 1556 if (! is_subbranch) 1557 { 1558 payload = svn_element__payload_create_ref(txn->rev, bid, eid, 1559 result_pool); 1560 } 1561 else 1562 { 1563 payload 1564 = svn_element__payload_create_subbranch(result_pool); 1565 } 1566 element = svn_element__content_create(this_parent_eid, 1567 this_name, payload, 1568 scratch_pool); 1569 SVN_ERR(branch_state_set_element(branch_state, eid, element, 1570 scratch_pool)); 1571 } 1572 } 1573 1574 branch_state->priv->is_flat = TRUE; 1575 *new_branch = branch_state; 1576 return SVN_NO_ERROR; 1577} 1578 1579svn_error_t * 1580svn_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) 1585{ 1586 svn_branch__txn_t *txn; 1587 svn_revnum_t rev; 1588 int first_eid, next_eid; 1589 int num_branches; 1590 svn_stringbuf_t *line; 1591 svn_boolean_t eof; 1592 int n; 1593 int j; 1594 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 " 1598 "branches %d", 1599 &rev, 1600 &first_eid, &next_eid, 1601 &num_branches); 1602 SVN_ERR_ASSERT(n == 4); 1603 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; 1607 1608 /* parse the branches */ 1609 for (j = 0; j < num_branches; j++) 1610 { 1611 svn_branch__state_t *branch; 1612 1613 SVN_ERR(svn_branch__state_parse(&branch, txn, stream, 1614 result_pool, scratch_pool)); 1615 APR_ARRAY_PUSH(txn->priv->branches, void *) = branch; 1616 } 1617 1618 *txn_p = txn; 1619 return SVN_NO_ERROR; 1620} 1621 1622/* Serialize the history metadata for BRANCH. 1623 */ 1624static svn_error_t * 1625history_serialize(svn_stream_t *stream, 1626 svn_branch__history_t *history, 1627 apr_pool_t *scratch_pool) 1628{ 1629 apr_array_header_t *ancestors_sorted; 1630 int i; 1631 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, 1636 scratch_pool); 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++) 1641 { 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; 1645 1646 SVN_ERR(svn_stream_printf(stream, scratch_pool, 1647 "parent: r%ld.%s\n", 1648 rev_bid->rev, rev_bid->bid)); 1649 } 1650 1651 return SVN_NO_ERROR; 1652} 1653 1654/* Write to STREAM a parseable representation of BRANCH. 1655 */ 1656svn_error_t * 1657svn_branch__state_serialize(svn_stream_t *stream, 1658 svn_branch__state_t *branch, 1659 apr_pool_t *scratch_pool) 1660{ 1661 svn_eid__hash_iter_t *ei; 1662 1663 SVN_ERR_ASSERT(branch->priv->is_flat); 1664 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))); 1670 1671 SVN_ERR(history_serialize(stream, branch->priv->history, 1672 scratch_pool)); 1673 1674 for (SVN_EID__HASH_ITER_SORTED_BY_EID(ei, branch->priv->element_tree->e_map, 1675 scratch_pool)) 1676 { 1677 int eid = ei->eid; 1678 svn_element__content_t *element = branch_get_element(branch, eid); 1679 int parent_eid; 1680 const char *name; 1681 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, 1686 "e%d: %s %d %s\n", 1687 eid, 1688 element ? ((! element->payload->is_subbranch_root) 1689 ? "normal" : "subbranch") 1690 : "none", 1691 parent_eid, name)); 1692 } 1693 return SVN_NO_ERROR; 1694} 1695 1696/* 1697 * ======================================================================== 1698 */ 1699 1700