2
* merge.c: merging changes into a working file
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
27
#include "translate.h"
28
#include "questions.h"
30
#include "svn_private_config.h"
35
svn_wc_merge (const char *left,
37
const char *merge_target,
38
svn_wc_adm_access_t *adm_access,
39
const char *left_label,
40
const char *right_label,
41
const char *target_label,
42
svn_boolean_t dry_run,
43
enum svn_wc_merge_outcome_t *merge_outcome,
44
const char *diff3_cmd,
47
const char *tmp_target, *result_target, *tmp_left, *tmp_right;
48
const char *mt_pt, *mt_bn;
49
apr_file_t *tmp_f, *result_f;
50
svn_boolean_t is_binary;
51
svn_subst_keywords_t *keywords;
53
const svn_wc_entry_t *entry;
54
svn_boolean_t contains_conflicts, special;
56
svn_path_split (merge_target, &mt_pt, &mt_bn, pool);
58
/* Sanity check: the merge target must be under revision control. */
59
SVN_ERR (svn_wc_entry (&entry, merge_target, adm_access, FALSE, pool));
62
*merge_outcome = svn_wc_merge_no_merge;
66
/* Decide if the merge target is a text or binary file. */
67
SVN_ERR (svn_wc_has_binary_prop (&is_binary, merge_target, adm_access, pool));
69
if (! is_binary) /* this is a text file */
71
/* Make sure a temporary copy of 'target' is available with keywords
72
contracted and line endings in repository-normal (LF) form.
73
This is the file that diff3 will read as the 'mine' file. */
74
SVN_ERR (svn_wc_translated_file (&tmp_target, merge_target, adm_access,
76
if (tmp_target == merge_target) /* contraction didn't happen */
78
/* The target is already in repository form, so we just need to
79
make a verbatim copy of it. */
80
SVN_ERR (svn_io_open_unique_file (&tmp_f, &tmp_target,
84
SVN_ERR (svn_io_file_close (tmp_f, pool));
85
SVN_ERR (svn_io_copy_file (merge_target,
86
tmp_target, TRUE, pool));
89
/* Open a second temporary file for writing; this is where diff3
90
will write the merged results. */
91
SVN_ERR (svn_io_open_unique_file (&result_f, &result_target,
92
merge_target, SVN_WC__TMP_EXT,
95
/* LEFT and RIGHT might be in totally different directories than
96
MERGE_TARGET, and our diff3 command wants them all to be in
97
the same directory. So make temporary copies of LEFT and
98
RIGHT right next to the target. */
99
SVN_ERR (svn_io_open_unique_file (&tmp_f, &tmp_left,
103
SVN_ERR (svn_io_file_close (tmp_f, pool));
104
SVN_ERR (svn_io_open_unique_file (&tmp_f, &tmp_right,
108
SVN_ERR (svn_io_file_close (tmp_f, pool));
110
SVN_ERR (svn_io_copy_file (left, tmp_left, TRUE, pool));
111
SVN_ERR (svn_io_copy_file (right, tmp_right, TRUE, pool));
113
/* Run an external merge if requested. */
118
SVN_ERR (svn_io_run_diff3 (".",
119
tmp_target, tmp_left, tmp_right,
120
target_label, left_label, right_label,
121
result_f, &exit_code, diff3_cmd, pool));
123
contains_conflicts = exit_code == 1;
128
const char *target_marker;
129
const char *left_marker;
130
const char *right_marker;
131
svn_stream_t *ostream;
133
ostream = svn_stream_from_aprfile(result_f, pool);
135
SVN_ERR (svn_diff_file_diff3 (&diff,
136
tmp_left, tmp_target, tmp_right,
139
/* Labels fall back to sensible defaults if not specified. */
141
target_marker = apr_psprintf (pool, "<<<<<<< %s", target_label);
143
target_marker = "<<<<<<< .working";
146
left_marker = apr_psprintf (pool, "||||||| %s", left_label);
148
left_marker = "||||||| .old";
151
right_marker = apr_psprintf (pool, ">>>>>>> %s", right_label);
153
right_marker = ">>>>>>> .new";
155
SVN_ERR (svn_diff_file_output_merge (ostream, diff,
156
tmp_left, tmp_target, tmp_right,
160
"=======", /* seperator */
161
FALSE, /* display original */
162
FALSE, /* resolve conflicts */
164
SVN_ERR (svn_stream_close (ostream));
166
contains_conflicts = svn_diff_contains_conflicts (diff);
169
/* Close the output file */
170
SVN_ERR (svn_io_file_close (result_f, pool));
172
if (contains_conflicts && ! dry_run) /* got a conflict */
174
/* Preserve the three pre-merge files, and modify the
175
entry (mark as conflicted, track the preserved files). */
176
apr_file_t *lcopy_f, *rcopy_f, *tcopy_f;
177
const char *left_copy, *right_copy, *target_copy;
178
const char *parentt, *left_base, *right_base, *target_base;
179
svn_wc_adm_access_t *parent_access;
180
svn_wc_entry_t tmp_entry;
184
SVN_ERR (svn_io_open_unique_file (&lcopy_f,
190
SVN_ERR (svn_io_file_close (lcopy_f, pool));
192
/* Have I mentioned how much I miss Lisp? */
194
SVN_ERR (svn_io_open_unique_file (&rcopy_f,
200
SVN_ERR (svn_io_file_close (rcopy_f, pool));
202
/* Why, how much more pleasant to be forced to unroll my loops.
203
If I'd been writing in Lisp, I might have mapped an inline
204
lambda form over a list, or something equally disgusting.
205
Thank goodness C was here to protect me! */
207
SVN_ERR (svn_io_open_unique_file (&tcopy_f,
213
SVN_ERR (svn_io_file_close (tcopy_f, pool));
215
/* We preserve all the files with keywords expanded and line
216
endings in local (working) form. */
218
/* NOTE: Callers must ensure that the svn:eol-style and
219
svn:keywords property values are correct in the currently
220
installed props. With 'svn merge', it's no big deal. But
221
when 'svn up' calls this routine, it needs to make sure that
222
this routine is using the newest property values that may
223
have been received *during* the update. Since this routine
224
will be run from within a log-command, install_file()
225
needs to make sure that a previous log-command to 'install
226
latest props' has already executed first. Ben and I just
227
checked, and that is indeed the order in which the log items
228
are written, so everything should be fine. Really. */
230
/* Create LEFT and RIGHT backup files, in expanded form.
231
We use merge_target's current properties to do the translation. */
232
SVN_ERR (svn_wc__get_keywords (&keywords, merge_target, adm_access,
234
SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
236
SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
238
SVN_ERR (svn_subst_copy_and_translate2 (left,
240
eol, eol ? TRUE : FALSE,
241
keywords, TRUE, special,
243
SVN_ERR (svn_subst_copy_and_translate2 (right,
245
eol, eol ? TRUE : FALSE,
246
keywords, TRUE, special,
249
/* Back up MERGE_TARGET verbatim (it's already in expanded form.) */
250
SVN_ERR (svn_io_copy_file (merge_target,
251
target_copy, TRUE, pool));
253
/* Derive the basenames of the 3 backup files. */
254
svn_path_split (left_copy, NULL, &left_base, pool);
255
svn_path_split (right_copy, NULL, &right_base, pool);
256
svn_path_split (target_copy, &parentt, &target_base, pool);
257
tmp_entry.conflict_old = left_base;
258
tmp_entry.conflict_new = right_base;
259
tmp_entry.conflict_wrk = target_base;
261
/* Mark merge_target's entry as "Conflicted", and start tracking
262
the backup files in the entry as well. */
263
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parentt,
265
SVN_ERR (svn_wc__entry_modify
266
(parent_access, mt_bn, &tmp_entry,
267
SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
268
| SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
269
| SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
272
*merge_outcome = svn_wc_merge_conflict;
275
else if (contains_conflicts && dry_run)
277
*merge_outcome = svn_wc_merge_conflict;
278
} /* end of conflict handling */
282
SVN_ERR (svn_io_files_contents_same_p (&same, result_target,
283
merge_target, pool));
285
*merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
288
if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
290
/* replace MERGE_TARGET with the new merged file, expanding. */
291
SVN_ERR (svn_wc__get_keywords (&keywords, merge_target, adm_access,
293
SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
295
SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
297
SVN_ERR (svn_subst_copy_and_translate2 (result_target, merge_target,
298
eol, eol ? TRUE : FALSE,
299
keywords, TRUE, special,
303
/* Don't forget to clean up tmp_target, result_target, tmp_left,
304
tmp_right. There are a lot of scratch files lying around. */
305
SVN_ERR_W (svn_io_remove_file (tmp_target, pool),
306
_("Unable to delete temporary file"));
307
SVN_ERR_W (svn_io_remove_file (result_target, pool),
308
_("Unable to delete temporary file"));
309
SVN_ERR_W (svn_io_remove_file (tmp_left, pool),
310
_("Unable to delete temporary file"));
311
SVN_ERR_W (svn_io_remove_file (tmp_right, pool),
312
_("Unable to delete temporary file"));
314
} /* end of merging for text files */
316
else if (! dry_run) /* merging procedure for binary files */
318
/* ### when making the binary-file backups, should we be honoring
319
keywords and eol stuff? */
321
apr_file_t *lcopy_f, *rcopy_f;
322
const char *left_copy, *right_copy;
323
const char *parentt, *left_base, *right_base;
324
svn_wc_adm_access_t *parent_access;
325
svn_wc_entry_t tmp_entry;
327
/* reserve names for backups of left and right fulltexts */
328
SVN_ERR (svn_io_open_unique_file (&lcopy_f,
334
SVN_ERR (svn_io_file_close (lcopy_f, pool));
336
SVN_ERR (svn_io_open_unique_file (&rcopy_f,
342
SVN_ERR (svn_io_file_close (rcopy_f, pool));
344
/* create the backup files */
345
SVN_ERR (svn_io_copy_file (left,
346
left_copy, TRUE, pool));
347
SVN_ERR (svn_io_copy_file (right,
348
right_copy, TRUE, pool));
350
/* Derive the basenames of the backup files. */
351
svn_path_split (left_copy, &parentt, &left_base, pool);
352
svn_path_split (right_copy, &parentt, &right_base, pool);
353
tmp_entry.conflict_old = left_base;
354
tmp_entry.conflict_new = right_base;
355
tmp_entry.conflict_wrk = NULL;
357
/* Mark merge_target's entry as "Conflicted", and start tracking
358
the backup files in the entry as well. */
359
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parentt, pool));
360
SVN_ERR (svn_wc__entry_modify
361
(parent_access, mt_bn, &tmp_entry,
362
SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
363
| SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
364
| SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
367
*merge_outcome = svn_wc_merge_conflict; /* a conflict happened */
369
} /* end of binary conflict handling */
371
*merge_outcome = svn_wc_merge_conflict; /* dry_run for binary files. */
373
/* Merging is complete. Regardless of text or binariness, we might
374
need to tweak the executable bit on the new working file. */
377
SVN_ERR (svn_wc__maybe_set_executable (NULL, merge_target, adm_access,
380
SVN_ERR (svn_wc__maybe_set_read_only (NULL, merge_target,