2 * conflicts.c: Tree conflicts.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 #include "cl-conflicts.h"
27 #include "svn_dirent_uri.h"
29 #include "private/svn_token.h"
33 #include "svn_private_config.h"
36 /* A map for svn_wc_conflict_action_t values to XML strings */
37 static const svn_token_map_t map_conflict_action_xml[] =
39 { "edit", svn_wc_conflict_action_edit },
40 { "delete", svn_wc_conflict_action_delete },
41 { "add", svn_wc_conflict_action_add },
42 { "replace", svn_wc_conflict_action_replace },
46 /* A map for svn_wc_conflict_reason_t values to XML strings */
47 static const svn_token_map_t map_conflict_reason_xml[] =
49 { "edit", svn_wc_conflict_reason_edited },
50 { "delete", svn_wc_conflict_reason_deleted },
51 { "missing", svn_wc_conflict_reason_missing },
52 { "obstruction", svn_wc_conflict_reason_obstructed },
53 { "add", svn_wc_conflict_reason_added },
54 { "replace", svn_wc_conflict_reason_replaced },
55 { "unversioned", svn_wc_conflict_reason_unversioned },
56 { "moved-away", svn_wc_conflict_reason_moved_away },
57 { "moved-here", svn_wc_conflict_reason_moved_here },
61 /* Return a localised string representation of the local part of a conflict;
62 NULL for non-localised odd cases. */
64 local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason,
65 svn_wc_operation_t operation)
70 case svn_node_symlink:
73 case svn_wc_conflict_reason_edited:
74 return _("local file edit");
75 case svn_wc_conflict_reason_obstructed:
76 return _("local file obstruction");
77 case svn_wc_conflict_reason_deleted:
78 return _("local file delete");
79 case svn_wc_conflict_reason_missing:
80 if (operation == svn_wc_operation_merge)
81 return _("local file missing or deleted or moved away");
83 return _("local file missing");
84 case svn_wc_conflict_reason_unversioned:
85 return _("local file unversioned");
86 case svn_wc_conflict_reason_added:
87 return _("local file add");
88 case svn_wc_conflict_reason_replaced:
89 return _("local file replace");
90 case svn_wc_conflict_reason_moved_away:
91 return _("local file moved away");
92 case svn_wc_conflict_reason_moved_here:
93 return _("local file moved here");
99 case svn_wc_conflict_reason_edited:
100 return _("local dir edit");
101 case svn_wc_conflict_reason_obstructed:
102 return _("local dir obstruction");
103 case svn_wc_conflict_reason_deleted:
104 return _("local dir delete");
105 case svn_wc_conflict_reason_missing:
106 if (operation == svn_wc_operation_merge)
107 return _("local dir missing or deleted or moved away");
109 return _("local dir missing");
110 case svn_wc_conflict_reason_unversioned:
111 return _("local dir unversioned");
112 case svn_wc_conflict_reason_added:
113 return _("local dir add");
114 case svn_wc_conflict_reason_replaced:
115 return _("local dir replace");
116 case svn_wc_conflict_reason_moved_away:
117 return _("local dir moved away");
118 case svn_wc_conflict_reason_moved_here:
119 return _("local dir moved here");
123 case svn_node_unknown:
126 case svn_wc_conflict_reason_edited:
127 return _("local edit");
128 case svn_wc_conflict_reason_obstructed:
129 return _("local obstruction");
130 case svn_wc_conflict_reason_deleted:
131 return _("local delete");
132 case svn_wc_conflict_reason_missing:
133 if (operation == svn_wc_operation_merge)
134 return _("local missing or deleted or moved away");
136 return _("local missing");
137 case svn_wc_conflict_reason_unversioned:
138 return _("local unversioned");
139 case svn_wc_conflict_reason_added:
140 return _("local add");
141 case svn_wc_conflict_reason_replaced:
142 return _("local replace");
143 case svn_wc_conflict_reason_moved_away:
144 return _("local moved away");
145 case svn_wc_conflict_reason_moved_here:
146 return _("local moved here");
153 /* Return a localised string representation of the incoming part of a
154 conflict; NULL for non-localised odd cases. */
156 incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action)
161 case svn_node_symlink:
164 case svn_wc_conflict_action_edit:
165 return _("incoming file edit");
166 case svn_wc_conflict_action_add:
167 return _("incoming file add");
168 case svn_wc_conflict_action_delete:
169 return _("incoming file delete or move");
170 case svn_wc_conflict_action_replace:
171 return _("incoming replace with file");
177 case svn_wc_conflict_action_edit:
178 return _("incoming dir edit");
179 case svn_wc_conflict_action_add:
180 return _("incoming dir add");
181 case svn_wc_conflict_action_delete:
182 return _("incoming dir delete or move");
183 case svn_wc_conflict_action_replace:
184 return _("incoming replace with dir");
188 case svn_node_unknown:
191 case svn_wc_conflict_action_edit:
192 return _("incoming edit");
193 case svn_wc_conflict_action_add:
194 return _("incoming add");
195 case svn_wc_conflict_action_delete:
196 return _("incoming delete or move");
197 case svn_wc_conflict_action_replace:
198 return _("incoming replace");
205 /* Return a localised string representation of the operation part of a
208 operation_str(svn_wc_operation_t operation)
212 case svn_wc_operation_update: return _("upon update");
213 case svn_wc_operation_switch: return _("upon switch");
214 case svn_wc_operation_merge: return _("upon merge");
215 case svn_wc_operation_none: return _("upon none");
217 SVN_ERR_MALFUNCTION_NO_RETURN();
222 svn_cl__get_human_readable_prop_conflict_description(
224 svn_client_conflict_t *conflict,
227 const char *reason_str, *action_str;
229 /* We provide separately translatable strings for the values that we
230 * know about, and a fall-back in case any other values occur. */
231 switch (svn_client_conflict_get_local_change(conflict))
233 case svn_wc_conflict_reason_edited:
234 reason_str = _("local edit");
236 case svn_wc_conflict_reason_added:
237 reason_str = _("local add");
239 case svn_wc_conflict_reason_deleted:
240 reason_str = _("local delete");
242 case svn_wc_conflict_reason_obstructed:
243 reason_str = _("local obstruction");
246 reason_str = apr_psprintf(
249 map_conflict_reason_xml,
250 svn_client_conflict_get_local_change(conflict)));
253 switch (svn_client_conflict_get_incoming_change(conflict))
255 case svn_wc_conflict_action_edit:
256 action_str = _("incoming edit");
258 case svn_wc_conflict_action_add:
259 action_str = _("incoming add");
261 case svn_wc_conflict_action_delete:
262 action_str = _("incoming delete");
265 action_str = apr_psprintf(
266 pool, _("incoming %s"),
268 map_conflict_action_xml,
269 svn_client_conflict_get_incoming_change(conflict)));
272 SVN_ERR_ASSERT(reason_str && action_str);
273 *desc = apr_psprintf(pool, _("%s, %s %s"),
274 reason_str, action_str,
276 svn_client_conflict_get_operation(conflict)));
281 svn_cl__get_human_readable_tree_conflict_description(
283 svn_client_conflict_t *conflict,
286 const char *action, *reason, *operation;
287 svn_node_kind_t incoming_kind;
288 svn_wc_conflict_action_t conflict_action;
289 svn_wc_conflict_reason_t conflict_reason;
290 svn_wc_operation_t conflict_operation;
291 svn_node_kind_t conflict_node_kind;
293 conflict_action = svn_client_conflict_get_incoming_change(conflict);
294 conflict_reason = svn_client_conflict_get_local_change(conflict);
295 conflict_operation = svn_client_conflict_get_operation(conflict);
296 conflict_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
298 /* Determine the node kind of the incoming change. */
299 incoming_kind = svn_node_unknown;
300 if (conflict_action == svn_wc_conflict_action_edit ||
301 conflict_action == svn_wc_conflict_action_delete)
303 /* Change is acting on 'src_left' version of the node. */
304 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
305 NULL, NULL, &incoming_kind, conflict, pool, pool));
307 else if (conflict_action == svn_wc_conflict_action_add ||
308 conflict_action == svn_wc_conflict_action_replace)
310 /* Change is acting on 'src_right' version of the node.
312 * ### For 'replace', the node kind is ambiguous. However, src_left
313 * ### is NULL for replace, so we must use src_right. */
314 SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
315 NULL, NULL, &incoming_kind, conflict, pool, pool));
318 reason = local_reason_str(conflict_node_kind, conflict_reason,
320 action = incoming_action_str(incoming_kind, conflict_action);
321 operation = operation_str(conflict_operation);
322 SVN_ERR_ASSERT(operation);
324 if (action && reason)
326 *desc = apr_psprintf(pool, _("%s, %s %s"),
327 reason, action, operation);
331 /* A catch-all message for very rare or nominally impossible cases.
332 It will not be pretty, but is closer to an internal error than
333 an ordinary user-facing string. */
334 *desc = apr_psprintf(pool, _("local: %s %s incoming: %s %s %s"),
335 svn_node_kind_to_word(conflict_node_kind),
336 svn_token__to_word(map_conflict_reason_xml,
338 svn_node_kind_to_word(incoming_kind),
339 svn_token__to_word(map_conflict_action_xml,
347 svn_cl__get_human_readable_action_description(
349 svn_wc_conflict_action_t action,
350 svn_wc_operation_t operation,
351 svn_node_kind_t kind,
354 const char *action_s, *operation_s;
356 action_s = incoming_action_str(kind, action);
357 operation_s = operation_str(operation);
359 SVN_ERR_ASSERT(operation_s);
361 *desc = apr_psprintf(pool, _("%s %s"),
362 action_s, operation_s);
368 /* Helper for svn_cl__append_tree_conflict_info_xml().
369 * Appends the repository location of a conflicted node to ATT_HASH.
370 * SIDE is the content of the version tag's side="..." attribute,
371 * currently one of "source-left" or "source-right".*/
373 add_conflict_version_xml(svn_stringbuf_t **pstr,
375 const char *repos_root_url,
376 const char *repos_relpath,
377 svn_revnum_t peg_rev,
378 svn_node_kind_t node_kind,
381 apr_hash_t *att_hash = apr_hash_make(pool);
384 svn_hash_sets(att_hash, "side", side);
387 svn_hash_sets(att_hash, "repos-url", repos_root_url);
390 svn_hash_sets(att_hash, "path-in-repos", repos_relpath);
392 if (SVN_IS_VALID_REVNUM(peg_rev))
393 svn_hash_sets(att_hash, "revision", apr_ltoa(pool, peg_rev));
395 if (node_kind != svn_node_unknown)
396 svn_hash_sets(att_hash, "kind", svn_cl__node_kind_str_xml(node_kind));
398 svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
399 "version", att_hash);
405 append_tree_conflict_info_xml(svn_stringbuf_t *str,
406 svn_client_conflict_t *conflict,
409 apr_hash_t *att_hash = apr_hash_make(pool);
411 const char *repos_root_url;
412 const char *repos_relpath;
413 svn_revnum_t peg_rev;
414 svn_node_kind_t node_kind;
416 svn_hash_sets(att_hash, "victim",
418 svn_client_conflict_get_local_abspath(conflict), pool));
420 svn_hash_sets(att_hash, "kind",
421 svn_cl__node_kind_str_xml(
422 svn_client_conflict_tree_get_victim_node_kind(conflict)));
424 svn_hash_sets(att_hash, "operation",
425 svn_cl__operation_str_xml(
426 svn_client_conflict_get_operation(conflict), pool));
428 tmp = svn_token__to_word(map_conflict_action_xml,
429 svn_client_conflict_get_incoming_change(conflict));
430 svn_hash_sets(att_hash, "action", tmp);
432 tmp = svn_token__to_word(map_conflict_reason_xml,
433 svn_client_conflict_get_local_change(conflict));
434 svn_hash_sets(att_hash, "reason", tmp);
436 /* Open the tree-conflict tag. */
437 svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
438 "tree-conflict", att_hash);
440 /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
442 SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL, conflict,
444 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
450 if (repos_root_url && repos_relpath)
451 SVN_ERR(add_conflict_version_xml(&str, "source-left",
452 repos_root_url, repos_relpath, peg_rev,
455 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
461 if (repos_root_url && repos_relpath)
462 SVN_ERR(add_conflict_version_xml(&str,
464 repos_root_url, repos_relpath, peg_rev,
467 svn_xml_make_close_tag(&str, pool, "tree-conflict");
473 svn_cl__append_conflict_info_xml(svn_stringbuf_t *str,
474 svn_client_conflict_t *conflict,
475 apr_pool_t *scratch_pool)
477 apr_hash_t *att_hash;
478 svn_boolean_t text_conflicted;
479 apr_array_header_t *props_conflicted;
480 svn_boolean_t tree_conflicted;
481 svn_wc_operation_t conflict_operation;
482 const char *repos_root_url;
483 const char *repos_relpath;
484 svn_revnum_t peg_rev;
485 svn_node_kind_t node_kind;
487 conflict_operation = svn_client_conflict_get_operation(conflict);
489 SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
493 scratch_pool, scratch_pool));
496 /* Uses other element type */
497 return svn_error_trace(
498 append_tree_conflict_info_xml(str, conflict, scratch_pool));
501 SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
503 scratch_pool, scratch_pool));
504 att_hash = apr_hash_make(scratch_pool);
506 svn_hash_sets(att_hash, "operation",
507 svn_cl__operation_str_xml(conflict_operation, scratch_pool));
509 svn_hash_sets(att_hash, "operation",
510 svn_cl__operation_str_xml(conflict_operation, scratch_pool));
514 const char *base_abspath;
515 const char *my_abspath;
516 const char *their_abspath;
518 svn_hash_sets(att_hash, "type", "text");
521 svn_xml_make_open_tag_hash(&str, scratch_pool,
522 svn_xml_normal, "conflict", att_hash);
524 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
525 &repos_relpath, &peg_rev, &node_kind, conflict,
526 scratch_pool, scratch_pool));
527 if (repos_root_url && repos_relpath)
528 SVN_ERR(add_conflict_version_xml(&str, "source-left",
529 repos_root_url, repos_relpath, peg_rev,
530 node_kind, scratch_pool));
532 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
533 &repos_relpath, &peg_rev, &node_kind, conflict,
534 scratch_pool, scratch_pool));
535 if (repos_root_url && repos_relpath)
536 SVN_ERR(add_conflict_version_xml(&str, "source-right",
537 repos_root_url, repos_relpath, peg_rev,
538 node_kind, scratch_pool));
540 SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
543 conflict, scratch_pool,
545 /* "<prev-base-file> xx </prev-base-file>" */
546 svn_cl__xml_tagged_cdata(
547 &str, scratch_pool, "prev-base-file", base_abspath);
549 /* "<prev-wc-file> xx </prev-wc-file>" */
550 svn_cl__xml_tagged_cdata(
551 &str, scratch_pool, "prev-wc-file", my_abspath);
553 /* "<cur-base-file> xx </cur-base-file>" */
554 svn_cl__xml_tagged_cdata(
555 &str, scratch_pool, "cur-base-file", their_abspath);
558 svn_xml_make_close_tag(&str, scratch_pool, "conflict");
561 if (props_conflicted->nelts > 0)
563 const char *reject_abspath;
565 svn_hash_sets(att_hash, "type", "property");
568 svn_xml_make_open_tag_hash(&str, scratch_pool,
569 svn_xml_normal, "conflict", att_hash);
571 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
572 &repos_relpath, &peg_rev, &node_kind, conflict,
573 scratch_pool, scratch_pool));
574 if (repos_root_url && repos_relpath)
575 SVN_ERR(add_conflict_version_xml(&str, "source-left",
576 repos_root_url, repos_relpath, peg_rev,
577 node_kind, scratch_pool));
579 SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
580 &repos_relpath, &peg_rev, &node_kind, conflict,
581 scratch_pool, scratch_pool));
582 if (repos_root_url && repos_relpath)
583 SVN_ERR(add_conflict_version_xml(&str, "source-right",
584 repos_root_url, repos_relpath, peg_rev,
585 node_kind, scratch_pool));
587 /* "<prop-file> xx </prop-file>" */
589 svn_client_conflict_prop_get_reject_abspath(conflict);
590 svn_cl__xml_tagged_cdata(
591 &str, scratch_pool, "prop-file", reject_abspath);
594 svn_xml_make_close_tag(&str, scratch_pool, "conflict");