~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_wc/merge.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * merge.c:  merging changes into a working file
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
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.
 
12
 *
 
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
 * ====================================================================
 
17
 */
 
18
 
 
19
 
 
20
 
 
21
#include "svn_wc.h"
 
22
#include "svn_diff.h"
 
23
#include "svn_path.h"
 
24
 
 
25
#include "wc.h"
 
26
#include "entries.h"
 
27
#include "translate.h"
 
28
#include "questions.h"
 
29
 
 
30
#include "svn_private_config.h"
 
31
 
 
32
 
 
33
 
 
34
svn_error_t *
 
35
svn_wc_merge (const char *left,
 
36
              const char *right,
 
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,
 
45
              apr_pool_t *pool)
 
46
{
 
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;
 
52
  const char *eol;
 
53
  const svn_wc_entry_t *entry;
 
54
  svn_boolean_t contains_conflicts, special;
 
55
 
 
56
  svn_path_split (merge_target, &mt_pt, &mt_bn, pool);
 
57
 
 
58
  /* Sanity check:  the merge target must be under revision control. */
 
59
  SVN_ERR (svn_wc_entry (&entry, merge_target, adm_access, FALSE, pool));
 
60
  if (! entry)
 
61
    {
 
62
      *merge_outcome = svn_wc_merge_no_merge;
 
63
      return SVN_NO_ERROR;
 
64
    }
 
65
 
 
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));
 
68
  
 
69
  if (! is_binary)              /* this is a text file */
 
70
    {
 
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,
 
75
                                       TRUE, pool));
 
76
      if (tmp_target == merge_target)  /* contraction didn't happen */
 
77
        {
 
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,
 
81
                                            merge_target,
 
82
                                            SVN_WC__TMP_EXT,
 
83
                                            FALSE, pool));
 
84
          SVN_ERR (svn_io_file_close (tmp_f, pool));
 
85
          SVN_ERR (svn_io_copy_file (merge_target,
 
86
                                     tmp_target, TRUE, pool));
 
87
        }
 
88
 
 
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,
 
93
                                        FALSE, pool));
 
94
 
 
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,
 
100
                                        tmp_target,
 
101
                                        SVN_WC__TMP_EXT,
 
102
                                        FALSE, pool));
 
103
      SVN_ERR (svn_io_file_close (tmp_f, pool));
 
104
      SVN_ERR (svn_io_open_unique_file (&tmp_f, &tmp_right,
 
105
                                        tmp_target,
 
106
                                        SVN_WC__TMP_EXT,
 
107
                                        FALSE, pool));
 
108
      SVN_ERR (svn_io_file_close (tmp_f, pool));
 
109
    
 
110
      SVN_ERR (svn_io_copy_file (left, tmp_left, TRUE, pool));
 
111
      SVN_ERR (svn_io_copy_file (right, tmp_right, TRUE, pool));
 
112
 
 
113
      /* Run an external merge if requested. */
 
114
      if (diff3_cmd)
 
115
        {
 
116
          int exit_code;
 
117
 
 
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));
 
122
          
 
123
          contains_conflicts = exit_code == 1;
 
124
        }
 
125
      else
 
126
        {
 
127
          svn_diff_t *diff;
 
128
          const char *target_marker;
 
129
          const char *left_marker;
 
130
          const char *right_marker;
 
131
          svn_stream_t *ostream;
 
132
 
 
133
          ostream = svn_stream_from_aprfile(result_f, pool);
 
134
 
 
135
          SVN_ERR (svn_diff_file_diff3 (&diff,
 
136
                                        tmp_left, tmp_target, tmp_right,
 
137
                                        pool));
 
138
 
 
139
          /* Labels fall back to sensible defaults if not specified. */
 
140
          if (target_label)
 
141
            target_marker = apr_psprintf (pool, "<<<<<<< %s", target_label);
 
142
          else
 
143
            target_marker = "<<<<<<< .working";
 
144
 
 
145
          if (left_label)
 
146
            left_marker = apr_psprintf (pool, "||||||| %s", left_label);
 
147
          else
 
148
            left_marker = "||||||| .old";
 
149
 
 
150
          if (right_label)
 
151
            right_marker = apr_psprintf (pool, ">>>>>>> %s", right_label);
 
152
          else
 
153
            right_marker = ">>>>>>> .new";
 
154
 
 
155
          SVN_ERR (svn_diff_file_output_merge (ostream, diff,
 
156
                                               tmp_left, tmp_target, tmp_right,
 
157
                                               left_marker,
 
158
                                               target_marker,
 
159
                                               right_marker,
 
160
                                               "=======", /* seperator */
 
161
                                               FALSE, /* display original */
 
162
                                               FALSE, /* resolve conflicts */
 
163
                                               pool));
 
164
          SVN_ERR (svn_stream_close (ostream));
 
165
 
 
166
          contains_conflicts = svn_diff_contains_conflicts (diff);
 
167
        }
 
168
 
 
169
      /* Close the output file */
 
170
      SVN_ERR (svn_io_file_close (result_f, pool));
 
171
 
 
172
      if (contains_conflicts && ! dry_run)  /* got a conflict */
 
