sync.c revision 299742
1/*
2 * ====================================================================
3 *    Licensed to the Apache Software Foundation (ASF) under one
4 *    or more contributor license agreements.  See the NOTICE file
5 *    distributed with this work for additional information
6 *    regarding copyright ownership.  The ASF licenses this file
7 *    to you under the Apache License, Version 2.0 (the
8 *    "License"); you may not use this file except in compliance
9 *    with the License.  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *    Unless required by applicable law or agreed to in writing,
14 *    software distributed under the License is distributed on an
15 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 *    KIND, either express or implied.  See the License for the
17 *    specific language governing permissions and limitations
18 *    under the License.
19 * ====================================================================
20 */
21
22#include "svn_hash.h"
23#include "svn_cmdline.h"
24#include "svn_config.h"
25#include "svn_pools.h"
26#include "svn_delta.h"
27#include "svn_dirent_uri.h"
28#include "svn_path.h"
29#include "svn_props.h"
30#include "svn_auth.h"
31#include "svn_opt.h"
32#include "svn_ra.h"
33#include "svn_utf.h"
34#include "svn_subst.h"
35#include "svn_string.h"
36
37#include "private/svn_string_private.h"
38
39#include "sync.h"
40
41#include "svn_private_config.h"
42
43#include <apr_network_io.h>
44#include <apr_signal.h>
45#include <apr_uuid.h>
46
47
48/* Normalize the encoding and line ending style of *STR, so that it contains
49 * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may
50 * point at a new svn_string_t* allocated in RESULT_POOL.
51 *
52 * If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in
53 * UTF-8.
54 *
55 * *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization.
56 * Otherwise it is set to FALSE.
57 *
58 * SCRATCH_POOL is used for temporary allocations.
59 */
60static svn_error_t *
61normalize_string(const svn_string_t **str,
62                 svn_boolean_t *was_normalized,
63                 const char *source_prop_encoding,
64                 apr_pool_t *result_pool,
65                 apr_pool_t *scratch_pool)
66{
67  svn_string_t *new_str;
68
69  *was_normalized = FALSE;
70
71  if (*str == NULL)
72    return SVN_NO_ERROR;
73
74  SVN_ERR_ASSERT((*str)->data != NULL);
75
76  if (source_prop_encoding == NULL)
77    source_prop_encoding = "UTF-8";
78
79  new_str = NULL;
80  SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized,
81                                      *str, source_prop_encoding, TRUE,
82                                      result_pool, scratch_pool));
83  *str = new_str;
84
85  return SVN_NO_ERROR;
86}
87
88/* Remove r0 references from the mergeinfo string *STR.
89 *
90 * r0 was never a valid mergeinfo reference and cannot be committed with
91 * recent servers, but can be committed through a server older than 1.6.18
92 * for HTTP or older than 1.6.17 for the other protocols. See issue #4476
93 * "Mergeinfo containing r0 makes svnsync and dump and load fail".
94 *
95 * Set *WAS_CHANGED to TRUE if *STR was changed, otherwise to FALSE.
96 */
97static svn_error_t *
98remove_r0_mergeinfo(const svn_string_t **str,
99                    svn_boolean_t *was_changed,
100                    apr_pool_t *result_pool,
101                    apr_pool_t *scratch_pool)
102{
103  svn_stringbuf_t *new_str = svn_stringbuf_create_empty(result_pool);
104  apr_array_header_t *lines;
105  int i;
106
107  SVN_ERR_ASSERT(*str && (*str)->data);
108
109  *was_changed = FALSE;
110
111  /* for each line */
112  lines = svn_cstring_split((*str)->data, "\n", FALSE, scratch_pool);
113
114  for (i = 0; i < lines->nelts; i++)
115    {
116      char *line = APR_ARRAY_IDX(lines, i, char *);
117      char *colon;
118      char *rangelist;
119
120      /* split at the last colon */
121      colon = strrchr(line, ':');
122
123      if (! colon)
124        return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
125                                 _("Missing colon in svn:mergeinfo "
126                                   "property"));
127
128      rangelist = colon + 1;
129
130      /* remove r0 */
131      if (colon[1] == '0')
132        {
133          if (strncmp(rangelist, "0*,", 3) == 0)
134            {
135              rangelist += 3;
136            }
137          else if (strcmp(rangelist, "0*") == 0
138                   || strncmp(rangelist, "0,", 2) == 0
139                   || strncmp(rangelist, "0-1*", 4) == 0
140                   || strncmp(rangelist, "0-1,", 4) == 0
141                   || strcmp(rangelist, "0-1") == 0)
142            {
143              rangelist += 2;
144            }
145          else if (strcmp(rangelist, "0") == 0)
146            {
147              rangelist += 1;
148            }
149          else if (strncmp(rangelist, "0-", 2) == 0)
150            {
151              rangelist[0] = '1';
152            }
153        }
154
155      /* reassemble */
156      if (rangelist[0])
157        {
158          if (new_str->len)
159            svn_stringbuf_appendbyte(new_str, '\n');
160          svn_stringbuf_appendbytes(new_str, line, colon + 1 - line);
161          svn_stringbuf_appendcstr(new_str, rangelist);
162        }
163    }
164
165  if (strcmp((*str)->data, new_str->data) != 0)
166    {
167      *was_changed = TRUE;
168    }
169
170  *str = svn_stringbuf__morph_into_string(new_str);
171  return SVN_NO_ERROR;
172}
173
174
175/* Normalize the encoding and line ending style of the values of properties
176 * in REV_PROPS that "need translation" (according to
177 * svn_prop_needs_translation(), which is currently all svn:* props) so that
178 * they are encoded in UTF-8 and contain only LF (\n) line endings.
179 *
180 * The number of properties that needed line ending normalization is returned in
181 * *NORMALIZED_COUNT.
182 *
183 * No re-encoding is performed if SOURCE_PROP_ENCODING is NULL.
184 */
185svn_error_t *
186svnsync_normalize_revprops(apr_hash_t *rev_props,
187                           int *normalized_count,
188                           const char *source_prop_encoding,
189                           apr_pool_t *pool)
190{
191  apr_hash_index_t *hi;
192  *normalized_count = 0;
193
194  for (hi = apr_hash_first(pool, rev_props);
195       hi;
196       hi = apr_hash_next(hi))
197    {
198      const char *propname = apr_hash_this_key(hi);
199      const svn_string_t *propval = apr_hash_this_val(hi);
200
201      if (svn_prop_needs_translation(propname))
202        {
203          svn_boolean_t was_normalized;
204          SVN_ERR(normalize_string(&propval, &was_normalized,
205                  source_prop_encoding, pool, pool));
206
207          /* Replace the existing prop value. */
208          svn_hash_sets(rev_props, propname, propval);
209
210          if (was_normalized)
211            (*normalized_count)++; /* Count it. */
212        }
213    }
214  return SVN_NO_ERROR;
215}
216
217
218/*** Synchronization Editor ***/
219
220/* This editor has a couple of jobs.
221 *
222 * First, it needs to filter out the propchanges that can't be passed over
223 * libsvn_ra.
224 *
225 * Second, it needs to adjust for the fact that we might not actually have
226 * permission to see all of the data from the remote repository, which means
227 * we could get revisions that are totally empty from our point of view.
228 *
229 * Third, it needs to adjust copyfrom paths, adding the root url for the
230 * destination repository to the beginning of them.
231 */
232
233
234/* Edit baton */
235typedef struct edit_baton_t {
236  const svn_delta_editor_t *wrapped_editor;
237  void *wrapped_edit_baton;
238  const char *to_url;  /* URL we're copying into, for correct copyfrom URLs */
239  const char *source_prop_encoding;
240  svn_boolean_t called_open_root;
241  svn_boolean_t got_textdeltas;
242  svn_revnum_t base_revision;
243  svn_boolean_t quiet;
244  svn_boolean_t mergeinfo_tweaked;  /* Did we tweak svn:mergeinfo? */
245  svn_boolean_t strip_mergeinfo;    /* Are we stripping svn:mergeinfo? */
246  svn_boolean_t migrate_svnmerge;   /* Are we converting svnmerge.py data? */
247  svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */
248  svn_boolean_t svnmerge_migrated;  /* Did we convert svnmerge.py data? */
249  svn_boolean_t svnmerge_blocked;   /* Was there any blocked svnmerge data? */
250  int *normalized_node_props_counter;  /* Where to count normalizations? */
251} edit_baton_t;
252
253
254/* A dual-purpose baton for files and directories. */
255typedef struct node_baton_t {
256  void *edit_baton;
257  void *wrapped_node_baton;
258} node_baton_t;
259
260
261/*** Editor vtable functions ***/
262
263static svn_error_t *
264set_target_revision(void *edit_baton,
265                    svn_revnum_t target_revision,
266                    apr_pool_t *pool)
267{
268  edit_baton_t *eb = edit_baton;
269  return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
270                                                 target_revision, pool);
271}
272
273static svn_error_t *
274open_root(void *edit_baton,
275          svn_revnum_t base_revision,
276          apr_pool_t *pool,
277          void **root_baton)
278{
279  edit_baton_t *eb = edit_baton;
280  node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton));
281
282  SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
283                                        base_revision, pool,
284                                        &dir_baton->wrapped_node_baton));
285
286  eb->called_open_root = TRUE;
287  dir_baton->edit_baton = edit_baton;
288  *root_baton = dir_baton;
289
290  return SVN_NO_ERROR;
291}
292
293static svn_error_t *
294delete_entry(const char *path,
295             svn_revnum_t base_revision,
296             void *parent_baton,
297             apr_pool_t *pool)
298{
299  node_baton_t *pb = parent_baton;
300  edit_baton_t *eb = pb->edit_baton;
301
302  return eb->wrapped_editor->delete_entry(path, base_revision,
303                                          pb->wrapped_node_baton, pool);
304}
305
306static svn_error_t *
307add_directory(const char *path,
308              void *parent_baton,
309              const char *copyfrom_path,
310              svn_revnum_t copyfrom_rev,
311              apr_pool_t *pool,
312              void **child_baton)
313{
314  node_baton_t *pb = parent_baton;
315  edit_baton_t *eb = pb->edit_baton;
316  node_baton_t *b = apr_palloc(pool, sizeof(*b));
317
318  /* if copyfrom_path is an fspath create a proper uri */
319  if (copyfrom_path && copyfrom_path[0] == '/')
320    copyfrom_path = svn_path_url_add_component2(eb->to_url,
321                                                copyfrom_path + 1, pool);
322
323  SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton,
324                                            copyfrom_path,
325                                            copyfrom_rev, pool,
326                                            &b->wrapped_node_baton));
327
328  b->edit_baton = eb;
329  *child_baton = b;
330
331  return SVN_NO_ERROR;
332}
333
334static svn_error_t *
335open_directory(const char *path,
336               void *parent_baton,
337               svn_revnum_t base_revision,
338               apr_pool_t *pool,
339               void **child_baton)
340{
341  node_baton_t *pb = parent_baton;
342  edit_baton_t *eb = pb->edit_baton;
343  node_baton_t *db = apr_palloc(pool, sizeof(*db));
344
345  SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton,
346                                             base_revision, pool,
347                                             &db->wrapped_node_baton));
348
349  db->edit_baton = eb;
350  *child_baton = db;
351
352  return SVN_NO_ERROR;
353}
354
355static svn_error_t *
356add_file(const char *path,
357         void *parent_baton,
358         const char *copyfrom_path,
359         svn_revnum_t copyfrom_rev,
360         apr_pool_t *pool,
361         void **file_baton)
362{
363  node_baton_t *pb = parent_baton;
364  edit_baton_t *eb = pb->edit_baton;
365  node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
366
367  /* if copyfrom_path is an fspath create a proper uri */
368  if (copyfrom_path && copyfrom_path[0] == '/')
369    copyfrom_path = svn_path_url_add_component2(eb->to_url,
370                                                copyfrom_path + 1, pool);
371
372  SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton,
373                                       copyfrom_path, copyfrom_rev,
374                                       pool, &fb->wrapped_node_baton));
375
376  fb->edit_baton = eb;
377  *file_baton = fb;
378
379  return SVN_NO_ERROR;
380}
381
382static svn_error_t *
383open_file(const char *path,
384          void *parent_baton,
385          svn_revnum_t base_revision,
386          apr_pool_t *pool,
387          void **file_baton)
388{
389  node_baton_t *pb = parent_baton;
390  edit_baton_t *eb = pb->edit_baton;
391  node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
392
393  SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton,
394                                        base_revision, pool,
395                                        &fb->wrapped_node_baton));
396
397  fb->edit_baton = eb;
398  *file_baton = fb;
399
400  return SVN_NO_ERROR;
401}
402
403static svn_error_t *
404apply_textdelta(void *file_baton,
405                const char *base_checksum,
406                apr_pool_t *pool,
407                svn_txdelta_window_handler_t *handler,
408                void **handler_baton)
409{
410  node_baton_t *fb = file_baton;
411  edit_baton_t *eb = fb->edit_baton;
412
413  if (! eb->quiet)
414    {
415      if (! eb->got_textdeltas)
416        SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data ")));
417      SVN_ERR(svn_cmdline_printf(pool, "."));
418      SVN_ERR(svn_cmdline_fflush(stdout));
419    }
420
421  eb->got_textdeltas = TRUE;
422  return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton,
423                                             base_checksum, pool,
424                                             handler, handler_baton);
425}
426
427static svn_error_t *
428close_file(void *file_baton,
429           const char *text_checksum,
430           apr_pool_t *pool)
431{
432  node_baton_t *fb = file_baton;
433  edit_baton_t *eb = fb->edit_baton;
434  return eb->wrapped_editor->close_file(fb->wrapped_node_baton,
435                                        text_checksum, pool);
436}
437
438static svn_error_t *
439absent_file(const char *path,
440            void *file_baton,
441            apr_pool_t *pool)
442{
443  node_baton_t *fb = file_baton;
444  edit_baton_t *eb = fb->edit_baton;
445  return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool);
446}
447
448static svn_error_t *
449close_directory(void *dir_baton,
450                apr_pool_t *pool)
451{
452  node_baton_t *db = dir_baton;
453  edit_baton_t *eb = db->edit_baton;
454  return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool);
455}
456
457static svn_error_t *
458absent_directory(const char *path,
459                 void *dir_baton,
460                 apr_pool_t *pool)
461{
462  node_baton_t *db = dir_baton;
463  edit_baton_t *eb = db->edit_baton;
464  return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton,
465                                              pool);
466}
467
468static svn_error_t *
469change_file_prop(void *file_baton,
470                 const char *name,
471                 const svn_string_t *value,
472                 apr_pool_t *pool)
473{
474  node_baton_t *fb = file_baton;
475  edit_baton_t *eb = fb->edit_baton;
476
477  /* only regular properties can pass over libsvn_ra */
478  if (svn_property_kind2(name) != svn_prop_regular_kind)
479    return SVN_NO_ERROR;
480
481  /* Maybe drop svn:mergeinfo.  */
482  if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
483    {
484      eb->mergeinfo_stripped = TRUE;
485      return SVN_NO_ERROR;
486    }
487
488  /* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */
489  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
490    {
491      eb->svnmerge_migrated = TRUE;
492      return SVN_NO_ERROR;
493    }
494
495  /* Remember if we see any svnmerge-blocked properties.  (They really
496     shouldn't be here, as this is a file, but whatever...)  */
497  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
498    {
499      eb->svnmerge_blocked = TRUE;
500    }
501
502  /* Normalize svn:* properties as necessary. */
503  if (svn_prop_needs_translation(name))
504    {
505      svn_boolean_t was_normalized;
506      svn_boolean_t mergeinfo_tweaked = FALSE;
507
508      /* Normalize encoding to UTF-8, and EOL style to LF. */
509      SVN_ERR(normalize_string(&value, &was_normalized,
510                               eb->source_prop_encoding, pool, pool));
511      /* Correct malformed mergeinfo. */
512      if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
513        {
514          SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
515                                      pool, pool));
516          if (mergeinfo_tweaked)
517            eb->mergeinfo_tweaked = TRUE;
518        }
519      if (was_normalized)
520        (*(eb->normalized_node_props_counter))++;
521    }
522
523  return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
524                                              name, value, pool);
525}
526
527static svn_error_t *
528change_dir_prop(void *dir_baton,
529                const char *name,
530                const svn_string_t *value,
531                apr_pool_t *pool)
532{
533  node_baton_t *db = dir_baton;
534  edit_baton_t *eb = db->edit_baton;
535
536  /* Only regular properties can pass over libsvn_ra */
537  if (svn_property_kind2(name) != svn_prop_regular_kind)
538    return SVN_NO_ERROR;
539
540  /* Maybe drop svn:mergeinfo.  */
541  if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
542    {
543      eb->mergeinfo_stripped = TRUE;
544      return SVN_NO_ERROR;
545    }
546
547  /* Maybe convert svnmerge-integrated data into svn:mergeinfo.  (We
548     ignore svnmerge-blocked for now.) */
549  /* ### FIXME: Consult the mirror repository's HEAD prop values and
550     ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */
551  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
552    {
553      if (value)
554        {
555          /* svnmerge-integrated differs from svn:mergeinfo in a pair
556             of ways.  First, it can use tabs, newlines, or spaces to
557             delimit source information.  Secondly, the source paths
558             are relative URLs, whereas svn:mergeinfo uses relative
559             paths (not URI-encoded). */
560          svn_error_t *err;
561          svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool);
562          svn_mergeinfo_t mergeinfo;
563          int i;
564          apr_array_header_t *sources =
565            svn_cstring_split(value->data, " \t\n", TRUE, pool);
566          svn_string_t *new_value;
567
568          for (i = 0; i < sources->nelts; i++)
569            {
570              const char *rel_path;
571              apr_array_header_t *path_revs =
572                svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *),
573                                  ":", TRUE, pool);
574
575              /* ### TODO: Warn? */
576              if (path_revs->nelts != 2)
577                continue;
578
579              /* Append this source's mergeinfo data. */
580              rel_path = APR_ARRAY_IDX(path_revs, 0, const char *);
581              rel_path = svn_path_uri_decode(rel_path, pool);
582              svn_stringbuf_appendcstr(mergeinfo_buf, rel_path);
583              svn_stringbuf_appendcstr(mergeinfo_buf, ":");
584              svn_stringbuf_appendcstr(mergeinfo_buf,
585                                       APR_ARRAY_IDX(path_revs, 1,
586                                                     const char *));
587              svn_stringbuf_appendcstr(mergeinfo_buf, "\n");
588            }
589
590          /* Try to parse the mergeinfo string we've created, just to
591             check for bogosity.  If all goes well, we'll unparse it
592             again and use that as our property value.  */
593          err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool);
594          if (err)
595            {
596              svn_error_clear(err);
597              return SVN_NO_ERROR;
598            }
599          SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool));
600          value = new_value;
601        }
602      name = SVN_PROP_MERGEINFO;
603      eb->svnmerge_migrated = TRUE;
604    }
605
606  /* Remember if we see any svnmerge-blocked properties. */
607  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
608    {
609      eb->svnmerge_blocked = TRUE;
610    }
611
612  /* Normalize svn:* properties as necessary. */
613  if (svn_prop_needs_translation(name))
614    {
615      svn_boolean_t was_normalized;
616      svn_boolean_t mergeinfo_tweaked = FALSE;
617
618      /* Normalize encoding to UTF-8, and EOL style to LF. */
619      SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding,
620                               pool, pool));
621      /* Maybe adjust svn:mergeinfo. */
622      if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
623        {
624          SVN_ERR(remove_r0_mergeinfo(&value, &mergeinfo_tweaked,
625                                      pool, pool));
626          if (mergeinfo_tweaked)
627            eb->mergeinfo_tweaked = TRUE;
628        }
629      if (was_normalized)
630        (*(eb->normalized_node_props_counter))++;
631    }
632
633  return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton,
634                                             name, value, pool);
635}
636
637static svn_error_t *
638close_edit(void *edit_baton,
639           apr_pool_t *pool)
640{
641  edit_baton_t *eb = edit_baton;
642
643  /* If we haven't opened the root yet, that means we're transfering
644     an empty revision, probably because we aren't allowed to see the
645     contents for some reason.  In any event, we need to open the root
646     and close it again, before we can close out the edit, or the
647     commit will fail. */
648
649  if (! eb->called_open_root)
650    {
651      void *baton;
652      SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
653                                            eb->base_revision, pool,
654                                            &baton));
655      SVN_ERR(eb->wrapped_editor->close_directory(baton, pool));
656    }
657
658  if (! eb->quiet)
659    {
660      if (eb->got_textdeltas)
661        SVN_ERR(svn_cmdline_printf(pool, "\n"));
662      if (eb->mergeinfo_tweaked)
663        SVN_ERR(svn_cmdline_printf(pool,
664                                   "NOTE: Adjusted Subversion mergeinfo in "
665                                   "this revision.\n"));
666      if (eb->mergeinfo_stripped)
667        SVN_ERR(svn_cmdline_printf(pool,
668                                   "NOTE: Dropped Subversion mergeinfo "
669                                   "from this revision.\n"));
670      if (eb->svnmerge_migrated)
671        SVN_ERR(svn_cmdline_printf(pool,
672                                   "NOTE: Migrated 'svnmerge-integrated' in "
673                                   "this revision.\n"));
674      if (eb->svnmerge_blocked)
675        SVN_ERR(svn_cmdline_printf(pool,
676                                   "NOTE: Saw 'svnmerge-blocked' in this "
677                                   "revision (but didn't migrate it).\n"));
678    }
679
680  return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
681}
682
683static svn_error_t *
684abort_edit(void *edit_baton,
685           apr_pool_t *pool)
686{
687  edit_baton_t *eb = edit_baton;
688  return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool);
689}
690
691
692/*** Editor factory function ***/
693
694svn_error_t *
695svnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor,
696                        void *wrapped_edit_baton,
697                        svn_revnum_t base_revision,
698                        const char *to_url,
699                        const char *source_prop_encoding,
700                        svn_boolean_t quiet,
701                        const svn_delta_editor_t **editor,
702                        void **edit_baton,
703                        int *normalized_node_props_counter,
704                        apr_pool_t *pool)
705{
706  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
707  edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb));
708
709  tree_editor->set_target_revision = set_target_revision;
710  tree_editor->open_root = open_root;
711  tree_editor->delete_entry = delete_entry;
712  tree_editor->add_directory = add_directory;
713  tree_editor->open_directory = open_directory;
714  tree_editor->change_dir_prop = change_dir_prop;
715  tree_editor->close_directory = close_directory;
716  tree_editor->absent_directory = absent_directory;
717  tree_editor->add_file = add_file;
718  tree_editor->open_file = open_file;
719  tree_editor->apply_textdelta = apply_textdelta;
720  tree_editor->change_file_prop = change_file_prop;
721  tree_editor->close_file = close_file;
722  tree_editor->absent_file = absent_file;
723  tree_editor->close_edit = close_edit;
724  tree_editor->abort_edit = abort_edit;
725
726  eb->wrapped_editor = wrapped_editor;
727  eb->wrapped_edit_baton = wrapped_edit_baton;
728  eb->base_revision = base_revision;
729  eb->to_url = to_url;
730  eb->source_prop_encoding = source_prop_encoding;
731  eb->quiet = quiet;
732  eb->normalized_node_props_counter = normalized_node_props_counter;
733
734  if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO"))
735    {
736      eb->strip_mergeinfo = TRUE;
737    }
738  if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE"))
739    {
740      /* Current we can't merge property values.  That's only possible
741         if all the properties to be merged were always modified in
742         exactly the same revisions, or if we allow ourselves to
743         lookup the current state of properties in the sync
744         destination.  So for now, migrating svnmerge.py data implies
745         stripping pre-existing svn:mergeinfo. */
746      /* ### FIXME: Do a real migration by consulting the mirror
747         ### repository's HEAD propvalues and merging svn:mergeinfo,
748         ### svnmerge-integrated, and svnmerge-blocked together. */
749      eb->migrate_svnmerge = TRUE;
750      eb->strip_mergeinfo = TRUE;
751    }
752
753  *editor = tree_editor;
754  *edit_baton = eb;
755
756  return SVN_NO_ERROR;
757}
758
759