2
* conflicts.c: routines for managing conflict data.
3
* NOTE: this code doesn't know where the conflict is
6
* ====================================================================
7
* Licensed to the Apache Software Foundation (ASF) under one
8
* or more contributor license agreements. See the NOTICE file
9
* distributed with this work for additional information
10
* regarding copyright ownership. The ASF licenses this file
11
* to you under the Apache License, Version 2.0 (the
12
* "License"); you may not use this file except in compliance
13
* with the License. You may obtain a copy of the License at
15
* http://www.apache.org/licenses/LICENSE-2.0
17
* Unless required by applicable law or agreed to in writing,
18
* software distributed under the License is distributed on an
19
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
* KIND, either express or implied. See the License for the
21
* specific language governing permissions and limitations
23
* ====================================================================
30
#include <apr_pools.h>
31
#include <apr_tables.h>
33
#include <apr_errno.h>
35
#include "svn_types.h"
36
#include "svn_pools.h"
37
#include "svn_string.h"
38
#include "svn_error.h"
39
#include "svn_dirent_uri.h"
46
#include "conflicts.h"
48
#include "private/svn_wc_private.h"
49
#include "private/svn_skel.h"
51
#include "svn_private_config.h"
54
svn_wc__conflict_skel_new(apr_pool_t *result_pool)
56
svn_skel_t *operation = svn_skel__make_empty_list(result_pool);
57
svn_skel_t *result = svn_skel__make_empty_list(result_pool);
59
svn_skel__prepend(operation, result);
65
prepend_prop_value(const svn_string_t *value,
67
apr_pool_t *result_pool)
69
svn_skel_t *value_skel = svn_skel__make_empty_list(result_pool);
73
const void *dup = apr_pmemdup(result_pool, value->data, value->len);
75
svn_skel__prepend(svn_skel__mem_atom(dup, value->len, result_pool),
79
svn_skel__prepend(value_skel, skel);
84
svn_wc__conflict_skel_add_prop_conflict(
86
const char *prop_name,
87
const svn_string_t *original_value,
88
const svn_string_t *mine_value,
89
const svn_string_t *incoming_value,
90
const svn_string_t *incoming_base_value,
91
apr_pool_t *result_pool,
92
apr_pool_t *scratch_pool)
94
svn_skel_t *prop_skel = svn_skel__make_empty_list(result_pool);
96
/* ### check that OPERATION has been filled in. */
98
/* See notes/wc-ng/conflict-storage */
99
prepend_prop_value(incoming_base_value, prop_skel, result_pool);
100
prepend_prop_value(incoming_value, prop_skel, result_pool);
101
prepend_prop_value(mine_value, prop_skel, result_pool);
102
prepend_prop_value(original_value, prop_skel, result_pool);
103
svn_skel__prepend_str(apr_pstrdup(result_pool, prop_name), prop_skel,
105
svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_skel, result_pool);
107
/* Now we append PROP_SKEL to the end of the provided conflict SKEL. */
108
svn_skel__append(skel, prop_skel);
116
/*** Resolving a conflict automatically ***/
119
/* Helper for resolve_conflict_on_entry. Delete the file FILE_ABSPATH
120
in if it exists. Set WAS_PRESENT to TRUE if the file existed, and
121
leave it UNTOUCHED otherwise. */
123
attempt_deletion(const char *file_abspath,
124
svn_boolean_t *was_present,
125
apr_pool_t *scratch_pool)
129
if (file_abspath == NULL)
132
err = svn_io_remove_file2(file_abspath, FALSE, scratch_pool);
134
if (err == NULL || !APR_STATUS_IS_ENOENT(err->apr_err))
137
return svn_error_trace(err);
140
svn_error_clear(err);
145
/* Conflict resolution involves removing the conflict files, if they exist,
146
and clearing the conflict filenames from the entry. The latter needs to
147
be done whether or not the conflict files exist.
149
Tree conflicts are not resolved here, because the data stored in one
150
entry does not refer to that entry but to children of it.
152
PATH is the path to the item to be resolved, BASE_NAME is the basename
153
of PATH, and CONFLICT_DIR is the access baton for PATH. ORIG_ENTRY is
154
the entry prior to resolution. RESOLVE_TEXT and RESOLVE_PROPS are TRUE
155
if text and property conflicts respectively are to be resolved.
157
If this call marks any conflict as resolved, set *DID_RESOLVE to true,
158
else do not change *DID_RESOLVE.
160
See svn_wc_resolved_conflict5() for how CONFLICT_CHOICE behaves.
162
### FIXME: This function should be loggy, otherwise an interruption can
163
### leave, for example, one of the conflict artifact files deleted but
164
### the entry still referring to it and trying to use it for the next
165
### attempt at resolving.
167
### Does this still apply in the world of WC-NG? -hkw
170
resolve_conflict_on_node(svn_wc__db_t *db,
171
const char *local_abspath,
172
svn_boolean_t resolve_text,
173
svn_boolean_t resolve_props,
174
svn_wc_conflict_choice_t conflict_choice,
175
svn_boolean_t *did_resolve,
178
svn_boolean_t found_file;
179
const char *conflict_old = NULL;
180
const char *conflict_new = NULL;
181
const char *conflict_working = NULL;
182
const char *prop_reject_file = NULL;
183
svn_wc__db_kind_t kind;
185
const apr_array_header_t *conflicts;
186
const char *conflict_dir_abspath;
188
*did_resolve = FALSE;
190
SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, pool));
191
SVN_ERR(svn_wc__db_read_conflicts(&conflicts, db, local_abspath,
194
for (i = 0; i < conflicts->nelts; i++)
196
const svn_wc_conflict_description2_t *desc;
198
desc = APR_ARRAY_IDX(conflicts, i,
199
const svn_wc_conflict_description2_t*);
201
if (desc->kind == svn_wc_conflict_kind_text)
203
conflict_old = desc->base_abspath;
204
conflict_new = desc->their_abspath;
205
conflict_working = desc->my_abspath;
207
else if (desc->kind == svn_wc_conflict_kind_property)
208
prop_reject_file = desc->their_abspath;
211
if (kind == svn_wc__db_kind_dir)
212
conflict_dir_abspath = local_abspath;
214
conflict_dir_abspath = svn_dirent_dirname(local_abspath, pool);
218
const char *auto_resolve_src;
220
/* Handle automatic conflict resolution before the temporary files are
221
* deleted, if necessary. */
222
switch (conflict_choice)
224
case svn_wc_conflict_choose_base:
225
auto_resolve_src = conflict_old;
227
case svn_wc_conflict_choose_mine_full:
228
auto_resolve_src = conflict_working;
230
case svn_wc_conflict_choose_theirs_full:
231
auto_resolve_src = conflict_new;
233
case svn_wc_conflict_choose_merged:
234
auto_resolve_src = NULL;
236
case svn_wc_conflict_choose_theirs_conflict:
237
case svn_wc_conflict_choose_mine_conflict:
239
if (conflict_old && conflict_working && conflict_new)
241
const char *temp_dir;
242
svn_stream_t *tmp_stream = NULL;
244
svn_diff_conflict_display_style_t style =
245
conflict_choice == svn_wc_conflict_choose_theirs_conflict
246
? svn_diff_conflict_display_latest
247
: svn_diff_conflict_display_modified;
249
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
250
conflict_dir_abspath,
252
SVN_ERR(svn_stream_open_unique(&tmp_stream,
255
svn_io_file_del_on_pool_cleanup,
258
SVN_ERR(svn_diff_file_diff3_2(&diff,
262
svn_diff_file_options_create(pool),
264
SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff,
268
/* markers ignored */
269
NULL, NULL, NULL, NULL,
272
SVN_ERR(svn_stream_close(tmp_stream));
275
auto_resolve_src = NULL;
279
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
280
_("Invalid 'conflict_result' argument"));
283
if (auto_resolve_src)
284
SVN_ERR(svn_io_copy_file(
285
svn_dirent_join(conflict_dir_abspath, auto_resolve_src, pool),
286
local_abspath, TRUE, pool));
289
/* Records whether we found any of the conflict files. */
294
SVN_ERR(attempt_deletion(conflict_old, &found_file, pool));
295
SVN_ERR(attempt_deletion(conflict_new, &found_file, pool));
296
SVN_ERR(attempt_deletion(conflict_working, &found_file, pool));
297
resolve_text = conflict_old || conflict_new || conflict_working;
301
if (prop_reject_file != NULL)
302
SVN_ERR(attempt_deletion(prop_reject_file, &found_file, pool));
304
resolve_props = FALSE;
307
if (resolve_text || resolve_props)
309
SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
310
resolve_text, resolve_props,
313
/* No feedback if no files were deleted and all we did was change the
314
entry, such a file did not appear as a conflict */
324
svn_wc__resolve_text_conflict(svn_wc__db_t *db,
325
const char *local_abspath,
326
apr_pool_t *scratch_pool)
328
svn_boolean_t ignored_result;
330
return svn_error_trace(resolve_conflict_on_node(
332
TRUE /* resolve_text */,
333
FALSE /* resolve_props */,
334
svn_wc_conflict_choose_merged,
342
resolve_one_conflict(svn_wc__db_t *db,
343
const char *local_abspath,
344
svn_boolean_t resolve_text,
345
const char *resolve_prop,
346
svn_boolean_t resolve_tree,
347
svn_wc_conflict_choice_t conflict_choice,
348
svn_wc_notify_func2_t notify_func,
350
apr_pool_t *scratch_pool)
352
const apr_array_header_t *conflicts;
353
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
355
svn_boolean_t resolved = FALSE;
357
SVN_ERR(svn_wc__db_read_conflicts(&conflicts, db, local_abspath,
358
scratch_pool, iterpool));
360
for (i = 0; i < conflicts->nelts; i++)
362
const svn_wc_conflict_description2_t *cd;
363
svn_boolean_t did_resolve;
365
cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
367
svn_pool_clear(iterpool);
371
case svn_wc_conflict_kind_tree:
375
/* For now, we only clear tree conflict information and resolve
376
* to the working state. There is no way to pick theirs-full
377
* or mine-full, etc. Throw an error if the user expects us
378
* to be smarter than we really are. */
379
if (conflict_choice != svn_wc_conflict_choose_merged)
381
return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
383
_("Tree conflicts can only be "
384
"resolved to 'working' state; "
385
"'%s' not resolved"),
386
svn_dirent_local_style(local_abspath,
390
SVN_ERR(svn_wc__db_op_set_tree_conflict(db, local_abspath, NULL,
396
case svn_wc_conflict_kind_text:
400
SVN_ERR(resolve_conflict_on_node(db,
402
TRUE /* resolve_text */,
403
FALSE /* resolve_props */,
412
case svn_wc_conflict_kind_property:
416
/* ### this is bogus. resolve_conflict_on_node() does not handle
417
### individual property resolution. */
418
if (*resolve_prop != '\0' &&
419
strcmp(resolve_prop, cd->property_name) != 0)
421
break; /* Skip this property conflict */
425
/* We don't have property name handling here yet :( */
426
SVN_ERR(resolve_conflict_on_node(db,
428
FALSE /* resolve_text */,
429
TRUE /* resolve_props */,
439
/* We can't resolve other conflict types */
445
if (notify_func && resolved)
446
notify_func(notify_baton,
447
svn_wc_create_notify(local_abspath, svn_wc_notify_resolved,
451
svn_pool_destroy(iterpool);
458
recursive_resolve_conflict(svn_wc__db_t *db,
459
const char *local_abspath,
460
svn_boolean_t this_is_conflicted,
462
svn_boolean_t resolve_text,
463
const char *resolve_prop,
464
svn_boolean_t resolve_tree,
465
svn_wc_conflict_choice_t conflict_choice,
466
svn_cancel_func_t cancel_func,
468
svn_wc_notify_func2_t notify_func,
470
apr_pool_t *scratch_pool)
472
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
473
const apr_array_header_t *children;
474
apr_hash_t *visited = apr_hash_make(scratch_pool);
475
svn_depth_t child_depth;
479
SVN_ERR(cancel_func(cancel_baton));
481
if (this_is_conflicted)
483
SVN_ERR(resolve_one_conflict(db,
489
notify_func, notify_baton,
493
if (depth < svn_depth_files)
496
child_depth = (depth < svn_depth_infinity) ? svn_depth_empty : depth;
498
SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
499
scratch_pool, iterpool));
501
for (i = 0; i < children->nelts; i++)
503
const char *name = APR_ARRAY_IDX(children, i, const char *);
504
const char *child_abspath;
505
svn_wc__db_status_t status;
506
svn_wc__db_kind_t kind;
507
svn_boolean_t conflicted;
509
svn_pool_clear(iterpool);
512
SVN_ERR(cancel_func(cancel_baton));
514
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
516
SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
517
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
518
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
519
&conflicted, NULL, NULL, NULL, NULL, NULL,
521
db, child_abspath, iterpool, iterpool));
523
if (status == svn_wc__db_status_not_present
524
|| status == svn_wc__db_status_excluded
525
|| status == svn_wc__db_status_server_excluded)
528
apr_hash_set(visited, name, APR_HASH_KEY_STRING, name);
529
if (kind == svn_wc__db_kind_dir && depth < svn_depth_immediates)
532
if (kind == svn_wc__db_kind_dir)
533
SVN_ERR(recursive_resolve_conflict(db,
541
cancel_func, cancel_baton,
542
notify_func, notify_baton,
545
SVN_ERR(resolve_one_conflict(db,
551
notify_func, notify_baton,
555
SVN_ERR(svn_wc__db_read_conflict_victims(&children, db, local_abspath,
556
scratch_pool, iterpool));
558
for (i = 0; i < children->nelts; i++)
560
const char *name = APR_ARRAY_IDX(children, i, const char *);
561
const char *child_abspath;
563
svn_pool_clear(iterpool);
565
if (apr_hash_get(visited, name, APR_HASH_KEY_STRING) != NULL)
566
continue; /* Already visited */
569
SVN_ERR(cancel_func(cancel_baton));
571
child_abspath = svn_dirent_join(local_abspath, name, iterpool);
573
/* We only have to resolve one level of tree conflicts. All other
574
conflicts are resolved in the other loop */
575
SVN_ERR(resolve_one_conflict(db,
577
FALSE /*resolve_text*/,
578
FALSE /*resolve_prop*/,
581
notify_func, notify_baton,
586
svn_pool_destroy(iterpool);
593
svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
594
const char *local_abspath,
596
svn_boolean_t resolve_text,
597
const char *resolve_prop,
598
svn_boolean_t resolve_tree,
599
svn_wc_conflict_choice_t conflict_choice,
600
svn_cancel_func_t cancel_func,
602
svn_wc_notify_func2_t notify_func,
604
apr_pool_t *scratch_pool)
606
svn_wc__db_kind_t kind;
607
svn_boolean_t conflicted;
608
/* ### the underlying code does NOT support resolving individual
609
### properties. bail out if the caller tries it. */
610
if (resolve_prop != NULL && *resolve_prop != '\0')
611
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
612
U_("Resolving a single property is not (yet) "
615
SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
616
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
617
NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
618
NULL, NULL, NULL, NULL, NULL, NULL,
619
wc_ctx->db, local_abspath,
620
scratch_pool, scratch_pool));
622
/* When the implementation still used the entry walker, depth
623
unknown was translated to infinity. */
624
if (kind != svn_wc__db_kind_dir)
625
depth = svn_depth_empty;
626
else if (depth == svn_depth_unknown)
627
depth = svn_depth_infinity;
629
return svn_error_trace(recursive_resolve_conflict(
638
cancel_func, cancel_baton,
639
notify_func, notify_baton,