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 static const svn_token_map_t map_conflict_kind_xml[] =
63 { "text", svn_wc_conflict_kind_text },
64 { "property", svn_wc_conflict_kind_property },
65 { "tree", svn_wc_conflict_kind_tree },
69 /* Return a localised string representation of the local part of a conflict;
70 NULL for non-localised odd cases. */
72 local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason)
79 case svn_wc_conflict_reason_edited:
80 return _("local file edit");
81 case svn_wc_conflict_reason_obstructed:
82 return _("local file obstruction");
83 case svn_wc_conflict_reason_deleted:
84 return _("local file delete");
85 case svn_wc_conflict_reason_missing:
86 return _("local file missing");
87 case svn_wc_conflict_reason_unversioned:
88 return _("local file unversioned");
89 case svn_wc_conflict_reason_added:
90 return _("local file add");
91 case svn_wc_conflict_reason_replaced:
92 return _("local file replace");
93 case svn_wc_conflict_reason_moved_away:
94 return _("local file moved away");
95 case svn_wc_conflict_reason_moved_here:
96 return _("local file moved here");
102 case svn_wc_conflict_reason_edited:
103 return _("local dir edit");
104 case svn_wc_conflict_reason_obstructed:
105 return _("local dir obstruction");
106 case svn_wc_conflict_reason_deleted:
107 return _("local dir delete");
108 case svn_wc_conflict_reason_missing:
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");
122 case svn_node_symlink:
124 case svn_node_unknown:
130 /* Return a localised string representation of the incoming part of a
131 conflict; NULL for non-localised odd cases. */
133 incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action)
140 case svn_wc_conflict_action_edit:
141 return _("incoming file edit");
142 case svn_wc_conflict_action_add:
143 return _("incoming file add");
144 case svn_wc_conflict_action_delete:
145 return _("incoming file delete");
146 case svn_wc_conflict_action_replace:
147 return _("incoming file replace");
153 case svn_wc_conflict_action_edit:
154 return _("incoming dir edit");
155 case svn_wc_conflict_action_add:
156 return _("incoming dir add");
157 case svn_wc_conflict_action_delete:
158 return _("incoming dir delete");
159 case svn_wc_conflict_action_replace:
160 return _("incoming dir replace");
163 case svn_node_symlink:
165 case svn_node_unknown:
171 /* Return a localised string representation of the operation part of a
174 operation_str(svn_wc_operation_t operation)
178 case svn_wc_operation_update: return _("upon update");
179 case svn_wc_operation_switch: return _("upon switch");
180 case svn_wc_operation_merge: return _("upon merge");
181 case svn_wc_operation_none: return _("upon none");
183 SVN_ERR_MALFUNCTION_NO_RETURN();
188 svn_cl__get_human_readable_prop_conflict_description(
190 const svn_wc_conflict_description2_t *conflict,
193 const char *reason_str, *action_str;
195 /* We provide separately translatable strings for the values that we
196 * know about, and a fall-back in case any other values occur. */
197 switch (conflict->reason)
199 case svn_wc_conflict_reason_edited:
200 reason_str = _("local edit");
202 case svn_wc_conflict_reason_added:
203 reason_str = _("local add");
205 case svn_wc_conflict_reason_deleted:
206 reason_str = _("local delete");
208 case svn_wc_conflict_reason_obstructed:
209 reason_str = _("local obstruction");
212 reason_str = apr_psprintf(pool, _("local %s"),
213 svn_token__to_word(map_conflict_reason_xml,
217 switch (conflict->action)
219 case svn_wc_conflict_action_edit:
220 action_str = _("incoming edit");
222 case svn_wc_conflict_action_add:
223 action_str = _("incoming add");
225 case svn_wc_conflict_action_delete:
226 action_str = _("incoming delete");
229 action_str = apr_psprintf(pool, _("incoming %s"),
230 svn_token__to_word(map_conflict_action_xml,
234 SVN_ERR_ASSERT(reason_str && action_str);
235 *desc = apr_psprintf(pool, _("%s, %s %s"),
236 reason_str, action_str,
237 operation_str(conflict->operation));
242 svn_cl__get_human_readable_tree_conflict_description(
244 const svn_wc_conflict_description2_t *conflict,
247 const char *action, *reason, *operation;
248 svn_node_kind_t incoming_kind;
250 /* Determine the node kind of the incoming change. */
251 incoming_kind = svn_node_unknown;
252 if (conflict->action == svn_wc_conflict_action_edit ||
253 conflict->action == svn_wc_conflict_action_delete)
255 /* Change is acting on 'src_left' version of the node. */
256 if (conflict->src_left_version)
257 incoming_kind = conflict->src_left_version->node_kind;
259 else if (conflict->action == svn_wc_conflict_action_add ||
260 conflict->action == svn_wc_conflict_action_replace)
262 /* Change is acting on 'src_right' version of the node.
264 * ### For 'replace', the node kind is ambiguous. However, src_left
265 * ### is NULL for replace, so we must use src_right. */
266 if (conflict->src_right_version)
267 incoming_kind = conflict->src_right_version->node_kind;
270 reason = local_reason_str(conflict->node_kind, conflict->reason);
271 action = incoming_action_str(incoming_kind, conflict->action);
272 operation = operation_str(conflict->operation);
273 SVN_ERR_ASSERT(operation);
275 if (action && reason)
277 *desc = apr_psprintf(pool, _("%s, %s %s"),
278 reason, action, operation);
282 /* A catch-all message for very rare or nominally impossible cases.
283 It will not be pretty, but is closer to an internal error than
284 an ordinary user-facing string. */
285 *desc = apr_psprintf(pool, _("local: %s %s incoming: %s %s %s"),
286 svn_node_kind_to_word(conflict->node_kind),
287 svn_token__to_word(map_conflict_reason_xml,
289 svn_node_kind_to_word(incoming_kind),
290 svn_token__to_word(map_conflict_action_xml,
298 /* Helper for svn_cl__append_tree_conflict_info_xml().
299 * Appends the attributes of the given VERSION to ATT_HASH.
300 * SIDE is the content of the version tag's side="..." attribute,
301 * currently one of "source-left" or "source-right".*/
303 add_conflict_version_xml(svn_stringbuf_t **pstr,
305 const svn_wc_conflict_version_t *version,
308 apr_hash_t *att_hash = apr_hash_make(pool);
311 svn_hash_sets(att_hash, "side", side);
313 if (version->repos_url)
314 svn_hash_sets(att_hash, "repos-url", version->repos_url);
316 if (version->path_in_repos)
317 svn_hash_sets(att_hash, "path-in-repos", version->path_in_repos);
319 if (SVN_IS_VALID_REVNUM(version->peg_rev))
320 svn_hash_sets(att_hash, "revision", apr_ltoa(pool, version->peg_rev));
322 if (version->node_kind != svn_node_unknown)
323 svn_hash_sets(att_hash, "kind",
324 svn_cl__node_kind_str_xml(version->node_kind));
326 svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
327 "version", att_hash);
333 append_tree_conflict_info_xml(svn_stringbuf_t *str,
334 const svn_wc_conflict_description2_t *conflict,
337 apr_hash_t *att_hash = apr_hash_make(pool);
340 svn_hash_sets(att_hash, "victim",
341 svn_dirent_basename(conflict->local_abspath, pool));
343 svn_hash_sets(att_hash, "kind",
344 svn_cl__node_kind_str_xml(conflict->node_kind));
346 svn_hash_sets(att_hash, "operation",
347 svn_cl__operation_str_xml(conflict->operation, pool));
349 tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
350 svn_hash_sets(att_hash, "action", tmp);
352 tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
353 svn_hash_sets(att_hash, "reason", tmp);
355 /* Open the tree-conflict tag. */
356 svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
357 "tree-conflict", att_hash);
359 /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
361 if (conflict->src_left_version)
362 SVN_ERR(add_conflict_version_xml(&str,
364 conflict->src_left_version,
367 if (conflict->src_right_version)
368 SVN_ERR(add_conflict_version_xml(&str,
370 conflict->src_right_version,
373 svn_xml_make_close_tag(&str, pool, "tree-conflict");
379 svn_cl__append_conflict_info_xml(svn_stringbuf_t *str,
380 const svn_wc_conflict_description2_t *conflict,
381 apr_pool_t *scratch_pool)
383 apr_hash_t *att_hash;
385 if (conflict->kind == svn_wc_conflict_kind_tree)
387 /* Uses other element type */
388 return svn_error_trace(
389 append_tree_conflict_info_xml(str, conflict, scratch_pool));
392 att_hash = apr_hash_make(scratch_pool);
394 svn_hash_sets(att_hash, "operation",
395 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
398 kind = svn_token__to_word(map_conflict_kind_xml, conflict->kind);
399 svn_hash_sets(att_hash, "type", kind);
401 svn_hash_sets(att_hash, "operation",
402 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
406 svn_xml_make_open_tag_hash(&str, scratch_pool,
407 svn_xml_normal, "conflict", att_hash);
409 if (conflict->src_left_version)
410 SVN_ERR(add_conflict_version_xml(&str,
412 conflict->src_left_version,
415 if (conflict->src_right_version)
416 SVN_ERR(add_conflict_version_xml(&str,
418 conflict->src_right_version,
421 switch (conflict->kind)
423 case svn_wc_conflict_kind_text:
424 /* "<prev-base-file> xx </prev-base-file>" */
425 svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-base-file",
426 conflict->base_abspath);
428 /* "<prev-wc-file> xx </prev-wc-file>" */
429 svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-wc-file",
430 conflict->my_abspath);
432 /* "<cur-base-file> xx </cur-base-file>" */
433 svn_cl__xml_tagged_cdata(&str, scratch_pool, "cur-base-file",
434 conflict->their_abspath);
438 case svn_wc_conflict_kind_property:
439 /* "<prop-file> xx </prop-file>" */
440 svn_cl__xml_tagged_cdata(&str, scratch_pool, "prop-file",
441 conflict->their_abspath);
445 case svn_wc_conflict_kind_tree:
446 SVN_ERR_MALFUNCTION(); /* Handled separately */
451 svn_xml_make_close_tag(&str, scratch_pool, "conflict");