]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_wc/merge.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_wc / merge.c
1 /*
2  * merge.c:  merging changes into a working file
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include "svn_wc.h"
25 #include "svn_diff.h"
26 #include "svn_dirent_uri.h"
27 #include "svn_path.h"
28 #include "svn_pools.h"
29
30 #include "wc.h"
31 #include "adm_files.h"
32 #include "conflicts.h"
33 #include "translate.h"
34 #include "workqueue.h"
35
36 #include "private/svn_skel.h"
37
38 #include "svn_private_config.h"
39
40 /* Contains some information on the merge target before merge, and some
41    information needed for the diff processing. */
42 typedef struct merge_target_t
43 {
44   svn_wc__db_t *db;                         /* The DB used to access target */
45   const char *local_abspath;                /* The absolute path to target */
46   const char *wri_abspath;                  /* The working copy of target */
47
48   apr_hash_t *old_actual_props;                 /* The set of actual properties
49                                                before merging */
50   const apr_array_header_t *prop_diff;      /* The property changes */
51
52   const char *diff3_cmd;                    /* The diff3 command and options */
53   const apr_array_header_t *merge_options;
54
55 } merge_target_t;
56
57 \f
58 /* Return a pointer to the svn_prop_t structure from PROP_DIFF
59    belonging to PROP_NAME, if any.  NULL otherwise.*/
60 static const svn_prop_t *
61 get_prop(const apr_array_header_t *prop_diff,
62          const char *prop_name)
63 {
64   if (prop_diff)
65     {
66       int i;
67       for (i = 0; i < prop_diff->nelts; i++)
68         {
69           const svn_prop_t *elt = &APR_ARRAY_IDX(prop_diff, i,
70                                                  svn_prop_t);
71
72           if (strcmp(elt->name, prop_name) == 0)
73             return elt;
74         }
75     }
76
77   return NULL;
78 }
79
80
81 /* Detranslate a working copy file MERGE_TARGET to achieve the effect of:
82
83    1. Detranslate
84    2. Install new props
85    3. Retranslate
86    4. Detranslate
87
88    in one pass, to get a file which can be compared with the left and right
89    files which are in repository normal form.
90
91    Property changes make this a little complex though. Changes in
92
93    - svn:mime-type
94    - svn:eol-style
95    - svn:keywords
96    - svn:special
97
98    may change the way a file is translated.
99
100    Effect for svn:mime-type:
101
102      If svn:mime-type is considered 'binary', we ignore svn:eol-style (but
103      still translate keywords).
104
105      I) both old and new mime-types are texty
106         -> just do the translation dance (as lined out below)
107            ### actually we do a shortcut with just one translation:
108            detranslate with the old keywords and ... eol-style
109            (the new re+detranslation is a no-op w.r.t. keywords [1])
110
111      II) the old one is texty, the new one is binary
112         -> detranslate with the old eol-style and keywords
113            (the new re+detranslation is a no-op [1])
114
115      III) the old one is binary, the new one texty
116         -> detranslate with the old keywords and new eol-style
117            (the old detranslation is a no-op w.r.t. eol, and
118             the new re+detranslation is a no-op w.r.t. keywords [1])
119
120      IV) the old and new ones are binary
121         -> detranslate with the old keywords
122            (the new re+detranslation is a no-op [1])
123
124    Effect for svn:eol-style
125
126      I) On add or change of svn:eol-style, use the new value
127
128      II) otherwise: use the old value (absent means 'no translation')
129
130    Effect for svn:keywords
131
132      Always use the old settings (re+detranslation are no-op [1]).
133
134      [1] Translation of keywords from repository normal form to WC form and
135          back is normally a no-op, but is not a no-op if text contains a kw
136          that is only enabled by the new props and is present in non-
137          contracted form (such as "$Rev: 1234 $").  If we want to catch this
138          case we should detranslate with both the old & the new keywords
139          together.
140
141    Effect for svn:special
142
143      Always use the old settings (re+detranslation are no-op).
144
145   Sets *DETRANSLATED_ABSPATH to the path to the detranslated file,
146   this may be the same as SOURCE_ABSPATH if FORCE_COPY is FALSE and no
147   translation is required.
148
149   If FORCE_COPY is FALSE and *DETRANSLATED_ABSPATH is a file distinct
150   from SOURCE_ABSPATH then the file will be deleted on RESULT_POOL
151   cleanup.
152
153   If FORCE_COPY is TRUE then *DETRANSLATED_ABSPATH will always be a
154   new file distinct from SOURCE_ABSPATH and it will be the callers
155   responsibility to delete the file.
156
157 */
158 static svn_error_t *
159 detranslate_wc_file(const char **detranslated_abspath,
160                     const merge_target_t *mt,
161                     svn_boolean_t force_copy,
162                     const char *source_abspath,
163                     svn_cancel_func_t cancel_func,
164                     void *cancel_baton,
165                     apr_pool_t *result_pool,
166                     apr_pool_t *scratch_pool)
167 {
168   svn_boolean_t old_is_binary, new_is_binary;
169   svn_subst_eol_style_t style;
170   const char *eol;
171   apr_hash_t *keywords;
172   svn_boolean_t special;
173
174   {
175     const char *old_mime_value
176       = svn_prop_get_value(mt->old_actual_props, SVN_PROP_MIME_TYPE);
177     const svn_prop_t *prop = get_prop(mt->prop_diff, SVN_PROP_MIME_TYPE);
178     const char *new_mime_value
179       = prop ? (prop->value ? prop->value->data : NULL) : old_mime_value;
180
181     old_is_binary = old_mime_value && svn_mime_type_is_binary(old_mime_value);
182     new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value);;
183   }
184
185   /* See what translations we want to do */
186   if (old_is_binary && new_is_binary)
187     {
188       /* Case IV. Old and new props 'binary': detranslate keywords only */
189       SVN_ERR(svn_wc__get_translate_info(NULL, NULL, &keywords, NULL,
190                                          mt->db, mt->local_abspath,
191                                          mt->old_actual_props, TRUE,
192                                          scratch_pool, scratch_pool));
193       /* ### Why override 'special'? Elsewhere it has precedence. */
194       special = FALSE;
195       eol = NULL;
196       style = svn_subst_eol_style_none;
197     }
198   else if (!old_is_binary && new_is_binary)
199     {
200       /* Case II. Old props indicate texty, new props indicate binary:
201          detranslate keywords and old eol-style */
202       SVN_ERR(svn_wc__get_translate_info(&style, &eol,
203                                          &keywords,
204                                          &special,
205                                          mt->db, mt->local_abspath,
206                                          mt->old_actual_props, TRUE,
207                                          scratch_pool, scratch_pool));
208     }
209   else
210     {
211       /* Case I & III. New props indicate texty, regardless of old props */
212
213       /* In case the file used to be special, detranslate specially */
214       SVN_ERR(svn_wc__get_translate_info(&style, &eol,
215                                          &keywords,
216                                          &special,
217                                          mt->db, mt->local_abspath,
218                                          mt->old_actual_props, TRUE,
219                                          scratch_pool, scratch_pool));
220
221       if (special)
222         {
223           keywords = NULL;
224           eol = NULL;
225           style = svn_subst_eol_style_none;
226         }
227       else
228         {
229           const svn_prop_t *prop;
230
231           /* In case a new eol style was set, use that for detranslation */
232           if ((prop = get_prop(mt->prop_diff, SVN_PROP_EOL_STYLE)) && prop->value)
233             {
234               /* Value added or changed */
235               svn_subst_eol_style_from_value(&style, &eol, prop->value->data);
236             }
237           else if (!old_is_binary)
238             {
239               /* Already fetched */
240             }
241           else
242             {
243               eol = NULL;
244               style = svn_subst_eol_style_none;
245             }
246         }
247     }
248
249   /* Now, detranslate with the settings we created above */
250
251   if (force_copy || keywords || eol || special)
252     {
253       const char *temp_dir_abspath;
254       const char *detranslated;
255
256       /* Force a copy into the temporary wc area to avoid having
257          temporary files created below to appear in the actual wc. */
258       SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
259                                              mt->wri_abspath,
260                                              scratch_pool, scratch_pool));
261
262       /* ### svn_subst_copy_and_translate4() also creates a tempfile
263          ### internally.  Anyway to piggyback on that? */
264       SVN_ERR(svn_io_open_unique_file3(NULL, &detranslated, temp_dir_abspath,
265                                        (force_copy
266                                         ? svn_io_file_del_none
267                                         : svn_io_file_del_on_pool_cleanup),
268                                        result_pool, scratch_pool));
269
270       /* Always 'repair' EOLs here, so that we can apply a diff that
271          changes from inconsistent newlines and no 'svn:eol-style' to
272          consistent newlines and 'svn:eol-style' set.  */
273
274       if (style == svn_subst_eol_style_native)
275         eol = SVN_SUBST_NATIVE_EOL_STR;
276       else if (style != svn_subst_eol_style_fixed
277                && style != svn_subst_eol_style_none)
278         return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
279
280       SVN_ERR(svn_subst_copy_and_translate4(source_abspath,
281                                             detranslated,
282                                             eol,
283                                             TRUE /* repair */,
284                                             keywords,
285                                             FALSE /* contract keywords */,
286                                             special,
287                                             cancel_func, cancel_baton,
288                                             scratch_pool));
289
290       SVN_ERR(svn_dirent_get_absolute(detranslated_abspath, detranslated,
291                                       result_pool));
292     }
293   else
294     *detranslated_abspath = apr_pstrdup(result_pool, source_abspath);
295
296   return SVN_NO_ERROR;
297 }
298
299 /* Updates (by copying and translating) the eol style in
300    OLD_TARGET_ABSPATH returning the filename containing the
301    correct eol style in NEW_TARGET_ABSPATH, if an eol style
302    change is contained in PROP_DIFF. */
303 static svn_error_t *
304 maybe_update_target_eols(const char **new_target_abspath,
305                          const apr_array_header_t *prop_diff,
306                          const char *old_target_abspath,
307                          svn_cancel_func_t cancel_func,
308                          void *cancel_baton,
309                          apr_pool_t *result_pool,
310                          apr_pool_t *scratch_pool)
311 {
312   const svn_prop_t *prop = get_prop(prop_diff, SVN_PROP_EOL_STYLE);
313
314   if (prop && prop->value)
315     {
316       const char *eol;
317       const char *tmp_new;
318
319       svn_subst_eol_style_from_value(NULL, &eol, prop->value->data);
320       SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_new, NULL,
321                                        svn_io_file_del_on_pool_cleanup,
322                                        result_pool, scratch_pool));
323
324       /* Always 'repair' EOLs here, so that we can apply a diff that
325          changes from inconsistent newlines and no 'svn:eol-style' to
326          consistent newlines and 'svn:eol-style' set.  */
327       SVN_ERR(svn_subst_copy_and_translate4(old_target_abspath,
328                                             tmp_new,
329                                             eol,
330                                             TRUE /* repair */,
331                                             NULL /* keywords */,
332                                             FALSE /* expand */,
333                                             FALSE /* special */,
334                                             cancel_func, cancel_baton,
335                                             scratch_pool));
336       *new_target_abspath = apr_pstrdup(result_pool, tmp_new);
337     }
338   else
339     *new_target_abspath = apr_pstrdup(result_pool, old_target_abspath);
340
341   return SVN_NO_ERROR;
342 }
343
344
345 /* Set *TARGET_MARKER, *LEFT_MARKER and *RIGHT_MARKER to strings suitable
346    for delimiting the alternative texts in a text conflict.  Include in each
347    marker a string that may be given by TARGET_LABEL, LEFT_LABEL and
348    RIGHT_LABEL respectively or a default value where any of those are NULL.
349
350    Allocate the results in POOL or statically. */
351 static void
352 init_conflict_markers(const char **target_marker,
353                       const char **left_marker,
354                       const char **right_marker,
355                       const char *target_label,
356                       const char *left_label,
357                       const char *right_label,
358                       apr_pool_t *pool)
359 {
360   /* Labels fall back to sensible defaults if not specified. */
361   if (target_label)
362     *target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
363   else
364     *target_marker = "<<<<<<< .working";
365
366   if (left_label)
367     *left_marker = apr_psprintf(pool, "||||||| %s", left_label);
368   else
369     *left_marker = "||||||| .old";
370
371   if (right_label)
372     *right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
373   else
374     *right_marker = ">>>>>>> .new";
375 }
376
377 /* Do a 3-way merge of the files at paths LEFT, DETRANSLATED_TARGET,
378  * and RIGHT, using diff options provided in MERGE_OPTIONS.  Store the merge
379  * result in the file RESULT_F.
380  * If there are conflicts, set *CONTAINS_CONFLICTS to true, and use
381  * TARGET_LABEL, LEFT_LABEL, and RIGHT_LABEL as labels for conflict
382  * markers.  Else, set *CONTAINS_CONFLICTS to false.
383  * Do all allocations in POOL. */
384 static svn_error_t *
385 do_text_merge(svn_boolean_t *contains_conflicts,
386               apr_file_t *result_f,
387               const apr_array_header_t *merge_options,
388               const char *detranslated_target,
389               const char *left,
390               const char *right,
391               const char *target_label,
392               const char *left_label,
393               const char *right_label,
394               apr_pool_t *pool)
395 {
396   svn_diff_t *diff;
397   svn_stream_t *ostream;
398   const char *target_marker;
399   const char *left_marker;
400   const char *right_marker;
401   svn_diff_file_options_t *diff3_options;
402
403   diff3_options = svn_diff_file_options_create(pool);
404
405   if (merge_options)
406     SVN_ERR(svn_diff_file_options_parse(diff3_options,
407                                         merge_options, pool));
408
409
410   init_conflict_markers(&target_marker, &left_marker, &right_marker,
411                         target_label, left_label, right_label, pool);
412
413   SVN_ERR(svn_diff_file_diff3_2(&diff, left, detranslated_target, right,
414                                 diff3_options, pool));
415
416   ostream = svn_stream_from_aprfile2(result_f, TRUE, pool);
417
418   SVN_ERR(svn_diff_file_output_merge2(ostream, diff,
419                                       left, detranslated_target, right,
420                                       left_marker,
421                                       target_marker,
422                                       right_marker,
423                                       "=======", /* separator */
424                                       svn_diff_conflict_display_modified_original_latest,
425                                       pool));
426   SVN_ERR(svn_stream_close(ostream));
427
428   *contains_conflicts = svn_diff_contains_conflicts(diff);
429
430   return SVN_NO_ERROR;
431 }
432
433 /* Same as do_text_merge() above, but use the external diff3
434  * command DIFF3_CMD to perform the merge.  Pass MERGE_OPTIONS
435  * to the diff3 command.  Do all allocations in POOL. */
436 static svn_error_t *
437 do_text_merge_external(svn_boolean_t *contains_conflicts,
438                        apr_file_t *result_f,
439                        const char *diff3_cmd,
440                        const apr_array_header_t *merge_options,
441                        const char *detranslated_target,
442                        const char *left_abspath,
443                        const char *right_abspath,
444                        const char *target_label,
445                        const char *left_label,
446                        const char *right_label,
447                        apr_pool_t *scratch_pool)
448 {
449   int exit_code;
450
451   SVN_ERR(svn_io_run_diff3_3(&exit_code, ".",
452                              detranslated_target, left_abspath, right_abspath,
453                              target_label, left_label, right_label,
454                              result_f, diff3_cmd,
455                              merge_options, scratch_pool));
456
457   *contains_conflicts = exit_code == 1;
458
459   return SVN_NO_ERROR;
460 }
461
462 /* Preserve the three pre-merge files.
463
464    Create three empty files, with unique names that each include the
465    basename of TARGET_ABSPATH and one of LEFT_LABEL, RIGHT_LABEL and
466    TARGET_LABEL, in the directory that contains TARGET_ABSPATH.  Typical
467    names are "foo.c.r37" or "foo.c.2.mine".  Set *LEFT_COPY, *RIGHT_COPY and
468    *TARGET_COPY to their absolute paths.
469
470    Set *WORK_ITEMS to a list of new work items that will write copies of
471    LEFT_ABSPATH, RIGHT_ABSPATH and TARGET_ABSPATH into the three files,
472    translated to working-copy form.
473
474    The translation to working-copy form will be done according to the
475    versioned properties of TARGET_ABSPATH that are current when the work
476    queue items are executed.
477
478    If target_abspath is not versioned use detranslated_target_abspath
479    as the target file.
480        ### NOT IMPLEMENTED -- 'detranslated_target_abspath' is not used.
481 */
482 static svn_error_t *
483 preserve_pre_merge_files(svn_skel_t **work_items,
484                          const char **left_copy,
485                          const char **right_copy,
486                          const char **target_copy,
487                          const merge_target_t *mt,
488                          const char *left_abspath,
489                          const char *right_abspath,
490                          const char *left_label,
491                          const char *right_label,
492                          const char *target_label,
493                          const char *detranslated_target_abspath,
494                          svn_cancel_func_t cancel_func,
495                          void *cancel_baton,
496                          apr_pool_t *result_pool,
497                          apr_pool_t *scratch_pool)
498 {
499   const char *tmp_left, *tmp_right, *detranslated_target_copy;
500   const char *dir_abspath, *target_name;
501   const char *wcroot_abspath, *temp_dir_abspath;
502   svn_skel_t *work_item, *last_items = NULL;
503
504   *work_items = NULL;
505
506   svn_dirent_split(&dir_abspath, &target_name, mt->local_abspath,
507                    scratch_pool);
508
509   SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, mt->db, mt->wri_abspath,
510                                 scratch_pool, scratch_pool));
511   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
512                                          mt->wri_abspath,
513                                          scratch_pool, scratch_pool));
514
515   /* Create three empty files in DIR_ABSPATH, naming them with unique names
516      that each include TARGET_NAME and one of {LEFT,RIGHT,TARGET}_LABEL,
517      and set *{LEFT,RIGHT,TARGET}_COPY to those names. */
518   SVN_ERR(svn_io_open_uniquely_named(
519             NULL, left_copy, dir_abspath, target_name, left_label,
520             svn_io_file_del_none, result_pool, scratch_pool));
521   SVN_ERR(svn_io_open_uniquely_named(
522             NULL, right_copy, dir_abspath, target_name, right_label,
523             svn_io_file_del_none, result_pool, scratch_pool));
524   SVN_ERR(svn_io_open_uniquely_named(
525             NULL, target_copy, dir_abspath, target_name, target_label,
526             svn_io_file_del_none, result_pool, scratch_pool));
527
528   /* We preserve all the files with keywords expanded and line
529      endings in local (working) form. */
530
531   /* The workingqueue requires its paths to be in the subtree
532      relative to the wcroot path they are executed in.
533
534      Make our LEFT and RIGHT files 'local' if they aren't... */
535   if (! svn_dirent_is_ancestor(wcroot_abspath, left_abspath))
536     {
537       SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_left, temp_dir_abspath,
538                                        svn_io_file_del_none,
539                                        scratch_pool, scratch_pool));
540       SVN_ERR(svn_io_copy_file(left_abspath, tmp_left, TRUE, scratch_pool));
541
542       /* And create a wq item to remove the file later */
543       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
544                                            tmp_left,
545                                            result_pool, scratch_pool));
546
547       last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
548     }
549   else
550     tmp_left = left_abspath;
551
552   if (! svn_dirent_is_ancestor(wcroot_abspath, right_abspath))
553     {
554       SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_right, temp_dir_abspath,
555                                        svn_io_file_del_none,
556                                        scratch_pool, scratch_pool));
557       SVN_ERR(svn_io_copy_file(right_abspath, tmp_right, TRUE, scratch_pool));
558
559       /* And create a wq item to remove the file later */
560       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
561                                            tmp_right,
562                                            result_pool, scratch_pool));
563
564       last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
565     }
566   else
567     tmp_right = right_abspath;
568
569   /* NOTE: Callers must ensure that the svn:eol-style and
570      svn:keywords property values are correct in the currently
571      installed props.  With 'svn merge', it's no big deal.  But
572      when 'svn up' calls this routine, it needs to make sure that
573      this routine is using the newest property values that may
574      have been received *during* the update.  Since this routine
575      will be run from within a log-command, merge_file()
576      needs to make sure that a previous log-command to 'install
577      latest props' has already executed first.  Ben and I just
578      checked, and that is indeed the order in which the log items
579      are written, so everything should be fine.  Really.  */
580
581   /* Create LEFT and RIGHT backup files, in expanded form.
582      We use TARGET_ABSPATH's current properties to do the translation. */
583   /* Derive the basenames of the 3 backup files. */
584   SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
585                                                 mt->db, mt->local_abspath,
586                                                 tmp_left, *left_copy,
587                                                 result_pool, scratch_pool));
588   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
589
590   SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
591                                                 mt->db, mt->local_abspath,
592                                                 tmp_right, *right_copy,
593                                                 result_pool, scratch_pool));
594   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
595
596   /* Back up TARGET_ABSPATH through detranslation/retranslation:
597      the new translation properties may not match the current ones */
598   SVN_ERR(detranslate_wc_file(&detranslated_target_copy, mt, TRUE,
599                               mt->local_abspath,
600                               cancel_func, cancel_baton,
601                               scratch_pool, scratch_pool));
602
603   SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
604                                                 mt->db, mt->local_abspath,
605                                                 detranslated_target_copy,
606                                                 *target_copy,
607                                                 result_pool, scratch_pool));
608   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
609
610   /* And maybe delete some tempfiles */
611   SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
612                                        detranslated_target_copy,
613                                        result_pool, scratch_pool));
614   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
615
616   *work_items = svn_wc__wq_merge(*work_items, last_items, result_pool);
617
618   return SVN_NO_ERROR;
619 }
620
621 /* Attempt a trivial merge of LEFT_ABSPATH and RIGHT_ABSPATH to
622  * the target file at TARGET_ABSPATH.
623  *
624  * These are the inherently trivial cases:
625  *
626  *   left == right == target         =>  no-op
627  *   left != right, left == target   =>  target := right
628  *
629  * This case is also treated as trivial:
630  *
631  *   left != right, right == target  =>  no-op
632  *
633  *   ### Strictly, this case is a conflict, and the no-op outcome is only
634  *       one of the possible resolutions.
635  *
636  *       TODO: Raise a conflict at this level and implement the 'no-op'
637  *       resolution of that conflict at a higher level, in preparation for
638  *       being able to support stricter conflict detection.
639  *
640  * This case is inherently trivial but not currently handled here:
641  *
642  *   left == right != target         =>  no-op
643  *
644  * The files at LEFT_ABSPATH and RIGHT_ABSPATH are in repository normal
645  * form.  The file at DETRANSLATED_TARGET_ABSPATH is a copy of the target,
646  * 'detranslated' to repository normal form, or may be the target file
647  * itself if no translation is necessary.
648  *
649  * When this function updates the target file, it translates to working copy
650  * form.
651  *
652  * On success, set *MERGE_OUTCOME to SVN_WC_MERGE_MERGED in case the
653  * target was changed, or to SVN_WC_MERGE_UNCHANGED if the target was not
654  * changed. Install work queue items allocated in RESULT_POOL in *WORK_ITEMS.
655  * On failure, set *MERGE_OUTCOME to SVN_WC_MERGE_NO_MERGE.
656  */
657 static svn_error_t *
658 merge_file_trivial(svn_skel_t **work_items,
659                    enum svn_wc_merge_outcome_t *merge_outcome,
660                    const char *left_abspath,
661                    const char *right_abspath,
662                    const char *target_abspath,
663                    const char *detranslated_target_abspath,
664                    svn_boolean_t dry_run,
665                    svn_wc__db_t *db,
666                    svn_cancel_func_t cancel_func,
667                    void *cancel_baton,
668                    apr_pool_t *result_pool,
669                    apr_pool_t *scratch_pool)
670 {
671   svn_skel_t *work_item;
672   svn_boolean_t same_left_right;
673   svn_boolean_t same_right_target;
674   svn_boolean_t same_left_target;
675   svn_node_kind_t kind;
676   svn_boolean_t is_special;
677
678   /* If the target is not a normal file, do not attempt a trivial merge. */
679   SVN_ERR(svn_io_check_special_path(target_abspath, &kind, &is_special,
680                                     scratch_pool));
681   if (kind != svn_node_file || is_special)
682     {
683       *merge_outcome = svn_wc_merge_no_merge;
684       return SVN_NO_ERROR;
685     }
686
687   /* Check the files */
688   SVN_ERR(svn_io_files_contents_three_same_p(&same_left_right,
689                                              &same_right_target,
690                                              &same_left_target,
691                                              left_abspath,
692                                              right_abspath,
693                                              detranslated_target_abspath,
694                                              scratch_pool));
695
696   /* If the LEFT side of the merge is equal to WORKING, then we can
697    * copy RIGHT directly. */
698   if (same_left_target)
699     {
700       /* If the left side equals the right side, there is no change to merge
701        * so we leave the target unchanged. */
702       if (same_left_right)
703         {
704           *merge_outcome = svn_wc_merge_unchanged;
705         }
706       else
707         {
708           *merge_outcome = svn_wc_merge_merged;
709           if (!dry_run)
710             {
711               const char *wcroot_abspath;
712               svn_boolean_t delete_src = FALSE;
713
714               /* The right_abspath might be outside our working copy. In that
715                  case we should copy the file to a safe location before
716                  installing to avoid breaking the workqueue.
717
718                  This matches the behavior in preserve_pre_merge_files */
719
720               SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
721                                             db, target_abspath,
722                                             scratch_pool, scratch_pool));
723
724               if (!svn_dirent_is_child(wcroot_abspath, right_abspath, NULL))
725                 {
726                   svn_stream_t *tmp_src;
727                   svn_stream_t *tmp_dst;
728
729                   SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath,
730                                                    scratch_pool,
731                                                    scratch_pool));
732
733                   SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath,
734                                                      NULL, NULL,
735                                                      db, target_abspath,
736                                                      scratch_pool,
737                                                      scratch_pool));
738
739                   SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst,
740                                            cancel_func, cancel_baton,
741                                            scratch_pool));
742
743                   delete_src = TRUE;
744                 }
745
746               SVN_ERR(svn_wc__wq_build_file_install(
747                         &work_item, db, target_abspath, right_abspath,
748                         FALSE /* use_commit_times */,
749                         FALSE /* record_fileinfo */,
750                         result_pool, scratch_pool));
751               *work_items = svn_wc__wq_merge(*work_items, work_item,
752                                              result_pool);
753
754               if (delete_src)
755                 {
756                   SVN_ERR(svn_wc__wq_build_file_remove(
757                                     &work_item, db, wcroot_abspath,
758                                     right_abspath,
759                                     result_pool, scratch_pool));
760                   *work_items = svn_wc__wq_merge(*work_items, work_item,
761                                                  result_pool);
762                 }
763             }
764         }
765
766       return SVN_NO_ERROR;
767     }
768   else
769     {
770       /* If the locally existing, changed file equals the incoming 'right'
771        * file, there is no conflict.  For binary files, we historically
772        * conflicted them needlessly, while merge_text_file figured it out
773        * eventually and returned svn_wc_merge_unchanged for them, which
774        * is what we do here. */
775       if (same_right_target)
776         {
777           *merge_outcome = svn_wc_merge_unchanged;
778           return SVN_NO_ERROR;
779         }
780     }
781
782   *merge_outcome = svn_wc_merge_no_merge;
783   return SVN_NO_ERROR;
784 }
785
786
787 /* Handle a non-trivial merge of 'text' files.  (Assume that a trivial
788  * merge was not possible.)
789  *
790  * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME according to the
791  * result -- to install the merged file, or to indicate a conflict.
792  *
793  * On successful merge, leave the result in a temporary file and set
794  * *WORK_ITEMS to hold work items that will translate and install that
795  * file into its proper form and place (unless DRY_RUN) and delete the
796  * temporary file (in any case).  Set *MERGE_OUTCOME to 'merged' or
797  * 'unchanged'.
798  *
799  * If a conflict occurs, set *MERGE_OUTCOME to 'conflicted', and (unless
800  * DRY_RUN) set *WORK_ITEMS and *CONFLICT_SKEL to record the conflict
801  * and copies of the pre-merge files.  See preserve_pre_merge_files()
802  * for details.
803  *
804  * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL
805  * must either point to an existing conflict skel or be NULL.
806  */
807 static svn_error_t*
808 merge_text_file(svn_skel_t **work_items,
809                 svn_skel_t **conflict_skel,
810                 enum svn_wc_merge_outcome_t *merge_outcome,
811                 const merge_target_t *mt,
812                 const char *left_abspath,
813                 const char *right_abspath,
814                 const char *left_label,
815                 const char *right_label,
816                 const char *target_label,
817                 svn_boolean_t dry_run,
818                 const char *detranslated_target_abspath,
819                 svn_cancel_func_t cancel_func,
820                 void *cancel_baton,
821                 apr_pool_t *result_pool,
822                 apr_pool_t *scratch_pool)
823 {
824   apr_pool_t *pool = scratch_pool;  /* ### temporary rename  */
825   svn_boolean_t contains_conflicts;
826   apr_file_t *result_f;
827   const char *result_target;
828   const char *base_name;
829   const char *temp_dir;
830   svn_skel_t *work_item;
831
832   *work_items = NULL;
833
834   base_name = svn_dirent_basename(mt->local_abspath, scratch_pool);
835
836   /* Open a second temporary file for writing; this is where diff3
837      will write the merged results.  We want to use a tempfile
838      with a name that reflects the original, in case this
839      ultimately winds up in a conflict resolution editor.  */
840   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, mt->db, mt->wri_abspath,
841                                          pool, pool));
842   SVN_ERR(svn_io_open_uniquely_named(&result_f, &result_target,
843                                      temp_dir, base_name, ".tmp",
844                                      svn_io_file_del_none, pool, pool));
845
846   /* Run the external or internal merge, as requested. */
847   if (mt->diff3_cmd)
848       SVN_ERR(do_text_merge_external(&contains_conflicts,
849                                      result_f,
850                                      mt->diff3_cmd,
851                                      mt->merge_options,
852                                      detranslated_target_abspath,
853                                      left_abspath,
854                                      right_abspath,
855                                      target_label,
856                                      left_label,
857                                      right_label,
858                                      pool));
859   else /* Use internal merge. */
860     SVN_ERR(do_text_merge(&contains_conflicts,
861                           result_f,
862                           mt->merge_options,
863                           detranslated_target_abspath,
864                           left_abspath,
865                           right_abspath,
866                           target_label,
867                           left_label,
868                           right_label,
869                           pool));
870
871   SVN_ERR(svn_io_file_close(result_f, pool));
872
873   /* Determine the MERGE_OUTCOME, and record any conflict. */
874   if (contains_conflicts && ! dry_run)
875     {
876       *merge_outcome = svn_wc_merge_conflict;
877       if (*merge_outcome == svn_wc_merge_conflict)
878         {
879           const char *left_copy, *right_copy, *target_copy;
880
881           /* Preserve the three conflict files */
882           SVN_ERR(preserve_pre_merge_files(
883                     &work_item,
884                     &left_copy, &right_copy, &target_copy,
885                     mt, left_abspath, right_abspath,
886                     left_label, right_label, target_label,
887                     detranslated_target_abspath,
888                     cancel_func, cancel_baton,
889                     result_pool, scratch_pool));
890           *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
891
892           /* Track the conflict marker files in the metadata. */
893
894           if (!*conflict_skel)
895             *conflict_skel = svn_wc__conflict_skel_create(result_pool);
896
897           SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel,
898                                                           mt->db, mt->local_abspath,
899                                                           target_copy,
900                                                           left_copy,
901                                                           right_copy,
902                                                           result_pool,
903                                                           scratch_pool));
904         }
905
906       if (*merge_outcome == svn_wc_merge_merged)
907         goto done;
908     }
909   else if (contains_conflicts && dry_run)
910       *merge_outcome = svn_wc_merge_conflict;
911   else
912     {
913       svn_boolean_t same, special;
914
915       /* If 'special', then use the detranslated form of the
916          target file.  This is so we don't try to follow symlinks,
917          but the same treatment is probably also appropriate for
918          whatever special file types we may invent in the future. */
919       SVN_ERR(svn_wc__get_translate_info(NULL, NULL, NULL,
920                                          &special, mt->db, mt->local_abspath,
921                                          mt->old_actual_props, TRUE,
922                                          pool, pool));
923       SVN_ERR(svn_io_files_contents_same_p(&same, result_target,
924                                            (special ?
925                                               detranslated_target_abspath :
926                                               mt->local_abspath),
927                                            pool));
928
929       *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
930     }
931
932   if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
933     {
934       /* replace TARGET_ABSPATH with the new merged file, expanding. */
935       SVN_ERR(svn_wc__wq_build_file_install(&work_item,
936                                             mt->db, mt->local_abspath,
937                                             result_target,
938                                             FALSE /* use_commit_times */,
939                                             FALSE /* record_fileinfo */,
940                                             result_pool, scratch_pool));
941       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
942     }
943
944 done:
945   /* Remove the tempfile after use */
946   SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, mt->local_abspath,
947                                        result_target,
948                                        result_pool, scratch_pool));
949
950   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
951
952   return SVN_NO_ERROR;
953 }
954
955 /* Handle a non-trivial merge of 'binary' files: don't actually merge, just
956  * flag a conflict.  (Assume that a trivial merge was not possible.)
957  *
958  * Copy* the files at LEFT_ABSPATH and RIGHT_ABSPATH into the same directory
959  * as the target file, giving them unique names that start with the target
960  * file's name and end with LEFT_LABEL and RIGHT_LABEL respectively.
961  * If the merge target has been 'detranslated' to repository normal form,
962  * move the detranslated file similarly to a unique name ending with
963  * TARGET_LABEL.
964  *
965  * ### * Why do we copy the left and right temp files when we could (maybe
966  *     not always?) move them?
967  *
968  * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL
969  * must either point to an existing conflict skel or be NULL.
970  *
971  * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME to indicate the
972  * conflict.
973  *
974  * ### Why do we not use preserve_pre_merge_files() in here?  The
975  *     behaviour would be slightly different, more consistent: the
976  *     preserved 'left' and 'right' files would be translated to working
977  *     copy form, which may make a difference when a binary file
978  *     contains keyword expansions or when some versions of the file are
979  *     not 'binary' even though we're merging in 'binary files' mode.
980  */
981 static svn_error_t *
982 merge_binary_file(svn_skel_t **work_items,
983                   svn_skel_t **conflict_skel,
984                   enum svn_wc_merge_outcome_t *merge_outcome,
985                   const merge_target_t *mt,
986                   const char *left_abspath,
987                   const char *right_abspath,
988                   const char *left_label,
989                   const char *right_label,
990                   const char *target_label,
991                   svn_boolean_t dry_run,
992                   const char *detranslated_target_abspath,
993                   apr_pool_t *result_pool,
994                   apr_pool_t *scratch_pool)
995 {
996   apr_pool_t *pool = scratch_pool;  /* ### temporary rename  */
997   /* ### when making the binary-file backups, should we be honoring
998      keywords and eol stuff?   */
999   const char *left_copy, *right_copy;
1000   const char *merge_dirpath, *merge_filename;
1001   const char *conflict_wrk;
1002
1003   *work_items = NULL;
1004
1005   svn_dirent_split(&merge_dirpath, &merge_filename, mt->local_abspath, pool);
1006
1007   if (dry_run)
1008     {
1009       *merge_outcome = svn_wc_merge_conflict;
1010       return SVN_NO_ERROR;
1011     }
1012
1013   /* reserve names for backups of left and right fulltexts */
1014   SVN_ERR(svn_io_open_uniquely_named(NULL,
1015                                      &left_copy,
1016                                      merge_dirpath,
1017                                      merge_filename,
1018                                      left_label,
1019                                      svn_io_file_del_none,
1020                                      pool, pool));
1021
1022   SVN_ERR(svn_io_open_uniquely_named(NULL,
1023                                      &right_copy,
1024                                      merge_dirpath,
1025                                      merge_filename,
1026                                      right_label,
1027                                      svn_io_file_del_none,
1028                                      pool, pool));
1029
1030   /* create the backup files */
1031   SVN_ERR(svn_io_copy_file(left_abspath, left_copy, TRUE, pool));
1032   SVN_ERR(svn_io_copy_file(right_abspath, right_copy, TRUE, pool));
1033
1034   /* Was the merge target detranslated? */
1035   if (strcmp(mt->local_abspath, detranslated_target_abspath) != 0)
1036     {
1037       /* Create a .mine file too */
1038       SVN_ERR(svn_io_open_uniquely_named(NULL,
1039                                          &conflict_wrk,
1040                                          merge_dirpath,
1041                                          merge_filename,
1042                                          target_label,
1043                                          svn_io_file_del_none,
1044                                          pool, pool));
1045       SVN_ERR(svn_wc__wq_build_file_move(work_items, mt->db,
1046                                          mt->local_abspath,
1047                                          detranslated_target_abspath,
1048                                          conflict_wrk,
1049                                          pool, result_pool));
1050     }
1051   else
1052     {
1053       conflict_wrk = NULL;
1054     }
1055
1056   /* Mark target_abspath's entry as "Conflicted", and start tracking
1057      the backup files in the entry as well. */
1058   if (!*conflict_skel)
1059     *conflict_skel = svn_wc__conflict_skel_create(result_pool);
1060
1061   SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel,
1062                                                   mt->db, mt->local_abspath,
1063                                                   conflict_wrk,
1064                                                   left_copy,
1065                                                   right_copy,
1066                                                   result_pool, scratch_pool));
1067
1068   *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */
1069
1070   return SVN_NO_ERROR;
1071 }
1072
1073 svn_error_t *
1074 svn_wc__internal_merge(svn_skel_t **work_items,
1075                        svn_skel_t **conflict_skel,
1076                        enum svn_wc_merge_outcome_t *merge_outcome,
1077                        svn_wc__db_t *db,
1078                        const char *left_abspath,
1079                        const char *right_abspath,
1080                        const char *target_abspath,
1081                        const char *wri_abspath,
1082                        const char *left_label,
1083                        const char *right_label,
1084                        const char *target_label,
1085                        apr_hash_t *old_actual_props,
1086                        svn_boolean_t dry_run,
1087                        const char *diff3_cmd,
1088                        const apr_array_header_t *merge_options,
1089                        const apr_array_header_t *prop_diff,
1090                        svn_cancel_func_t cancel_func,
1091                        void *cancel_baton,
1092                        apr_pool_t *result_pool,
1093                        apr_pool_t *scratch_pool)
1094 {
1095   const char *detranslated_target_abspath;
1096   svn_boolean_t is_binary = FALSE;
1097   const svn_prop_t *mimeprop;
1098   svn_skel_t *work_item;
1099   merge_target_t mt;
1100
1101   SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
1102   SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
1103   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
1104
1105   *work_items = NULL;
1106
1107   /* Fill the merge target baton */
1108   mt.db = db;
1109   mt.local_abspath = target_abspath;
1110   mt.wri_abspath = wri_abspath;
1111   mt.old_actual_props = old_actual_props;
1112   mt.prop_diff = prop_diff;
1113   mt.diff3_cmd = diff3_cmd;
1114   mt.merge_options = merge_options;
1115
1116   /* Decide if the merge target is a text or binary file. */
1117   if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
1118       && mimeprop->value)
1119     is_binary = svn_mime_type_is_binary(mimeprop->value->data);
1120   else
1121     {
1122       const char *value = svn_prop_get_value(mt.old_actual_props,
1123                                              SVN_PROP_MIME_TYPE);
1124
1125       is_binary = value && svn_mime_type_is_binary(value);
1126     }
1127
1128   SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt,
1129                               (! is_binary) && diff3_cmd != NULL,
1130                               target_abspath,
1131                               cancel_func, cancel_baton,
1132                               scratch_pool, scratch_pool));
1133
1134   /* We cannot depend on the left file to contain the same eols as the
1135      right file. If the merge target has mods, this will mark the entire
1136      file as conflicted, so we need to compensate. */
1137   SVN_ERR(maybe_update_target_eols(&left_abspath, prop_diff, left_abspath,
1138                                    cancel_func, cancel_baton,
1139                                    scratch_pool, scratch_pool));
1140
1141   SVN_ERR(merge_file_trivial(work_items, merge_outcome,
1142                              left_abspath, right_abspath,
1143                              target_abspath, detranslated_target_abspath,
1144                              dry_run, db, cancel_func, cancel_baton,
1145                              result_pool, scratch_pool));
1146   if (*merge_outcome == svn_wc_merge_no_merge)
1147     {
1148       /* We have a non-trivial merge.  If we classify it as a merge of
1149        * 'binary' files we'll just raise a conflict, otherwise we'll do
1150        * the actual merge of 'text' file contents. */
1151       if (is_binary)
1152         {
1153           /* Raise a text conflict */
1154           SVN_ERR(merge_binary_file(work_items,
1155                                     conflict_skel,
1156                                     merge_outcome,
1157                                     &mt,
1158                                     left_abspath,
1159                                     right_abspath,
1160                                     left_label,
1161                                     right_label,
1162                                     target_label,
1163                                     dry_run,
1164                                     detranslated_target_abspath,
1165                                     result_pool, scratch_pool));
1166         }
1167       else
1168         {
1169           SVN_ERR(merge_text_file(work_items,
1170                                   conflict_skel,
1171                                   merge_outcome,
1172                                   &mt,
1173                                   left_abspath,
1174                                   right_abspath,
1175                                   left_label,
1176                                   right_label,
1177                                   target_label,
1178                                   dry_run,
1179                                   detranslated_target_abspath,
1180                                   cancel_func, cancel_baton,
1181                                   result_pool, scratch_pool));
1182         }
1183     }
1184
1185   /* Merging is complete.  Regardless of text or binariness, we might
1186      need to tweak the executable bit on the new working file, and
1187      possibly make it read-only. */
1188   if (! dry_run)
1189     {
1190       SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
1191                                                target_abspath,
1192                                                result_pool, scratch_pool));
1193       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1194     }
1195
1196   return SVN_NO_ERROR;
1197 }
1198
1199
1200 svn_error_t *
1201 svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome,
1202               enum svn_wc_notify_state_t *merge_props_outcome,
1203               svn_wc_context_t *wc_ctx,
1204               const char *left_abspath,
1205               const char *right_abspath,
1206               const char *target_abspath,
1207               const char *left_label,
1208               const char *right_label,
1209               const char *target_label,
1210               const svn_wc_conflict_version_t *left_version,
1211               const svn_wc_conflict_version_t *right_version,
1212               svn_boolean_t dry_run,
1213               const char *diff3_cmd,
1214               const apr_array_header_t *merge_options,
1215               apr_hash_t *original_props,
1216               const apr_array_header_t *prop_diff,
1217               svn_wc_conflict_resolver_func2_t conflict_func,
1218               void *conflict_baton,
1219               svn_cancel_func_t cancel_func,
1220               void *cancel_baton,
1221               apr_pool_t *scratch_pool)
1222 {
1223   const char *dir_abspath = svn_dirent_dirname(target_abspath, scratch_pool);
1224   svn_skel_t *work_items;
1225   svn_skel_t *conflict_skel = NULL;
1226   apr_hash_t *pristine_props = NULL;
1227   apr_hash_t *old_actual_props;
1228   apr_hash_t *new_actual_props = NULL;
1229
1230   SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
1231   SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
1232   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
1233
1234   /* Before we do any work, make sure we hold a write lock.  */
1235   if (!dry_run)
1236     SVN_ERR(svn_wc__write_check(wc_ctx->db, dir_abspath, scratch_pool));
1237
1238   /* Sanity check:  the merge target must be a file under revision control */
1239   {
1240     svn_wc__db_status_t status;
1241     svn_node_kind_t kind;
1242     svn_boolean_t had_props;
1243     svn_boolean_t props_mod;
1244     svn_boolean_t conflicted;
1245
1246     SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
1247                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1248                                  NULL, NULL, NULL, NULL, NULL, NULL,
1249                                  &conflicted, NULL, &had_props, &props_mod,
1250                                  NULL, NULL, NULL,
1251                                  wc_ctx->db, target_abspath,
1252                                  scratch_pool, scratch_pool));
1253
1254     if (kind != svn_node_file || (status != svn_wc__db_status_normal
1255                                   && status != svn_wc__db_status_added))
1256       {
1257         *merge_content_outcome = svn_wc_merge_no_merge;
1258         if (merge_props_outcome)
1259           *merge_props_outcome = svn_wc_notify_state_unchanged;
1260         return SVN_NO_ERROR;
1261       }
1262
1263     if (conflicted)
1264       {
1265         svn_boolean_t text_conflicted;
1266         svn_boolean_t prop_conflicted;
1267         svn_boolean_t tree_conflicted;
1268
1269         SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted,
1270                                               &prop_conflicted,
1271                                               &tree_conflicted,
1272                                               wc_ctx->db, target_abspath,
1273                                               scratch_pool));
1274
1275         /* We can't install two prop conflicts on a single node, so
1276            avoid even checking that we have to merge it */
1277         if (text_conflicted || prop_conflicted || tree_conflicted)
1278           {
1279             return svn_error_createf(
1280                             SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
1281                             _("Can't merge into conflicted node '%s'"),
1282                             svn_dirent_local_style(target_abspath,
1283                                                    scratch_pool));
1284           }
1285         /* else: Conflict was resolved by removing markers */
1286       }
1287
1288     if (merge_props_outcome && had_props)
1289       {
1290         SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1291                                                wc_ctx->db, target_abspath,
1292                                                scratch_pool, scratch_pool));
1293       }
1294     else if (merge_props_outcome)
1295       pristine_props = apr_hash_make(scratch_pool);
1296
1297     if (props_mod)
1298       {
1299         SVN_ERR(svn_wc__db_read_props(&old_actual_props,
1300                                       wc_ctx->db, target_abspath,
1301                                       scratch_pool, scratch_pool));
1302       }
1303     else if (pristine_props)
1304       old_actual_props = pristine_props;
1305     else
1306       old_actual_props = apr_hash_make(scratch_pool);
1307   }
1308
1309   /* Merge the properties, if requested.  We merge the properties first
1310    * because the properties can affect the text (EOL style, keywords). */
1311   if (merge_props_outcome)
1312     {
1313       int i;
1314
1315       /* The PROPCHANGES may not have non-"normal" properties in it. If entry
1316          or wc props were allowed, then the following code would install them
1317          into the BASE and/or WORKING properties(!).  */
1318       for (i = prop_diff->nelts; i--; )
1319         {
1320           const svn_prop_t *change = &APR_ARRAY_IDX(prop_diff, i, svn_prop_t);
1321
1322           if (!svn_wc_is_normal_prop(change->name))
1323             return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
1324                                      _("The property '%s' may not be merged "
1325                                        "into '%s'."),
1326                                      change->name,
1327                                      svn_dirent_local_style(target_abspath,
1328                                                             scratch_pool));
1329         }
1330
1331       SVN_ERR(svn_wc__merge_props(&conflict_skel,
1332                                   merge_props_outcome,
1333                                   &new_actual_props,
1334                                   wc_ctx->db, target_abspath,
1335                                   original_props, pristine_props, old_actual_props,
1336                                   prop_diff,
1337                                   scratch_pool, scratch_pool));
1338     }
1339
1340   /* Merge the text. */
1341   SVN_ERR(svn_wc__internal_merge(&work_items,
1342                                  &conflict_skel,
1343                                  merge_content_outcome,
1344                                  wc_ctx->db,
1345                                  left_abspath,
1346                                  right_abspath,
1347                                  target_abspath,
1348                                  target_abspath,
1349                                  left_label, right_label, target_label,
1350                                  old_actual_props,
1351                                  dry_run,
1352                                  diff3_cmd,
1353                                  merge_options,
1354                                  prop_diff,
1355                                  cancel_func, cancel_baton,
1356                                  scratch_pool, scratch_pool));
1357
1358   /* If this isn't a dry run, then update the DB, run the work, and
1359    * call the conflict resolver callback.  */
1360   if (!dry_run)
1361     {
1362       if (conflict_skel)
1363         {
1364           svn_skel_t *work_item;
1365
1366           SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel,
1367                                                      left_version,
1368                                                      right_version,
1369                                                      scratch_pool,
1370                                                      scratch_pool));
1371
1372           SVN_ERR(svn_wc__conflict_create_markers(&work_item,
1373                                                   wc_ctx->db, target_abspath,
1374                                                   conflict_skel,
1375                                                   scratch_pool, scratch_pool));
1376
1377           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1378         }
1379
1380       if (new_actual_props)
1381         SVN_ERR(svn_wc__db_op_set_props(wc_ctx->db, target_abspath,
1382                                         new_actual_props,
1383                                         svn_wc__has_magic_property(prop_diff),
1384                                         conflict_skel, work_items,
1385                                         scratch_pool));
1386       else if (conflict_skel)
1387         SVN_ERR(svn_wc__db_op_mark_conflict(wc_ctx->db, target_abspath,
1388                                             conflict_skel, work_items,
1389                                             scratch_pool));
1390       else if (work_items)
1391         SVN_ERR(svn_wc__db_wq_add(wc_ctx->db, target_abspath, work_items,
1392                                   scratch_pool));
1393
1394       if (work_items)
1395         SVN_ERR(svn_wc__wq_run(wc_ctx->db, target_abspath,
1396                                cancel_func, cancel_baton,
1397                                scratch_pool));
1398
1399       if (conflict_skel && conflict_func)
1400         {
1401           svn_boolean_t text_conflicted, prop_conflicted;
1402
1403           SVN_ERR(svn_wc__conflict_invoke_resolver(
1404                     wc_ctx->db, target_abspath,
1405                     conflict_skel, merge_options,
1406                     conflict_func, conflict_baton,
1407                     cancel_func, cancel_baton,
1408                     scratch_pool));
1409
1410           /* Reset *MERGE_CONTENT_OUTCOME etc. if a conflict was resolved. */
1411           SVN_ERR(svn_wc__internal_conflicted_p(
1412                     &text_conflicted, &prop_conflicted, NULL,
1413                     wc_ctx->db, target_abspath, scratch_pool));
1414           if (*merge_props_outcome == svn_wc_notify_state_conflicted
1415               && ! prop_conflicted)
1416             *merge_props_outcome = svn_wc_notify_state_merged;
1417           if (*merge_content_outcome == svn_wc_merge_conflict
1418               && ! text_conflicted)
1419             *merge_content_outcome = svn_wc_merge_merged;
1420         }
1421     }
1422
1423   return SVN_NO_ERROR;
1424 }