1251881Speter/*
2251881Speter * ====================================================================
3251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
4251881Speter *    or more contributor license agreements.  See the NOTICE file
5251881Speter *    distributed with this work for additional information
6251881Speter *    regarding copyright ownership.  The ASF licenses this file
7251881Speter *    to you under the Apache License, Version 2.0 (the
8251881Speter *    "License"); you may not use this file except in compliance
9251881Speter *    with the License.  You may obtain a copy of the License at
10251881Speter *
11251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
12251881Speter *
13251881Speter *    Unless required by applicable law or agreed to in writing,
14251881Speter *    software distributed under the License is distributed on an
15251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16251881Speter *    KIND, either express or implied.  See the License for the
17251881Speter *    specific language governing permissions and limitations
18251881Speter *    under the License.
19251881Speter * ====================================================================
20251881Speter */
21251881Speter
22251881Speter#include "svn_hash.h"
23251881Speter#include "svn_cmdline.h"
24251881Speter#include "svn_config.h"
25251881Speter#include "svn_pools.h"
26251881Speter#include "svn_delta.h"
27251881Speter#include "svn_dirent_uri.h"
28251881Speter#include "svn_path.h"
29251881Speter#include "svn_props.h"
30251881Speter#include "svn_auth.h"
31251881Speter#include "svn_opt.h"
32251881Speter#include "svn_ra.h"
33251881Speter#include "svn_utf.h"
34251881Speter#include "svn_subst.h"
35251881Speter#include "svn_string.h"
36251881Speter
37251881Speter#include "sync.h"
38251881Speter
39251881Speter#include "svn_private_config.h"
40251881Speter
41251881Speter#include <apr_network_io.h>
42251881Speter#include <apr_signal.h>
43251881Speter#include <apr_uuid.h>
44251881Speter
45251881Speter
46251881Speter/* Normalize the encoding and line ending style of *STR, so that it contains
47251881Speter * only LF (\n) line endings and is encoded in UTF-8. After return, *STR may
48251881Speter * point at a new svn_string_t* allocated in RESULT_POOL.
49251881Speter *
50251881Speter * If SOURCE_PROP_ENCODING is NULL, then *STR is presumed to be encoded in
51251881Speter * UTF-8.
52251881Speter *
53251881Speter * *WAS_NORMALIZED is set to TRUE when *STR needed line ending normalization.
54251881Speter * Otherwise it is set to FALSE.
55251881Speter *
56251881Speter * SCRATCH_POOL is used for temporary allocations.
57251881Speter */
58251881Speterstatic svn_error_t *
59251881Speternormalize_string(const svn_string_t **str,
60251881Speter                 svn_boolean_t *was_normalized,
61251881Speter                 const char *source_prop_encoding,
62251881Speter                 apr_pool_t *result_pool,
63251881Speter                 apr_pool_t *scratch_pool)
64251881Speter{
65251881Speter  svn_string_t *new_str;
66251881Speter
67251881Speter  *was_normalized = FALSE;
68251881Speter
69251881Speter  if (*str == NULL)
70251881Speter    return SVN_NO_ERROR;
71251881Speter
72251881Speter  SVN_ERR_ASSERT((*str)->data != NULL);
73251881Speter
74251881Speter  if (source_prop_encoding == NULL)
75251881Speter    source_prop_encoding = "UTF-8";
76251881Speter
77251881Speter  new_str = NULL;
78251881Speter  SVN_ERR(svn_subst_translate_string2(&new_str, NULL, was_normalized,
79251881Speter                                      *str, source_prop_encoding, TRUE,
80251881Speter                                      result_pool, scratch_pool));
81251881Speter  *str = new_str;
82251881Speter
83251881Speter  return SVN_NO_ERROR;
84251881Speter}
85251881Speter
86251881Speter
87251881Speter/* Normalize the encoding and line ending style of the values of properties
88251881Speter * in REV_PROPS that "need translation" (according to
89251881Speter * svn_prop_needs_translation(), which is currently all svn:* props) so that
90251881Speter * they are encoded in UTF-8 and contain only LF (\n) line endings.
91251881Speter *
92251881Speter * The number of properties that needed line ending normalization is returned in
93251881Speter * *NORMALIZED_COUNT.
94251881Speter *
95251881Speter * No re-encoding is performed if SOURCE_PROP_ENCODING is NULL.
96251881Speter */
97251881Spetersvn_error_t *
98251881Spetersvnsync_normalize_revprops(apr_hash_t *rev_props,
99251881Speter                           int *normalized_count,
100251881Speter                           const char *source_prop_encoding,
101251881Speter                           apr_pool_t *pool)
102251881Speter{
103251881Speter  apr_hash_index_t *hi;
104251881Speter  *normalized_count = 0;
105251881Speter
106251881Speter  for (hi = apr_hash_first(pool, rev_props);
107251881Speter       hi;
108251881Speter       hi = apr_hash_next(hi))
109251881Speter    {
110251881Speter      const char *propname = svn__apr_hash_index_key(hi);
111251881Speter      const svn_string_t *propval = svn__apr_hash_index_val(hi);
112251881Speter
113251881Speter      if (svn_prop_needs_translation(propname))
114251881Speter        {
115251881Speter          svn_boolean_t was_normalized;
116251881Speter          SVN_ERR(normalize_string(&propval, &was_normalized,
117251881Speter                  source_prop_encoding, pool, pool));
118251881Speter
119251881Speter          /* Replace the existing prop value. */
120251881Speter          svn_hash_sets(rev_props, propname, propval);
121251881Speter
122251881Speter          if (was_normalized)
123251881Speter            (*normalized_count)++; /* Count it. */
124251881Speter        }
125251881Speter    }
126251881Speter  return SVN_NO_ERROR;
127251881Speter}
128251881Speter
129251881Speter
130251881Speter/*** Synchronization Editor ***/
131251881Speter
132251881Speter/* This editor has a couple of jobs.
133251881Speter *
134251881Speter * First, it needs to filter out the propchanges that can't be passed over
135251881Speter * libsvn_ra.
136251881Speter *
137251881Speter * Second, it needs to adjust for the fact that we might not actually have
138251881Speter * permission to see all of the data from the remote repository, which means
139251881Speter * we could get revisions that are totally empty from our point of view.
140251881Speter *
141251881Speter * Third, it needs to adjust copyfrom paths, adding the root url for the
142251881Speter * destination repository to the beginning of them.
143251881Speter */
144251881Speter
145251881Speter
146251881Speter/* Edit baton */
147251881Spetertypedef struct edit_baton_t {
148251881Speter  const svn_delta_editor_t *wrapped_editor;
149251881Speter  void *wrapped_edit_baton;
150251881Speter  const char *to_url;  /* URL we're copying into, for correct copyfrom URLs */
151251881Speter  const char *source_prop_encoding;
152251881Speter  svn_boolean_t called_open_root;
153251881Speter  svn_boolean_t got_textdeltas;
154251881Speter  svn_revnum_t base_revision;
155251881Speter  svn_boolean_t quiet;
156251881Speter  svn_boolean_t strip_mergeinfo;    /* Are we stripping svn:mergeinfo? */
157251881Speter  svn_boolean_t migrate_svnmerge;   /* Are we converting svnmerge.py data? */
158251881Speter  svn_boolean_t mergeinfo_stripped; /* Did we strip svn:mergeinfo? */
159251881Speter  svn_boolean_t svnmerge_migrated;  /* Did we convert svnmerge.py data? */
160251881Speter  svn_boolean_t svnmerge_blocked;   /* Was there any blocked svnmerge data? */
161251881Speter  int *normalized_node_props_counter;  /* Where to count normalizations? */
162251881Speter} edit_baton_t;
163251881Speter
164251881Speter
165251881Speter/* A dual-purpose baton for files and directories. */
166251881Spetertypedef struct node_baton_t {
167251881Speter  void *edit_baton;
168251881Speter  void *wrapped_node_baton;
169251881Speter} node_baton_t;
170251881Speter
171251881Speter
172251881Speter/*** Editor vtable functions ***/
173251881Speter
174251881Speterstatic svn_error_t *
175251881Speterset_target_revision(void *edit_baton,
176251881Speter                    svn_revnum_t target_revision,
177251881Speter                    apr_pool_t *pool)
178251881Speter{
179251881Speter  edit_baton_t *eb = edit_baton;
180251881Speter  return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
181251881Speter                                                 target_revision, pool);
182251881Speter}
183251881Speter
184251881Speterstatic svn_error_t *
185251881Speteropen_root(void *edit_baton,
186251881Speter          svn_revnum_t base_revision,
187251881Speter          apr_pool_t *pool,
188251881Speter          void **root_baton)
189251881Speter{
190251881Speter  edit_baton_t *eb = edit_baton;
191251881Speter  node_baton_t *dir_baton = apr_palloc(pool, sizeof(*dir_baton));
192251881Speter
193251881Speter  SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
194251881Speter                                        base_revision, pool,
195251881Speter                                        &dir_baton->wrapped_node_baton));
196251881Speter
197251881Speter  eb->called_open_root = TRUE;
198251881Speter  dir_baton->edit_baton = edit_baton;
199251881Speter  *root_baton = dir_baton;
200251881Speter
201251881Speter  return SVN_NO_ERROR;
202251881Speter}
203251881Speter
204251881Speterstatic svn_error_t *
205251881Speterdelete_entry(const char *path,
206251881Speter             svn_revnum_t base_revision,
207251881Speter             void *parent_baton,
208251881Speter             apr_pool_t *pool)
209251881Speter{
210251881Speter  node_baton_t *pb = parent_baton;
211251881Speter  edit_baton_t *eb = pb->edit_baton;
212251881Speter
213251881Speter  return eb->wrapped_editor->delete_entry(path, base_revision,
214251881Speter                                          pb->wrapped_node_baton, pool);
215251881Speter}
216251881Speter
217251881Speterstatic svn_error_t *
218251881Speteradd_directory(const char *path,
219251881Speter              void *parent_baton,
220251881Speter              const char *copyfrom_path,
221251881Speter              svn_revnum_t copyfrom_rev,
222251881Speter              apr_pool_t *pool,
223251881Speter              void **child_baton)
224251881Speter{
225251881Speter  node_baton_t *pb = parent_baton;
226251881Speter  edit_baton_t *eb = pb->edit_baton;
227251881Speter  node_baton_t *b = apr_palloc(pool, sizeof(*b));
228251881Speter
229251881Speter  /* if copyfrom_path is an fspath create a proper uri */
230251881Speter  if (copyfrom_path && copyfrom_path[0] == '/')
231251881Speter    copyfrom_path = svn_path_url_add_component2(eb->to_url,
232251881Speter                                                copyfrom_path + 1, pool);
233251881Speter
234251881Speter  SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_node_baton,
235251881Speter                                            copyfrom_path,
236251881Speter                                            copyfrom_rev, pool,
237251881Speter                                            &b->wrapped_node_baton));
238251881Speter
239251881Speter  b->edit_baton = eb;
240251881Speter  *child_baton = b;
241251881Speter
242251881Speter  return SVN_NO_ERROR;
243251881Speter}
244251881Speter
245251881Speterstatic svn_error_t *
246251881Speteropen_directory(const char *path,
247251881Speter               void *parent_baton,
248251881Speter               svn_revnum_t base_revision,
249251881Speter               apr_pool_t *pool,
250251881Speter               void **child_baton)
251251881Speter{
252251881Speter  node_baton_t *pb = parent_baton;
253251881Speter  edit_baton_t *eb = pb->edit_baton;
254251881Speter  node_baton_t *db = apr_palloc(pool, sizeof(*db));
255251881Speter
256251881Speter  SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_node_baton,
257251881Speter                                             base_revision, pool,
258251881Speter                                             &db->wrapped_node_baton));
259251881Speter
260251881Speter  db->edit_baton = eb;
261251881Speter  *child_baton = db;
262251881Speter
263251881Speter  return SVN_NO_ERROR;
264251881Speter}
265251881Speter
266251881Speterstatic svn_error_t *
267251881Speteradd_file(const char *path,
268251881Speter         void *parent_baton,
269251881Speter         const char *copyfrom_path,
270251881Speter         svn_revnum_t copyfrom_rev,
271251881Speter         apr_pool_t *pool,
272251881Speter         void **file_baton)
273251881Speter{
274251881Speter  node_baton_t *pb = parent_baton;
275251881Speter  edit_baton_t *eb = pb->edit_baton;
276251881Speter  node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
277251881Speter
278251881Speter  /* if copyfrom_path is an fspath create a proper uri */
279251881Speter  if (copyfrom_path && copyfrom_path[0] == '/')
280251881Speter    copyfrom_path = svn_path_url_add_component2(eb->to_url,
281251881Speter                                                copyfrom_path + 1, pool);
282251881Speter
283251881Speter  SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_node_baton,
284251881Speter                                       copyfrom_path, copyfrom_rev,
285251881Speter                                       pool, &fb->wrapped_node_baton));
286251881Speter
287251881Speter  fb->edit_baton = eb;
288251881Speter  *file_baton = fb;
289251881Speter
290251881Speter  return SVN_NO_ERROR;
291251881Speter}
292251881Speter
293251881Speterstatic svn_error_t *
294251881Speteropen_file(const char *path,
295251881Speter          void *parent_baton,
296251881Speter          svn_revnum_t base_revision,
297251881Speter          apr_pool_t *pool,
298251881Speter          void **file_baton)
299251881Speter{
300251881Speter  node_baton_t *pb = parent_baton;
301251881Speter  edit_baton_t *eb = pb->edit_baton;
302251881Speter  node_baton_t *fb = apr_palloc(pool, sizeof(*fb));
303251881Speter
304251881Speter  SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_node_baton,
305251881Speter                                        base_revision, pool,
306251881Speter                                        &fb->wrapped_node_baton));
307251881Speter
308251881Speter  fb->edit_baton = eb;
309251881Speter  *file_baton = fb;
310251881Speter
311251881Speter  return SVN_NO_ERROR;
312251881Speter}
313251881Speter
314251881Speterstatic svn_error_t *
315251881Speterapply_textdelta(void *file_baton,
316251881Speter                const char *base_checksum,
317251881Speter                apr_pool_t *pool,
318251881Speter                svn_txdelta_window_handler_t *handler,
319251881Speter                void **handler_baton)
320251881Speter{
321251881Speter  node_baton_t *fb = file_baton;
322251881Speter  edit_baton_t *eb = fb->edit_baton;
323251881Speter
324251881Speter  if (! eb->quiet)
325251881Speter    {
326251881Speter      if (! eb->got_textdeltas)
327251881Speter        SVN_ERR(svn_cmdline_printf(pool, _("Transmitting file data ")));
328251881Speter      SVN_ERR(svn_cmdline_printf(pool, "."));
329251881Speter      SVN_ERR(svn_cmdline_fflush(stdout));
330251881Speter    }
331251881Speter
332251881Speter  eb->got_textdeltas = TRUE;
333251881Speter  return eb->wrapped_editor->apply_textdelta(fb->wrapped_node_baton,
334251881Speter                                             base_checksum, pool,
335251881Speter                                             handler, handler_baton);
336251881Speter}
337251881Speter
338251881Speterstatic svn_error_t *
339251881Speterclose_file(void *file_baton,
340251881Speter           const char *text_checksum,
341251881Speter           apr_pool_t *pool)
342251881Speter{
343251881Speter  node_baton_t *fb = file_baton;
344251881Speter  edit_baton_t *eb = fb->edit_baton;
345251881Speter  return eb->wrapped_editor->close_file(fb->wrapped_node_baton,
346251881Speter                                        text_checksum, pool);
347251881Speter}
348251881Speter
349251881Speterstatic svn_error_t *
350251881Speterabsent_file(const char *path,
351251881Speter            void *file_baton,
352251881Speter            apr_pool_t *pool)
353251881Speter{
354251881Speter  node_baton_t *fb = file_baton;
355251881Speter  edit_baton_t *eb = fb->edit_baton;
356251881Speter  return eb->wrapped_editor->absent_file(path, fb->wrapped_node_baton, pool);
357251881Speter}
358251881Speter
359251881Speterstatic svn_error_t *
360251881Speterclose_directory(void *dir_baton,
361251881Speter                apr_pool_t *pool)
362251881Speter{
363251881Speter  node_baton_t *db = dir_baton;
364251881Speter  edit_baton_t *eb = db->edit_baton;
365251881Speter  return eb->wrapped_editor->close_directory(db->wrapped_node_baton, pool);
366251881Speter}
367251881Speter
368251881Speterstatic svn_error_t *
369251881Speterabsent_directory(const char *path,
370251881Speter                 void *dir_baton,
371251881Speter                 apr_pool_t *pool)
372251881Speter{
373251881Speter  node_baton_t *db = dir_baton;
374251881Speter  edit_baton_t *eb = db->edit_baton;
375251881Speter  return eb->wrapped_editor->absent_directory(path, db->wrapped_node_baton,
376251881Speter                                              pool);
377251881Speter}
378251881Speter
379251881Speterstatic svn_error_t *
380251881Speterchange_file_prop(void *file_baton,
381251881Speter                 const char *name,
382251881Speter                 const svn_string_t *value,
383251881Speter                 apr_pool_t *pool)
384251881Speter{
385251881Speter  node_baton_t *fb = file_baton;
386251881Speter  edit_baton_t *eb = fb->edit_baton;
387251881Speter
388251881Speter  /* only regular properties can pass over libsvn_ra */
389251881Speter  if (svn_property_kind2(name) != svn_prop_regular_kind)
390251881Speter    return SVN_NO_ERROR;
391251881Speter
392251881Speter  /* Maybe drop svn:mergeinfo.  */
393251881Speter  if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
394251881Speter    {
395251881Speter      eb->mergeinfo_stripped = TRUE;
396251881Speter      return SVN_NO_ERROR;
397251881Speter    }
398251881Speter
399251881Speter  /* Maybe drop (errantly set, as this is a file) svnmerge.py properties. */
400251881Speter  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
401251881Speter    {
402251881Speter      eb->svnmerge_migrated = TRUE;
403251881Speter      return SVN_NO_ERROR;
404251881Speter    }
405251881Speter
406251881Speter  /* Remember if we see any svnmerge-blocked properties.  (They really
407251881Speter     shouldn't be here, as this is a file, but whatever...)  */
408251881Speter  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
409251881Speter    {
410251881Speter      eb->svnmerge_blocked = TRUE;
411251881Speter    }
412251881Speter
413251881Speter  /* Normalize svn:* properties as necessary. */
414251881Speter  if (svn_prop_needs_translation(name))
415251881Speter    {
416251881Speter      svn_boolean_t was_normalized;
417251881Speter      SVN_ERR(normalize_string(&value, &was_normalized,
418251881Speter                               eb->source_prop_encoding, pool, pool));
419251881Speter      if (was_normalized)
420251881Speter        (*(eb->normalized_node_props_counter))++;
421251881Speter    }
422251881Speter
423251881Speter  return eb->wrapped_editor->change_file_prop(fb->wrapped_node_baton,
424251881Speter                                              name, value, pool);
425251881Speter}
426251881Speter
427251881Speterstatic svn_error_t *
428251881Speterchange_dir_prop(void *dir_baton,
429251881Speter                const char *name,
430251881Speter                const svn_string_t *value,
431251881Speter                apr_pool_t *pool)
432251881Speter{
433251881Speter  node_baton_t *db = dir_baton;
434251881Speter  edit_baton_t *eb = db->edit_baton;
435251881Speter
436251881Speter  /* Only regular properties can pass over libsvn_ra */
437251881Speter  if (svn_property_kind2(name) != svn_prop_regular_kind)
438251881Speter    return SVN_NO_ERROR;
439251881Speter
440251881Speter  /* Maybe drop svn:mergeinfo.  */
441251881Speter  if (eb->strip_mergeinfo && (strcmp(name, SVN_PROP_MERGEINFO) == 0))
442251881Speter    {
443251881Speter      eb->mergeinfo_stripped = TRUE;
444251881Speter      return SVN_NO_ERROR;
445251881Speter    }
446251881Speter
447251881Speter  /* Maybe convert svnmerge-integrated data into svn:mergeinfo.  (We
448251881Speter     ignore svnmerge-blocked for now.) */
449251881Speter  /* ### FIXME: Consult the mirror repository's HEAD prop values and
450251881Speter     ### merge svn:mergeinfo, svnmerge-integrated, and svnmerge-blocked. */
451251881Speter  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-integrated") == 0))
452251881Speter    {
453251881Speter      if (value)
454251881Speter        {
455251881Speter          /* svnmerge-integrated differs from svn:mergeinfo in a pair
456251881Speter             of ways.  First, it can use tabs, newlines, or spaces to
457251881Speter             delimit source information.  Secondly, the source paths
458251881Speter             are relative URLs, whereas svn:mergeinfo uses relative
459251881Speter             paths (not URI-encoded). */
460251881Speter          svn_error_t *err;
461251881Speter          svn_stringbuf_t *mergeinfo_buf = svn_stringbuf_create_empty(pool);
462251881Speter          svn_mergeinfo_t mergeinfo;
463251881Speter          int i;
464251881Speter          apr_array_header_t *sources =
465251881Speter            svn_cstring_split(value->data, " \t\n", TRUE, pool);
466251881Speter          svn_string_t *new_value;
467251881Speter
468251881Speter          for (i = 0; i < sources->nelts; i++)
469251881Speter            {
470251881Speter              const char *rel_path;
471251881Speter              apr_array_header_t *path_revs =
472251881Speter                svn_cstring_split(APR_ARRAY_IDX(sources, i, const char *),
473251881Speter                                  ":", TRUE, pool);
474251881Speter
475251881Speter              /* ### TODO: Warn? */
476251881Speter              if (path_revs->nelts != 2)
477251881Speter                continue;
478251881Speter
479251881Speter              /* Append this source's mergeinfo data. */
480251881Speter              rel_path = APR_ARRAY_IDX(path_revs, 0, const char *);
481251881Speter              rel_path = svn_path_uri_decode(rel_path, pool);
482251881Speter              svn_stringbuf_appendcstr(mergeinfo_buf, rel_path);
483251881Speter              svn_stringbuf_appendcstr(mergeinfo_buf, ":");
484251881Speter              svn_stringbuf_appendcstr(mergeinfo_buf,
485251881Speter                                       APR_ARRAY_IDX(path_revs, 1,
486251881Speter                                                     const char *));
487251881Speter              svn_stringbuf_appendcstr(mergeinfo_buf, "\n");
488251881Speter            }
489251881Speter
490251881Speter          /* Try to parse the mergeinfo string we've created, just to
491251881Speter             check for bogosity.  If all goes well, we'll unparse it
492251881Speter             again and use that as our property value.  */
493251881Speter          err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_buf->data, pool);
494251881Speter          if (err)
495251881Speter            {
496251881Speter              svn_error_clear(err);
497251881Speter              return SVN_NO_ERROR;
498251881Speter            }
499251881Speter          SVN_ERR(svn_mergeinfo_to_string(&new_value, mergeinfo, pool));
500251881Speter          value = new_value;
501251881Speter        }
502251881Speter      name = SVN_PROP_MERGEINFO;
503251881Speter      eb->svnmerge_migrated = TRUE;
504251881Speter    }
505251881Speter
506251881Speter  /* Remember if we see any svnmerge-blocked properties. */
507251881Speter  if (eb->migrate_svnmerge && (strcmp(name, "svnmerge-blocked") == 0))
508251881Speter    {
509251881Speter      eb->svnmerge_blocked = TRUE;
510251881Speter    }
511251881Speter
512251881Speter  /* Normalize svn:* properties as necessary. */
513251881Speter  if (svn_prop_needs_translation(name))
514251881Speter    {
515251881Speter      svn_boolean_t was_normalized;
516251881Speter      SVN_ERR(normalize_string(&value, &was_normalized, eb->source_prop_encoding,
517251881Speter                               pool, pool));
518251881Speter      if (was_normalized)
519251881Speter        (*(eb->normalized_node_props_counter))++;
520251881Speter    }
521251881Speter
522251881Speter  return eb->wrapped_editor->change_dir_prop(db->wrapped_node_baton,
523251881Speter                                             name, value, pool);
524251881Speter}
525251881Speter
526251881Speterstatic svn_error_t *
527251881Speterclose_edit(void *edit_baton,
528251881Speter           apr_pool_t *pool)
529251881Speter{
530251881Speter  edit_baton_t *eb = edit_baton;
531251881Speter
532251881Speter  /* If we haven't opened the root yet, that means we're transfering
533251881Speter     an empty revision, probably because we aren't allowed to see the
534251881Speter     contents for some reason.  In any event, we need to open the root
535251881Speter     and close it again, before we can close out the edit, or the
536251881Speter     commit will fail. */
537251881Speter
538251881Speter  if (! eb->called_open_root)
539251881Speter    {
540251881Speter      void *baton;
541251881Speter      SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
542251881Speter                                            eb->base_revision, pool,
543251881Speter                                            &baton));
544251881Speter      SVN_ERR(eb->wrapped_editor->close_directory(baton, pool));
545251881Speter    }
546251881Speter
547251881Speter  if (! eb->quiet)
548251881Speter    {
549251881Speter      if (eb->got_textdeltas)
550251881Speter        SVN_ERR(svn_cmdline_printf(pool, "\n"));
551251881Speter      if (eb->mergeinfo_stripped)
552251881Speter        SVN_ERR(svn_cmdline_printf(pool,
553251881Speter                                   "NOTE: Dropped Subversion mergeinfo "
554251881Speter                                   "from this revision.\n"));
555251881Speter      if (eb->svnmerge_migrated)
556251881Speter        SVN_ERR(svn_cmdline_printf(pool,
557251881Speter                                   "NOTE: Migrated 'svnmerge-integrated' in "
558251881Speter                                   "this revision.\n"));
559251881Speter      if (eb->svnmerge_blocked)
560251881Speter        SVN_ERR(svn_cmdline_printf(pool,
561251881Speter                                   "NOTE: Saw 'svnmerge-blocked' in this "
562251881Speter                                   "revision (but didn't migrate it).\n"));
563251881Speter    }
564251881Speter
565251881Speter  return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool);
566251881Speter}
567251881Speter
568251881Speterstatic svn_error_t *
569251881Speterabort_edit(void *edit_baton,
570251881Speter           apr_pool_t *pool)
571251881Speter{
572251881Speter  edit_baton_t *eb = edit_baton;
573251881Speter  return eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool);
574251881Speter}
575251881Speter
576251881Speter
577251881Speter/*** Editor factory function ***/
578251881Speter
579251881Spetersvn_error_t *
580251881Spetersvnsync_get_sync_editor(const svn_delta_editor_t *wrapped_editor,
581251881Speter                        void *wrapped_edit_baton,
582251881Speter                        svn_revnum_t base_revision,
583251881Speter                        const char *to_url,
584251881Speter                        const char *source_prop_encoding,
585251881Speter                        svn_boolean_t quiet,
586251881Speter                        const svn_delta_editor_t **editor,
587251881Speter                        void **edit_baton,
588251881Speter                        int *normalized_node_props_counter,
589251881Speter                        apr_pool_t *pool)
590251881Speter{
591251881Speter  svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
592251881Speter  edit_baton_t *eb = apr_pcalloc(pool, sizeof(*eb));
593251881Speter
594251881Speter  tree_editor->set_target_revision = set_target_revision;
595251881Speter  tree_editor->open_root = open_root;
596251881Speter  tree_editor->delete_entry = delete_entry;
597251881Speter  tree_editor->add_directory = add_directory;
598251881Speter  tree_editor->open_directory = open_directory;
599251881Speter  tree_editor->change_dir_prop = change_dir_prop;
600251881Speter  tree_editor->close_directory = close_directory;
601251881Speter  tree_editor->absent_directory = absent_directory;
602251881Speter  tree_editor->add_file = add_file;
603251881Speter  tree_editor->open_file = open_file;
604251881Speter  tree_editor->apply_textdelta = apply_textdelta;
605251881Speter  tree_editor->change_file_prop = change_file_prop;
606251881Speter  tree_editor->close_file = close_file;
607251881Speter  tree_editor->absent_file = absent_file;
608251881Speter  tree_editor->close_edit = close_edit;
609251881Speter  tree_editor->abort_edit = abort_edit;
610251881Speter
611251881Speter  eb->wrapped_editor = wrapped_editor;
612251881Speter  eb->wrapped_edit_baton = wrapped_edit_baton;
613251881Speter  eb->base_revision = base_revision;
614251881Speter  eb->to_url = to_url;
615251881Speter  eb->source_prop_encoding = source_prop_encoding;
616251881Speter  eb->quiet = quiet;
617251881Speter  eb->normalized_node_props_counter = normalized_node_props_counter;
618251881Speter
619251881Speter  if (getenv("SVNSYNC_UNSUPPORTED_STRIP_MERGEINFO"))
620251881Speter    {
621251881Speter      eb->strip_mergeinfo = TRUE;
622251881Speter    }
623251881Speter  if (getenv("SVNSYNC_UNSUPPORTED_MIGRATE_SVNMERGE"))
624251881Speter    {
625251881Speter      /* Current we can't merge property values.  That's only possible
626251881Speter         if all the properties to be merged were always modified in
627251881Speter         exactly the same revisions, or if we allow ourselves to
628251881Speter         lookup the current state of properties in the sync
629251881Speter         destination.  So for now, migrating svnmerge.py data implies
630251881Speter         stripping pre-existing svn:mergeinfo. */
631251881Speter      /* ### FIXME: Do a real migration by consulting the mirror
632251881Speter         ### repository's HEAD propvalues and merging svn:mergeinfo,
633251881Speter         ### svnmerge-integrated, and svnmerge-blocked together. */
634251881Speter      eb->migrate_svnmerge = TRUE;
635251881Speter      eb->strip_mergeinfo = TRUE;
636251881Speter    }
637251881Speter
638251881Speter  *editor = tree_editor;
639251881Speter  *edit_baton = eb;
640251881Speter
641251881Speter  return SVN_NO_ERROR;
642251881Speter}
643251881Speter
644