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
40 void
svn_branch__get_outer_branch_and_eid(svn_branch__state_t ** outer_branch_p,int * outer_eid_p,const svn_branch__state_t * branch,apr_pool_t * scratch_pool)41 svn_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
58 const char *
svn_branch__get_root_rrpath(const svn_branch__state_t * branch,apr_pool_t * result_pool)59 svn_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
82 const char *
svn_branch__get_rrpath_by_eid(const svn_branch__state_t * branch,int eid,apr_pool_t * result_pool)83 svn_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
98 svn_error_t *
svn_branch__get_subbranch_at_eid(svn_branch__state_t * branch,svn_branch__state_t ** subbranch_p,int eid,apr_pool_t * scratch_pool)99 svn_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 */
126 static svn_error_t *
svn_branch__get_immediate_subbranch_eids(svn_branch__state_t * branch,apr_array_header_t ** subbranch_eids_p,apr_pool_t * result_pool,apr_pool_t * scratch_pool)127 svn_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
153 svn_error_t *
svn_branch__get_immediate_subbranches(svn_branch__state_t * branch,apr_array_header_t ** subbranches_p,apr_pool_t * result_pool,apr_pool_t * scratch_pool)154 svn_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
183 svn_branch__subtree_t *
svn_branch__subtree_create(apr_hash_t * e_map,int root_eid,apr_pool_t * result_pool)184 svn_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
195 svn_error_t *
svn_branch__get_subtree(svn_branch__state_t * branch,svn_branch__subtree_t ** subtree_p,int eid,apr_pool_t * result_pool)196 svn_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
247 svn_branch__subtree_t *
svn_branch__subtree_get_subbranch_at_eid(svn_branch__subtree_t * subtree,int eid,apr_pool_t * result_pool)248 svn_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 */
259 static svn_error_t *
branch_instantiate_elements(svn_branch__state_t * to_branch,const svn_element__tree_t * elements,apr_pool_t * scratch_pool)260 branch_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
279 svn_error_t *
svn_branch__instantiate_elements_r(svn_branch__state_t * to_branch,svn_branch__subtree_t elements,apr_pool_t * scratch_pool)280 svn_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
323 svn_error_t *
svn_branch__find_nested_branch_element_by_relpath(svn_branch__state_t ** branch_p,int * eid_p,svn_branch__state_t * root_branch,const char * relpath,apr_pool_t * scratch_pool)324 svn_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
386 svn_error_t *
svn_branch__repos_find_el_rev_by_path_rev(svn_branch__el_rev_id_t ** el_rev_p,const svn_branch__repos_t * repos,svn_revnum_t revnum,const char * branch_id,const char * relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)387 svn_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 */
418 static svn_error_t *
branch_in_rev_or_txn(svn_branch__state_t ** branch_p,const svn_branch__rev_bid_eid_t * src_el_rev,svn_branch__txn_t * txn,apr_pool_t * result_pool)419 branch_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
446 struct 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. */
455 static apr_array_header_t *
nested_branch_txn_get_branches(const svn_branch__txn_t * txn,apr_pool_t * result_pool)456 nested_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. */
468 static svn_error_t *
nested_branch_txn_delete_branch(svn_branch__txn_t * txn,const char * bid,apr_pool_t * scratch_pool)469 nested_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. */
482 static svn_error_t *
nested_branch_txn_get_num_new_eids(const svn_branch__txn_t * txn,int * num_new_eids_p,apr_pool_t * scratch_pool)483 nested_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. */
496 static svn_error_t *
nested_branch_txn_new_eid(svn_branch__txn_t * txn,svn_branch__eid_t * eid_p,apr_pool_t * scratch_pool)497 nested_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. */
510 static svn_error_t *
nested_branch_txn_open_branch(svn_branch__txn_t * txn,svn_branch__state_t ** new_branch_p,const char * new_branch_id,int root_eid,svn_branch__rev_bid_eid_t * tree_ref,apr_pool_t * result_pool,apr_pool_t * scratch_pool)511 nested_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. */
550 static svn_error_t *
nested_branch_txn_finalize_eids(svn_branch__txn_t * txn,apr_pool_t * scratch_pool)551 nested_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. */
562 static svn_error_t *
nested_branch_txn_serialize(svn_branch__txn_t * txn,svn_stream_t * stream,apr_pool_t * scratch_pool)563 nested_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. */
576 static svn_error_t *
nested_branch_txn_sequence_point(svn_branch__txn_t * txn,apr_pool_t * scratch_pool)577 nested_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. */
612 static svn_error_t *
nested_branch_txn_complete(svn_branch__txn_t * txn,apr_pool_t * scratch_pool)613 nested_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. */
623 static svn_error_t *
nested_branch_txn_abort(svn_branch__txn_t * txn,apr_pool_t * scratch_pool)624 nested_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
633 svn_branch__txn_t *
svn_branch__nested_txn_create(svn_branch__txn_t * wrapped_txn,apr_pool_t * result_pool)634 svn_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