173
        {
 
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;
 
181
      
 
182
          /* I miss Lisp. */
 
183
 
 
184
          SVN_ERR (svn_io_open_unique_file (&lcopy_f,
 
185
                                            &left_copy,
 
186
                                            merge_target,
 
187
                                            left_label,
 
188
                                            FALSE,
 
189
                                            pool));
 
190
          SVN_ERR (svn_io_file_close (lcopy_f, pool));
 
191
 
 
192
          /* Have I mentioned how much I miss Lisp? */
 
193
 
 
194
          SVN_ERR (svn_io_open_unique_file (&rcopy_f,
 
195
                                            &right_copy,
 
196
                                            merge_target,
 
197
                                            right_label,
 
198
                                            FALSE,
 
199
                                            pool));
 
200
          SVN_ERR (svn_io_file_close (rcopy_f, pool));
 
201
 
 
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! */
 
206
 
 
207
          SVN_ERR (svn_io_open_unique_file (&tcopy_f,
 
208
                                            &target_copy,
 
209
                                            merge_target,
 
210
                                            target_label,
 
211
                                            FALSE,
 
212
                                            pool));
 
213
          SVN_ERR (svn_io_file_close (tcopy_f, pool));
 
214
 
 
215
          /* We preserve all the files with keywords expanded and line
 
216
             endings in local (working) form. */
 
217
 
 
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.  */
 
229
 
 
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,
 
233
                                         NULL, pool));
 
234
          SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
 
235
                                          pool));
 
236
          SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
 
237
                                        pool));
 
238
          SVN_ERR (svn_subst_copy_and_translate2 (left, 
 
239
                                                  left_copy,
 
240
                                                  eol, eol ? TRUE : FALSE, 
 
241
                                                  keywords, TRUE, special,
 
242
                                                  pool));
 
243
          SVN_ERR (svn_subst_copy_and_translate2 (right,
 
244
                                                  right_copy,
 
245
                                                  eol, eol ? TRUE : FALSE, 
 
246
                                                  keywords, TRUE, special,
 
247
                                                  pool));
 
248
 
 
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));
 
252
 
 
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;
 
260
 
 
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,
 
264
                                        pool));
 
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,
 
270
                    TRUE, pool));
 
271
 
 
272
          *merge_outcome = svn_wc_merge_conflict;
 
273
 
 
274
        }
 
275
      else if (contains_conflicts && dry_run)
 
276
        {
 
277
          *merge_outcome = svn_wc_merge_conflict;
 
278
        } /* end of conflict handling */
 
279
      else
 
280
        {
 
281
          svn_boolean_t same;
 
282
          SVN_ERR (svn_io_files_contents_same_p (&same, result_target,
 
283
                                                  merge_target, pool));
 
284
 
 
285
          *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
 
286
        }
 
287
 
 
288
      if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
 
289
        {
 
290
          /* replace MERGE_TARGET with the new merged file, expanding. */
 
291
          SVN_ERR (svn_wc__get_keywords (&keywords, merge_target, adm_access,
 
292
                                         NULL, pool));
 
293
          SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
 
294
                                          pool));
 
295
          SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
 
296
                                        pool));
 
297
          SVN_ERR (svn_subst_copy_and_translate2 (result_target, merge_target,
 
298
                                                  eol, eol ? TRUE : FALSE, 
 
299
                                                  keywords, TRUE, special,
 
300
                                                  pool));
 
301
        }
 
302
 
 
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"));
 
313
 
 
314
    } /* end of merging for text files */
 
315
 
 
316
  else if (! dry_run) /* merging procedure for binary files */
 
317
    {
 
318
      /* ### when making the binary-file backups, should we be honoring
 
319
         keywords and eol stuff?   */
 
320
 
 
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;
 
326
      
 
327
      /* reserve names for backups of left and right fulltexts */
 
328
      SVN_ERR (svn_io_open_unique_file (&lcopy_f,
 
329
                                        &left_copy,
 
330
                                        merge_target,
 
331
                                        left_label,
 
332
                                        FALSE,
 
333
                                        pool));
 
334
      SVN_ERR (svn_io_file_close (lcopy_f, pool));
 
335
 
 
336
      SVN_ERR (svn_io_open_unique_file (&rcopy_f,
 
337
                                        &right_copy,
 
338
                                        merge_target,
 
339
                                        right_label,
 
340
                                        FALSE,
 
341
                                        pool));
 
342
      SVN_ERR (svn_io_file_close (rcopy_f, pool));
 
343
 
 
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));
 
349
      
 
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;
 
356
 
 
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,
 
365
                TRUE, pool));
 
366
 
 
367
      *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */
 
368
 
 
369
    } /* end of binary conflict handling */
 
370
  else
 
371
    *merge_outcome = svn_wc_merge_conflict; /* dry_run for binary files. */
 
372
 
 
373
  /* Merging is complete.  Regardless of text or binariness, we might
 
374
     need to tweak the executable bit on the new working file.  */
 
375
  if (! dry_run)
 
376
    {
 
377
      SVN_ERR (svn_wc__maybe_set_executable (NULL, merge_target, adm_access,
 
378
                                             pool));
 
379
 
 
380
      SVN_ERR (svn_wc__maybe_set_read_only (NULL, merge_target,
 
381
                                            adm_access, pool));
 
382
 
 
383
    }
 
384
  return SVN_NO_ERROR;
 
385
}