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