]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_delta/branch_compat.c
Merge lld trunk r366426, and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_delta / branch_compat.c
1 /*
2  * branch_compat.c : Branching compatibility layer.
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 "svn_types.h"
25 #include "svn_error.h"
26 #include "svn_delta.h"
27 #include "svn_dirent_uri.h"
28 #include "svn_path.h"
29 #include "svn_hash.h"
30 #include "svn_iter.h"
31 #include "svn_props.h"
32 #include "svn_pools.h"
33
34 #include "private/svn_branch_impl.h"
35 #include "private/svn_branch_repos.h"
36 #include "private/svn_branch_nested.h"
37 #include "private/svn_delta_private.h"
38 #include "private/svn_branch_compat.h"
39
40 #include "svn_private_config.h"
41
42
43 /* Verify EXPR is true; raise an error if not. */
44 #define VERIFY(expr) SVN_ERR_ASSERT(expr)
45
46
47 /*
48  * ===================================================================
49  * Minor data types
50  * ===================================================================
51  */
52
53 /** A location in a committed revision.
54  *
55  * @a rev shall not be #SVN_INVALID_REVNUM unless the interface using this
56  * type specifically allows it and defines its meaning. */
57 typedef struct svn_pathrev_t
58 {
59   svn_revnum_t rev;
60   const char *relpath;
61 } svn_pathrev_t;
62
63 /* Return true iff PEG_PATH1 and PEG_PATH2 are both the same location.
64  */
65 static svn_boolean_t
66 pathrev_equal(const svn_pathrev_t *p1,
67               const svn_pathrev_t *p2)
68 {
69   if (p1->rev != p2->rev)
70     return FALSE;
71   if (strcmp(p1->relpath, p2->relpath) != 0)
72     return FALSE;
73
74   return TRUE;
75 }
76
77 #if 0
78 /* Return a human-readable string representation of LOC. */
79 static const char *
80 pathrev_str(const svn_pathrev_t *loc,
81             apr_pool_t *result_pool)
82 {
83   if (! loc)
84     return "<nil>";
85   return apr_psprintf(result_pool, "%s@%ld",
86                       loc->relpath, loc->rev);
87 }
88
89 /* Return a string representation of the (string) keys of HASH. */
90 static const char *
91 hash_keys_str(apr_hash_t *hash)
92 {
93   const char *str = NULL;
94   apr_pool_t *pool;
95   apr_hash_index_t *hi;
96
97   if (! hash)
98     return "<nil>";
99
100   pool = apr_hash_pool_get(hash);
101   for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
102     {
103       const char *key = apr_hash_this_key(hi);
104
105       if (!str)
106         str = key;
107       else
108         str = apr_psprintf(pool, "%s, %s", str, key);
109     }
110   return apr_psprintf(pool, "{%s}", str);
111 }
112 #endif
113
114 /**
115  * Merge two hash tables into one new hash table. The values of the overlay
116  * hash override the values of the base if both have the same key.
117  *
118  * Unlike apr_hash_overlay(), this doesn't care whether the input hashes use
119  * the same hash function, nor about the relationship between the three pools.
120  *
121  * @param p The pool to use for the new hash table
122  * @param overlay The table to add to the initial table
123  * @param base The table that represents the initial values of the new table
124  * @return A new hash table containing all of the data from the two passed in
125  * @remark Makes a shallow copy: keys and values are not copied
126  */
127 static apr_hash_t *
128 hash_overlay(apr_hash_t *overlay,
129              apr_hash_t *base)
130 {
131   apr_pool_t *pool = apr_hash_pool_get(base);
132   apr_hash_t *result = apr_hash_copy(pool, base);
133   apr_hash_index_t *hi;
134
135   for (hi = apr_hash_first(pool, overlay); hi; hi = apr_hash_next(hi))
136     {
137       svn_hash_sets(result, apr_hash_this_key(hi), apr_hash_this_val(hi));
138     }
139   return result;
140 }
141
142
143 /*
144  * ========================================================================
145  * Configuration Options
146  * ========================================================================
147  */
148
149 /* Features that are not wanted for this commit editor shim but may be
150  * wanted in a similar but different shim such as for an update editor. */
151 /* #define SHIM_WITH_ADD_ABSENT */
152 /* #define SHIM_WITH_UNLOCK */
153
154 /* Whether to support switching from relative to absolute paths in the
155  * Ev1 methods. */
156 /* #define SHIM_WITH_ABS_PATHS */
157
158
159 /*
160  * ========================================================================
161  * Shim Connector
162  * ========================================================================
163  *
164  * The shim connector enables a more exact round-trip conversion from an
165  * Ev1 drive to Ev3 and back to Ev1.
166  */
167 struct svn_branch__compat_shim_connector_t
168 {
169   /* Set to true if and when an Ev1 receiving shim receives an absolute
170    * path (prefixed with '/') from the delta edit, and causes the Ev1
171    * sending shim to send absolute paths.
172    * ### NOT IMPLEMENTED
173    */
174 #ifdef SHIM_WITH_ABS_PATHS
175   svn_boolean_t *ev1_absolute_paths;
176 #endif
177
178   /* The Ev1 set_target_revision and start_edit methods, respectively, will
179    * call the TARGET_REVISION_FUNC and START_EDIT_FUNC callbacks, if non-null.
180    * Otherwise, default calls will be used.
181    *
182    * (Possibly more useful for update editors than for commit editors?) */
183   svn_branch__compat_set_target_revision_func_t target_revision_func;
184
185   /* If not null, a callback that the Ev3 driver may call to
186    * provide the "base revision" of the root directory, even if it is not
187    * going to modify that directory. (If it does modify it, then it will
188    * pass in the appropriate base revision at that time.) If null
189    * or if the driver does not call it, then the Ev1
190    * open_root() method will be called with SVN_INVALID_REVNUM as the base
191    * revision parameter.
192    */
193   svn_delta__start_edit_func_t start_edit_func;
194
195 #ifdef SHIM_WITH_UNLOCK
196   /* A callback which will be called when an unlocking action is received.
197      (For update editors?) */
198   svn_delta__unlock_func_t unlock_func;
199 #endif
200
201   void *baton;
202 };
203
204 svn_error_t *
205 svn_branch__compat_insert_shims(
206                         const svn_delta_editor_t **new_deditor,
207                         void **new_dedit_baton,
208                         const svn_delta_editor_t *old_deditor,
209                         void *old_dedit_baton,
210                         const char *repos_root,
211                         const char *base_relpath,
212                         svn_branch__compat_fetch_func_t fetch_func,
213                         void *fetch_baton,
214                         apr_pool_t *result_pool,
215                         apr_pool_t *scratch_pool)
216 {
217 #if 0
218   svn_branch__txn_t *edit_txn;
219   svn_branch__compat_shim_connector_t *shim_connector;
220
221 #ifdef SVN_DEBUG
222   /*SVN_ERR(svn_delta__get_debug_editor(&old_deditor, &old_dedit_baton,
223                                       old_deditor, old_dedit_baton,
224                                       "[OUT] ", result_pool));*/
225 #endif
226   SVN_ERR(svn_branch__compat_txn_from_delta_for_commit(
227                         &edit_txn,
228                         &shim_connector,
229                         old_deditor, old_dedit_baton,
230                         branching_txn,
231                         repos_root,
232                         fetch_func, fetch_baton,
233                         NULL, NULL /*cancel*/,
234                         result_pool, scratch_pool));
235   SVN_ERR(svn_branch__compat_delta_from_txn_for_commit(
236                         new_deditor, new_dedit_baton,
237                         edit_txn,
238                         repos_root, base_relpath,
239                         fetch_func, fetch_baton,
240                         shim_connector,
241                         result_pool, scratch_pool));
242 #ifdef SVN_DEBUG
243   /*SVN_ERR(svn_delta__get_debug_editor(new_deditor, new_dedit_baton,
244                                       *new_deditor, *new_dedit_baton,
245                                       "[IN]  ", result_pool));*/
246 #endif
247 #else
248   *new_deditor = old_deditor;
249   *new_dedit_baton = old_dedit_baton;
250 #endif
251   return SVN_NO_ERROR;
252 }
253
254
255 /*
256  * ========================================================================
257  * Buffering the Delta Editor Changes
258  * ========================================================================
259  */
260
261 /* The kind of Ev1 restructuring operation on a particular path. For each
262  * visited path we use exactly one restructuring action. */
263 enum restructure_action_t
264 {
265   RESTRUCTURE_NONE = 0,
266   RESTRUCTURE_ADD,         /* add the node, maybe replacing. maybe copy  */
267 #ifdef SHIM_WITH_ADD_ABSENT
268   RESTRUCTURE_ADD_ABSENT,  /* add an absent node, possibly replacing  */
269 #endif
270   RESTRUCTURE_DELETE       /* delete this node  */
271 };
272
273 /* Records everything about how this node is to be changed, from an Ev1
274  * point of view.  */
275 typedef struct change_node_t
276 {
277   /* what kind of (tree) restructure is occurring at this node?  */
278   enum restructure_action_t action;
279
280   svn_node_kind_t kind;  /* the NEW kind of this node  */
281
282   /* We may need to specify the revision we are altering or the revision
283      to delete or replace. These are mutually exclusive, but are separate
284      for clarity. */
285   /* CHANGING_REV is the base revision of the change if ACTION is 'none',
286      else is SVN_INVALID_REVNUM. (If ACTION is 'add' and COPYFROM_PATH
287      is non-null, then COPYFROM_REV serves the equivalent purpose for the
288      copied node.) */
289   /* ### Can also be SVN_INVALID_REVNUM for a pre-existing file/dir,
290          meaning the base is the youngest revision. This is probably not
291          a good idea -- it is at least confusing -- and we should instead
292          resolve to a real revnum when Ev1 passes in SVN_INVALID_REVNUM
293          in such cases. */
294   svn_revnum_t changing_rev;
295   /* If ACTION is 'delete' or if ACTION is 'add' and it is a replacement,
296      DELETING is TRUE and DELETING_REV is the revision to delete. */
297   /* ### Can also be SVN_INVALID_REVNUM for a pre-existing file/dir,
298          meaning the base is the youngest revision. This is probably not
299          a good idea -- it is at least confusing -- and we should instead
300          resolve to a real revnum when Ev1 passes in SVN_INVALID_REVNUM
301          in such cases. */
302   svn_boolean_t deleting;
303   svn_revnum_t deleting_rev;
304
305   /* new/final set of props to apply; null => no *change*, not no props */
306   apr_hash_t *props;
307
308   /* new fulltext; null => no change */
309   svn_boolean_t contents_changed;
310   svn_stringbuf_t *contents_text;
311
312   /* If COPYFROM_PATH is not NULL, then copy PATH@REV to this node.
313      RESTRUCTURE must be RESTRUCTURE_ADD.  */
314   const char *copyfrom_path;
315   svn_revnum_t copyfrom_rev;
316
317 #ifdef SHIM_WITH_UNLOCK
318   /* Record whether an incoming propchange unlocked this node.  */
319   svn_boolean_t unlock;
320 #endif
321 } change_node_t;
322
323 #if 0
324 /* Return a string representation of CHANGE. */
325 static const char *
326 change_node_str(change_node_t *change,
327                 apr_pool_t *result_pool)
328 {
329   const char *copyfrom = "<nil>";
330   const char *str;
331
332   if (change->copyfrom_path)
333     copyfrom = apr_psprintf(result_pool, "'%s'@%ld",
334                             change->copyfrom_path, change->copyfrom_rev);
335   str = apr_psprintf(result_pool,
336                      "action=%d, kind=%s, changing_rev=%ld, "
337                      "deleting=%d, deleting_rev=%ld, ..., "
338                      "copyfrom=%s",
339                      change->action,
340                      svn_node_kind_to_word(change->kind),
341                      change->changing_rev,
342                      change->deleting, change->deleting_rev,
343                      copyfrom);
344   return str;
345 }
346 #endif
347
348 /* Check whether RELPATH is known to exist, known to not exist, or unknown. */
349 static svn_tristate_t
350 check_existence(apr_hash_t *changes,
351                 const char *relpath)
352 {
353   apr_pool_t *changes_pool = apr_hash_pool_get(changes);
354   apr_pool_t *scratch_pool = changes_pool;
355   change_node_t *change = svn_hash_gets(changes, relpath);
356   svn_tristate_t exists = svn_tristate_unknown;
357
358   if (change && change->action != RESTRUCTURE_DELETE)
359     exists = svn_tristate_true;
360   else if (change && change->action == RESTRUCTURE_DELETE)
361     exists = svn_tristate_false;
362   else
363     {
364       const char *parent_path = relpath;
365
366       /* Find the nearest parent change. If that's a delete or a simple
367          (non-recursive) add, this path cannot exist, else we don't know. */
368       while ((parent_path = svn_relpath_dirname(parent_path, scratch_pool)),
369              *parent_path)
370         {
371           change = svn_hash_gets(changes, parent_path);
372           if (change)
373             {
374               if ((change->action == RESTRUCTURE_ADD && !change->copyfrom_path)
375                   || change->action == RESTRUCTURE_DELETE)
376                 exists = svn_tristate_false;
377               break;
378             }
379         }
380     }
381
382   return exists;
383 }
384
385 /* Insert a new Ev1-style change for RELPATH, or return an existing one.
386  *
387  * Verify Ev3 rules. Primary differences from Ev1 rules are ...
388  *
389  * If ACTION is 'delete', elide any previous explicit deletes inside
390  * that subtree. (Other changes inside that subtree are not allowed.) We
391  * do not store multiple change records per path even with nested moves
392  * -- the most complex change is delete + copy which all fits in one
393  * record with action='add'.
394  */
395 static svn_error_t *
396 insert_change(change_node_t **change_p, apr_hash_t *changes,
397               const char *relpath,
398               enum restructure_action_t action)
399 {
400   apr_pool_t *changes_pool = apr_hash_pool_get(changes);
401   change_node_t *change = svn_hash_gets(changes, relpath);
402
403   /* Check whether this op is allowed. */
404   switch (action)
405     {
406     case RESTRUCTURE_NONE:
407       if (change)
408         {
409           /* A no-restructure change is allowed after add, but not allowed
410            * (in Ev3) after another no-restructure change, nor a delete. */
411           VERIFY(change->action == RESTRUCTURE_ADD);
412         }
413       break;
414
415     case RESTRUCTURE_ADD:
416       if (change)
417         {
418           /* Add or copy is allowed after delete (and replaces the delete),
419            * but not allowed after an add or a no-restructure change. */
420           VERIFY(change->action == RESTRUCTURE_DELETE);
421           change->action = action;
422         }
423       break;
424
425 #ifdef SHIM_WITH_ADD_ABSENT
426     case RESTRUCTURE_ADD_ABSENT:
427       /* ### */
428       break;
429 #endif
430
431     case RESTRUCTURE_DELETE:
432       SVN_ERR_MALFUNCTION();
433     }
434
435   if (change)
436     {
437       if (action != RESTRUCTURE_NONE)
438         {
439           change->action = action;
440         }
441     }
442   else
443     {
444       /* Create a new change. Callers will set the other fields as needed. */
445       change = apr_pcalloc(changes_pool, sizeof(*change));
446       change->action = action;
447       change->changing_rev = SVN_INVALID_REVNUM;
448
449       svn_hash_sets(changes, apr_pstrdup(changes_pool, relpath), change);
450     }
451
452   *change_p = change;
453   return SVN_NO_ERROR;
454 }
455
456 /* Modify CHANGES so as to delete the subtree at RELPATH.
457  *
458  * Insert a new Ev1-style change record for RELPATH (or perhaps remove
459  * the existing record if this would have the same effect), and remove
460  * any change records for sub-paths under RELPATH.
461  *
462  * Follow Ev3 rules, although without knowing whether this delete is
463  * part of a move. Ev3 (incremental) "rm" operation says each node to
464  * be removed "MAY be a child of a copy but otherwise SHOULD NOT have
465  * been created or modified in this edit". "mv" operation says ...
466  */
467 static svn_error_t *
468 delete_subtree(apr_hash_t *changes,
469                const char *relpath,
470                svn_revnum_t deleting_rev)
471 {
472   apr_pool_t *changes_pool = apr_hash_pool_get(changes);
473   apr_pool_t *scratch_pool = changes_pool;
474   change_node_t *change = svn_hash_gets(changes, relpath);
475
476   if (change)
477     {
478       /* If this previous change was a non-replacing addition, there
479          is no longer any change to be made at this path. If it was
480          a replacement or a modification, it now becomes a delete.
481          (If it was a delete, this attempt to delete is an error.) */
482        VERIFY(change->action != RESTRUCTURE_DELETE);
483        if (change->action == RESTRUCTURE_ADD && !change->deleting)
484          {
485            svn_hash_sets(changes, relpath, NULL);
486            change = NULL;
487          }
488        else
489          {
490            change->action = RESTRUCTURE_DELETE;
491          }
492     }
493   else
494     {
495       /* There was no change recorded at this path. Record a delete. */
496       change = apr_pcalloc(changes_pool, sizeof(*change));
497       change->action = RESTRUCTURE_DELETE;
498       change->changing_rev = SVN_INVALID_REVNUM;
499       change->deleting = TRUE;
500       change->deleting_rev = deleting_rev;
501
502       svn_hash_sets(changes, apr_pstrdup(changes_pool, relpath), change);
503     }
504
505   /* Elide all child ops. */
506   {
507     apr_hash_index_t *hi;
508
509     for (hi = apr_hash_first(scratch_pool, changes);
510          hi; hi = apr_hash_next(hi))
511       {
512         const char *this_relpath = apr_hash_this_key(hi);
513         const char *r = svn_relpath_skip_ancestor(relpath, this_relpath);
514
515         if (r && r[0])
516           {
517             svn_hash_sets(changes, this_relpath, NULL);
518           }
519       }
520   }
521
522   return SVN_NO_ERROR;
523 }
524
525
526 /*
527  * ===================================================================
528  * Commit Editor converter to join a v1 driver to a v3 consumer
529  * ===================================================================
530  *
531  * ...
532  */
533
534 svn_error_t *
535 svn_branch__compat_delta_from_txn_for_commit(
536                         const svn_delta_editor_t **deditor,
537                         void **dedit_baton,
538                         svn_branch__txn_t *edit_txn,
539                         const char *repos_root_url,
540                         const char *base_relpath,
541                         svn_branch__compat_fetch_func_t fetch_func,
542                         void *fetch_baton,
543                         const svn_branch__compat_shim_connector_t *shim_connector,
544                         apr_pool_t *result_pool,
545                         apr_pool_t *scratch_pool)
546 {
547   /* ### ... */
548
549   return SVN_NO_ERROR;
550 }
551
552 svn_error_t *
553 svn_branch__compat_delta_from_txn_for_update(
554                         const svn_delta_editor_t **deditor,
555                         void **dedit_baton,
556                         svn_branch__compat_update_editor3_t *update_editor,
557                         const char *repos_root_url,
558                         const char *base_repos_relpath,
559                         svn_branch__compat_fetch_func_t fetch_func,
560                         void *fetch_baton,
561                         apr_pool_t *result_pool,
562                         apr_pool_t *scratch_pool)
563 {
564   svn_branch__compat_shim_connector_t *shim_connector
565     = apr_pcalloc(result_pool, sizeof(*shim_connector));
566
567   shim_connector->target_revision_func = update_editor->set_target_revision_func;
568   shim_connector->baton = update_editor->set_target_revision_baton;
569 #ifdef SHIM_WITH_ABS_PATHS
570   shim_connector->ev1_absolute_paths /*...*/;
571 #endif
572
573   SVN_ERR(svn_branch__compat_delta_from_txn_for_commit(
574                         deditor, dedit_baton,
575                         update_editor->edit_txn,
576                         repos_root_url, base_repos_relpath,
577                         fetch_func, fetch_baton,
578                         shim_connector,
579                         result_pool, scratch_pool));
580   /*SVN_ERR(svn_delta__get_debug_editor(deditor, dedit_baton,
581                                       *deditor, *dedit_baton,
582                                       "[UP>1] ", result_pool));*/
583
584   return SVN_NO_ERROR;
585 }
586
587
588 /*
589  * ===================================================================
590  * Commit Editor converter to join a v3 driver to a v1 consumer
591  * ===================================================================
592  *
593  * This editor buffers all the changes before driving the Ev1 at the end,
594  * since it needs to do a single depth-first traversal of the path space
595  * and this cannot be started until all moves are known.
596  *
597  * Moves are converted to copy-and-delete, with the copy being from
598  * the source peg rev. (### Should it request copy-from revision "-1"?)
599  *
600  * It works like this:
601  *
602  *                +------+--------+
603  *                | path | change |
604  *      Ev3  -->  +------+--------+  -->  Ev1
605  *                | ...  | ...    |
606  *                | ...  | ...    |
607  *
608  *   1. Ev3 changes are accumulated in a per-path table, EB->changes.
609  *
610  *   2. On Ev3 close-edit, walk through the table in a depth-first order,
611  *      sending the equivalent Ev1 action for each change.
612  *
613  * TODO
614  *
615  * ### For changes inside a copied subtree, the calls to the "open dir"
616  *     and "open file" Ev1 methods may be passing the wrong revision
617  *     number: see comment in apply_change().
618  *
619  * ### Have we got our rel-paths in order? Ev1, Ev3 and callbacks may
620  *     all expect different paths. Are they relative to repos root or to
621  *     some base path? Leading slash (unimplemented 'send_abs_paths'
622  *     feature), etc.
623  *
624  * ### May be tidier for OPEN_ROOT_FUNC callback (see open_root_ev3())
625  *     not to actually open the root in advance, but instead just to
626  *     remember the base revision that the driver wants us to specify
627  *     when we do open it.
628  */
629
630
631
632 /*
633  * ========================================================================
634  * Driving the Delta Editor
635  * ========================================================================
636  */
637
638 /* Information needed for driving the delta editor. */
639 struct svn_branch__txn_priv_t
640 {
641   /* The Ev1 "delta editor" */
642   const svn_delta_editor_t *deditor;
643   void *dedit_baton;
644
645   /* Callbacks */
646   svn_branch__compat_fetch_func_t fetch_func;
647   void *fetch_baton;
648
649   /* The Ev1 root directory baton if we have opened the root, else null. */
650   void *ev1_root_dir_baton;
651
652 #ifdef SHIM_WITH_ABS_PATHS
653   const svn_boolean_t *make_abs_paths;
654 #endif
655
656   /* Repository root URL
657      ### Some code allows this to be null -- but is that valid? */
658   const char *repos_root_url;
659
660   /* Ev1 changes recorded so far: REPOS_RELPATH -> change_node_ev3_t */
661   apr_hash_t *changes;
662
663   /* The branching state on which the per-element API is working */
664   svn_branch__txn_t *txn;
665
666   apr_pool_t *edit_pool;
667 };
668
669 /* Get all the (Ev1) paths that have changes.
670  */
671 static const apr_array_header_t *
672 get_unsorted_paths(apr_hash_t *changes,
673                    apr_pool_t *scratch_pool)
674 {
675   apr_array_header_t *paths = apr_array_make(scratch_pool, 0, sizeof(void *));
676   apr_hash_index_t *hi;
677
678   for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
679     {
680       const char *this_path = apr_hash_this_key(hi);
681       APR_ARRAY_PUSH(paths, const char *) = this_path;
682     }
683
684   return paths;
685 }
686
687 #if 0 /* needed only for shim connector */
688 /*  */
689 static svn_error_t *
690 set_target_revision_ev3(void *edit_baton,
691                         svn_revnum_t target_revision,
692                         apr_pool_t *scratch_pool)
693 {
694   svn_branch__txn_priv_t *eb = edit_baton;
695
696   SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
697                                            scratch_pool));
698
699   return SVN_NO_ERROR;
700 }
701 #endif
702
703 /*  */
704 static svn_error_t *
705 open_root_ev3(void *baton,
706               svn_revnum_t base_revision)
707 {
708   svn_branch__txn_priv_t *eb = baton;
709
710   SVN_ERR(eb->deditor->open_root(eb->dedit_baton, base_revision,
711                                  eb->edit_pool, &eb->ev1_root_dir_baton));
712   return SVN_NO_ERROR;
713 }
714
715 /* If RELPATH is a child of a copy, return the path of the copy root,
716  * else return NULL.
717  */
718 static const char *
719 find_enclosing_copy(apr_hash_t *changes,
720                     const char *relpath,
721                     apr_pool_t *result_pool)
722 {
723   while (*relpath)
724     {
725       const change_node_t *change = svn_hash_gets(changes, relpath);
726
727       if (change)
728         {
729           if (change->copyfrom_path)
730             return relpath;
731           if (change->action != RESTRUCTURE_NONE)
732             return NULL;
733         }
734       relpath = svn_relpath_dirname(relpath, result_pool);
735     }
736
737   return NULL;
738 }
739
740 /* Set *BASE_PROPS to the 'base' properties, against which any changes
741  * will be described, for the changed path described in CHANGES at
742  * REPOS_RELPATH.
743  *
744  * For a copied path, including a copy child path, fetch from the copy
745  * source path. For a plain add, return an empty set. For a delete,
746  * return NULL.
747  */
748 static svn_error_t *
749 fetch_base_props(apr_hash_t **base_props,
750                  apr_hash_t *changes,
751                  const char *repos_relpath,
752                  svn_branch__compat_fetch_func_t fetch_func,
753                  void *fetch_baton,
754                  apr_pool_t *result_pool,
755                  apr_pool_t *scratch_pool)
756 {
757   const change_node_t *change = svn_hash_gets(changes, repos_relpath);
758   const char *source_path = NULL;
759   svn_revnum_t source_rev;
760
761   if (change->action == RESTRUCTURE_DELETE)
762     {
763       *base_props = NULL;
764     }
765
766   else if (change->action == RESTRUCTURE_ADD && ! change->copyfrom_path)
767     {
768       *base_props = apr_hash_make(result_pool);
769     }
770   else if (change->copyfrom_path)
771     {
772       source_path = change->copyfrom_path;
773       source_rev = change->copyfrom_rev;
774     }
775   else /* RESTRUCTURE_NONE */
776     {
777       /* It's an edit, but possibly to a copy child. Discover if it's a
778          copy child, & find the copy-from source. */
779
780       const char *copy_path
781         = find_enclosing_copy(changes, repos_relpath, scratch_pool);
782
783       if (copy_path)
784         {
785           const change_node_t *enclosing_copy
786             = svn_hash_gets(changes, copy_path);
787           const char *remainder
788             = svn_relpath_skip_ancestor(copy_path, repos_relpath);
789
790           source_path = svn_relpath_join(enclosing_copy->copyfrom_path,
791                                          remainder, scratch_pool);
792           source_rev = enclosing_copy->copyfrom_rev;
793         }
794       else
795         {
796           /* It's a plain edit (not a copy child path). */
797           source_path = repos_relpath;
798           source_rev = change->changing_rev;
799         }
800     }
801
802   if (source_path)
803     {
804       SVN_ERR(fetch_func(NULL, base_props, NULL, NULL,
805                          fetch_baton, source_path, source_rev,
806                          result_pool, scratch_pool));
807     }
808
809   return SVN_NO_ERROR;
810 }
811
812 /* Send property changes to Ev1 for the CHANGE at REPOS_RELPATH.
813  *
814  * Ev1 requires exactly one prop-change call for each prop whose value
815  * has changed. Therefore we *have* to fetch the original props from the
816  * repository, provide them as OLD_PROPS, and calculate the changes.
817  */
818 static svn_error_t *
819 drive_ev1_props(const char *repos_relpath,
820                 const change_node_t *change,
821                 apr_hash_t *old_props,
822                 const svn_delta_editor_t *deditor,
823                 void *node_baton,
824                 apr_pool_t *scratch_pool)
825 {
826   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
827   apr_array_header_t *propdiffs;
828   int i;
829
830   SVN_ERR_ASSERT(change->action != RESTRUCTURE_DELETE);
831
832   /* If there are no property changes, then just exit. */
833   if (change->props == NULL)
834     return SVN_NO_ERROR;
835
836   SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
837
838   /* Apply property changes. These should be changes against the empty set
839      for a new node, or changes against the source node for a copied node. */
840   for (i = 0; i < propdiffs->nelts; i++)
841     {
842       const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
843
844       svn_pool_clear(iterpool);
845
846       if (change->kind == svn_node_dir)
847         SVN_ERR(deditor->change_dir_prop(node_baton,
848                                          prop->name, prop->value,
849                                          iterpool));
850       else
851         SVN_ERR(deditor->change_file_prop(node_baton,
852                                           prop->name, prop->value,
853                                           iterpool));
854     }
855
856 #ifdef SHIM_WITH_UNLOCK
857   /* Handle the funky unlock protocol. Note: only possibly on files.  */
858   if (change->unlock)
859     {
860       SVN_ERR_ASSERT(change->kind == svn_node_file);
861       SVN_ERR(deditor->change_file_prop(node_baton,
862                                             SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
863                                             iterpool));
864     }
865 #endif
866
867   svn_pool_destroy(iterpool);
868   return SVN_NO_ERROR;
869 }
870
871 /* Drive the Ev1 editor with the change recorded in EB->changes for the
872  * path EV1_RELPATH.
873  *
874  * Conforms to svn_delta_path_driver_cb_func_t.
875  */
876 static svn_error_t *
877 apply_change(void **dir_baton,
878              void *parent_baton,
879              void *callback_baton,
880              const char *ev1_relpath,
881              apr_pool_t *result_pool)
882 {
883   apr_pool_t *scratch_pool = result_pool;
884   const svn_branch__txn_priv_t *eb = callback_baton;
885   const change_node_t *change = svn_hash_gets(eb->changes, ev1_relpath);
886   void *file_baton = NULL;
887   apr_hash_t *base_props;
888
889   /* The callback should only be called for paths in CHANGES.  */
890   SVN_ERR_ASSERT(change != NULL);
891
892   /* Typically, we are not creating new directory batons.  */
893   *dir_baton = NULL;
894
895   SVN_ERR(fetch_base_props(&base_props, eb->changes, ev1_relpath,
896                            eb->fetch_func, eb->fetch_baton,
897                            scratch_pool, scratch_pool));
898
899   /* Are we editing the root of the tree?  */
900   if (parent_baton == NULL)
901     {
902       /* The root dir was already opened. */
903       *dir_baton = eb->ev1_root_dir_baton;
904
905       /* Only property edits are allowed on the root.  */
906       SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
907       SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
908                               eb->deditor, *dir_baton, scratch_pool));
909
910       /* No further action possible for the root.  */
911       return SVN_NO_ERROR;
912     }
913
914   if (change->action == RESTRUCTURE_DELETE)
915     {
916       SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
917                                         parent_baton, scratch_pool));
918
919       /* No futher action possible for this node.  */
920       return SVN_NO_ERROR;
921     }
922
923   /* If we're not deleting this node, then we should know its kind.  */
924   SVN_ERR_ASSERT(change->kind != svn_node_unknown);
925
926 #ifdef SHIM_WITH_ADD_ABSENT
927   if (change->action == RESTRUCTURE_ADD_ABSENT)
928     {
929       if (change->kind == svn_node_dir)
930         SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
931                                               scratch_pool));
932       else if (change->kind == svn_node_file)
933         SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
934                                          scratch_pool));
935       else
936         SVN_ERR_MALFUNCTION();
937
938       /* No further action possible for this node.  */
939       return SVN_NO_ERROR;
940     }
941 #endif
942   /* RESTRUCTURE_NONE or RESTRUCTURE_ADD  */
943
944   if (change->action == RESTRUCTURE_ADD)
945     {
946       const char *copyfrom_url = NULL;
947       svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
948
949       /* Do we have an old node to delete first? If so, delete it. */
950       if (change->deleting)
951         SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
952                                           parent_baton, scratch_pool));
953
954       /* If it's a copy, determine the copy source location. */
955       if (change->copyfrom_path)
956         {
957           /* ### What's this about URL vs. fspath? REPOS_ROOT_URL isn't
958              optional, is it, at least in a commit editor? */
959           if (eb->repos_root_url)
960             copyfrom_url = svn_path_url_add_component2(eb->repos_root_url,
961                                                        change->copyfrom_path,
962                                                        scratch_pool);
963           else
964             {
965               copyfrom_url = change->copyfrom_path;
966
967               /* Make this an FS path by prepending "/" */
968               if (copyfrom_url[0] != '/')
969                 copyfrom_url = apr_pstrcat(scratch_pool, "/",
970                                            copyfrom_url, SVN_VA_NULL);
971             }
972
973           copyfrom_rev = change->copyfrom_rev;
974         }
975
976       if (change->kind == svn_node_dir)
977         SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
978                                            copyfrom_url, copyfrom_rev,
979                                            result_pool, dir_baton));
980       else if (change->kind == svn_node_file)
981         SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
982                                       copyfrom_url, copyfrom_rev,
983                                       result_pool, &file_baton));
984       else
985         SVN_ERR_MALFUNCTION();
986     }
987   else /* RESTRUCTURE_NONE */
988     {
989       /* ### The code that inserts a "plain edit" change record sets
990          'changing_rev' to the peg rev of the pegged part of the path,
991          even when the full path refers to a child of a copy. Should we
992          instead be using the copy source rev here, in that case? (Like
993          when we fetch the base properties.) */
994
995       if (change->kind == svn_node_dir)
996         SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
997                                             change->changing_rev,
998                                             result_pool, dir_baton));
999       else if (change->kind == svn_node_file)
1000         SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
1001                                        change->changing_rev,
1002                                        result_pool, &file_baton));
1003       else
1004         SVN_ERR_MALFUNCTION();
1005     }
1006
1007   /* Apply any properties in CHANGE to the node.  */
1008   if (change->kind == svn_node_dir)
1009     SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
1010                             eb->deditor, *dir_baton, scratch_pool));
1011   else
1012     SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
1013                             eb->deditor, file_baton, scratch_pool));
1014
1015   /* Send the text content delta, if new text content is provided. */
1016   if (change->contents_text)
1017     {
1018       svn_stream_t *read_stream;
1019       svn_txdelta_window_handler_t handler;
1020       void *handler_baton;
1021
1022       read_stream = svn_stream_from_stringbuf(change->contents_text,
1023                                               scratch_pool);
1024       /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
1025          ### shim code...  */
1026       SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
1027                                            &handler, &handler_baton));
1028       /* ### it would be nice to send a true txdelta here, but whatever.  */
1029       SVN_ERR(svn_txdelta_send_stream(read_stream, handler, handler_baton,
1030                                       NULL, scratch_pool));
1031       SVN_ERR(svn_stream_close(read_stream));
1032     }
1033
1034   if (file_baton)
1035     {
1036       SVN_ERR(eb->deditor->close_file(file_baton, NULL, scratch_pool));
1037     }
1038
1039   return SVN_NO_ERROR;
1040 }
1041
1042 /*
1043  * ========================================================================
1044  * Old-repository storage paths for branch elements
1045  * ========================================================================
1046  *
1047  * To support top-level branches, we map each top-level branch to its own
1048  * directory in the old repository, with each nested branch in a subdirectory:
1049  *
1050  *   B0  =>  ^/top0/...
1051  *           ^/top0/.../trunk/...  <= B0.12
1052  *   B1  =>  ^/top1/...
1053  *
1054  * It may be better to put each branch in its own directory:
1055  *
1056  *   B0     =>  ^/B0/...
1057  *   B0.12  =>  ^/B0.12/...
1058  *   B1     =>  ^/B1/...
1059  *
1060  * (A branch root is not necessarily a directory, it could be a file.)
1061  */
1062
1063 /* Get the old-repository path for the storage of the root element of BRANCH.
1064  *
1065  * Currently, this is the same as the nested-branching hierarchical path
1066  * (and thus assumes there is only one top-level branch).
1067  */
1068 static const char *
1069 branch_get_storage_root_rrpath(const svn_branch__state_t *branch,
1070                                apr_pool_t *result_pool)
1071 {
1072   int top_branch_num = atoi(branch->bid + 1);
1073   const char *top_path = apr_psprintf(result_pool, "top%d", top_branch_num);
1074   const char *nested_path = svn_branch__get_root_rrpath(branch, result_pool);
1075
1076   return svn_relpath_join(top_path, nested_path, result_pool);
1077 }
1078
1079 /* Get the old-repository path for the storage of element EID of BRANCH.
1080  *
1081  * If the element EID doesn't exist in BRANCH, return NULL.
1082  */
1083 static const char *
1084 branch_get_storage_rrpath_by_eid(const svn_branch__state_t *branch,
1085                                  int eid,
1086                                  apr_pool_t *result_pool)
1087 {
1088   const char *path = svn_branch__get_path_by_eid(branch, eid, result_pool);
1089   const char *rrpath = NULL;
1090
1091   if (path)
1092     {
1093       rrpath = svn_relpath_join(branch_get_storage_root_rrpath(branch,
1094                                                                result_pool),
1095                                 path, result_pool);
1096     }
1097   return rrpath;
1098 }
1099
1100 /* Return, in *STORAGE_PATHREV_P, the storage-rrpath-rev for BRANCH_REF.
1101  */
1102 static svn_error_t *
1103 storage_pathrev_from_branch_ref(svn_pathrev_t *storage_pathrev_p,
1104                                 svn_element__branch_ref_t branch_ref,
1105                                 svn_branch__repos_t *repos,
1106                                 apr_pool_t *result_pool,
1107                                 apr_pool_t *scratch_pool)
1108 {
1109   svn_branch__el_rev_id_t *el_rev;
1110
1111   SVN_ERR(svn_branch__repos_find_el_rev_by_id(&el_rev,
1112                                               repos,
1113                                               branch_ref.rev,
1114                                               branch_ref.branch_id,
1115                                               branch_ref.eid,
1116                                               scratch_pool, scratch_pool));
1117
1118   storage_pathrev_p->rev = el_rev->rev;
1119   storage_pathrev_p->relpath
1120     = branch_get_storage_rrpath_by_eid(el_rev->branch, el_rev->eid,
1121                                        result_pool);
1122
1123   return SVN_NO_ERROR;
1124 }
1125
1126 /*
1127  * ========================================================================
1128  * Editor for Commit (independent per-element changes; element-id addressing)
1129  * ========================================================================
1130  */
1131
1132 /*  */
1133 #define PAYLOAD_IS_ONLY_BY_REFERENCE(payload) \
1134     ((payload)->kind == svn_node_unknown)
1135
1136 /* Fetch a payload as *PAYLOAD_P from its storage-pathrev PATH_REV.
1137  * Fetch names of immediate children of PATH_REV as *CHILDREN_NAMES.
1138  * Either of the outputs may be null if not wanted.
1139  */
1140 static svn_error_t *
1141 payload_fetch(svn_element__payload_t **payload_p,
1142               apr_hash_t **children_names,
1143               svn_branch__txn_priv_t *eb,
1144               const svn_pathrev_t *path_rev,
1145               apr_pool_t *result_pool,
1146               apr_pool_t *scratch_pool)
1147 {
1148   svn_element__payload_t *payload
1149     = apr_pcalloc(result_pool, sizeof (*payload));
1150
1151   SVN_ERR(eb->fetch_func(&payload->kind,
1152                          &payload->props,
1153                          &payload->text,
1154                          children_names,
1155                          eb->fetch_baton,
1156                          path_rev->relpath, path_rev->rev,
1157                          result_pool, scratch_pool));
1158
1159   SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1160   SVN_ERR_ASSERT(payload->kind == svn_node_dir
1161                  || payload->kind == svn_node_file);
1162   if (payload_p)
1163     *payload_p = payload;
1164   return SVN_NO_ERROR;
1165 }
1166
1167 /* Return the storage-pathrev of PAYLOAD as *STORAGE_PATHREV_P.
1168  *
1169  * Find the storage-pathrev from PAYLOAD->branch_ref.
1170  */
1171 static svn_error_t *
1172 payload_get_storage_pathrev(svn_pathrev_t *storage_pathrev_p,
1173                             svn_element__payload_t *payload,
1174                             svn_branch__repos_t *repos,
1175                             apr_pool_t *result_pool)
1176 {
1177   SVN_ERR_ASSERT(payload->branch_ref.branch_id /* && ... */);
1178
1179   SVN_ERR(storage_pathrev_from_branch_ref(storage_pathrev_p,
1180                                           payload->branch_ref, repos,
1181                                           result_pool, result_pool));
1182   return SVN_NO_ERROR;
1183 }
1184
1185 svn_error_t *
1186 svn_branch__compat_fetch(svn_element__payload_t **payload_p,
1187                          svn_branch__txn_t *txn,
1188                          svn_element__branch_ref_t branch_ref,
1189                          svn_branch__compat_fetch_func_t fetch_func,
1190                          void *fetch_baton,
1191                          apr_pool_t *result_pool,
1192                          apr_pool_t *scratch_pool)
1193 {
1194   svn_branch__txn_priv_t eb;
1195   svn_pathrev_t storage_pathrev;
1196
1197   /* Simulate the existence of /top0 in r0. */
1198   if (branch_ref.rev == 0 && branch_ref.eid == 0)
1199     {
1200       *payload_p = svn_element__payload_create_dir(apr_hash_make(result_pool),
1201                                                    result_pool);
1202       return SVN_NO_ERROR;
1203     }
1204
1205   eb.txn = txn;
1206   eb.fetch_func = fetch_func;
1207   eb.fetch_baton = fetch_baton;
1208
1209   SVN_ERR(storage_pathrev_from_branch_ref(&storage_pathrev,
1210                                           branch_ref, txn->repos,
1211                                           scratch_pool, scratch_pool));
1212
1213   SVN_ERR(payload_fetch(payload_p, NULL,
1214                         &eb, &storage_pathrev, result_pool, scratch_pool));
1215   (*payload_p)->branch_ref = branch_ref;
1216   return SVN_NO_ERROR;
1217 }
1218
1219 /* Fill in the actual payload, from its reference, if not already done.
1220  */
1221 static svn_error_t *
1222 payload_resolve(svn_element__payload_t *payload,
1223                 svn_branch__txn_priv_t *eb,
1224                 apr_pool_t *scratch_pool)
1225 {
1226   svn_pathrev_t storage;
1227
1228   SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1229
1230   if (! PAYLOAD_IS_ONLY_BY_REFERENCE(payload))
1231     return SVN_NO_ERROR;
1232
1233   SVN_ERR(payload_get_storage_pathrev(&storage, payload,
1234                                       eb->txn->repos,
1235                                       scratch_pool));
1236
1237   SVN_ERR(eb->fetch_func(&payload->kind,
1238                          &payload->props,
1239                          &payload->text,
1240                          NULL,
1241                          eb->fetch_baton,
1242                          storage.relpath, storage.rev,
1243                          payload->pool, scratch_pool));
1244
1245   SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1246   SVN_ERR_ASSERT(! PAYLOAD_IS_ONLY_BY_REFERENCE(payload));
1247   return SVN_NO_ERROR;
1248 }
1249
1250 /* Update *PATHS, a hash of (storage_rrpath -> svn_branch__el_rev_id_t),
1251  * creating or filling in entries for all elements in BRANCH.
1252  */
1253 static svn_error_t *
1254 convert_branch_to_paths(apr_hash_t *paths,
1255                         svn_branch__state_t *branch,
1256                         apr_pool_t *result_pool,
1257                         apr_pool_t *scratch_pool)
1258 {
1259   apr_hash_index_t *hi;
1260   svn_element__tree_t *elements;
1261
1262   /* assert(branch is at a sequence point); */
1263
1264   SVN_ERR(svn_branch__state_get_elements(branch, &elements, scratch_pool));
1265   for (hi = apr_hash_first(scratch_pool, elements->e_map);
1266        hi; hi = apr_hash_next(hi))
1267     {
1268       int eid = svn_eid__hash_this_key(hi);
1269       svn_element__content_t *element = apr_hash_this_val(hi);
1270       const char *rrpath
1271         = branch_get_storage_rrpath_by_eid(branch, eid, result_pool);
1272       svn_branch__el_rev_id_t *ba;
1273
1274       /* A subbranch-root element carries no payload; the corresponding
1275          inner branch root element will provide the payload for this path. */
1276       if (element->payload->is_subbranch_root)
1277         continue;
1278
1279       /* No other element should exist at this path, given that we avoid
1280          storing anything for a subbranch-root element. */
1281       SVN_ERR_ASSERT(! svn_hash_gets(paths, rrpath));
1282
1283       /* Fill in the details. */
1284       ba = svn_branch__el_rev_id_create(branch, eid, branch->txn->rev,
1285                                         result_pool);
1286       svn_hash_sets(paths, rrpath, ba);
1287       /*SVN_DBG(("branch-to-path[%d]: b%s e%d -> %s",
1288                i, svn_branch__get_id(branch, scratch_pool), eid, rrpath));*/
1289     }
1290   return SVN_NO_ERROR;
1291 }
1292
1293 /* Produce a mapping from paths to element ids, covering all elements in
1294  * BRANCH and all its sub-branches, recursively.
1295  *
1296  * Update *PATHS_UNION, a hash of (storage_rrpath -> svn_branch__el_rev_id_t),
1297  * creating or filling in entries for all elements in all branches at and
1298  * under BRANCH, recursively.
1299  */
1300 static svn_error_t *
1301 convert_branch_to_paths_r(apr_hash_t *paths_union,
1302                           svn_branch__state_t *branch,
1303                           apr_pool_t *result_pool,
1304                           apr_pool_t *scratch_pool)
1305 {
1306   apr_array_header_t *subbranches;
1307   int i;
1308
1309   /*SVN_DBG(("[%d] branch={b%s e%d at '%s'}", idx,
1310            svn_branch__get_id(branch, scratch_pool), branch->root_eid,
1311            svn_branch__get_root_rrpath(branch, scratch_pool)));*/
1312   SVN_ERR(convert_branch_to_paths(paths_union, branch,
1313                                   result_pool, scratch_pool));
1314
1315   SVN_ERR(svn_branch__get_immediate_subbranches(branch, &subbranches,
1316                                                scratch_pool, scratch_pool));
1317   /* Rercurse into sub-branches */
1318   for (i = 0; i < subbranches->nelts; i++)
1319     {
1320       svn_branch__state_t *b = APR_ARRAY_IDX(subbranches, i, void *);
1321
1322       SVN_ERR(convert_branch_to_paths_r(paths_union, b, result_pool,
1323                                         scratch_pool));
1324     }
1325   return SVN_NO_ERROR;
1326 }
1327
1328 /* Return TRUE iff INITIAL_PAYLOAD and FINAL_PAYLOAD are both non-null
1329  * and have the same properties.
1330  */
1331 static svn_boolean_t
1332 props_equal(svn_element__payload_t *initial_payload,
1333             svn_element__payload_t *final_payload,
1334             apr_pool_t *scratch_pool)
1335 {
1336   apr_array_header_t *prop_diffs;
1337
1338   if (!initial_payload || !final_payload)
1339     return FALSE;
1340
1341   svn_error_clear(svn_prop_diffs(&prop_diffs,
1342                                  initial_payload->props,
1343                                  final_payload->props,
1344                                  scratch_pool));
1345   return (prop_diffs->nelts == 0);
1346 }
1347
1348 /* Return TRUE iff INITIAL_PAYLOAD and FINAL_PAYLOAD are both file payload
1349  * and have the same text.
1350  */
1351 static svn_boolean_t
1352 text_equal(svn_element__payload_t *initial_payload,
1353            svn_element__payload_t *final_payload)
1354 {
1355   if (!initial_payload || !final_payload
1356       || initial_payload->kind != svn_node_file
1357       || final_payload->kind != svn_node_file)
1358     {
1359       return FALSE;
1360     }
1361
1362   return svn_stringbuf_compare(initial_payload->text,
1363                                final_payload->text);
1364 }
1365
1366 /* Return the copy-from location to be used if this is to be a copy;
1367  * otherwise return NULL.
1368  *
1369  * ### Currently this is indicated by payload-by-reference, which is
1370  *     an inadequate indication.
1371  */
1372 static svn_error_t *
1373 get_copy_from(svn_pathrev_t *copyfrom_pathrev_p,
1374               svn_element__payload_t *final_payload,
1375               svn_branch__txn_priv_t *eb,
1376               apr_pool_t *result_pool)
1377 {
1378   if (final_payload->branch_ref.branch_id)
1379     {
1380       SVN_ERR(payload_get_storage_pathrev(copyfrom_pathrev_p, final_payload,
1381                                           eb->txn->repos,
1382                                           result_pool));
1383     }
1384   else
1385     {
1386       copyfrom_pathrev_p->relpath = NULL;
1387       copyfrom_pathrev_p->rev = SVN_INVALID_REVNUM;
1388     }
1389
1390   return SVN_NO_ERROR;
1391 }
1392
1393 /* Return a hash whose keys are the names of the immediate children of
1394  * RRPATH in PATHS.
1395  */
1396 static apr_hash_t *
1397 get_immediate_children_names(apr_hash_t *paths,
1398                              const char *parent_rrpath,
1399                              apr_pool_t *result_pool,
1400                              apr_pool_t *scratch_pool)
1401 {
1402   apr_hash_t *children = apr_hash_make(result_pool);
1403   apr_hash_index_t *hi;
1404
1405   for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
1406     {
1407       const char *this_rrpath = apr_hash_this_key(hi);
1408
1409       if (this_rrpath[0]
1410           && strcmp(parent_rrpath, svn_relpath_dirname(this_rrpath,
1411                                                        scratch_pool)) == 0)
1412         {
1413           svn_hash_sets(children,
1414                         svn_relpath_basename(this_rrpath, result_pool), "");
1415         }
1416     }
1417
1418   return children;
1419 }
1420
1421 /* Generate Ev1 instructions to edit from a current state to a final state
1422  * at RRPATH, recursing for child paths of RRPATH.
1423  *
1424  * The current state at RRPATH might not be the initial state because,
1425  * although neither RRPATH nor any sub-paths have been explicitly visited
1426  * before, the current state at RRPATH and its sub-paths might be the
1427  * result of a copy.
1428  *
1429  * PRED_LOC is the predecessor location of the node currently at RRPATH in
1430  * the Ev1 transaction, or NULL if there is no node currently at RRPATH.
1431  * If the node is copied, including a child of a copy, this is its copy-from
1432  * location, otherwise this is its location in the txn base revision.
1433  * (The current node cannot be a plain added node on entry to this function,
1434  * as the function must be called only once for each path and there is no
1435  * recursive add operation.) PRED_LOC identifies the node content that the
1436  * that the Ev1 edit needs to delete, replace, update or leave unchanged.
1437  *
1438  * Process a single hierarchy of nested branches, rooted in the top-level
1439  * branch TOP_BRANCH_NUM.
1440  */
1441 static svn_error_t *
1442 drive_changes_r(const char *rrpath,
1443                 svn_pathrev_t *pred_loc,
1444                 apr_hash_t *paths_final,
1445                 const char *top_branch_id,
1446                 svn_branch__txn_priv_t *eb,
1447                 apr_pool_t *scratch_pool)
1448 {
1449   /* The el-rev-id of the element that will finally exist at RRPATH. */
1450   svn_branch__el_rev_id_t *final_el_rev = svn_hash_gets(paths_final, rrpath);
1451   svn_element__payload_t *final_payload;
1452   svn_pathrev_t final_copy_from;
1453   svn_boolean_t succession;
1454
1455   /*SVN_DBG(("rrpath '%s' current=%s, final=e%d)",
1456            rrpath,
1457            pred_loc ? pathrev_str(*pred_loc, scratch_pool) : "<nil>",
1458            final_el_rev ? final_el_rev->eid : -1));*/
1459
1460   SVN_ERR_ASSERT(!pred_loc
1461                  || (pred_loc->relpath && SVN_IS_VALID_REVNUM(pred_loc->rev)));
1462
1463   if (final_el_rev)
1464     {
1465       svn_element__content_t *final_element;
1466
1467       SVN_ERR(svn_branch__state_get_element(final_el_rev->branch, &final_element,
1468                                             final_el_rev->eid, scratch_pool));
1469       /* A non-null FINAL address means an element exists there. */
1470       SVN_ERR_ASSERT(final_element);
1471
1472       final_payload = final_element->payload;
1473
1474       /* Decide whether the state at this path should be a copy (incl. a
1475          copy-child) */
1476       SVN_ERR(get_copy_from(&final_copy_from, final_payload, eb, scratch_pool));
1477       /* It doesn't make sense to have a non-copy inside a copy */
1478       /*SVN_ERR_ASSERT(!(parent_is_copy && !final_copy_from));*/
1479    }
1480   else
1481     {
1482       final_payload = NULL;
1483       final_copy_from.relpath = NULL;
1484     }
1485
1486   /* Succession means:
1487        for a copy (inc. child)  -- copy-from same place as natural predecessor
1488        otherwise                -- it's succession if it's the same element
1489                                    (which also implies the same kind) */
1490   if (pred_loc && final_copy_from.relpath)
1491     {
1492       succession = pathrev_equal(pred_loc, &final_copy_from);
1493     }
1494   else if (pred_loc && final_el_rev)
1495     {
1496       svn_branch__el_rev_id_t *pred_el_rev;
1497
1498       SVN_ERR(svn_branch__repos_find_el_rev_by_path_rev(&pred_el_rev,
1499                                             eb->txn->repos,
1500                                             pred_loc->rev,
1501                                             top_branch_id,
1502                                             pred_loc->relpath,
1503                                             scratch_pool, scratch_pool));
1504
1505       succession = (pred_el_rev->eid == final_el_rev->eid);
1506     }
1507   else
1508     {
1509       succession = FALSE;
1510     }
1511
1512   /* If there's an initial node that isn't also going to be the final
1513      node at this path, then it's being deleted or replaced: delete it. */
1514   if (pred_loc && !succession)
1515     {
1516       /* Issue an Ev1 delete unless this path is inside a path at which
1517          we've already issued a delete. */
1518       if (check_existence(eb->changes, rrpath) != svn_tristate_false)
1519         {
1520           /*SVN_DBG(("ev1:del(%s)", rrpath));*/
1521           /* ### We don't need "delete_subtree", we only need to insert a
1522              single delete operation, as we know we haven't
1523              inserted any changes inside this subtree. */
1524           SVN_ERR(delete_subtree(eb->changes, rrpath, pred_loc->rev));
1525         }
1526       else
1527         {
1528           /*SVN_DBG(("ev1:del(%s): parent is already deleted", rrpath))*/
1529         }
1530     }
1531
1532   /* If there's a final node, it's being added or modified.
1533      Or it's unchanged -- we do nothing in that case. */
1534   if (final_el_rev)
1535     {
1536       svn_element__payload_t *current_payload = NULL;
1537       apr_hash_t *current_children = NULL;
1538       change_node_t *change = NULL;
1539
1540       /* Get the full payload of the final node. If we have
1541          only a reference to the payload, fetch it in full. */
1542       SVN_ERR_ASSERT(final_payload);
1543       SVN_ERR(payload_resolve(final_payload, eb, scratch_pool));
1544
1545       /* If the final node was also the initial node, it's being
1546          modified, otherwise it's being added (perhaps a replacement). */
1547       if (succession)
1548         {
1549           /* Get full payload of the current node */
1550           SVN_ERR(payload_fetch(&current_payload, &current_children,
1551                                 eb, pred_loc,
1552                                 scratch_pool, scratch_pool));
1553
1554           /* If no changes to make, then skip this path */
1555           if (svn_element__payload_equal(current_payload,
1556                                          final_payload, scratch_pool))
1557             {
1558               /*SVN_DBG(("ev1:no-op(%s)", rrpath));*/
1559             }
1560           else
1561             {
1562               /*SVN_DBG(("ev1:mod(%s)", rrpath));*/
1563               SVN_ERR(insert_change(&change, eb->changes, rrpath,
1564                                     RESTRUCTURE_NONE));
1565               change->changing_rev = pred_loc->rev;
1566             }
1567         }
1568       else /* add or copy/move */
1569         {
1570           /*SVN_DBG(("ev1:add(%s)", rrpath));*/
1571           SVN_ERR(insert_change(&change, eb->changes, rrpath,
1572                                 RESTRUCTURE_ADD));
1573
1574           /* If the node is to be copied (and possibly modified) ... */
1575           if (final_copy_from.relpath)
1576             {
1577               change->copyfrom_rev = final_copy_from.rev;
1578               change->copyfrom_path = final_copy_from.relpath;
1579
1580               /* Get full payload of the copy source node */
1581               SVN_ERR(payload_fetch(&current_payload, &current_children,
1582                                     eb, &final_copy_from,
1583                                     scratch_pool, scratch_pool));
1584             }
1585         }
1586
1587       if (change)
1588         {
1589           /* Copy the required content into the change record. Avoid no-op
1590              changes of props / text, not least to minimize clutter when
1591              debugging Ev1 operations. */
1592           SVN_ERR_ASSERT(final_payload->kind == svn_node_dir
1593                          || final_payload->kind == svn_node_file);
1594           change->kind = final_payload->kind;
1595           if (!props_equal(current_payload, final_payload, scratch_pool))
1596             {
1597               change->props = final_payload->props;
1598             }
1599           if (final_payload->kind == svn_node_file
1600               && !text_equal(current_payload, final_payload))
1601             {
1602               change->contents_text = final_payload->text;
1603             }
1604         }
1605
1606       /* Recurse to process this directory's children */
1607       if (final_payload->kind == svn_node_dir)
1608         {
1609           apr_hash_t *final_children;
1610           apr_hash_t *union_children;
1611           apr_hash_index_t *hi;
1612
1613           final_children = get_immediate_children_names(paths_final, rrpath,
1614                                                         scratch_pool,
1615                                                         scratch_pool);
1616           union_children = (current_children
1617                             ? hash_overlay(current_children, final_children)
1618                             : final_children);
1619           for (hi = apr_hash_first(scratch_pool, union_children);
1620                hi; hi = apr_hash_next(hi))
1621             {
1622               const char *name = apr_hash_this_key(hi);
1623               const char *this_rrpath = svn_relpath_join(rrpath, name,
1624                                                          scratch_pool);
1625               svn_boolean_t child_in_current
1626                 = current_children && svn_hash_gets(current_children, name);
1627               svn_pathrev_t *child_pred = NULL;
1628
1629               if (child_in_current)
1630                 {
1631                  /* If the parent dir is copied, then this child has been
1632                     copied along with it: predecessor is parent's copy-from
1633                     location extended by the child's name. */
1634                   child_pred = apr_palloc(scratch_pool, sizeof(*child_pred));
1635                   if (final_copy_from.relpath)
1636                     {
1637                       child_pred->rev = final_copy_from.rev;
1638                       child_pred->relpath
1639                         = svn_relpath_join(final_copy_from.relpath, name,
1640                                            scratch_pool);
1641                     }
1642                   else
1643                     {
1644                       child_pred->rev = pred_loc->rev;
1645                       child_pred->relpath = this_rrpath;
1646                     }
1647                }
1648
1649               SVN_ERR(drive_changes_r(this_rrpath,
1650                                       child_pred,
1651                                       paths_final, top_branch_id,
1652                                       eb, scratch_pool));
1653             }
1654         }
1655     }
1656
1657   return SVN_NO_ERROR;
1658 }
1659
1660 /*
1661  * Drive svn_delta_editor_t (actions: add/copy/delete/modify) from
1662  * a before-and-after element mapping.
1663  */
1664 static svn_error_t *
1665 drive_changes(svn_branch__txn_priv_t *eb,
1666               apr_pool_t *scratch_pool)
1667 {
1668   apr_array_header_t *branches;
1669   int i;
1670   const apr_array_header_t *paths;
1671
1672   /* Convert the element mappings to an svn_delta_editor_t traversal.
1673
1674         1. find union of paths in initial and final states, across all branches.
1675         2. traverse paths in depth-first order.
1676         3. modify/delete/add/replace as needed at each path.
1677    */
1678
1679   /* Process one hierarchy of nested branches at a time. */
1680   branches = svn_branch__txn_get_branches(eb->txn, scratch_pool);
1681   for (i = 0; i < branches->nelts; i++)
1682     {
1683       svn_branch__state_t *root_branch = APR_ARRAY_IDX(branches, i, void *);
1684       apr_hash_t *paths_final;
1685
1686       const char *top_path = branch_get_storage_root_rrpath(root_branch,
1687                                                             scratch_pool);
1688       svn_pathrev_t current;
1689       svn_branch__state_t *base_root_branch;
1690       svn_boolean_t branch_is_new;
1691
1692       if (strchr(root_branch->bid, '.'))
1693         continue;  /* that's not a root branch */
1694
1695       SVN_ERR(svn_branch__repos_get_branch_by_id(&base_root_branch,
1696                                                  eb->txn->repos,
1697                                                  eb->txn->base_rev,
1698                                                  root_branch->bid, scratch_pool));
1699       branch_is_new = !base_root_branch;
1700
1701       paths_final = apr_hash_make(scratch_pool);
1702       SVN_ERR(convert_branch_to_paths_r(paths_final,
1703                                         root_branch,
1704                                         scratch_pool, scratch_pool));
1705
1706       current.rev = eb->txn->base_rev;
1707       current.relpath = top_path;
1708
1709       /* Create the top-level storage node if the branch is new, or if this is
1710          the first commit to branch B0 which was created in r0 but had no
1711          storage node there. */
1712       if (branch_is_new || current.rev == 0)
1713         {
1714           change_node_t *change;
1715
1716           SVN_ERR(insert_change(&change, eb->changes, top_path, RESTRUCTURE_ADD));
1717           change->kind = svn_node_dir;
1718         }
1719
1720       SVN_ERR(drive_changes_r(top_path, &current,
1721                               paths_final, svn_branch__get_id(root_branch,
1722                                                               scratch_pool),
1723                               eb, scratch_pool));
1724     }
1725
1726   /* If the driver has not explicitly opened the root directory via the
1727      start_edit (aka open_root) callback, do so now. */
1728   if (eb->ev1_root_dir_baton == NULL)
1729     SVN_ERR(open_root_ev3(eb, SVN_INVALID_REVNUM));
1730
1731   /* Make the path driver visit the root dir of the edit. Otherwise, it
1732      will attempt an open_root() instead, which we already did. */
1733   if (! svn_hash_gets(eb->changes, ""))
1734     {
1735       change_node_t *change;
1736
1737       SVN_ERR(insert_change(&change, eb->changes, "", RESTRUCTURE_NONE));
1738       change->kind = svn_node_dir;
1739     }
1740
1741   /* Apply the appropriate Ev1 change to each Ev1-relative path. */
1742   paths = get_unsorted_paths(eb->changes, scratch_pool);
1743   SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton,
1744                                  paths, TRUE /*sort*/,
1745                                  apply_change, (void *)eb,
1746                                  scratch_pool));
1747
1748   return SVN_NO_ERROR;
1749 }
1750
1751 /* An #svn_branch__txn_t method. */
1752 static apr_array_header_t *
1753 compat_branch_txn_get_branches(const svn_branch__txn_t *txn,
1754                                apr_pool_t *result_pool)
1755 {
1756   /* Just forwarding: nothing more is needed. */
1757   apr_array_header_t *branches
1758     = svn_branch__txn_get_branches(txn->priv->txn,
1759                                    result_pool);
1760
1761   return branches;
1762 }
1763
1764 /* An #svn_branch__txn_t method. */
1765 static svn_error_t *
1766 compat_branch_txn_delete_branch(svn_branch__txn_t *txn,
1767                                 const char *bid,
1768                                 apr_pool_t *scratch_pool)
1769 {
1770   /* Just forwarding: nothing more is needed. */
1771   SVN_ERR(svn_branch__txn_delete_branch(txn->priv->txn,
1772                                         bid,
1773                                         scratch_pool));
1774   return SVN_NO_ERROR;
1775 }
1776
1777 /* An #svn_branch__txn_t method. */
1778 static svn_error_t *
1779 compat_branch_txn_get_num_new_eids(const svn_branch__txn_t *txn,
1780                                    int *num_new_eids_p,
1781                                    apr_pool_t *scratch_pool)
1782 {
1783   /* Just forwarding: nothing more is needed. */
1784   SVN_ERR(svn_branch__txn_get_num_new_eids(txn->priv->txn,
1785                                            num_new_eids_p,
1786                                            scratch_pool));
1787   return SVN_NO_ERROR;
1788 }
1789
1790 /* An #svn_branch__txn_t method. */
1791 static svn_error_t *
1792 compat_branch_txn_new_eid(svn_branch__txn_t *txn,
1793                           svn_branch__eid_t *eid_p,
1794                           apr_pool_t *scratch_pool)
1795 {
1796   /* Just forwarding: nothing more is needed. */
1797   SVN_ERR(svn_branch__txn_new_eid(txn->priv->txn,
1798                                   eid_p,
1799                                   scratch_pool));
1800   return SVN_NO_ERROR;
1801 }
1802
1803 /* An #svn_branch__txn_t method. */
1804 static svn_error_t *
1805 compat_branch_txn_finalize_eids(svn_branch__txn_t *txn,
1806                                 apr_pool_t *scratch_pool)
1807 {
1808   /* Just forwarding: nothing more is needed. */
1809   SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->txn,
1810                                         scratch_pool));
1811   return SVN_NO_ERROR;
1812 }
1813
1814 /* An #svn_branch__txn_t method. */
1815 static svn_error_t *
1816 compat_branch_txn_open_branch(svn_branch__txn_t *txn,
1817                               svn_branch__state_t **new_branch_p,
1818                               const char *new_branch_id,
1819                               int root_eid,
1820                               svn_branch__rev_bid_eid_t *tree_ref,
1821                               apr_pool_t *result_pool,
1822                               apr_pool_t *scratch_pool)
1823 {
1824   /* Just forwarding: nothing more is needed. */
1825   SVN_ERR(svn_branch__txn_open_branch(txn->priv->txn,
1826                                       new_branch_p,
1827                                       new_branch_id, root_eid, tree_ref,
1828                                       result_pool,
1829                                       scratch_pool));
1830   return SVN_NO_ERROR;
1831 }
1832
1833 /* An #svn_branch__txn_t method. */
1834 static svn_error_t *
1835 compat_branch_txn_serialize(svn_branch__txn_t *txn,
1836                             svn_stream_t *stream,
1837                             apr_pool_t *scratch_pool)
1838 {
1839   /* Just forwarding: nothing more is needed. */
1840   SVN_ERR(svn_branch__txn_serialize(txn->priv->txn,
1841                                     stream,
1842                                     scratch_pool));
1843   return SVN_NO_ERROR;
1844 }
1845
1846 /* An #svn_branch__txn_t method. */
1847 static svn_error_t *
1848 compat_branch_txn_sequence_point(svn_branch__txn_t *txn,
1849                                  apr_pool_t *scratch_pool)
1850 {
1851   /* Just forwarding: nothing more is needed. */
1852   SVN_ERR(svn_branch__txn_sequence_point(txn->priv->txn,
1853                                          scratch_pool));
1854   return SVN_NO_ERROR;
1855 }
1856
1857 /* An #svn_branch__txn_t method. */
1858 static svn_error_t *
1859 compat_branch_txn_complete(svn_branch__txn_t *txn,
1860                            apr_pool_t *scratch_pool)
1861 {
1862   svn_branch__txn_priv_t *eb = txn->priv;
1863   svn_error_t *err;
1864
1865   /* Convert the transaction to a revision */
1866   SVN_ERR(svn_branch__txn_sequence_point(txn->priv->txn, scratch_pool));
1867   SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->txn, scratch_pool));
1868
1869   err = drive_changes(eb, scratch_pool);
1870
1871   if (!err)
1872      {
1873        err = svn_error_compose_create(err, eb->deditor->close_edit(
1874                                                             eb->dedit_baton,
1875                                                             scratch_pool));
1876      }
1877
1878   if (err)
1879     svn_error_clear(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
1880
1881   SVN_ERR(svn_branch__txn_complete(txn->priv->txn, scratch_pool));
1882
1883   return err;
1884 }
1885
1886 /* An #svn_branch__txn_t method. */
1887 static svn_error_t *
1888 compat_branch_txn_abort(svn_branch__txn_t *txn,
1889                         apr_pool_t *scratch_pool)
1890 {
1891   svn_branch__txn_priv_t *eb = txn->priv;
1892
1893   SVN_ERR(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
1894
1895   SVN_ERR(svn_branch__txn_abort(txn->priv->txn,
1896                                 scratch_pool));
1897   return SVN_NO_ERROR;
1898 }
1899
1900 /* Baton for wrap_fetch_func. */
1901 typedef struct wrap_fetch_baton_t
1902 {
1903   /* Wrapped fetcher */
1904   svn_branch__compat_fetch_func_t fetch_func;
1905   void *fetch_baton;
1906 } wrap_fetch_baton_t;
1907
1908 /* The purpose of this fetcher-wrapper is to make it appear that B0
1909  * was created (as an empty dir) in r0.
1910  */
1911 static svn_error_t *
1912 wrap_fetch_func(svn_node_kind_t *kind,
1913                 apr_hash_t **props,
1914                 svn_stringbuf_t **file_text,
1915                 apr_hash_t **children_names,
1916                 void *baton,
1917                 const char *repos_relpath,
1918                 svn_revnum_t revision,
1919                 apr_pool_t *result_pool,
1920                 apr_pool_t *scratch_pool)
1921 {
1922   wrap_fetch_baton_t *b = baton;
1923
1924   if (revision == 0 && strcmp(repos_relpath, "top0") == 0)
1925     {
1926       if (kind)
1927         *kind = svn_node_dir;
1928       if (props)
1929         *props = apr_hash_make(result_pool);
1930       if (file_text)
1931         *file_text = NULL;
1932       if (children_names)
1933         *children_names = apr_hash_make(result_pool);
1934     }
1935   else
1936     {
1937       SVN_ERR(b->fetch_func(kind, props, file_text, children_names,
1938                             b->fetch_baton,
1939                             repos_relpath, revision,
1940                             result_pool, scratch_pool));
1941     }
1942
1943   return SVN_NO_ERROR;
1944 }
1945
1946 svn_error_t *
1947 svn_branch__compat_txn_from_delta_for_commit(
1948                         svn_branch__txn_t **txn_p,
1949                         svn_branch__compat_shim_connector_t **shim_connector,
1950                         const svn_delta_editor_t *deditor,
1951                         void *dedit_baton,
1952                         svn_branch__txn_t *branching_txn,
1953                         const char *repos_root_url,
1954                         svn_branch__compat_fetch_func_t fetch_func,
1955                         void *fetch_baton,
1956                         svn_cancel_func_t cancel_func,
1957                         void *cancel_baton,
1958                         apr_pool_t *result_pool,
1959                         apr_pool_t *scratch_pool)
1960 {
1961   static const svn_branch__txn_vtable_t vtable = {
1962     {0},
1963     compat_branch_txn_get_branches,
1964     compat_branch_txn_delete_branch,
1965     compat_branch_txn_get_num_new_eids,
1966     compat_branch_txn_new_eid,
1967     compat_branch_txn_open_branch,
1968     compat_branch_txn_finalize_eids,
1969     compat_branch_txn_serialize,
1970     compat_branch_txn_sequence_point,
1971     compat_branch_txn_complete,
1972     compat_branch_txn_abort
1973   };
1974   svn_branch__txn_t *txn;
1975   svn_branch__txn_priv_t *eb = apr_pcalloc(result_pool, sizeof(*eb));
1976   wrap_fetch_baton_t *wb = apr_pcalloc(result_pool, sizeof(*wb));
1977
1978   eb->deditor = deditor;
1979   eb->dedit_baton = dedit_baton;
1980
1981   eb->repos_root_url = apr_pstrdup(result_pool, repos_root_url);
1982
1983   eb->changes = apr_hash_make(result_pool);
1984
1985   wb->fetch_func = fetch_func;
1986   wb->fetch_baton = fetch_baton;
1987   eb->fetch_func = wrap_fetch_func;
1988   eb->fetch_baton = wb;
1989
1990   eb->edit_pool = result_pool;
1991
1992   branching_txn = svn_branch__nested_txn_create(branching_txn, result_pool);
1993
1994   eb->txn = branching_txn;
1995
1996   txn = svn_branch__txn_create(&vtable, NULL, NULL, result_pool);
1997   txn->priv = eb;
1998   txn->repos = branching_txn->repos;
1999   txn->rev = branching_txn->rev;
2000   txn->base_rev = branching_txn->base_rev;
2001   *txn_p = txn;
2002
2003   if (shim_connector)
2004     {
2005 #if 0
2006       *shim_connector = apr_palloc(result_pool, sizeof(**shim_connector));
2007 #ifdef SHIM_WITH_ABS_PATHS
2008       (*shim_connector)->ev1_absolute_paths
2009         = apr_palloc(result_pool, sizeof(svn_boolean_t));
2010       eb->make_abs_paths = (*shim_connector)->ev1_absolute_paths;
2011 #endif
2012       (*shim_connector)->target_revision_func = set_target_revision_ev3;
2013       (*shim_connector)->start_edit_func = open_root_ev3;
2014 #ifdef SHIM_WITH_UNLOCK
2015       (*shim_connector)->unlock_func = do_unlock;
2016 #endif
2017       (*shim_connector)->baton = eb;
2018 #endif
2019     }
2020
2021   return SVN_NO_ERROR;
2022 }
2023
2024 svn_error_t *
2025 svn_branch__compat_txn_from_delta_for_update(
2026                         svn_branch__compat_update_editor3_t **update_editor_p,
2027                         const svn_delta_editor_t *deditor,
2028                         void *dedit_baton,
2029                         svn_branch__txn_t *branching_txn,
2030                         const char *repos_root_url,
2031                         const char *base_repos_relpath,
2032                         svn_branch__compat_fetch_func_t fetch_func,
2033                         void *fetch_baton,
2034                         svn_cancel_func_t cancel_func,
2035                         void *cancel_baton,
2036                         apr_pool_t *result_pool,
2037                         apr_pool_t *scratch_pool)
2038 {
2039   svn_branch__compat_update_editor3_t *update_editor
2040     = apr_pcalloc(result_pool, sizeof(*update_editor));
2041   svn_branch__compat_shim_connector_t *shim_connector;
2042
2043   /*(("svn_delta__ev3_from_delta_for_update(base='%s')...",
2044            base_repos_relpath));*/
2045
2046   /*SVN_ERR(svn_delta__get_debug_editor(&deditor, &dedit_baton,
2047                                       deditor, dedit_baton,
2048                                       "[1>UP] ", result_pool));*/
2049   SVN_ERR(svn_branch__compat_txn_from_delta_for_commit(
2050                         &update_editor->edit_txn,
2051                         &shim_connector,
2052                         deditor, dedit_baton,
2053                         branching_txn, repos_root_url,
2054                         fetch_func, fetch_baton,
2055                         cancel_func, cancel_baton,
2056                         result_pool, scratch_pool));
2057
2058   update_editor->set_target_revision_func = shim_connector->target_revision_func;
2059   update_editor->set_target_revision_baton = shim_connector->baton;
2060   /* shim_connector->start_edit_func = open_root_ev3; */
2061 #ifdef SHIM_WITH_ABS_PATHS
2062   update_editor->ev1_absolute_paths /*...*/;
2063 #endif
2064 #ifdef SHIM_WITH_UNLOCK
2065   update_editor->unlock_func = do_unlock;
2066 #endif
2067
2068   *update_editor_p = update_editor;
2069   return SVN_NO_ERROR;
2070 }