1333347Speter/*
2333347Speter * branch_compat.c : Branching compatibility layer.
3333347Speter *
4333347Speter * ====================================================================
5333347Speter *    Licensed to the Apache Software Foundation (ASF) under one
6333347Speter *    or more contributor license agreements.  See the NOTICE file
7333347Speter *    distributed with this work for additional information
8333347Speter *    regarding copyright ownership.  The ASF licenses this file
9333347Speter *    to you under the Apache License, Version 2.0 (the
10333347Speter *    "License"); you may not use this file except in compliance
11333347Speter *    with the License.  You may obtain a copy of the License at
12333347Speter *
13333347Speter *      http://www.apache.org/licenses/LICENSE-2.0
14333347Speter *
15333347Speter *    Unless required by applicable law or agreed to in writing,
16333347Speter *    software distributed under the License is distributed on an
17333347Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18333347Speter *    KIND, either express or implied.  See the License for the
19333347Speter *    specific language governing permissions and limitations
20333347Speter *    under the License.
21333347Speter * ====================================================================
22333347Speter */
23333347Speter
24333347Speter#include "svn_types.h"
25333347Speter#include "svn_error.h"
26333347Speter#include "svn_delta.h"
27333347Speter#include "svn_dirent_uri.h"
28333347Speter#include "svn_path.h"
29333347Speter#include "svn_hash.h"
30333347Speter#include "svn_iter.h"
31333347Speter#include "svn_props.h"
32333347Speter#include "svn_pools.h"
33333347Speter
34333347Speter#include "private/svn_branch_impl.h"
35333347Speter#include "private/svn_branch_repos.h"
36333347Speter#include "private/svn_branch_nested.h"
37333347Speter#include "private/svn_delta_private.h"
38333347Speter#include "private/svn_branch_compat.h"
39333347Speter
40333347Speter#include "svn_private_config.h"
41333347Speter
42333347Speter
43333347Speter/* Verify EXPR is true; raise an error if not. */
44333347Speter#define VERIFY(expr) SVN_ERR_ASSERT(expr)
45333347Speter
46333347Speter
47333347Speter/*
48333347Speter * ===================================================================
49333347Speter * Minor data types
50333347Speter * ===================================================================
51333347Speter */
52333347Speter
53333347Speter/** A location in a committed revision.
54333347Speter *
55333347Speter * @a rev shall not be #SVN_INVALID_REVNUM unless the interface using this
56333347Speter * type specifically allows it and defines its meaning. */
57333347Spetertypedef struct svn_pathrev_t
58333347Speter{
59333347Speter  svn_revnum_t rev;
60333347Speter  const char *relpath;
61333347Speter} svn_pathrev_t;
62333347Speter
63333347Speter/* Return true iff PEG_PATH1 and PEG_PATH2 are both the same location.
64333347Speter */
65333347Speterstatic svn_boolean_t
66333347Speterpathrev_equal(const svn_pathrev_t *p1,
67333347Speter              const svn_pathrev_t *p2)
68333347Speter{
69333347Speter  if (p1->rev != p2->rev)
70333347Speter    return FALSE;
71333347Speter  if (strcmp(p1->relpath, p2->relpath) != 0)
72333347Speter    return FALSE;
73333347Speter
74333347Speter  return TRUE;
75333347Speter}
76333347Speter
77333347Speter#if 0
78333347Speter/* Return a human-readable string representation of LOC. */
79333347Speterstatic const char *
80333347Speterpathrev_str(const svn_pathrev_t *loc,
81333347Speter            apr_pool_t *result_pool)
82333347Speter{
83333347Speter  if (! loc)
84333347Speter    return "<nil>";
85333347Speter  return apr_psprintf(result_pool, "%s@%ld",
86333347Speter                      loc->relpath, loc->rev);
87333347Speter}
88333347Speter
89333347Speter/* Return a string representation of the (string) keys of HASH. */
90333347Speterstatic const char *
91333347Speterhash_keys_str(apr_hash_t *hash)
92333347Speter{
93333347Speter  const char *str = NULL;
94333347Speter  apr_pool_t *pool;
95333347Speter  apr_hash_index_t *hi;
96333347Speter
97333347Speter  if (! hash)
98333347Speter    return "<nil>";
99333347Speter
100333347Speter  pool = apr_hash_pool_get(hash);
101333347Speter  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
102333347Speter    {
103333347Speter      const char *key = apr_hash_this_key(hi);
104333347Speter
105333347Speter      if (!str)
106333347Speter        str = key;
107333347Speter      else
108333347Speter        str = apr_psprintf(pool, "%s, %s", str, key);
109333347Speter    }
110333347Speter  return apr_psprintf(pool, "{%s}", str);
111333347Speter}
112333347Speter#endif
113333347Speter
114333347Speter/**
115333347Speter * Merge two hash tables into one new hash table. The values of the overlay
116333347Speter * hash override the values of the base if both have the same key.
117333347Speter *
118333347Speter * Unlike apr_hash_overlay(), this doesn't care whether the input hashes use
119333347Speter * the same hash function, nor about the relationship between the three pools.
120333347Speter *
121333347Speter * @param p The pool to use for the new hash table
122333347Speter * @param overlay The table to add to the initial table
123333347Speter * @param base The table that represents the initial values of the new table
124333347Speter * @return A new hash table containing all of the data from the two passed in
125333347Speter * @remark Makes a shallow copy: keys and values are not copied
126333347Speter */
127333347Speterstatic apr_hash_t *
128333347Speterhash_overlay(apr_hash_t *overlay,
129333347Speter             apr_hash_t *base)
130333347Speter{
131333347Speter  apr_pool_t *pool = apr_hash_pool_get(base);
132333347Speter  apr_hash_t *result = apr_hash_copy(pool, base);
133333347Speter  apr_hash_index_t *hi;
134333347Speter
135333347Speter  for (hi = apr_hash_first(pool, overlay); hi; hi = apr_hash_next(hi))
136333347Speter    {
137333347Speter      svn_hash_sets(result, apr_hash_this_key(hi), apr_hash_this_val(hi));
138333347Speter    }
139333347Speter  return result;
140333347Speter}
141333347Speter
142333347Speter
143333347Speter/*
144333347Speter * ========================================================================
145333347Speter * Configuration Options
146333347Speter * ========================================================================
147333347Speter */
148333347Speter
149333347Speter/* Features that are not wanted for this commit editor shim but may be
150333347Speter * wanted in a similar but different shim such as for an update editor. */
151333347Speter/* #define SHIM_WITH_ADD_ABSENT */
152333347Speter/* #define SHIM_WITH_UNLOCK */
153333347Speter
154333347Speter/* Whether to support switching from relative to absolute paths in the
155333347Speter * Ev1 methods. */
156333347Speter/* #define SHIM_WITH_ABS_PATHS */
157333347Speter
158333347Speter
159333347Speter/*
160333347Speter * ========================================================================
161333347Speter * Shim Connector
162333347Speter * ========================================================================
163333347Speter *
164333347Speter * The shim connector enables a more exact round-trip conversion from an
165333347Speter * Ev1 drive to Ev3 and back to Ev1.
166333347Speter */
167333347Speterstruct svn_branch__compat_shim_connector_t
168333347Speter{
169333347Speter  /* Set to true if and when an Ev1 receiving shim receives an absolute
170333347Speter   * path (prefixed with '/') from the delta edit, and causes the Ev1
171333347Speter   * sending shim to send absolute paths.
172333347Speter   * ### NOT IMPLEMENTED
173333347Speter   */
174333347Speter#ifdef SHIM_WITH_ABS_PATHS
175333347Speter  svn_boolean_t *ev1_absolute_paths;
176333347Speter#endif
177333347Speter
178333347Speter  /* The Ev1 set_target_revision and start_edit methods, respectively, will
179333347Speter   * call the TARGET_REVISION_FUNC and START_EDIT_FUNC callbacks, if non-null.
180333347Speter   * Otherwise, default calls will be used.
181333347Speter   *
182333347Speter   * (Possibly more useful for update editors than for commit editors?) */
183333347Speter  svn_branch__compat_set_target_revision_func_t target_revision_func;
184333347Speter
185333347Speter  /* If not null, a callback that the Ev3 driver may call to
186333347Speter   * provide the "base revision" of the root directory, even if it is not
187333347Speter   * going to modify that directory. (If it does modify it, then it will
188333347Speter   * pass in the appropriate base revision at that time.) If null
189333347Speter   * or if the driver does not call it, then the Ev1
190333347Speter   * open_root() method will be called with SVN_INVALID_REVNUM as the base
191333347Speter   * revision parameter.
192333347Speter   */
193333347Speter  svn_delta__start_edit_func_t start_edit_func;
194333347Speter
195333347Speter#ifdef SHIM_WITH_UNLOCK
196333347Speter  /* A callback which will be called when an unlocking action is received.
197333347Speter     (For update editors?) */
198333347Speter  svn_delta__unlock_func_t unlock_func;
199333347Speter#endif
200333347Speter
201333347Speter  void *baton;
202333347Speter};
203333347Speter
204333347Spetersvn_error_t *
205333347Spetersvn_branch__compat_insert_shims(
206333347Speter                        const svn_delta_editor_t **new_deditor,
207333347Speter                        void **new_dedit_baton,
208333347Speter                        const svn_delta_editor_t *old_deditor,
209333347Speter                        void *old_dedit_baton,
210333347Speter                        const char *repos_root,
211333347Speter                        const char *base_relpath,
212333347Speter                        svn_branch__compat_fetch_func_t fetch_func,
213333347Speter                        void *fetch_baton,
214333347Speter                        apr_pool_t *result_pool,
215333347Speter                        apr_pool_t *scratch_pool)
216333347Speter{
217333347Speter#if 0
218333347Speter  svn_branch__txn_t *edit_txn;
219333347Speter  svn_branch__compat_shim_connector_t *shim_connector;
220333347Speter
221333347Speter#ifdef SVN_DEBUG
222333347Speter  /*SVN_ERR(svn_delta__get_debug_editor(&old_deditor, &old_dedit_baton,
223333347Speter                                      old_deditor, old_dedit_baton,
224333347Speter                                      "[OUT] ", result_pool));*/
225333347Speter#endif
226333347Speter  SVN_ERR(svn_branch__compat_txn_from_delta_for_commit(
227333347Speter                        &edit_txn,
228333347Speter                        &shim_connector,
229333347Speter                        old_deditor, old_dedit_baton,
230333347Speter                        branching_txn,
231333347Speter                        repos_root,
232333347Speter                        fetch_func, fetch_baton,
233333347Speter                        NULL, NULL /*cancel*/,
234333347Speter                        result_pool, scratch_pool));
235333347Speter  SVN_ERR(svn_branch__compat_delta_from_txn_for_commit(
236333347Speter                        new_deditor, new_dedit_baton,
237333347Speter                        edit_txn,
238333347Speter                        repos_root, base_relpath,
239333347Speter                        fetch_func, fetch_baton,
240333347Speter                        shim_connector,
241333347Speter                        result_pool, scratch_pool));
242333347Speter#ifdef SVN_DEBUG
243333347Speter  /*SVN_ERR(svn_delta__get_debug_editor(new_deditor, new_dedit_baton,
244333347Speter                                      *new_deditor, *new_dedit_baton,
245333347Speter                                      "[IN]  ", result_pool));*/
246333347Speter#endif
247333347Speter#else
248333347Speter  *new_deditor = old_deditor;
249333347Speter  *new_dedit_baton = old_dedit_baton;
250333347Speter#endif
251333347Speter  return SVN_NO_ERROR;
252333347Speter}
253333347Speter
254333347Speter
255333347Speter/*
256333347Speter * ========================================================================
257333347Speter * Buffering the Delta Editor Changes
258333347Speter * ========================================================================
259333347Speter */
260333347Speter
261333347Speter/* The kind of Ev1 restructuring operation on a particular path. For each
262333347Speter * visited path we use exactly one restructuring action. */
263333347Speterenum restructure_action_t
264333347Speter{
265333347Speter  RESTRUCTURE_NONE = 0,
266333347Speter  RESTRUCTURE_ADD,         /* add the node, maybe replacing. maybe copy  */
267333347Speter#ifdef SHIM_WITH_ADD_ABSENT
268333347Speter  RESTRUCTURE_ADD_ABSENT,  /* add an absent node, possibly replacing  */
269333347Speter#endif
270333347Speter  RESTRUCTURE_DELETE       /* delete this node  */
271333347Speter};
272333347Speter
273333347Speter/* Records everything about how this node is to be changed, from an Ev1
274333347Speter * point of view.  */
275333347Spetertypedef struct change_node_t
276333347Speter{
277333347Speter  /* what kind of (tree) restructure is occurring at this node?  */
278333347Speter  enum restructure_action_t action;
279333347Speter
280333347Speter  svn_node_kind_t kind;  /* the NEW kind of this node  */
281333347Speter
282333347Speter  /* We may need to specify the revision we are altering or the revision
283333347Speter     to delete or replace. These are mutually exclusive, but are separate
284333347Speter     for clarity. */
285333347Speter  /* CHANGING_REV is the base revision of the change if ACTION is 'none',
286333347Speter     else is SVN_INVALID_REVNUM. (If ACTION is 'add' and COPYFROM_PATH
287333347Speter     is non-null, then COPYFROM_REV serves the equivalent purpose for the
288333347Speter     copied node.) */
289333347Speter  /* ### Can also be SVN_INVALID_REVNUM for a pre-existing file/dir,
290333347Speter         meaning the base is the youngest revision. This is probably not
291333347Speter         a good idea -- it is at least confusing -- and we should instead
292333347Speter         resolve to a real revnum when Ev1 passes in SVN_INVALID_REVNUM
293333347Speter         in such cases. */
294333347Speter  svn_revnum_t changing_rev;
295333347Speter  /* If ACTION is 'delete' or if ACTION is 'add' and it is a replacement,
296333347Speter     DELETING is TRUE and DELETING_REV is the revision to delete. */
297333347Speter  /* ### Can also be SVN_INVALID_REVNUM for a pre-existing file/dir,
298333347Speter         meaning the base is the youngest revision. This is probably not
299333347Speter         a good idea -- it is at least confusing -- and we should instead
300333347Speter         resolve to a real revnum when Ev1 passes in SVN_INVALID_REVNUM
301333347Speter         in such cases. */
302333347Speter  svn_boolean_t deleting;
303333347Speter  svn_revnum_t deleting_rev;
304333347Speter
305333347Speter  /* new/final set of props to apply; null => no *change*, not no props */
306333347Speter  apr_hash_t *props;
307333347Speter
308333347Speter  /* new fulltext; null => no change */
309333347Speter  svn_boolean_t contents_changed;
310333347Speter  svn_stringbuf_t *contents_text;
311333347Speter
312333347Speter  /* If COPYFROM_PATH is not NULL, then copy PATH@REV to this node.
313333347Speter     RESTRUCTURE must be RESTRUCTURE_ADD.  */
314333347Speter  const char *copyfrom_path;
315333347Speter  svn_revnum_t copyfrom_rev;
316333347Speter
317333347Speter#ifdef SHIM_WITH_UNLOCK
318333347Speter  /* Record whether an incoming propchange unlocked this node.  */
319333347Speter  svn_boolean_t unlock;
320333347Speter#endif
321333347Speter} change_node_t;
322333347Speter
323333347Speter#if 0
324333347Speter/* Return a string representation of CHANGE. */
325333347Speterstatic const char *
326333347Speterchange_node_str(change_node_t *change,
327333347Speter                apr_pool_t *result_pool)
328333347Speter{
329333347Speter  const char *copyfrom = "<nil>";
330333347Speter  const char *str;
331333347Speter
332333347Speter  if (change->copyfrom_path)
333333347Speter    copyfrom = apr_psprintf(result_pool, "'%s'@%ld",
334333347Speter                            change->copyfrom_path, change->copyfrom_rev);
335333347Speter  str = apr_psprintf(result_pool,
336333347Speter                     "action=%d, kind=%s, changing_rev=%ld, "
337333347Speter                     "deleting=%d, deleting_rev=%ld, ..., "
338333347Speter                     "copyfrom=%s",
339333347Speter                     change->action,
340333347Speter                     svn_node_kind_to_word(change->kind),
341333347Speter                     change->changing_rev,
342333347Speter                     change->deleting, change->deleting_rev,
343333347Speter                     copyfrom);
344333347Speter  return str;
345333347Speter}
346333347Speter#endif
347333347Speter
348333347Speter/* Check whether RELPATH is known to exist, known to not exist, or unknown. */
349333347Speterstatic svn_tristate_t
350333347Spetercheck_existence(apr_hash_t *changes,
351333347Speter                const char *relpath)
352333347Speter{
353333347Speter  apr_pool_t *changes_pool = apr_hash_pool_get(changes);
354333347Speter  apr_pool_t *scratch_pool = changes_pool;
355333347Speter  change_node_t *change = svn_hash_gets(changes, relpath);
356333347Speter  svn_tristate_t exists = svn_tristate_unknown;
357333347Speter
358333347Speter  if (change && change->action != RESTRUCTURE_DELETE)
359333347Speter    exists = svn_tristate_true;
360333347Speter  else if (change && change->action == RESTRUCTURE_DELETE)
361333347Speter    exists = svn_tristate_false;
362333347Speter  else
363333347Speter    {
364333347Speter      const char *parent_path = relpath;
365333347Speter
366333347Speter      /* Find the nearest parent change. If that's a delete or a simple
367333347Speter         (non-recursive) add, this path cannot exist, else we don't know. */
368333347Speter      while ((parent_path = svn_relpath_dirname(parent_path, scratch_pool)),
369333347Speter             *parent_path)
370333347Speter        {
371333347Speter          change = svn_hash_gets(changes, parent_path);
372333347Speter          if (change)
373333347Speter            {
374333347Speter              if ((change->action == RESTRUCTURE_ADD && !change->copyfrom_path)
375333347Speter                  || change->action == RESTRUCTURE_DELETE)
376333347Speter                exists = svn_tristate_false;
377333347Speter              break;
378333347Speter            }
379333347Speter        }
380333347Speter    }
381333347Speter
382333347Speter  return exists;
383333347Speter}
384333347Speter
385333347Speter/* Insert a new Ev1-style change for RELPATH, or return an existing one.
386333347Speter *
387333347Speter * Verify Ev3 rules. Primary differences from Ev1 rules are ...
388333347Speter *
389333347Speter * If ACTION is 'delete', elide any previous explicit deletes inside
390333347Speter * that subtree. (Other changes inside that subtree are not allowed.) We
391333347Speter * do not store multiple change records per path even with nested moves
392333347Speter * -- the most complex change is delete + copy which all fits in one
393333347Speter * record with action='add'.
394333347Speter */
395333347Speterstatic svn_error_t *
396333347Speterinsert_change(change_node_t **change_p, apr_hash_t *changes,
397333347Speter              const char *relpath,
398333347Speter              enum restructure_action_t action)
399333347Speter{
400333347Speter  apr_pool_t *changes_pool = apr_hash_pool_get(changes);
401333347Speter  change_node_t *change = svn_hash_gets(changes, relpath);
402333347Speter
403333347Speter  /* Check whether this op is allowed. */
404333347Speter  switch (action)
405333347Speter    {
406333347Speter    case RESTRUCTURE_NONE:
407333347Speter      if (change)
408333347Speter        {
409333347Speter          /* A no-restructure change is allowed after add, but not allowed
410333347Speter           * (in Ev3) after another no-restructure change, nor a delete. */
411333347Speter          VERIFY(change->action == RESTRUCTURE_ADD);
412333347Speter        }
413333347Speter      break;
414333347Speter
415333347Speter    case RESTRUCTURE_ADD:
416333347Speter      if (change)
417333347Speter        {
418333347Speter          /* Add or copy is allowed after delete (and replaces the delete),
419333347Speter           * but not allowed after an add or a no-restructure change. */
420333347Speter          VERIFY(change->action == RESTRUCTURE_DELETE);
421333347Speter          change->action = action;
422333347Speter        }
423333347Speter      break;
424333347Speter
425333347Speter#ifdef SHIM_WITH_ADD_ABSENT
426333347Speter    case RESTRUCTURE_ADD_ABSENT:
427333347Speter      /* ### */
428333347Speter      break;
429333347Speter#endif
430333347Speter
431333347Speter    case RESTRUCTURE_DELETE:
432333347Speter      SVN_ERR_MALFUNCTION();
433333347Speter    }
434333347Speter
435333347Speter  if (change)
436333347Speter    {
437333347Speter      if (action != RESTRUCTURE_NONE)
438333347Speter        {
439333347Speter          change->action = action;
440333347Speter        }
441333347Speter    }
442333347Speter  else
443333347Speter    {
444333347Speter      /* Create a new change. Callers will set the other fields as needed. */
445333347Speter      change = apr_pcalloc(changes_pool, sizeof(*change));
446333347Speter      change->action = action;
447333347Speter      change->changing_rev = SVN_INVALID_REVNUM;
448333347Speter
449333347Speter      svn_hash_sets(changes, apr_pstrdup(changes_pool, relpath), change);
450333347Speter    }
451333347Speter
452333347Speter  *change_p = change;
453333347Speter  return SVN_NO_ERROR;
454333347Speter}
455333347Speter
456333347Speter/* Modify CHANGES so as to delete the subtree at RELPATH.
457333347Speter *
458333347Speter * Insert a new Ev1-style change record for RELPATH (or perhaps remove
459333347Speter * the existing record if this would have the same effect), and remove
460333347Speter * any change records for sub-paths under RELPATH.
461333347Speter *
462333347Speter * Follow Ev3 rules, although without knowing whether this delete is
463333347Speter * part of a move. Ev3 (incremental) "rm" operation says each node to
464333347Speter * be removed "MAY be a child of a copy but otherwise SHOULD NOT have
465333347Speter * been created or modified in this edit". "mv" operation says ...
466333347Speter */
467333347Speterstatic svn_error_t *
468333347Speterdelete_subtree(apr_hash_t *changes,
469333347Speter               const char *relpath,
470333347Speter               svn_revnum_t deleting_rev)
471333347Speter{
472333347Speter  apr_pool_t *changes_pool = apr_hash_pool_get(changes);
473333347Speter  apr_pool_t *scratch_pool = changes_pool;
474333347Speter  change_node_t *change = svn_hash_gets(changes, relpath);
475333347Speter
476333347Speter  if (change)
477333347Speter    {
478333347Speter      /* If this previous change was a non-replacing addition, there
479333347Speter         is no longer any change to be made at this path. If it was
480333347Speter         a replacement or a modification, it now becomes a delete.
481333347Speter         (If it was a delete, this attempt to delete is an error.) */
482333347Speter       VERIFY(change->action != RESTRUCTURE_DELETE);
483333347Speter       if (change->action == RESTRUCTURE_ADD && !change->deleting)
484333347Speter         {
485333347Speter           svn_hash_sets(changes, relpath, NULL);
486333347Speter           change = NULL;
487333347Speter         }
488333347Speter       else
489333347Speter         {
490333347Speter           change->action = RESTRUCTURE_DELETE;
491333347Speter         }
492333347Speter    }
493333347Speter  else
494333347Speter    {
495333347Speter      /* There was no change recorded at this path. Record a delete. */
496333347Speter      change = apr_pcalloc(changes_pool, sizeof(*change));
497333347Speter      change->action = RESTRUCTURE_DELETE;
498333347Speter      change->changing_rev = SVN_INVALID_REVNUM;
499333347Speter      change->deleting = TRUE;
500333347Speter      change->deleting_rev = deleting_rev;
501333347Speter
502333347Speter      svn_hash_sets(changes, apr_pstrdup(changes_pool, relpath), change);
503333347Speter    }
504333347Speter
505333347Speter  /* Elide all child ops. */
506333347Speter  {
507333347Speter    apr_hash_index_t *hi;
508333347Speter
509333347Speter    for (hi = apr_hash_first(scratch_pool, changes);
510333347Speter         hi; hi = apr_hash_next(hi))
511333347Speter      {
512333347Speter        const char *this_relpath = apr_hash_this_key(hi);
513333347Speter        const char *r = svn_relpath_skip_ancestor(relpath, this_relpath);
514333347Speter
515333347Speter        if (r && r[0])
516333347Speter          {
517333347Speter            svn_hash_sets(changes, this_relpath, NULL);
518333347Speter          }
519333347Speter      }
520333347Speter  }
521333347Speter
522333347Speter  return SVN_NO_ERROR;
523333347Speter}
524333347Speter
525333347Speter
526333347Speter/*
527333347Speter * ===================================================================
528333347Speter * Commit Editor converter to join a v1 driver to a v3 consumer
529333347Speter * ===================================================================
530333347Speter *
531333347Speter * ...
532333347Speter */
533333347Speter
534333347Spetersvn_error_t *
535333347Spetersvn_branch__compat_delta_from_txn_for_commit(
536333347Speter                        const svn_delta_editor_t **deditor,
537333347Speter                        void **dedit_baton,
538333347Speter                        svn_branch__txn_t *edit_txn,
539333347Speter                        const char *repos_root_url,
540333347Speter                        const char *base_relpath,
541333347Speter                        svn_branch__compat_fetch_func_t fetch_func,
542333347Speter                        void *fetch_baton,
543333347Speter                        const svn_branch__compat_shim_connector_t *shim_connector,
544333347Speter                        apr_pool_t *result_pool,
545333347Speter                        apr_pool_t *scratch_pool)
546333347Speter{
547333347Speter  /* ### ... */
548333347Speter
549333347Speter  return SVN_NO_ERROR;
550333347Speter}
551333347Speter
552333347Spetersvn_error_t *
553333347Spetersvn_branch__compat_delta_from_txn_for_update(
554333347Speter                        const svn_delta_editor_t **deditor,
555333347Speter                        void **dedit_baton,
556333347Speter                        svn_branch__compat_update_editor3_t *update_editor,
557333347Speter                        const char *repos_root_url,
558333347Speter                        const char *base_repos_relpath,
559333347Speter                        svn_branch__compat_fetch_func_t fetch_func,
560333347Speter                        void *fetch_baton,
561333347Speter                        apr_pool_t *result_pool,
562333347Speter                        apr_pool_t *scratch_pool)
563333347Speter{
564333347Speter  svn_branch__compat_shim_connector_t *shim_connector
565333347Speter    = apr_pcalloc(result_pool, sizeof(*shim_connector));
566333347Speter
567333347Speter  shim_connector->target_revision_func = update_editor->set_target_revision_func;
568333347Speter  shim_connector->baton = update_editor->set_target_revision_baton;
569333347Speter#ifdef SHIM_WITH_ABS_PATHS
570333347Speter  shim_connector->ev1_absolute_paths /*...*/;
571333347Speter#endif
572333347Speter
573333347Speter  SVN_ERR(svn_branch__compat_delta_from_txn_for_commit(
574333347Speter                        deditor, dedit_baton,
575333347Speter                        update_editor->edit_txn,
576333347Speter                        repos_root_url, base_repos_relpath,
577333347Speter                        fetch_func, fetch_baton,
578333347Speter                        shim_connector,
579333347Speter                        result_pool, scratch_pool));
580333347Speter  /*SVN_ERR(svn_delta__get_debug_editor(deditor, dedit_baton,
581333347Speter                                      *deditor, *dedit_baton,
582333347Speter                                      "[UP>1] ", result_pool));*/
583333347Speter
584333347Speter  return SVN_NO_ERROR;
585333347Speter}
586333347Speter
587333347Speter
588333347Speter/*
589333347Speter * ===================================================================
590333347Speter * Commit Editor converter to join a v3 driver to a v1 consumer
591333347Speter * ===================================================================
592333347Speter *
593333347Speter * This editor buffers all the changes before driving the Ev1 at the end,
594333347Speter * since it needs to do a single depth-first traversal of the path space
595333347Speter * and this cannot be started until all moves are known.
596333347Speter *
597333347Speter * Moves are converted to copy-and-delete, with the copy being from
598333347Speter * the source peg rev. (### Should it request copy-from revision "-1"?)
599333347Speter *
600333347Speter * It works like this:
601333347Speter *
602333347Speter *                +------+--------+
603333347Speter *                | path | change |
604333347Speter *      Ev3  -->  +------+--------+  -->  Ev1
605333347Speter *                | ...  | ...    |
606333347Speter *                | ...  | ...    |
607333347Speter *
608333347Speter *   1. Ev3 changes are accumulated in a per-path table, EB->changes.
609333347Speter *
610333347Speter *   2. On Ev3 close-edit, walk through the table in a depth-first order,
611333347Speter *      sending the equivalent Ev1 action for each change.
612333347Speter *
613333347Speter * TODO
614333347Speter *
615333347Speter * ### For changes inside a copied subtree, the calls to the "open dir"
616333347Speter *     and "open file" Ev1 methods may be passing the wrong revision
617333347Speter *     number: see comment in apply_change().
618333347Speter *
619333347Speter * ### Have we got our rel-paths in order? Ev1, Ev3 and callbacks may
620333347Speter *     all expect different paths. Are they relative to repos root or to
621333347Speter *     some base path? Leading slash (unimplemented 'send_abs_paths'
622333347Speter *     feature), etc.
623333347Speter *
624333347Speter * ### May be tidier for OPEN_ROOT_FUNC callback (see open_root_ev3())
625333347Speter *     not to actually open the root in advance, but instead just to
626333347Speter *     remember the base revision that the driver wants us to specify
627333347Speter *     when we do open it.
628333347Speter */
629333347Speter
630333347Speter
631333347Speter
632333347Speter/*
633333347Speter * ========================================================================
634333347Speter * Driving the Delta Editor
635333347Speter * ========================================================================
636333347Speter */
637333347Speter
638333347Speter/* Information needed for driving the delta editor. */
639333347Speterstruct svn_branch__txn_priv_t
640333347Speter{
641333347Speter  /* The Ev1 "delta editor" */
642333347Speter  const svn_delta_editor_t *deditor;
643333347Speter  void *dedit_baton;
644333347Speter
645333347Speter  /* Callbacks */
646333347Speter  svn_branch__compat_fetch_func_t fetch_func;
647333347Speter  void *fetch_baton;
648333347Speter
649333347Speter  /* The Ev1 root directory baton if we have opened the root, else null. */
650333347Speter  void *ev1_root_dir_baton;
651333347Speter
652333347Speter#ifdef SHIM_WITH_ABS_PATHS
653333347Speter  const svn_boolean_t *make_abs_paths;
654333347Speter#endif
655333347Speter
656333347Speter  /* Repository root URL
657333347Speter     ### Some code allows this to be null -- but is that valid? */
658333347Speter  const char *repos_root_url;
659333347Speter
660333347Speter  /* Ev1 changes recorded so far: REPOS_RELPATH -> change_node_ev3_t */
661333347Speter  apr_hash_t *changes;
662333347Speter
663333347Speter  /* The branching state on which the per-element API is working */
664333347Speter  svn_branch__txn_t *txn;
665333347Speter
666333347Speter  apr_pool_t *edit_pool;
667333347Speter};
668333347Speter
669333347Speter/* Get all the (Ev1) paths that have changes.
670333347Speter */
671333347Speterstatic const apr_array_header_t *
672333347Speterget_unsorted_paths(apr_hash_t *changes,
673333347Speter                   apr_pool_t *scratch_pool)
674333347Speter{
675333347Speter  apr_array_header_t *paths = apr_array_make(scratch_pool, 0, sizeof(void *));
676333347Speter  apr_hash_index_t *hi;
677333347Speter
678333347Speter  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
679333347Speter    {
680333347Speter      const char *this_path = apr_hash_this_key(hi);
681333347Speter      APR_ARRAY_PUSH(paths, const char *) = this_path;
682333347Speter    }
683333347Speter
684333347Speter  return paths;
685333347Speter}
686333347Speter
687333347Speter#if 0 /* needed only for shim connector */
688333347Speter/*  */
689333347Speterstatic svn_error_t *
690333347Speterset_target_revision_ev3(void *edit_baton,
691333347Speter                        svn_revnum_t target_revision,
692333347Speter                        apr_pool_t *scratch_pool)
693333347Speter{
694333347Speter  svn_branch__txn_priv_t *eb = edit_baton;
695333347Speter
696333347Speter  SVN_ERR(eb->deditor->set_target_revision(eb->dedit_baton, target_revision,
697333347Speter                                           scratch_pool));
698333347Speter
699333347Speter  return SVN_NO_ERROR;
700333347Speter}
701333347Speter#endif
702333347Speter
703333347Speter/*  */
704333347Speterstatic svn_error_t *
705333347Speteropen_root_ev3(void *baton,
706333347Speter              svn_revnum_t base_revision)
707333347Speter{
708333347Speter  svn_branch__txn_priv_t *eb = baton;
709333347Speter
710333347Speter  SVN_ERR(eb->deditor->open_root(eb->dedit_baton, base_revision,
711333347Speter                                 eb->edit_pool, &eb->ev1_root_dir_baton));
712333347Speter  return SVN_NO_ERROR;
713333347Speter}
714333347Speter
715333347Speter/* If RELPATH is a child of a copy, return the path of the copy root,
716333347Speter * else return NULL.
717333347Speter */
718333347Speterstatic const char *
719333347Speterfind_enclosing_copy(apr_hash_t *changes,
720333347Speter                    const char *relpath,
721333347Speter                    apr_pool_t *result_pool)
722333347Speter{
723333347Speter  while (*relpath)
724333347Speter    {
725333347Speter      const change_node_t *change = svn_hash_gets(changes, relpath);
726333347Speter
727333347Speter      if (change)
728333347Speter        {
729333347Speter          if (change->copyfrom_path)
730333347Speter            return relpath;
731333347Speter          if (change->action != RESTRUCTURE_NONE)
732333347Speter            return NULL;
733333347Speter        }
734333347Speter      relpath = svn_relpath_dirname(relpath, result_pool);
735333347Speter    }
736333347Speter
737333347Speter  return NULL;
738333347Speter}
739333347Speter
740333347Speter/* Set *BASE_PROPS to the 'base' properties, against which any changes
741333347Speter * will be described, for the changed path described in CHANGES at
742333347Speter * REPOS_RELPATH.
743333347Speter *
744333347Speter * For a copied path, including a copy child path, fetch from the copy
745333347Speter * source path. For a plain add, return an empty set. For a delete,
746333347Speter * return NULL.
747333347Speter */
748333347Speterstatic svn_error_t *
749333347Speterfetch_base_props(apr_hash_t **base_props,
750333347Speter                 apr_hash_t *changes,
751333347Speter                 const char *repos_relpath,
752333347Speter                 svn_branch__compat_fetch_func_t fetch_func,
753333347Speter                 void *fetch_baton,
754333347Speter                 apr_pool_t *result_pool,
755333347Speter                 apr_pool_t *scratch_pool)
756333347Speter{
757333347Speter  const change_node_t *change = svn_hash_gets(changes, repos_relpath);
758333347Speter  const char *source_path = NULL;
759333347Speter  svn_revnum_t source_rev;
760333347Speter
761333347Speter  if (change->action == RESTRUCTURE_DELETE)
762333347Speter    {
763333347Speter      *base_props = NULL;
764333347Speter    }
765333347Speter
766333347Speter  else if (change->action == RESTRUCTURE_ADD && ! change->copyfrom_path)
767333347Speter    {
768333347Speter      *base_props = apr_hash_make(result_pool);
769333347Speter    }
770333347Speter  else if (change->copyfrom_path)
771333347Speter    {
772333347Speter      source_path = change->copyfrom_path;
773333347Speter      source_rev = change->copyfrom_rev;
774333347Speter    }
775333347Speter  else /* RESTRUCTURE_NONE */
776333347Speter    {
777333347Speter      /* It's an edit, but possibly to a copy child. Discover if it's a
778333347Speter         copy child, & find the copy-from source. */
779333347Speter
780333347Speter      const char *copy_path
781333347Speter        = find_enclosing_copy(changes, repos_relpath, scratch_pool);
782333347Speter
783333347Speter      if (copy_path)
784333347Speter        {
785333347Speter          const change_node_t *enclosing_copy
786333347Speter            = svn_hash_gets(changes, copy_path);
787333347Speter          const char *remainder
788333347Speter            = svn_relpath_skip_ancestor(copy_path, repos_relpath);
789333347Speter
790333347Speter          source_path = svn_relpath_join(enclosing_copy->copyfrom_path,
791333347Speter                                         remainder, scratch_pool);
792333347Speter          source_rev = enclosing_copy->copyfrom_rev;
793333347Speter        }
794333347Speter      else
795333347Speter        {
796333347Speter          /* It's a plain edit (not a copy child path). */
797333347Speter          source_path = repos_relpath;
798333347Speter          source_rev = change->changing_rev;
799333347Speter        }
800333347Speter    }
801333347Speter
802333347Speter  if (source_path)
803333347Speter    {
804333347Speter      SVN_ERR(fetch_func(NULL, base_props, NULL, NULL,
805333347Speter                         fetch_baton, source_path, source_rev,
806333347Speter                         result_pool, scratch_pool));
807333347Speter    }
808333347Speter
809333347Speter  return SVN_NO_ERROR;
810333347Speter}
811333347Speter
812333347Speter/* Send property changes to Ev1 for the CHANGE at REPOS_RELPATH.
813333347Speter *
814333347Speter * Ev1 requires exactly one prop-change call for each prop whose value
815333347Speter * has changed. Therefore we *have* to fetch the original props from the
816333347Speter * repository, provide them as OLD_PROPS, and calculate the changes.
817333347Speter */
818333347Speterstatic svn_error_t *
819333347Speterdrive_ev1_props(const char *repos_relpath,
820333347Speter                const change_node_t *change,
821333347Speter                apr_hash_t *old_props,
822333347Speter                const svn_delta_editor_t *deditor,
823333347Speter                void *node_baton,
824333347Speter                apr_pool_t *scratch_pool)
825333347Speter{
826333347Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
827333347Speter  apr_array_header_t *propdiffs;
828333347Speter  int i;
829333347Speter
830333347Speter  SVN_ERR_ASSERT(change->action != RESTRUCTURE_DELETE);
831333347Speter
832333347Speter  /* If there are no property changes, then just exit. */
833333347Speter  if (change->props == NULL)
834333347Speter    return SVN_NO_ERROR;
835333347Speter
836333347Speter  SVN_ERR(svn_prop_diffs(&propdiffs, change->props, old_props, scratch_pool));
837333347Speter
838333347Speter  /* Apply property changes. These should be changes against the empty set
839333347Speter     for a new node, or changes against the source node for a copied node. */
840333347Speter  for (i = 0; i < propdiffs->nelts; i++)
841333347Speter    {
842333347Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
843333347Speter
844333347Speter      svn_pool_clear(iterpool);
845333347Speter
846333347Speter      if (change->kind == svn_node_dir)
847333347Speter        SVN_ERR(deditor->change_dir_prop(node_baton,
848333347Speter                                         prop->name, prop->value,
849333347Speter                                         iterpool));
850333347Speter      else
851333347Speter        SVN_ERR(deditor->change_file_prop(node_baton,
852333347Speter                                          prop->name, prop->value,
853333347Speter                                          iterpool));
854333347Speter    }
855333347Speter
856333347Speter#ifdef SHIM_WITH_UNLOCK
857333347Speter  /* Handle the funky unlock protocol. Note: only possibly on files.  */
858333347Speter  if (change->unlock)
859333347Speter    {
860333347Speter      SVN_ERR_ASSERT(change->kind == svn_node_file);
861333347Speter      SVN_ERR(deditor->change_file_prop(node_baton,
862333347Speter                                            SVN_PROP_ENTRY_LOCK_TOKEN, NULL,
863333347Speter                                            iterpool));
864333347Speter    }
865333347Speter#endif
866333347Speter
867333347Speter  svn_pool_destroy(iterpool);
868333347Speter  return SVN_NO_ERROR;
869333347Speter}
870333347Speter
871333347Speter/* Drive the Ev1 editor with the change recorded in EB->changes for the
872333347Speter * path EV1_RELPATH.
873333347Speter *
874333347Speter * Conforms to svn_delta_path_driver_cb_func_t.
875333347Speter */
876333347Speterstatic svn_error_t *
877333347Speterapply_change(void **dir_baton,
878362181Sdim             const svn_delta_editor_t *editor,
879362181Sdim             void *edit_baton,
880333347Speter             void *parent_baton,
881333347Speter             void *callback_baton,
882333347Speter             const char *ev1_relpath,
883333347Speter             apr_pool_t *result_pool)
884333347Speter{
885333347Speter  apr_pool_t *scratch_pool = result_pool;
886333347Speter  const svn_branch__txn_priv_t *eb = callback_baton;
887333347Speter  const change_node_t *change = svn_hash_gets(eb->changes, ev1_relpath);
888333347Speter  void *file_baton = NULL;
889333347Speter  apr_hash_t *base_props;
890333347Speter
891333347Speter  /* The callback should only be called for paths in CHANGES.  */
892333347Speter  SVN_ERR_ASSERT(change != NULL);
893333347Speter
894333347Speter  /* Typically, we are not creating new directory batons.  */
895333347Speter  *dir_baton = NULL;
896333347Speter
897333347Speter  SVN_ERR(fetch_base_props(&base_props, eb->changes, ev1_relpath,
898333347Speter                           eb->fetch_func, eb->fetch_baton,
899333347Speter                           scratch_pool, scratch_pool));
900333347Speter
901333347Speter  /* Are we editing the root of the tree?  */
902333347Speter  if (parent_baton == NULL)
903333347Speter    {
904333347Speter      /* The root dir was already opened. */
905333347Speter      *dir_baton = eb->ev1_root_dir_baton;
906333347Speter
907333347Speter      /* Only property edits are allowed on the root.  */
908333347Speter      SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
909333347Speter      SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
910362181Sdim                              editor, *dir_baton, scratch_pool));
911333347Speter
912333347Speter      /* No further action possible for the root.  */
913333347Speter      return SVN_NO_ERROR;
914333347Speter    }
915333347Speter
916333347Speter  if (change->action == RESTRUCTURE_DELETE)
917333347Speter    {
918362181Sdim      SVN_ERR(editor->delete_entry(ev1_relpath, change->deleting_rev,
919362181Sdim                                   parent_baton, scratch_pool));
920333347Speter
921333347Speter      /* No futher action possible for this node.  */
922333347Speter      return SVN_NO_ERROR;
923333347Speter    }
924333347Speter
925333347Speter  /* If we're not deleting this node, then we should know its kind.  */
926333347Speter  SVN_ERR_ASSERT(change->kind != svn_node_unknown);
927333347Speter
928333347Speter#ifdef SHIM_WITH_ADD_ABSENT
929333347Speter  if (change->action == RESTRUCTURE_ADD_ABSENT)
930333347Speter    {
931333347Speter      if (change->kind == svn_node_dir)
932362181Sdim        SVN_ERR(editor->absent_directory(ev1_relpath, parent_baton,
933362181Sdim                                         scratch_pool));
934333347Speter      else if (change->kind == svn_node_file)
935362181Sdim        SVN_ERR(editor->absent_file(ev1_relpath, parent_baton,
936362181Sdim                                    scratch_pool));
937333347Speter      else
938333347Speter        SVN_ERR_MALFUNCTION();
939333347Speter
940333347Speter      /* No further action possible for this node.  */
941333347Speter      return SVN_NO_ERROR;
942333347Speter    }
943333347Speter#endif
944333347Speter  /* RESTRUCTURE_NONE or RESTRUCTURE_ADD  */
945333347Speter
946333347Speter  if (change->action == RESTRUCTURE_ADD)
947333347Speter    {
948333347Speter      const char *copyfrom_url = NULL;
949333347Speter      svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
950333347Speter
951333347Speter      /* Do we have an old node to delete first? If so, delete it. */
952333347Speter      if (change->deleting)
953362181Sdim        SVN_ERR(editor->delete_entry(ev1_relpath, change->deleting_rev,
954362181Sdim                                     parent_baton, scratch_pool));
955333347Speter
956333347Speter      /* If it's a copy, determine the copy source location. */
957333347Speter      if (change->copyfrom_path)
958333347Speter        {
959333347Speter          /* ### What's this about URL vs. fspath? REPOS_ROOT_URL isn't
960333347Speter             optional, is it, at least in a commit editor? */
961333347Speter          if (eb->repos_root_url)
962333347Speter            copyfrom_url = svn_path_url_add_component2(eb->repos_root_url,
963333347Speter                                                       change->copyfrom_path,
964333347Speter                                                       scratch_pool);
965333347Speter          else
966333347Speter            {
967333347Speter              copyfrom_url = change->copyfrom_path;
968333347Speter
969333347Speter              /* Make this an FS path by prepending "/" */
970333347Speter              if (copyfrom_url[0] != '/')
971333347Speter                copyfrom_url = apr_pstrcat(scratch_pool, "/",
972333347Speter                                           copyfrom_url, SVN_VA_NULL);
973333347Speter            }
974333347Speter
975333347Speter          copyfrom_rev = change->copyfrom_rev;
976333347Speter        }
977333347Speter
978333347Speter      if (change->kind == svn_node_dir)
979362181Sdim        SVN_ERR(editor->add_directory(ev1_relpath, parent_baton,
980362181Sdim                                      copyfrom_url, copyfrom_rev,
981362181Sdim                                      result_pool, dir_baton));
982333347Speter      else if (change->kind == svn_node_file)
983362181Sdim        SVN_ERR(editor->add_file(ev1_relpath, parent_baton,
984362181Sdim                                 copyfrom_url, copyfrom_rev,
985362181Sdim                                 result_pool, &file_baton));
986333347Speter      else
987333347Speter        SVN_ERR_MALFUNCTION();
988333347Speter    }
989333347Speter  else /* RESTRUCTURE_NONE */
990333347Speter    {
991333347Speter      /* ### The code that inserts a "plain edit" change record sets
992333347Speter         'changing_rev' to the peg rev of the pegged part of the path,
993333347Speter         even when the full path refers to a child of a copy. Should we
994333347Speter         instead be using the copy source rev here, in that case? (Like
995333347Speter         when we fetch the base properties.) */
996333347Speter
997333347Speter      if (change->kind == svn_node_dir)
998362181Sdim        SVN_ERR(editor->open_directory(ev1_relpath, parent_baton,
999362181Sdim                                       change->changing_rev,
1000362181Sdim                                       result_pool, dir_baton));
1001333347Speter      else if (change->kind == svn_node_file)
1002362181Sdim        SVN_ERR(editor->open_file(ev1_relpath, parent_baton,
1003362181Sdim                                  change->changing_rev,
1004362181Sdim                                  result_pool, &file_baton));
1005333347Speter      else
1006333347Speter        SVN_ERR_MALFUNCTION();
1007333347Speter    }
1008333347Speter
1009333347Speter  /* Apply any properties in CHANGE to the node.  */
1010333347Speter  if (change->kind == svn_node_dir)
1011333347Speter    SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
1012362181Sdim                            editor, *dir_baton, scratch_pool));
1013333347Speter  else
1014333347Speter    SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
1015362181Sdim                            editor, file_baton, scratch_pool));
1016333347Speter
1017333347Speter  /* Send the text content delta, if new text content is provided. */
1018333347Speter  if (change->contents_text)
1019333347Speter    {
1020333347Speter      svn_stream_t *read_stream;
1021333347Speter      svn_txdelta_window_handler_t handler;
1022333347Speter      void *handler_baton;
1023333347Speter
1024333347Speter      read_stream = svn_stream_from_stringbuf(change->contents_text,
1025333347Speter                                              scratch_pool);
1026333347Speter      /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
1027333347Speter         ### shim code...  */
1028362181Sdim      SVN_ERR(editor->apply_textdelta(file_baton, NULL, scratch_pool,
1029333347Speter                                           &handler, &handler_baton));
1030333347Speter      /* ### it would be nice to send a true txdelta here, but whatever.  */
1031333347Speter      SVN_ERR(svn_txdelta_send_stream(read_stream, handler, handler_baton,
1032333347Speter                                      NULL, scratch_pool));
1033333347Speter      SVN_ERR(svn_stream_close(read_stream));
1034333347Speter    }
1035333347Speter
1036333347Speter  if (file_baton)
1037333347Speter    {
1038362181Sdim      SVN_ERR(editor->close_file(file_baton, NULL, scratch_pool));
1039333347Speter    }
1040333347Speter
1041333347Speter  return SVN_NO_ERROR;
1042333347Speter}
1043333347Speter
1044333347Speter/*
1045333347Speter * ========================================================================
1046333347Speter * Old-repository storage paths for branch elements
1047333347Speter * ========================================================================
1048333347Speter *
1049333347Speter * To support top-level branches, we map each top-level branch to its own
1050333347Speter * directory in the old repository, with each nested branch in a subdirectory:
1051333347Speter *
1052333347Speter *   B0  =>  ^/top0/...
1053333347Speter *           ^/top0/.../trunk/...  <= B0.12
1054333347Speter *   B1  =>  ^/top1/...
1055333347Speter *
1056333347Speter * It may be better to put each branch in its own directory:
1057333347Speter *
1058333347Speter *   B0     =>  ^/B0/...
1059333347Speter *   B0.12  =>  ^/B0.12/...
1060333347Speter *   B1     =>  ^/B1/...
1061333347Speter *
1062333347Speter * (A branch root is not necessarily a directory, it could be a file.)
1063333347Speter */
1064333347Speter
1065333347Speter/* Get the old-repository path for the storage of the root element of BRANCH.
1066333347Speter *
1067333347Speter * Currently, this is the same as the nested-branching hierarchical path
1068333347Speter * (and thus assumes there is only one top-level branch).
1069333347Speter */
1070333347Speterstatic const char *
1071333347Speterbranch_get_storage_root_rrpath(const svn_branch__state_t *branch,
1072333347Speter                               apr_pool_t *result_pool)
1073333347Speter{
1074333347Speter  int top_branch_num = atoi(branch->bid + 1);
1075333347Speter  const char *top_path = apr_psprintf(result_pool, "top%d", top_branch_num);
1076333347Speter  const char *nested_path = svn_branch__get_root_rrpath(branch, result_pool);
1077333347Speter
1078333347Speter  return svn_relpath_join(top_path, nested_path, result_pool);
1079333347Speter}
1080333347Speter
1081333347Speter/* Get the old-repository path for the storage of element EID of BRANCH.
1082333347Speter *
1083333347Speter * If the element EID doesn't exist in BRANCH, return NULL.
1084333347Speter */
1085333347Speterstatic const char *
1086333347Speterbranch_get_storage_rrpath_by_eid(const svn_branch__state_t *branch,
1087333347Speter                                 int eid,
1088333347Speter                                 apr_pool_t *result_pool)
1089333347Speter{
1090333347Speter  const char *path = svn_branch__get_path_by_eid(branch, eid, result_pool);
1091333347Speter  const char *rrpath = NULL;
1092333347Speter
1093333347Speter  if (path)
1094333347Speter    {
1095333347Speter      rrpath = svn_relpath_join(branch_get_storage_root_rrpath(branch,
1096333347Speter                                                               result_pool),
1097333347Speter                                path, result_pool);
1098333347Speter    }
1099333347Speter  return rrpath;
1100333347Speter}
1101333347Speter
1102333347Speter/* Return, in *STORAGE_PATHREV_P, the storage-rrpath-rev for BRANCH_REF.
1103333347Speter */
1104333347Speterstatic svn_error_t *
1105333347Speterstorage_pathrev_from_branch_ref(svn_pathrev_t *storage_pathrev_p,
1106333347Speter                                svn_element__branch_ref_t branch_ref,
1107333347Speter                                svn_branch__repos_t *repos,
1108333347Speter                                apr_pool_t *result_pool,
1109333347Speter                                apr_pool_t *scratch_pool)
1110333347Speter{
1111333347Speter  svn_branch__el_rev_id_t *el_rev;
1112333347Speter
1113333347Speter  SVN_ERR(svn_branch__repos_find_el_rev_by_id(&el_rev,
1114333347Speter                                              repos,
1115333347Speter                                              branch_ref.rev,
1116333347Speter                                              branch_ref.branch_id,
1117333347Speter                                              branch_ref.eid,
1118333347Speter                                              scratch_pool, scratch_pool));
1119333347Speter
1120333347Speter  storage_pathrev_p->rev = el_rev->rev;
1121333347Speter  storage_pathrev_p->relpath
1122333347Speter    = branch_get_storage_rrpath_by_eid(el_rev->branch, el_rev->eid,
1123333347Speter                                       result_pool);
1124333347Speter
1125333347Speter  return SVN_NO_ERROR;
1126333347Speter}
1127333347Speter
1128333347Speter/*
1129333347Speter * ========================================================================
1130333347Speter * Editor for Commit (independent per-element changes; element-id addressing)
1131333347Speter * ========================================================================
1132333347Speter */
1133333347Speter
1134333347Speter/*  */
1135333347Speter#define PAYLOAD_IS_ONLY_BY_REFERENCE(payload) \
1136333347Speter    ((payload)->kind == svn_node_unknown)
1137333347Speter
1138333347Speter/* Fetch a payload as *PAYLOAD_P from its storage-pathrev PATH_REV.
1139333347Speter * Fetch names of immediate children of PATH_REV as *CHILDREN_NAMES.
1140333347Speter * Either of the outputs may be null if not wanted.
1141333347Speter */
1142333347Speterstatic svn_error_t *
1143333347Speterpayload_fetch(svn_element__payload_t **payload_p,
1144333347Speter              apr_hash_t **children_names,
1145333347Speter              svn_branch__txn_priv_t *eb,
1146333347Speter              const svn_pathrev_t *path_rev,
1147333347Speter              apr_pool_t *result_pool,
1148333347Speter              apr_pool_t *scratch_pool)
1149333347Speter{
1150333347Speter  svn_element__payload_t *payload
1151333347Speter    = apr_pcalloc(result_pool, sizeof (*payload));
1152333347Speter
1153333347Speter  SVN_ERR(eb->fetch_func(&payload->kind,
1154333347Speter                         &payload->props,
1155333347Speter                         &payload->text,
1156333347Speter                         children_names,
1157333347Speter                         eb->fetch_baton,
1158333347Speter                         path_rev->relpath, path_rev->rev,
1159333347Speter                         result_pool, scratch_pool));
1160333347Speter
1161333347Speter  SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1162333347Speter  SVN_ERR_ASSERT(payload->kind == svn_node_dir
1163333347Speter                 || payload->kind == svn_node_file);
1164333347Speter  if (payload_p)
1165333347Speter    *payload_p = payload;
1166333347Speter  return SVN_NO_ERROR;
1167333347Speter}
1168333347Speter
1169333347Speter/* Return the storage-pathrev of PAYLOAD as *STORAGE_PATHREV_P.
1170333347Speter *
1171333347Speter * Find the storage-pathrev from PAYLOAD->branch_ref.
1172333347Speter */
1173333347Speterstatic svn_error_t *
1174333347Speterpayload_get_storage_pathrev(svn_pathrev_t *storage_pathrev_p,
1175333347Speter                            svn_element__payload_t *payload,
1176333347Speter                            svn_branch__repos_t *repos,
1177333347Speter                            apr_pool_t *result_pool)
1178333347Speter{
1179333347Speter  SVN_ERR_ASSERT(payload->branch_ref.branch_id /* && ... */);
1180333347Speter
1181333347Speter  SVN_ERR(storage_pathrev_from_branch_ref(storage_pathrev_p,
1182333347Speter                                          payload->branch_ref, repos,
1183333347Speter                                          result_pool, result_pool));
1184333347Speter  return SVN_NO_ERROR;
1185333347Speter}
1186333347Speter
1187333347Spetersvn_error_t *
1188333347Spetersvn_branch__compat_fetch(svn_element__payload_t **payload_p,
1189333347Speter                         svn_branch__txn_t *txn,
1190333347Speter                         svn_element__branch_ref_t branch_ref,
1191333347Speter                         svn_branch__compat_fetch_func_t fetch_func,
1192333347Speter                         void *fetch_baton,
1193333347Speter                         apr_pool_t *result_pool,
1194333347Speter                         apr_pool_t *scratch_pool)
1195333347Speter{
1196333347Speter  svn_branch__txn_priv_t eb;
1197333347Speter  svn_pathrev_t storage_pathrev;
1198333347Speter
1199333347Speter  /* Simulate the existence of /top0 in r0. */
1200333347Speter  if (branch_ref.rev == 0 && branch_ref.eid == 0)
1201333347Speter    {
1202333347Speter      *payload_p = svn_element__payload_create_dir(apr_hash_make(result_pool),
1203333347Speter                                                   result_pool);
1204333347Speter      return SVN_NO_ERROR;
1205333347Speter    }
1206333347Speter
1207333347Speter  eb.txn = txn;
1208333347Speter  eb.fetch_func = fetch_func;
1209333347Speter  eb.fetch_baton = fetch_baton;
1210333347Speter
1211333347Speter  SVN_ERR(storage_pathrev_from_branch_ref(&storage_pathrev,
1212333347Speter                                          branch_ref, txn->repos,
1213333347Speter                                          scratch_pool, scratch_pool));
1214333347Speter
1215333347Speter  SVN_ERR(payload_fetch(payload_p, NULL,
1216333347Speter                        &eb, &storage_pathrev, result_pool, scratch_pool));
1217333347Speter  (*payload_p)->branch_ref = branch_ref;
1218333347Speter  return SVN_NO_ERROR;
1219333347Speter}
1220333347Speter
1221333347Speter/* Fill in the actual payload, from its reference, if not already done.
1222333347Speter */
1223333347Speterstatic svn_error_t *
1224333347Speterpayload_resolve(svn_element__payload_t *payload,
1225333347Speter                svn_branch__txn_priv_t *eb,
1226333347Speter                apr_pool_t *scratch_pool)
1227333347Speter{
1228333347Speter  svn_pathrev_t storage;
1229333347Speter
1230333347Speter  SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1231333347Speter
1232333347Speter  if (! PAYLOAD_IS_ONLY_BY_REFERENCE(payload))
1233333347Speter    return SVN_NO_ERROR;
1234333347Speter
1235333347Speter  SVN_ERR(payload_get_storage_pathrev(&storage, payload,
1236333347Speter                                      eb->txn->repos,
1237333347Speter                                      scratch_pool));
1238333347Speter
1239333347Speter  SVN_ERR(eb->fetch_func(&payload->kind,
1240333347Speter                         &payload->props,
1241333347Speter                         &payload->text,
1242333347Speter                         NULL,
1243333347Speter                         eb->fetch_baton,
1244333347Speter                         storage.relpath, storage.rev,
1245333347Speter                         payload->pool, scratch_pool));
1246333347Speter
1247333347Speter  SVN_ERR_ASSERT(svn_element__payload_invariants(payload));
1248333347Speter  SVN_ERR_ASSERT(! PAYLOAD_IS_ONLY_BY_REFERENCE(payload));
1249333347Speter  return SVN_NO_ERROR;
1250333347Speter}
1251333347Speter
1252333347Speter/* Update *PATHS, a hash of (storage_rrpath -> svn_branch__el_rev_id_t),
1253333347Speter * creating or filling in entries for all elements in BRANCH.
1254333347Speter */
1255333347Speterstatic svn_error_t *
1256333347Speterconvert_branch_to_paths(apr_hash_t *paths,
1257333347Speter                        svn_branch__state_t *branch,
1258333347Speter                        apr_pool_t *result_pool,
1259333347Speter                        apr_pool_t *scratch_pool)
1260333347Speter{
1261333347Speter  apr_hash_index_t *hi;
1262333347Speter  svn_element__tree_t *elements;
1263333347Speter
1264333347Speter  /* assert(branch is at a sequence point); */
1265333347Speter
1266333347Speter  SVN_ERR(svn_branch__state_get_elements(branch, &elements, scratch_pool));
1267333347Speter  for (hi = apr_hash_first(scratch_pool, elements->e_map);
1268333347Speter       hi; hi = apr_hash_next(hi))
1269333347Speter    {
1270333347Speter      int eid = svn_eid__hash_this_key(hi);
1271333347Speter      svn_element__content_t *element = apr_hash_this_val(hi);
1272333347Speter      const char *rrpath
1273333347Speter        = branch_get_storage_rrpath_by_eid(branch, eid, result_pool);
1274333347Speter      svn_branch__el_rev_id_t *ba;
1275333347Speter
1276333347Speter      /* A subbranch-root element carries no payload; the corresponding
1277333347Speter         inner branch root element will provide the payload for this path. */
1278333347Speter      if (element->payload->is_subbranch_root)
1279333347Speter        continue;
1280333347Speter
1281333347Speter      /* No other element should exist at this path, given that we avoid
1282333347Speter         storing anything for a subbranch-root element. */
1283333347Speter      SVN_ERR_ASSERT(! svn_hash_gets(paths, rrpath));
1284333347Speter
1285333347Speter      /* Fill in the details. */
1286333347Speter      ba = svn_branch__el_rev_id_create(branch, eid, branch->txn->rev,
1287333347Speter                                        result_pool);
1288333347Speter      svn_hash_sets(paths, rrpath, ba);
1289333347Speter      /*SVN_DBG(("branch-to-path[%d]: b%s e%d -> %s",
1290333347Speter               i, svn_branch__get_id(branch, scratch_pool), eid, rrpath));*/
1291333347Speter    }
1292333347Speter  return SVN_NO_ERROR;
1293333347Speter}
1294333347Speter
1295333347Speter/* Produce a mapping from paths to element ids, covering all elements in
1296333347Speter * BRANCH and all its sub-branches, recursively.
1297333347Speter *
1298333347Speter * Update *PATHS_UNION, a hash of (storage_rrpath -> svn_branch__el_rev_id_t),
1299333347Speter * creating or filling in entries for all elements in all branches at and
1300333347Speter * under BRANCH, recursively.
1301333347Speter */
1302333347Speterstatic svn_error_t *
1303333347Speterconvert_branch_to_paths_r(apr_hash_t *paths_union,
1304333347Speter                          svn_branch__state_t *branch,
1305333347Speter                          apr_pool_t *result_pool,
1306333347Speter                          apr_pool_t *scratch_pool)
1307333347Speter{
1308333347Speter  apr_array_header_t *subbranches;
1309333347Speter  int i;
1310333347Speter
1311333347Speter  /*SVN_DBG(("[%d] branch={b%s e%d at '%s'}", idx,
1312333347Speter           svn_branch__get_id(branch, scratch_pool), branch->root_eid,
1313333347Speter           svn_branch__get_root_rrpath(branch, scratch_pool)));*/
1314333347Speter  SVN_ERR(convert_branch_to_paths(paths_union, branch,
1315333347Speter                                  result_pool, scratch_pool));
1316333347Speter
1317333347Speter  SVN_ERR(svn_branch__get_immediate_subbranches(branch, &subbranches,
1318333347Speter                                               scratch_pool, scratch_pool));
1319333347Speter  /* Rercurse into sub-branches */
1320333347Speter  for (i = 0; i < subbranches->nelts; i++)
1321333347Speter    {
1322333347Speter      svn_branch__state_t *b = APR_ARRAY_IDX(subbranches, i, void *);
1323333347Speter
1324333347Speter      SVN_ERR(convert_branch_to_paths_r(paths_union, b, result_pool,
1325333347Speter                                        scratch_pool));
1326333347Speter    }
1327333347Speter  return SVN_NO_ERROR;
1328333347Speter}
1329333347Speter
1330333347Speter/* Return TRUE iff INITIAL_PAYLOAD and FINAL_PAYLOAD are both non-null
1331333347Speter * and have the same properties.
1332333347Speter */
1333333347Speterstatic svn_boolean_t
1334333347Speterprops_equal(svn_element__payload_t *initial_payload,
1335333347Speter            svn_element__payload_t *final_payload,
1336333347Speter            apr_pool_t *scratch_pool)
1337333347Speter{
1338333347Speter  apr_array_header_t *prop_diffs;
1339333347Speter
1340333347Speter  if (!initial_payload || !final_payload)
1341333347Speter    return FALSE;
1342333347Speter
1343333347Speter  svn_error_clear(svn_prop_diffs(&prop_diffs,
1344333347Speter                                 initial_payload->props,
1345333347Speter                                 final_payload->props,
1346333347Speter                                 scratch_pool));
1347333347Speter  return (prop_diffs->nelts == 0);
1348333347Speter}
1349333347Speter
1350333347Speter/* Return TRUE iff INITIAL_PAYLOAD and FINAL_PAYLOAD are both file payload
1351333347Speter * and have the same text.
1352333347Speter */
1353333347Speterstatic svn_boolean_t
1354333347Spetertext_equal(svn_element__payload_t *initial_payload,
1355333347Speter           svn_element__payload_t *final_payload)
1356333347Speter{
1357333347Speter  if (!initial_payload || !final_payload
1358333347Speter      || initial_payload->kind != svn_node_file
1359333347Speter      || final_payload->kind != svn_node_file)
1360333347Speter    {
1361333347Speter      return FALSE;
1362333347Speter    }
1363333347Speter
1364333347Speter  return svn_stringbuf_compare(initial_payload->text,
1365333347Speter                               final_payload->text);
1366333347Speter}
1367333347Speter
1368333347Speter/* Return the copy-from location to be used if this is to be a copy;
1369333347Speter * otherwise return NULL.
1370333347Speter *
1371333347Speter * ### Currently this is indicated by payload-by-reference, which is
1372333347Speter *     an inadequate indication.
1373333347Speter */
1374333347Speterstatic svn_error_t *
1375333347Speterget_copy_from(svn_pathrev_t *copyfrom_pathrev_p,
1376333347Speter              svn_element__payload_t *final_payload,
1377333347Speter              svn_branch__txn_priv_t *eb,
1378333347Speter              apr_pool_t *result_pool)
1379333347Speter{
1380333347Speter  if (final_payload->branch_ref.branch_id)
1381333347Speter    {
1382333347Speter      SVN_ERR(payload_get_storage_pathrev(copyfrom_pathrev_p, final_payload,
1383333347Speter                                          eb->txn->repos,
1384333347Speter                                          result_pool));
1385333347Speter    }
1386333347Speter  else
1387333347Speter    {
1388333347Speter      copyfrom_pathrev_p->relpath = NULL;
1389333347Speter      copyfrom_pathrev_p->rev = SVN_INVALID_REVNUM;
1390333347Speter    }
1391333347Speter
1392333347Speter  return SVN_NO_ERROR;
1393333347Speter}
1394333347Speter
1395333347Speter/* Return a hash whose keys are the names of the immediate children of
1396333347Speter * RRPATH in PATHS.
1397333347Speter */
1398333347Speterstatic apr_hash_t *
1399333347Speterget_immediate_children_names(apr_hash_t *paths,
1400333347Speter                             const char *parent_rrpath,
1401333347Speter                             apr_pool_t *result_pool,
1402333347Speter                             apr_pool_t *scratch_pool)
1403333347Speter{
1404333347Speter  apr_hash_t *children = apr_hash_make(result_pool);
1405333347Speter  apr_hash_index_t *hi;
1406333347Speter
1407333347Speter  for (hi = apr_hash_first(scratch_pool, paths); hi; hi = apr_hash_next(hi))
1408333347Speter    {
1409333347Speter      const char *this_rrpath = apr_hash_this_key(hi);
1410333347Speter
1411333347Speter      if (this_rrpath[0]
1412333347Speter          && strcmp(parent_rrpath, svn_relpath_dirname(this_rrpath,
1413333347Speter                                                       scratch_pool)) == 0)
1414333347Speter        {
1415333347Speter          svn_hash_sets(children,
1416333347Speter                        svn_relpath_basename(this_rrpath, result_pool), "");
1417333347Speter        }
1418333347Speter    }
1419333347Speter
1420333347Speter  return children;
1421333347Speter}
1422333347Speter
1423333347Speter/* Generate Ev1 instructions to edit from a current state to a final state
1424333347Speter * at RRPATH, recursing for child paths of RRPATH.
1425333347Speter *
1426333347Speter * The current state at RRPATH might not be the initial state because,
1427333347Speter * although neither RRPATH nor any sub-paths have been explicitly visited
1428333347Speter * before, the current state at RRPATH and its sub-paths might be the
1429333347Speter * result of a copy.
1430333347Speter *
1431333347Speter * PRED_LOC is the predecessor location of the node currently at RRPATH in
1432333347Speter * the Ev1 transaction, or NULL if there is no node currently at RRPATH.
1433333347Speter * If the node is copied, including a child of a copy, this is its copy-from
1434333347Speter * location, otherwise this is its location in the txn base revision.
1435333347Speter * (The current node cannot be a plain added node on entry to this function,
1436333347Speter * as the function must be called only once for each path and there is no
1437333347Speter * recursive add operation.) PRED_LOC identifies the node content that the
1438333347Speter * that the Ev1 edit needs to delete, replace, update or leave unchanged.
1439333347Speter *
1440333347Speter * Process a single hierarchy of nested branches, rooted in the top-level
1441333347Speter * branch TOP_BRANCH_NUM.
1442333347Speter */
1443333347Speterstatic svn_error_t *
1444333347Speterdrive_changes_r(const char *rrpath,
1445333347Speter                svn_pathrev_t *pred_loc,
1446333347Speter                apr_hash_t *paths_final,
1447333347Speter                const char *top_branch_id,
1448333347Speter                svn_branch__txn_priv_t *eb,
1449333347Speter                apr_pool_t *scratch_pool)
1450333347Speter{
1451333347Speter  /* The el-rev-id of the element that will finally exist at RRPATH. */
1452333347Speter  svn_branch__el_rev_id_t *final_el_rev = svn_hash_gets(paths_final, rrpath);
1453333347Speter  svn_element__payload_t *final_payload;
1454333347Speter  svn_pathrev_t final_copy_from;
1455333347Speter  svn_boolean_t succession;
1456333347Speter
1457333347Speter  /*SVN_DBG(("rrpath '%s' current=%s, final=e%d)",
1458333347Speter           rrpath,
1459333347Speter           pred_loc ? pathrev_str(*pred_loc, scratch_pool) : "<nil>",
1460333347Speter           final_el_rev ? final_el_rev->eid : -1));*/
1461333347Speter
1462333347Speter  SVN_ERR_ASSERT(!pred_loc
1463333347Speter                 || (pred_loc->relpath && SVN_IS_VALID_REVNUM(pred_loc->rev)));
1464333347Speter
1465333347Speter  if (final_el_rev)
1466333347Speter    {
1467333347Speter      svn_element__content_t *final_element;
1468333347Speter
1469333347Speter      SVN_ERR(svn_branch__state_get_element(final_el_rev->branch, &final_element,
1470333347Speter                                            final_el_rev->eid, scratch_pool));
1471333347Speter      /* A non-null FINAL address means an element exists there. */
1472333347Speter      SVN_ERR_ASSERT(final_element);
1473333347Speter
1474333347Speter      final_payload = final_element->payload;
1475333347Speter
1476333347Speter      /* Decide whether the state at this path should be a copy (incl. a
1477333347Speter         copy-child) */
1478333347Speter      SVN_ERR(get_copy_from(&final_copy_from, final_payload, eb, scratch_pool));
1479333347Speter      /* It doesn't make sense to have a non-copy inside a copy */
1480333347Speter      /*SVN_ERR_ASSERT(!(parent_is_copy && !final_copy_from));*/
1481333347Speter   }
1482333347Speter  else
1483333347Speter    {
1484333347Speter      final_payload = NULL;
1485333347Speter      final_copy_from.relpath = NULL;
1486333347Speter    }
1487333347Speter
1488333347Speter  /* Succession means:
1489333347Speter       for a copy (inc. child)  -- copy-from same place as natural predecessor
1490333347Speter       otherwise                -- it's succession if it's the same element
1491333347Speter                                   (which also implies the same kind) */
1492333347Speter  if (pred_loc && final_copy_from.relpath)
1493333347Speter    {
1494333347Speter      succession = pathrev_equal(pred_loc, &final_copy_from);
1495333347Speter    }
1496333347Speter  else if (pred_loc && final_el_rev)
1497333347Speter    {
1498333347Speter      svn_branch__el_rev_id_t *pred_el_rev;
1499333347Speter
1500333347Speter      SVN_ERR(svn_branch__repos_find_el_rev_by_path_rev(&pred_el_rev,
1501333347Speter                                            eb->txn->repos,
1502333347Speter                                            pred_loc->rev,
1503333347Speter                                            top_branch_id,
1504333347Speter                                            pred_loc->relpath,
1505333347Speter                                            scratch_pool, scratch_pool));
1506333347Speter
1507333347Speter      succession = (pred_el_rev->eid == final_el_rev->eid);
1508333347Speter    }
1509333347Speter  else
1510333347Speter    {
1511333347Speter      succession = FALSE;
1512333347Speter    }
1513333347Speter
1514333347Speter  /* If there's an initial node that isn't also going to be the final
1515333347Speter     node at this path, then it's being deleted or replaced: delete it. */
1516333347Speter  if (pred_loc && !succession)
1517333347Speter    {
1518333347Speter      /* Issue an Ev1 delete unless this path is inside a path at which
1519333347Speter         we've already issued a delete. */
1520333347Speter      if (check_existence(eb->changes, rrpath) != svn_tristate_false)
1521333347Speter        {
1522333347Speter          /*SVN_DBG(("ev1:del(%s)", rrpath));*/
1523333347Speter          /* ### We don't need "delete_subtree", we only need to insert a
1524333347Speter             single delete operation, as we know we haven't
1525333347Speter             inserted any changes inside this subtree. */
1526333347Speter          SVN_ERR(delete_subtree(eb->changes, rrpath, pred_loc->rev));
1527333347Speter        }
1528333347Speter      else
1529333347Speter        {
1530333347Speter          /*SVN_DBG(("ev1:del(%s): parent is already deleted", rrpath))*/
1531333347Speter        }
1532333347Speter    }
1533333347Speter
1534333347Speter  /* If there's a final node, it's being added or modified.
1535333347Speter     Or it's unchanged -- we do nothing in that case. */
1536333347Speter  if (final_el_rev)
1537333347Speter    {
1538333347Speter      svn_element__payload_t *current_payload = NULL;
1539333347Speter      apr_hash_t *current_children = NULL;
1540333347Speter      change_node_t *change = NULL;
1541333347Speter
1542333347Speter      /* Get the full payload of the final node. If we have
1543333347Speter         only a reference to the payload, fetch it in full. */
1544333347Speter      SVN_ERR_ASSERT(final_payload);
1545333347Speter      SVN_ERR(payload_resolve(final_payload, eb, scratch_pool));
1546333347Speter
1547333347Speter      /* If the final node was also the initial node, it's being
1548333347Speter         modified, otherwise it's being added (perhaps a replacement). */
1549333347Speter      if (succession)
1550333347Speter        {
1551333347Speter          /* Get full payload of the current node */
1552333347Speter          SVN_ERR(payload_fetch(&current_payload, &current_children,
1553333347Speter                                eb, pred_loc,
1554333347Speter                                scratch_pool, scratch_pool));
1555333347Speter
1556333347Speter          /* If no changes to make, then skip this path */
1557333347Speter          if (svn_element__payload_equal(current_payload,
1558333347Speter                                         final_payload, scratch_pool))
1559333347Speter            {
1560333347Speter              /*SVN_DBG(("ev1:no-op(%s)", rrpath));*/
1561333347Speter            }
1562333347Speter          else
1563333347Speter            {
1564333347Speter              /*SVN_DBG(("ev1:mod(%s)", rrpath));*/
1565333347Speter              SVN_ERR(insert_change(&change, eb->changes, rrpath,
1566333347Speter                                    RESTRUCTURE_NONE));
1567333347Speter              change->changing_rev = pred_loc->rev;
1568333347Speter            }
1569333347Speter        }
1570333347Speter      else /* add or copy/move */
1571333347Speter        {
1572333347Speter          /*SVN_DBG(("ev1:add(%s)", rrpath));*/
1573333347Speter          SVN_ERR(insert_change(&change, eb->changes, rrpath,
1574333347Speter                                RESTRUCTURE_ADD));
1575333347Speter
1576333347Speter          /* If the node is to be copied (and possibly modified) ... */
1577333347Speter          if (final_copy_from.relpath)
1578333347Speter            {
1579333347Speter              change->copyfrom_rev = final_copy_from.rev;
1580333347Speter              change->copyfrom_path = final_copy_from.relpath;
1581333347Speter
1582333347Speter              /* Get full payload of the copy source node */
1583333347Speter              SVN_ERR(payload_fetch(&current_payload, &current_children,
1584333347Speter                                    eb, &final_copy_from,
1585333347Speter                                    scratch_pool, scratch_pool));
1586333347Speter            }
1587333347Speter        }
1588333347Speter
1589333347Speter      if (change)
1590333347Speter        {
1591333347Speter          /* Copy the required content into the change record. Avoid no-op
1592333347Speter             changes of props / text, not least to minimize clutter when
1593333347Speter             debugging Ev1 operations. */
1594333347Speter          SVN_ERR_ASSERT(final_payload->kind == svn_node_dir
1595333347Speter                         || final_payload->kind == svn_node_file);
1596333347Speter          change->kind = final_payload->kind;
1597333347Speter          if (!props_equal(current_payload, final_payload, scratch_pool))
1598333347Speter            {
1599333347Speter              change->props = final_payload->props;
1600333347Speter            }
1601333347Speter          if (final_payload->kind == svn_node_file
1602333347Speter              && !text_equal(current_payload, final_payload))
1603333347Speter            {
1604333347Speter              change->contents_text = final_payload->text;
1605333347Speter            }
1606333347Speter        }
1607333347Speter
1608333347Speter      /* Recurse to process this directory's children */
1609333347Speter      if (final_payload->kind == svn_node_dir)
1610333347Speter        {
1611333347Speter          apr_hash_t *final_children;
1612333347Speter          apr_hash_t *union_children;
1613333347Speter          apr_hash_index_t *hi;
1614333347Speter
1615333347Speter          final_children = get_immediate_children_names(paths_final, rrpath,
1616333347Speter                                                        scratch_pool,
1617333347Speter                                                        scratch_pool);
1618333347Speter          union_children = (current_children
1619333347Speter                            ? hash_overlay(current_children, final_children)
1620333347Speter                            : final_children);
1621333347Speter          for (hi = apr_hash_first(scratch_pool, union_children);
1622333347Speter               hi; hi = apr_hash_next(hi))
1623333347Speter            {
1624333347Speter              const char *name = apr_hash_this_key(hi);
1625333347Speter              const char *this_rrpath = svn_relpath_join(rrpath, name,
1626333347Speter                                                         scratch_pool);
1627333347Speter              svn_boolean_t child_in_current
1628333347Speter                = current_children && svn_hash_gets(current_children, name);
1629333347Speter              svn_pathrev_t *child_pred = NULL;
1630333347Speter
1631333347Speter              if (child_in_current)
1632333347Speter                {
1633333347Speter                 /* If the parent dir is copied, then this child has been
1634333347Speter                    copied along with it: predecessor is parent's copy-from
1635333347Speter                    location extended by the child's name. */
1636333347Speter                  child_pred = apr_palloc(scratch_pool, sizeof(*child_pred));
1637333347Speter                  if (final_copy_from.relpath)
1638333347Speter                    {
1639333347Speter                      child_pred->rev = final_copy_from.rev;
1640333347Speter                      child_pred->relpath
1641333347Speter                        = svn_relpath_join(final_copy_from.relpath, name,
1642333347Speter                                           scratch_pool);
1643333347Speter                    }
1644333347Speter                  else
1645333347Speter                    {
1646333347Speter                      child_pred->rev = pred_loc->rev;
1647333347Speter                      child_pred->relpath = this_rrpath;
1648333347Speter                    }
1649333347Speter               }
1650333347Speter
1651333347Speter              SVN_ERR(drive_changes_r(this_rrpath,
1652333347Speter                                      child_pred,
1653333347Speter                                      paths_final, top_branch_id,
1654333347Speter                                      eb, scratch_pool));
1655333347Speter            }
1656333347Speter        }
1657333347Speter    }
1658333347Speter
1659333347Speter  return SVN_NO_ERROR;
1660333347Speter}
1661333347Speter
1662333347Speter/*
1663333347Speter * Drive svn_delta_editor_t (actions: add/copy/delete/modify) from
1664333347Speter * a before-and-after element mapping.
1665333347Speter */
1666333347Speterstatic svn_error_t *
1667333347Speterdrive_changes(svn_branch__txn_priv_t *eb,
1668333347Speter              apr_pool_t *scratch_pool)
1669333347Speter{
1670333347Speter  apr_array_header_t *branches;
1671333347Speter  int i;
1672333347Speter  const apr_array_header_t *paths;
1673333347Speter
1674333347Speter  /* Convert the element mappings to an svn_delta_editor_t traversal.
1675333347Speter
1676333347Speter        1. find union of paths in initial and final states, across all branches.
1677333347Speter        2. traverse paths in depth-first order.
1678333347Speter        3. modify/delete/add/replace as needed at each path.
1679333347Speter   */
1680333347Speter
1681333347Speter  /* Process one hierarchy of nested branches at a time. */
1682333347Speter  branches = svn_branch__txn_get_branches(eb->txn, scratch_pool);
1683333347Speter  for (i = 0; i < branches->nelts; i++)
1684333347Speter    {
1685333347Speter      svn_branch__state_t *root_branch = APR_ARRAY_IDX(branches, i, void *);
1686333347Speter      apr_hash_t *paths_final;
1687333347Speter
1688333347Speter      const char *top_path = branch_get_storage_root_rrpath(root_branch,
1689333347Speter                                                            scratch_pool);
1690333347Speter      svn_pathrev_t current;
1691333347Speter      svn_branch__state_t *base_root_branch;
1692333347Speter      svn_boolean_t branch_is_new;
1693333347Speter
1694333347Speter      if (strchr(root_branch->bid, '.'))
1695333347Speter        continue;  /* that's not a root branch */
1696333347Speter
1697333347Speter      SVN_ERR(svn_branch__repos_get_branch_by_id(&base_root_branch,
1698333347Speter                                                 eb->txn->repos,
1699333347Speter                                                 eb->txn->base_rev,
1700333347Speter                                                 root_branch->bid, scratch_pool));
1701333347Speter      branch_is_new = !base_root_branch;
1702333347Speter
1703333347Speter      paths_final = apr_hash_make(scratch_pool);
1704333347Speter      SVN_ERR(convert_branch_to_paths_r(paths_final,
1705333347Speter                                        root_branch,
1706333347Speter                                        scratch_pool, scratch_pool));
1707333347Speter
1708333347Speter      current.rev = eb->txn->base_rev;
1709333347Speter      current.relpath = top_path;
1710333347Speter
1711333347Speter      /* Create the top-level storage node if the branch is new, or if this is
1712333347Speter         the first commit to branch B0 which was created in r0 but had no
1713333347Speter         storage node there. */
1714333347Speter      if (branch_is_new || current.rev == 0)
1715333347Speter        {
1716333347Speter          change_node_t *change;
1717333347Speter
1718333347Speter          SVN_ERR(insert_change(&change, eb->changes, top_path, RESTRUCTURE_ADD));
1719333347Speter          change->kind = svn_node_dir;
1720333347Speter        }
1721333347Speter
1722333347Speter      SVN_ERR(drive_changes_r(top_path, &current,
1723333347Speter                              paths_final, svn_branch__get_id(root_branch,
1724333347Speter                                                              scratch_pool),
1725333347Speter                              eb, scratch_pool));
1726333347Speter    }
1727333347Speter
1728333347Speter  /* If the driver has not explicitly opened the root directory via the
1729333347Speter     start_edit (aka open_root) callback, do so now. */
1730333347Speter  if (eb->ev1_root_dir_baton == NULL)
1731333347Speter    SVN_ERR(open_root_ev3(eb, SVN_INVALID_REVNUM));
1732333347Speter
1733333347Speter  /* Make the path driver visit the root dir of the edit. Otherwise, it
1734333347Speter     will attempt an open_root() instead, which we already did. */
1735333347Speter  if (! svn_hash_gets(eb->changes, ""))
1736333347Speter    {
1737333347Speter      change_node_t *change;
1738333347Speter
1739333347Speter      SVN_ERR(insert_change(&change, eb->changes, "", RESTRUCTURE_NONE));
1740333347Speter      change->kind = svn_node_dir;
1741333347Speter    }
1742333347Speter
1743333347Speter  /* Apply the appropriate Ev1 change to each Ev1-relative path. */
1744333347Speter  paths = get_unsorted_paths(eb->changes, scratch_pool);
1745362181Sdim  SVN_ERR(svn_delta_path_driver3(eb->deditor, eb->dedit_baton,
1746333347Speter                                 paths, TRUE /*sort*/,
1747333347Speter                                 apply_change, (void *)eb,
1748333347Speter                                 scratch_pool));
1749333347Speter
1750333347Speter  return SVN_NO_ERROR;
1751333347Speter}
1752333347Speter
1753333347Speter/* An #svn_branch__txn_t method. */
1754333347Speterstatic apr_array_header_t *
1755333347Spetercompat_branch_txn_get_branches(const svn_branch__txn_t *txn,
1756333347Speter                               apr_pool_t *result_pool)
1757333347Speter{
1758333347Speter  /* Just forwarding: nothing more is needed. */
1759333347Speter  apr_array_header_t *branches
1760333347Speter    = svn_branch__txn_get_branches(txn->priv->txn,
1761333347Speter                                   result_pool);
1762333347Speter
1763333347Speter  return branches;
1764333347Speter}
1765333347Speter
1766333347Speter/* An #svn_branch__txn_t method. */
1767333347Speterstatic svn_error_t *
1768333347Spetercompat_branch_txn_delete_branch(svn_branch__txn_t *txn,
1769333347Speter                                const char *bid,
1770333347Speter                                apr_pool_t *scratch_pool)
1771333347Speter{
1772333347Speter  /* Just forwarding: nothing more is needed. */
1773333347Speter  SVN_ERR(svn_branch__txn_delete_branch(txn->priv->txn,
1774333347Speter                                        bid,
1775333347Speter                                        scratch_pool));
1776333347Speter  return SVN_NO_ERROR;
1777333347Speter}
1778333347Speter
1779333347Speter/* An #svn_branch__txn_t method. */
1780333347Speterstatic svn_error_t *
1781333347Spetercompat_branch_txn_get_num_new_eids(const svn_branch__txn_t *txn,
1782333347Speter                                   int *num_new_eids_p,
1783333347Speter                                   apr_pool_t *scratch_pool)
1784333347Speter{
1785333347Speter  /* Just forwarding: nothing more is needed. */
1786333347Speter  SVN_ERR(svn_branch__txn_get_num_new_eids(txn->priv->txn,
1787333347Speter                                           num_new_eids_p,
1788333347Speter                                           scratch_pool));
1789333347Speter  return SVN_NO_ERROR;
1790333347Speter}
1791333347Speter
1792333347Speter/* An #svn_branch__txn_t method. */
1793333347Speterstatic svn_error_t *
1794333347Spetercompat_branch_txn_new_eid(svn_branch__txn_t *txn,
1795333347Speter                          svn_branch__eid_t *eid_p,
1796333347Speter                          apr_pool_t *scratch_pool)
1797333347Speter{
1798333347Speter  /* Just forwarding: nothing more is needed. */
1799333347Speter  SVN_ERR(svn_branch__txn_new_eid(txn->priv->txn,
1800333347Speter                                  eid_p,
1801333347Speter                                  scratch_pool));
1802333347Speter  return SVN_NO_ERROR;
1803333347Speter}
1804333347Speter
1805333347Speter/* An #svn_branch__txn_t method. */
1806333347Speterstatic svn_error_t *
1807333347Spetercompat_branch_txn_finalize_eids(svn_branch__txn_t *txn,
1808333347Speter                                apr_pool_t *scratch_pool)
1809333347Speter{
1810333347Speter  /* Just forwarding: nothing more is needed. */
1811333347Speter  SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->txn,
1812333347Speter                                        scratch_pool));
1813333347Speter  return SVN_NO_ERROR;
1814333347Speter}
1815333347Speter
1816333347Speter/* An #svn_branch__txn_t method. */
1817333347Speterstatic svn_error_t *
1818333347Spetercompat_branch_txn_open_branch(svn_branch__txn_t *txn,
1819333347Speter                              svn_branch__state_t **new_branch_p,
1820333347Speter                              const char *new_branch_id,
1821333347Speter                              int root_eid,
1822333347Speter                              svn_branch__rev_bid_eid_t *tree_ref,
1823333347Speter                              apr_pool_t *result_pool,
1824333347Speter                              apr_pool_t *scratch_pool)
1825333347Speter{
1826333347Speter  /* Just forwarding: nothing more is needed. */
1827333347Speter  SVN_ERR(svn_branch__txn_open_branch(txn->priv->txn,
1828333347Speter                                      new_branch_p,
1829333347Speter                                      new_branch_id, root_eid, tree_ref,
1830333347Speter                                      result_pool,
1831333347Speter                                      scratch_pool));
1832333347Speter  return SVN_NO_ERROR;
1833333347Speter}
1834333347Speter
1835333347Speter/* An #svn_branch__txn_t method. */
1836333347Speterstatic svn_error_t *
1837333347Spetercompat_branch_txn_serialize(svn_branch__txn_t *txn,
1838333347Speter                            svn_stream_t *stream,
1839333347Speter                            apr_pool_t *scratch_pool)
1840333347Speter{
1841333347Speter  /* Just forwarding: nothing more is needed. */
1842333347Speter  SVN_ERR(svn_branch__txn_serialize(txn->priv->txn,
1843333347Speter                                    stream,
1844333347Speter                                    scratch_pool));
1845333347Speter  return SVN_NO_ERROR;
1846333347Speter}
1847333347Speter
1848333347Speter/* An #svn_branch__txn_t method. */
1849333347Speterstatic svn_error_t *
1850333347Spetercompat_branch_txn_sequence_point(svn_branch__txn_t *txn,
1851333347Speter                                 apr_pool_t *scratch_pool)
1852333347Speter{
1853333347Speter  /* Just forwarding: nothing more is needed. */
1854333347Speter  SVN_ERR(svn_branch__txn_sequence_point(txn->priv->txn,
1855333347Speter                                         scratch_pool));
1856333347Speter  return SVN_NO_ERROR;
1857333347Speter}
1858333347Speter
1859333347Speter/* An #svn_branch__txn_t method. */
1860333347Speterstatic svn_error_t *
1861333347Spetercompat_branch_txn_complete(svn_branch__txn_t *txn,
1862333347Speter                           apr_pool_t *scratch_pool)
1863333347Speter{
1864333347Speter  svn_branch__txn_priv_t *eb = txn->priv;
1865333347Speter  svn_error_t *err;
1866333347Speter
1867333347Speter  /* Convert the transaction to a revision */
1868333347Speter  SVN_ERR(svn_branch__txn_sequence_point(txn->priv->txn, scratch_pool));
1869333347Speter  SVN_ERR(svn_branch__txn_finalize_eids(txn->priv->txn, scratch_pool));
1870333347Speter
1871333347Speter  err = drive_changes(eb, scratch_pool);
1872333347Speter
1873333347Speter  if (!err)
1874333347Speter     {
1875333347Speter       err = svn_error_compose_create(err, eb->deditor->close_edit(
1876333347Speter                                                            eb->dedit_baton,
1877333347Speter                                                            scratch_pool));
1878333347Speter     }
1879333347Speter
1880333347Speter  if (err)
1881333347Speter    svn_error_clear(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
1882333347Speter
1883333347Speter  SVN_ERR(svn_branch__txn_complete(txn->priv->txn, scratch_pool));
1884333347Speter
1885333347Speter  return err;
1886333347Speter}
1887333347Speter
1888333347Speter/* An #svn_branch__txn_t method. */
1889333347Speterstatic svn_error_t *
1890333347Spetercompat_branch_txn_abort(svn_branch__txn_t *txn,
1891333347Speter                        apr_pool_t *scratch_pool)
1892333347Speter{
1893333347Speter  svn_branch__txn_priv_t *eb = txn->priv;
1894333347Speter
1895333347Speter  SVN_ERR(eb->deditor->abort_edit(eb->dedit_baton, scratch_pool));
1896333347Speter
1897333347Speter  SVN_ERR(svn_branch__txn_abort(txn->priv->txn,
1898333347Speter                                scratch_pool));
1899333347Speter  return SVN_NO_ERROR;
1900333347Speter}
1901333347Speter
1902333347Speter/* Baton for wrap_fetch_func. */
1903333347Spetertypedef struct wrap_fetch_baton_t
1904333347Speter{
1905333347Speter  /* Wrapped fetcher */
1906333347Speter  svn_branch__compat_fetch_func_t fetch_func;
1907333347Speter  void *fetch_baton;
1908333347Speter} wrap_fetch_baton_t;
1909333347Speter
1910333347Speter/* The purpose of this fetcher-wrapper is to make it appear that B0
1911333347Speter * was created (as an empty dir) in r0.
1912333347Speter */
1913333347Speterstatic svn_error_t *
1914333347Speterwrap_fetch_func(svn_node_kind_t *kind,
1915333347Speter                apr_hash_t **props,
1916333347Speter                svn_stringbuf_t **file_text,
1917333347Speter                apr_hash_t **children_names,
1918333347Speter                void *baton,
1919333347Speter                const char *repos_relpath,
1920333347Speter                svn_revnum_t revision,
1921333347Speter                apr_pool_t *result_pool,
1922333347Speter                apr_pool_t *scratch_pool)
1923333347Speter{
1924333347Speter  wrap_fetch_baton_t *b = baton;
1925333347Speter
1926333347Speter  if (revision == 0 && strcmp(repos_relpath, "top0") == 0)
1927333347Speter    {
1928333347Speter      if (kind)
1929333347Speter        *kind = svn_node_dir;
1930333347Speter      if (props)
1931333347Speter        *props = apr_hash_make(result_pool);
1932333347Speter      if (file_text)
1933333347Speter        *file_text = NULL;
1934333347Speter      if (children_names)
1935333347Speter        *children_names = apr_hash_make(result_pool);
1936333347Speter    }
1937333347Speter  else
1938333347Speter    {
1939333347Speter      SVN_ERR(b->fetch_func(kind, props, file_text, children_names,
1940333347Speter                            b->fetch_baton,
1941333347Speter                            repos_relpath, revision,
1942333347Speter                            result_pool, scratch_pool));
1943333347Speter    }
1944333347Speter
1945333347Speter  return SVN_NO_ERROR;
1946333347Speter}
1947333347Speter
1948333347Spetersvn_error_t *
1949333347Spetersvn_branch__compat_txn_from_delta_for_commit(
1950333347Speter                        svn_branch__txn_t **txn_p,
1951333347Speter                        svn_branch__compat_shim_connector_t **shim_connector,
1952333347Speter                        const svn_delta_editor_t *deditor,
1953333347Speter                        void *dedit_baton,
1954333347Speter                        svn_branch__txn_t *branching_txn,
1955333347Speter                        const char *repos_root_url,
1956333347Speter                        svn_branch__compat_fetch_func_t fetch_func,
1957333347Speter                        void *fetch_baton,
1958333347Speter                        svn_cancel_func_t cancel_func,
1959333347Speter                        void *cancel_baton,
1960333347Speter                        apr_pool_t *result_pool,
1961333347Speter                        apr_pool_t *scratch_pool)
1962333347Speter{
1963333347Speter  static const svn_branch__txn_vtable_t vtable = {
1964333347Speter    {0},
1965333347Speter    compat_branch_txn_get_branches,
1966333347Speter    compat_branch_txn_delete_branch,
1967333347Speter    compat_branch_txn_get_num_new_eids,
1968333347Speter    compat_branch_txn_new_eid,
1969333347Speter    compat_branch_txn_open_branch,
1970333347Speter    compat_branch_txn_finalize_eids,
1971333347Speter    compat_branch_txn_serialize,
1972333347Speter    compat_branch_txn_sequence_point,
1973333347Speter    compat_branch_txn_complete,
1974333347Speter    compat_branch_txn_abort
1975333347Speter  };
1976333347Speter  svn_branch__txn_t *txn;
1977333347Speter  svn_branch__txn_priv_t *eb = apr_pcalloc(result_pool, sizeof(*eb));
1978333347Speter  wrap_fetch_baton_t *wb = apr_pcalloc(result_pool, sizeof(*wb));
1979333347Speter
1980333347Speter  eb->deditor = deditor;
1981333347Speter  eb->dedit_baton = dedit_baton;
1982333347Speter
1983333347Speter  eb->repos_root_url = apr_pstrdup(result_pool, repos_root_url);
1984333347Speter
1985333347Speter  eb->changes = apr_hash_make(result_pool);
1986333347Speter
1987333347Speter  wb->fetch_func = fetch_func;
1988333347Speter  wb->fetch_baton = fetch_baton;
1989333347Speter  eb->fetch_func = wrap_fetch_func;
1990333347Speter  eb->fetch_baton = wb;
1991333347Speter
1992333347Speter  eb->edit_pool = result_pool;
1993333347Speter
1994333347Speter  branching_txn = svn_branch__nested_txn_create(branching_txn, result_pool);
1995333347Speter
1996333347Speter  eb->txn = branching_txn;
1997333347Speter
1998333347Speter  txn = svn_branch__txn_create(&vtable, NULL, NULL, result_pool);
1999333347Speter  txn->priv = eb;
2000333347Speter  txn->repos = branching_txn->repos;
2001333347Speter  txn->rev = branching_txn->rev;
2002333347Speter  txn->base_rev = branching_txn->base_rev;
2003333347Speter  *txn_p = txn;
2004333347Speter
2005333347Speter  if (shim_connector)
2006333347Speter    {
2007333347Speter#if 0
2008333347Speter      *shim_connector = apr_palloc(result_pool, sizeof(**shim_connector));
2009333347Speter#ifdef SHIM_WITH_ABS_PATHS
2010333347Speter      (*shim_connector)->ev1_absolute_paths
2011333347Speter        = apr_palloc(result_pool, sizeof(svn_boolean_t));
2012333347Speter      eb->make_abs_paths = (*shim_connector)->ev1_absolute_paths;
2013333347Speter#endif
2014333347Speter      (*shim_connector)->target_revision_func = set_target_revision_ev3;
2015333347Speter      (*shim_connector)->start_edit_func = open_root_ev3;
2016333347Speter#ifdef SHIM_WITH_UNLOCK
2017333347Speter      (*shim_connector)->unlock_func = do_unlock;
2018333347Speter#endif
2019333347Speter      (*shim_connector)->baton = eb;
2020333347Speter#endif
2021333347Speter    }
2022333347Speter
2023333347Speter  return SVN_NO_ERROR;
2024333347Speter}
2025333347Speter
2026333347Spetersvn_error_t *
2027333347Spetersvn_branch__compat_txn_from_delta_for_update(
2028333347Speter                        svn_branch__compat_update_editor3_t **update_editor_p,
2029333347Speter                        const svn_delta_editor_t *deditor,
2030333347Speter                        void *dedit_baton,
2031333347Speter                        svn_branch__txn_t *branching_txn,
2032333347Speter                        const char *repos_root_url,
2033333347Speter                        const char *base_repos_relpath,
2034333347Speter                        svn_branch__compat_fetch_func_t fetch_func,
2035333347Speter                        void *fetch_baton,
2036333347Speter                        svn_cancel_func_t cancel_func,
2037333347Speter                        void *cancel_baton,
2038333347Speter                        apr_pool_t *result_pool,
2039333347Speter                        apr_pool_t *scratch_pool)
2040333347Speter{
2041333347Speter  svn_branch__compat_update_editor3_t *update_editor
2042333347Speter    = apr_pcalloc(result_pool, sizeof(*update_editor));
2043333347Speter  svn_branch__compat_shim_connector_t *shim_connector;
2044333347Speter
2045333347Speter  /*(("svn_delta__ev3_from_delta_for_update(base='%s')...",
2046333347Speter           base_repos_relpath));*/
2047333347Speter
2048333347Speter  /*SVN_ERR(svn_delta__get_debug_editor(&deditor, &dedit_baton,
2049333347Speter                                      deditor, dedit_baton,
2050333347Speter                                      "[1>UP] ", result_pool));*/
2051333347Speter  SVN_ERR(svn_branch__compat_txn_from_delta_for_commit(
2052333347Speter                        &update_editor->edit_txn,
2053333347Speter                        &shim_connector,
2054333347Speter                        deditor, dedit_baton,
2055333347Speter                        branching_txn, repos_root_url,
2056333347Speter                        fetch_func, fetch_baton,
2057333347Speter                        cancel_func, cancel_baton,
2058333347Speter                        result_pool, scratch_pool));
2059333347Speter
2060333347Speter  update_editor->set_target_revision_func = shim_connector->target_revision_func;
2061333347Speter  update_editor->set_target_revision_baton = shim_connector->baton;
2062333347Speter  /* shim_connector->start_edit_func = open_root_ev3; */
2063333347Speter#ifdef SHIM_WITH_ABS_PATHS
2064333347Speter  update_editor->ev1_absolute_paths /*...*/;
2065333347Speter#endif
2066333347Speter#ifdef SHIM_WITH_UNLOCK
2067333347Speter  update_editor->unlock_func = do_unlock;
2068333347Speter#endif
2069333347Speter
2070333347Speter  *update_editor_p = update_editor;
2071333347Speter  return SVN_NO_ERROR;
2072333347Speter}
2073