branch_nested.c revision 362181
1/* 2 * branch_nested.c : Nested Branches 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_branch_nested.h" 34#include "private/svn_branch_impl.h" 35#include "private/svn_branch_repos.h" 36 37#include "svn_private_config.h" 38 39 40void 41svn_branch__get_outer_branch_and_eid(svn_branch__state_t **outer_branch_p, 42 int *outer_eid_p, 43 const svn_branch__state_t *branch, 44 apr_pool_t *scratch_pool) 45{ 46 const char *outer_bid; 47 48 svn_branch__id_unnest(&outer_bid, outer_eid_p, branch->bid, scratch_pool); 49 *outer_branch_p = NULL; 50 if (outer_bid) 51 { 52 *outer_branch_p 53 = svn_branch__txn_get_branch_by_id(branch->txn, outer_bid, 54 scratch_pool); 55 } 56} 57 58const char * 59svn_branch__get_root_rrpath(const svn_branch__state_t *branch, 60 apr_pool_t *result_pool) 61{ 62 svn_branch__state_t *outer_branch; 63 int outer_eid; 64 const char *root_rrpath; 65 66 svn_branch__get_outer_branch_and_eid(&outer_branch, &outer_eid, branch, 67 result_pool); 68 if (outer_branch) 69 { 70 root_rrpath 71 = svn_branch__get_rrpath_by_eid(outer_branch, outer_eid, result_pool); 72 } 73 else 74 { 75 root_rrpath = ""; 76 } 77 78 SVN_ERR_ASSERT_NO_RETURN(root_rrpath); 79 return root_rrpath; 80} 81 82const char * 83svn_branch__get_rrpath_by_eid(const svn_branch__state_t *branch, 84 int eid, 85 apr_pool_t *result_pool) 86{ 87 const char *path = svn_branch__get_path_by_eid(branch, eid, result_pool); 88 const char *rrpath = NULL; 89 90 if (path) 91 { 92 rrpath = svn_relpath_join(svn_branch__get_root_rrpath(branch, result_pool), 93 path, result_pool); 94 } 95 return rrpath; 96} 97 98svn_error_t * 99svn_branch__get_subbranch_at_eid(svn_branch__state_t *branch, 100 svn_branch__state_t **subbranch_p, 101 int eid, 102 apr_pool_t *scratch_pool) 103{ 104 svn_element__content_t *element; 105 106 SVN_ERR(svn_branch__state_get_element(branch, &element, eid, scratch_pool)); 107 if (element && element->payload->is_subbranch_root) 108 { 109 const char *branch_id = svn_branch__get_id(branch, scratch_pool); 110 const char *subbranch_id = svn_branch__id_nest(branch_id, eid, 111 scratch_pool); 112 113 *subbranch_p = svn_branch__txn_get_branch_by_id(branch->txn, subbranch_id, 114 scratch_pool); 115 } 116 else 117 { 118 *subbranch_p = NULL; 119 } 120 return SVN_NO_ERROR; 121} 122 123/* Set *SUBBRANCH_EIDS_P an array of EIDs of the subbranch-root elements in 124 * BRANCH. 125 */ 126static svn_error_t * 127svn_branch__get_immediate_subbranch_eids(svn_branch__state_t *branch, 128 apr_array_header_t **subbranch_eids_p, 129 apr_pool_t *result_pool, 130 apr_pool_t *scratch_pool) 131{ 132 apr_array_header_t *subbranch_eids 133 = apr_array_make(result_pool, 0, sizeof(int)); 134 svn_element__tree_t *elements; 135 apr_hash_index_t *hi; 136 137 SVN_ERR(svn_branch__state_get_elements(branch, &elements, scratch_pool)); 138 for (hi = apr_hash_first(scratch_pool, elements->e_map); 139 hi; hi = apr_hash_next(hi)) 140 { 141 int eid = svn_eid__hash_this_key(hi); 142 svn_element__content_t *element = apr_hash_this_val(hi); 143 144 if (element->payload->is_subbranch_root) 145 { 146 APR_ARRAY_PUSH(subbranch_eids, int) = eid; 147 } 148 } 149 *subbranch_eids_p = subbranch_eids; 150 return SVN_NO_ERROR; 151} 152 153svn_error_t * 154svn_branch__get_immediate_subbranches(svn_branch__state_t *branch, 155 apr_array_header_t **subbranches_p, 156 apr_pool_t *result_pool, 157 apr_pool_t *scratch_pool) 158{ 159 apr_array_header_t *subbranch_eids; 160 apr_array_header_t *subbranches 161 = apr_array_make(result_pool, 0, sizeof(void *)); 162 const char *branch_id = svn_branch__get_id(branch, scratch_pool); 163 int i; 164 165 SVN_ERR(svn_branch__get_immediate_subbranch_eids(branch, &subbranch_eids, 166 scratch_pool, scratch_pool)); 167 for (i = 0; i < subbranch_eids->nelts; i++) 168 { 169 int eid = APR_ARRAY_IDX(subbranch_eids, i, int); 170 const char *subbranch_id 171 = svn_branch__id_nest(branch_id, eid, scratch_pool); 172 svn_branch__state_t *subbranch 173 = svn_branch__txn_get_branch_by_id(branch->txn, subbranch_id, 174 scratch_pool); 175 176 SVN_ERR_ASSERT_NO_RETURN(subbranch); 177 APR_ARRAY_PUSH(subbranches, void *) = subbranch; 178 } 179 *subbranches_p = subbranches; 180 return SVN_NO_ERROR; 181} 182 183svn_branch__subtree_t * 184svn_branch__subtree_create(apr_hash_t *e_map, 185 int root_eid, 186 apr_pool_t *result_pool) 187{ 188 svn_branch__subtree_t *subtree = apr_pcalloc(result_pool, sizeof(*subtree)); 189 190 subtree->tree = svn_element__tree_create(e_map, root_eid, result_pool); 191 subtree->subbranches = apr_hash_make(result_pool); 192 return subtree; 193} 194 195svn_error_t * 196svn_branch__get_subtree(svn_branch__state_t *branch, 197 svn_branch__subtree_t **subtree_p, 198 int eid, 199 apr_pool_t *result_pool) 200{ 201 svn_element__tree_t *element_tree; 202 svn_branch__subtree_t *new_subtree; 203 apr_array_header_t *subbranch_eids; 204 int i; 205 apr_pool_t *iterpool = result_pool; /* ### not a proper iterpool */ 206 207 SVN_ERR(svn_branch__state_get_elements(branch, &element_tree, result_pool)); 208 element_tree = svn_element__tree_get_subtree_at_eid(element_tree, eid, 209 result_pool); 210 new_subtree 211 = svn_branch__subtree_create(element_tree->e_map, eid, result_pool); 212 213 /* Add subbranches */ 214 SVN_ERR(svn_branch__get_immediate_subbranch_eids(branch, &subbranch_eids, 215 result_pool, result_pool)); 216 for (i = 0; i < subbranch_eids->nelts; i++) 217 { 218 int outer_eid = APR_ARRAY_IDX(subbranch_eids, i, int); 219 const char *subbranch_relpath_in_subtree; 220 221 subbranch_relpath_in_subtree 222 = svn_element__tree_get_path_by_eid(new_subtree->tree, outer_eid, 223 iterpool); 224 225 /* Is it pathwise at or below EID? If so, add it into the subtree. */ 226 if (subbranch_relpath_in_subtree) 227 { 228 svn_branch__state_t *subbranch; 229 svn_branch__subtree_t *this_subtree; 230 231 SVN_ERR(svn_branch__get_subbranch_at_eid(branch, &subbranch, 232 outer_eid, iterpool)); 233 if (subbranch) 234 { 235 SVN_ERR(svn_branch__get_subtree(subbranch, &this_subtree, 236 svn_branch__root_eid(subbranch), 237 result_pool)); 238 svn_eid__hash_set(new_subtree->subbranches, outer_eid, 239 this_subtree); 240 } 241 } 242 } 243 *subtree_p = new_subtree; 244 return SVN_NO_ERROR; 245} 246 247svn_branch__subtree_t * 248svn_branch__subtree_get_subbranch_at_eid(svn_branch__subtree_t *subtree, 249 int eid, 250 apr_pool_t *result_pool) 251{ 252 subtree = svn_eid__hash_get(subtree->subbranches, eid); 253 254 return subtree; 255} 256 257/* Instantiate ELEMENTS in TO_BRANCH. 258 */ 259static svn_error_t * 260branch_instantiate_elements(svn_branch__state_t *to_branch, 261 const svn_element__tree_t *elements, 262 apr_pool_t *scratch_pool) 263{ 264 apr_hash_index_t *hi; 265 266 for (hi = apr_hash_first(scratch_pool, elements->e_map); 267 hi; hi = apr_hash_next(hi)) 268 { 269 int this_eid = svn_eid__hash_this_key(hi); 270 svn_element__content_t *this_element = apr_hash_this_val(hi); 271 272 SVN_ERR(svn_branch__state_set_element(to_branch, this_eid, 273 this_element, scratch_pool)); 274 } 275 276 return SVN_NO_ERROR; 277} 278 279svn_error_t * 280svn_branch__instantiate_elements_r(svn_branch__state_t *to_branch, 281 svn_branch__subtree_t elements, 282 apr_pool_t *scratch_pool) 283{ 284 SVN_ERR(branch_instantiate_elements(to_branch, elements.tree, 285 scratch_pool)); 286 287 /* branch any subbranches */ 288 { 289 apr_hash_index_t *hi; 290 291 for (hi = apr_hash_first(scratch_pool, elements.subbranches); 292 hi; hi = apr_hash_next(hi)) 293 { 294 int this_outer_eid = svn_eid__hash_this_key(hi); 295 svn_branch__subtree_t *this_subtree = apr_hash_this_val(hi); 296 const char *new_branch_id; 297 svn_branch__state_t *new_branch; 298 /*### svn_branch__history_t *history;*/ 299 300 /* branch this subbranch into NEW_BRANCH (recursing) */ 301 new_branch_id = svn_branch__id_nest(to_branch->bid, this_outer_eid, 302 scratch_pool); 303 SVN_ERR(svn_branch__txn_open_branch(to_branch->txn, &new_branch, 304 new_branch_id, 305 this_subtree->tree->root_eid, 306 NULL /*tree_ref*/, 307 scratch_pool, scratch_pool)); 308 /*### SVN_ERR(svn_branch__state_set_history(new_branch, history, 309 scratch_pool));*/ 310 311 SVN_ERR(svn_branch__instantiate_elements_r(new_branch, *this_subtree, 312 scratch_pool)); 313 } 314 } 315 316 return SVN_NO_ERROR; 317} 318 319/* 320 * ======================================================================== 321 */ 322 323svn_error_t * 324svn_branch__find_nested_branch_element_by_relpath( 325 svn_branch__state_t **branch_p, 326 int *eid_p, 327 svn_branch__state_t *root_branch, 328 const char *relpath, 329 apr_pool_t *scratch_pool) 330{ 331 /* The path we're looking for is (path-wise) in this branch. See if it 332 is also in a sub-branch. */ 333 /* Loop invariants: RELPATH is the path we're looking for, relative to 334 ROOT_BRANCH which is the current level of nesting that we've descended 335 into. */ 336 while (TRUE) 337 { 338 apr_array_header_t *subbranch_eids; 339 int i; 340 svn_boolean_t found = FALSE; 341 342 SVN_ERR(svn_branch__get_immediate_subbranch_eids( 343 root_branch, &subbranch_eids, scratch_pool, scratch_pool)); 344 for (i = 0; i < subbranch_eids->nelts; i++) 345 { 346 int outer_eid = APR_ARRAY_IDX(subbranch_eids, i, int); 347 const char *relpath_to_subbranch; 348 const char *relpath_in_subbranch; 349 350 /* Check whether the RELPATH we're looking for is within this 351 subbranch at OUTER_EID. If it is, recurse in the subbranch. */ 352 relpath_to_subbranch 353 = svn_branch__get_path_by_eid(root_branch, outer_eid, scratch_pool); 354 relpath_in_subbranch 355 = svn_relpath_skip_ancestor(relpath_to_subbranch, relpath); 356 if (relpath_in_subbranch) 357 { 358 svn_branch__state_t *subbranch; 359 360 SVN_ERR(svn_branch__get_subbranch_at_eid( 361 root_branch, &subbranch, outer_eid, scratch_pool)); 362 /* If the branch hierarchy is not 'flat' then we might find 363 there is no actual branch where the subbranch-root element 364 says there should be one. In that case, ignore it. */ 365 if (subbranch) 366 { 367 root_branch = subbranch; 368 relpath = relpath_in_subbranch; 369 found = TRUE; 370 break; 371 } 372 } 373 } 374 if (! found) 375 { 376 break; 377 } 378 } 379 380 *branch_p = root_branch; 381 if (eid_p) 382 *eid_p = svn_branch__get_eid_by_path(root_branch, relpath, scratch_pool); 383 return SVN_NO_ERROR; 384} 385 386svn_error_t * 387svn_branch__repos_find_el_rev_by_path_rev(svn_branch__el_rev_id_t **el_rev_p, 388 const svn_branch__repos_t *repos, 389 svn_revnum_t revnum, 390 const char *branch_id, 391 const char *relpath, 392 apr_pool_t *result_pool, 393 apr_pool_t *scratch_pool) 394{ 395 svn_branch__el_rev_id_t *el_rev = apr_palloc(result_pool, sizeof(*el_rev)); 396 svn_branch__state_t *branch; 397 398 SVN_ERR(svn_branch__repos_get_branch_by_id(&branch, 399 repos, revnum, branch_id, 400 scratch_pool)); 401 el_rev->rev = revnum; 402 SVN_ERR(svn_branch__find_nested_branch_element_by_relpath(&el_rev->branch, 403 &el_rev->eid, 404 branch, relpath, 405 scratch_pool)); 406 407 /* Any relpath must at least be within the originally given branch */ 408 SVN_ERR_ASSERT_NO_RETURN(el_rev->branch); 409 *el_rev_p = el_rev; 410 return SVN_NO_ERROR; 411} 412 413/* Set *BRANCH_P to the branch found in the repository of TXN, at the 414 * location (in a revision or in this txn) SRC_EL_REV. 415 * 416 * Return an error if REVNUM or BRANCH_ID is not found. 417 */ 418static svn_error_t * 419branch_in_rev_or_txn(svn_branch__state_t **branch_p, 420 const svn_branch__rev_bid_eid_t *src_el_rev, 421 svn_branch__txn_t *txn, 422 apr_pool_t *result_pool) 423{ 424 if (SVN_IS_VALID_REVNUM(src_el_rev->rev)) 425 { 426 SVN_ERR(svn_branch__repos_get_branch_by_id(branch_p, 427 txn->repos, 428 src_el_rev->rev, 429 src_el_rev->bid, 430 result_pool)); 431 } 432 else 433 { 434 *branch_p 435 = svn_branch__txn_get_branch_by_id( 436 txn, src_el_rev->bid, result_pool); 437 if (! *branch_p) 438 return svn_error_createf(SVN_BRANCH__ERR, NULL, 439 _("Branch %s not found"), 440 src_el_rev->bid); 441 } 442 443 return SVN_NO_ERROR; 444} 445 446struct svn_branch__txn_priv_t 447{ 448 /* The underlying branch-txn that supports only non-nested branching. */ 449 svn_branch__txn_t *wrapped_txn; 450 451}; 452 453/* Implements nested branching. 454 * An #svn_branch__txn_t method. */ 455static apr_array_header_t * 456nested_branch_txn_get_branches(const svn_branch__txn_t *txn, 457 apr_pool_t *result_pool) 458{ 459 /* Just forwarding: nothing more is needed. */ 460 apr_array_header_t *branches 461 = svn_branch__txn_get_branches(txn->priv->wrapped_txn, 462 result_pool); 463 464 return branches; 465} 466 467/* An #svn_branch__txn_t method. */ 468static svn_error_t * 469nested_branch_txn_delete_branch(svn_branch__txn_t *txn, 470 const char *bid, 471 apr_pool_t *scratch_pool) 472{ 473 /* Just forwarding: nothing more is needed. */ 474 SVN_ERR(svn_branch__txn_delete_branch(txn->priv->wrapped_txn, 475 bid, 476 scratch_pool)); 477 return SVN_NO_ERROR; 478} 479 480/* Implements nested branching. 481 * An #svn_branch__txn_t method. */ 482static svn_error_t * 483nested_branch_txn_get_num_new_eids(const svn_branch__txn_t *txn, 484 int *num_new_eids_p, 485 apr_pool_t *scratch_pool) 486{ 487 /* Just forwarding: nothing more is needed. */ 488 SVN_ERR(svn_branch__txn_get_num_new_eids(txn->priv->wrapped_txn, 489 num_new_eids_p, 490 scratch_pool)); 491 return SVN_NO_ERROR; 492} 493 494/* Implements nested branching. 495 * An #svn_branch__txn_t method. */ 496static svn_error_t * 497nested_branch_txn_new_eid(svn_branch__txn_t *txn, 498 svn_branch__eid_t *eid_p, 499 apr_pool_t *scratch_pool) 500{ 501 /* Just forwarding: nothing more is needed. */ 502 SVN_ERR(svn_branch__txn_new_eid(txn->priv->wrapped_txn, 503 eid_p, 504 scratch_pool)); 505 return SVN_NO_ERROR; 506} 507 508/* Implements nested branching. 509 * An #svn_branch__txn_t method. */ 510static svn_error_t * 511nested_branch_txn_open_branch(svn_branch__txn_t *txn, 512 svn_branch__state_t **new_branch_p, 513 const char *new_branch_id, 514 int root_eid, 515 svn_branch__rev_bid_eid_t *tree_ref, 516 apr_pool_t *result_pool, 517 apr_pool_t *scratch_pool) 518{ 519 svn_branch__state_t *new_branch; 520 521 SVN_ERR(svn_branch__txn_open_branch(txn->priv->wrapped_txn, 522 &new_branch, 523 new_branch_id, root_eid, tree_ref, 524 result_pool, 525 scratch_pool)); 526 527 /* Recursively branch any nested branches */ 528 if (tree_ref) 529 { 530 svn_branch__state_t *from_branch; 531 svn_branch__subtree_t *from_subtree; 532 533 /* (The way we're doing it here also redundantly re-instantiates all the 534 elements in NEW_BRANCH.) */ 535 SVN_ERR(branch_in_rev_or_txn(&from_branch, tree_ref, 536 txn->priv->wrapped_txn, scratch_pool)); 537 SVN_ERR(svn_branch__get_subtree(from_branch, &from_subtree, 538 tree_ref->eid, scratch_pool)); 539 SVN_ERR(svn_branch__instantiate_elements_r(new_branch, *from_subtree, 540 scratch_pool)); 541 } 542 543 if (new_branch_p) 544 *new_branch_p = new_branch; 545 return SVN_NO_ERROR; 546} 547 548/* Implements nested branching. 549 * An #svn_branch__txn_t method. */ 550static svn_error_t * 551nested_branch_txn_finalize_eids(svn_branch__txn_t *txn, 552 apr_pool_t *scratch_pool) 553{ 554 /* Just forwarding: nothing more is needed. */ 555 SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->wrapped_txn, 556 scratch_pool)); 557 return SVN_NO_ERROR; 558} 559 560/* Implements nested branching. 561 * An #svn_branch__txn_t method. */ 562static svn_error_t * 563nested_branch_txn_serialize(svn_branch__txn_t *txn, 564 svn_stream_t *stream, 565 apr_pool_t *scratch_pool) 566{ 567 /* Just forwarding: nothing more is needed. */ 568 SVN_ERR(svn_branch__txn_serialize(txn->priv->wrapped_txn, 569 stream, 570 scratch_pool)); 571 return SVN_NO_ERROR; 572} 573 574/* Implements nested branching. 575 * An #svn_branch__txn_t method. */ 576static svn_error_t * 577nested_branch_txn_sequence_point(svn_branch__txn_t *txn, 578 apr_pool_t *scratch_pool) 579{ 580 svn_branch__txn_t *wrapped_txn = txn->priv->wrapped_txn; 581 apr_array_header_t *branches; 582 int i; 583 584 /* first, purge elements in each branch */ 585 SVN_ERR(svn_branch__txn_sequence_point(wrapped_txn, scratch_pool)); 586 587 /* second, purge branches that are no longer nested */ 588 branches = svn_branch__txn_get_branches(wrapped_txn, scratch_pool); 589 for (i = 0; i < branches->nelts; i++) 590 { 591 svn_branch__state_t *b = APR_ARRAY_IDX(branches, i, void *); 592 svn_branch__state_t *outer_branch; 593 int outer_eid; 594 595 svn_branch__get_outer_branch_and_eid(&outer_branch, &outer_eid, 596 b, scratch_pool); 597 if (outer_branch) 598 { 599 svn_element__content_t *element; 600 601 SVN_ERR(svn_branch__state_get_element(outer_branch, &element, 602 outer_eid, scratch_pool)); 603 if (! element) 604 SVN_ERR(svn_branch__txn_delete_branch(wrapped_txn, b->bid, 605 scratch_pool)); 606 } 607 } 608 return SVN_NO_ERROR; 609} 610 611/* An #svn_branch__txn_t method. */ 612static svn_error_t * 613nested_branch_txn_complete(svn_branch__txn_t *txn, 614 apr_pool_t *scratch_pool) 615{ 616 /* Just forwarding: nothing more is needed. */ 617 SVN_ERR(svn_branch__txn_complete(txn->priv->wrapped_txn, 618 scratch_pool)); 619 return SVN_NO_ERROR; 620} 621 622/* An #svn_branch__txn_t method. */ 623static svn_error_t * 624nested_branch_txn_abort(svn_branch__txn_t *txn, 625 apr_pool_t *scratch_pool) 626{ 627 /* Just forwarding: nothing more is needed. */ 628 SVN_ERR(svn_branch__txn_abort(txn->priv->wrapped_txn, 629 scratch_pool)); 630 return SVN_NO_ERROR; 631} 632 633svn_branch__txn_t * 634svn_branch__nested_txn_create(svn_branch__txn_t *wrapped_txn, 635 apr_pool_t *result_pool) 636{ 637 static const svn_branch__txn_vtable_t vtable = { 638 {0}, 639 nested_branch_txn_get_branches, 640 nested_branch_txn_delete_branch, 641 nested_branch_txn_get_num_new_eids, 642 nested_branch_txn_new_eid, 643 nested_branch_txn_open_branch, 644 nested_branch_txn_finalize_eids, 645 nested_branch_txn_serialize, 646 nested_branch_txn_sequence_point, 647 nested_branch_txn_complete, 648 nested_branch_txn_abort, 649 }; 650 svn_branch__txn_t *txn 651 = svn_branch__txn_create(&vtable, NULL, NULL, result_pool); 652 653 txn->priv = apr_pcalloc(result_pool, sizeof(*txn->priv)); 654 txn->priv->wrapped_txn = wrapped_txn; 655 txn->repos = wrapped_txn->repos; 656 txn->rev = wrapped_txn->rev; 657 txn->base_rev = wrapped_txn->base_rev; 658 return txn; 659} 660 661