]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/svn/cl-conflicts.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / svn / cl-conflicts.c
1 /*
2  * conflicts.c: Tree conflicts.
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include "cl-conflicts.h"
25 #include "svn_hash.h"
26 #include "svn_xml.h"
27 #include "svn_dirent_uri.h"
28 #include "svn_path.h"
29 #include "private/svn_token.h"
30
31 #include "cl.h"
32
33 #include "svn_private_config.h"
34
35
36 /* A map for svn_wc_conflict_action_t values to XML strings */
37 static const svn_token_map_t map_conflict_action_xml[] =
38 {
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 },
43   { NULL,               0 }
44 };
45
46 /* A map for svn_wc_conflict_reason_t values to XML strings */
47 static const svn_token_map_t map_conflict_reason_xml[] =
48 {
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 },
58   { NULL,               0 }
59 };
60
61 static const svn_token_map_t map_conflict_kind_xml[] =
62 {
63   { "text",             svn_wc_conflict_kind_text },
64   { "property",         svn_wc_conflict_kind_property },
65   { "tree",             svn_wc_conflict_kind_tree },
66   { NULL,               0 }
67 };
68
69 /* Return a localised string representation of the local part of a conflict;
70    NULL for non-localised odd cases. */
71 static const char *
72 local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason)
73 {
74   switch (kind)
75     {
76       case svn_node_file:
77         switch (reason)
78           {
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");
97           }
98         break;
99       case svn_node_dir:
100         switch (reason)
101           {
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");
120           }
121         break;
122       case svn_node_symlink:
123       case svn_node_none:
124       case svn_node_unknown:
125         break;
126     }
127   return NULL;
128 }
129
130 /* Return a localised string representation of the incoming part of a
131    conflict; NULL for non-localised odd cases. */
132 static const char *
133 incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action)
134 {
135   switch (kind)
136     {
137       case svn_node_file:
138         switch (action)
139           {
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");
148           }
149         break;
150       case svn_node_dir:
151         switch (action)
152           {
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");
161           }
162         break;
163       case svn_node_symlink:
164       case svn_node_none:
165       case svn_node_unknown:
166         break;
167     }
168   return NULL;
169 }
170
171 /* Return a localised string representation of the operation part of a
172    conflict. */
173 static const char *
174 operation_str(svn_wc_operation_t operation)
175 {
176   switch (operation)
177     {
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");
182     }
183   SVN_ERR_MALFUNCTION_NO_RETURN();
184   return NULL;
185 }
186
187 svn_error_t *
188 svn_cl__get_human_readable_prop_conflict_description(
189   const char **desc,
190   const svn_wc_conflict_description2_t *conflict,
191   apr_pool_t *pool)
192 {
193   const char *reason_str, *action_str;
194
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)
198     {
199       case svn_wc_conflict_reason_edited:
200         reason_str = _("local edit");
201         break;
202       case svn_wc_conflict_reason_added:
203         reason_str = _("local add");
204         break;
205       case svn_wc_conflict_reason_deleted:
206         reason_str = _("local delete");
207         break;
208       case svn_wc_conflict_reason_obstructed:
209         reason_str = _("local obstruction");
210         break;
211       default:
212         reason_str = apr_psprintf(pool, _("local %s"),
213                                   svn_token__to_word(map_conflict_reason_xml,
214                                                      conflict->reason));
215         break;
216     }
217   switch (conflict->action)
218     {
219       case svn_wc_conflict_action_edit:
220         action_str = _("incoming edit");
221         break;
222       case svn_wc_conflict_action_add:
223         action_str = _("incoming add");
224         break;
225       case svn_wc_conflict_action_delete:
226         action_str = _("incoming delete");
227         break;
228       default:
229         action_str = apr_psprintf(pool, _("incoming %s"),
230                                   svn_token__to_word(map_conflict_action_xml,
231                                                      conflict->action));
232         break;
233     }
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));
238   return SVN_NO_ERROR;
239 }
240
241 svn_error_t *
242 svn_cl__get_human_readable_tree_conflict_description(
243   const char **desc,
244   const svn_wc_conflict_description2_t *conflict,
245   apr_pool_t *pool)
246 {
247   const char *action, *reason, *operation;
248   svn_node_kind_t incoming_kind;
249
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)
254     {
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;
258     }
259   else if (conflict->action == svn_wc_conflict_action_add ||
260            conflict->action == svn_wc_conflict_action_replace)
261     {
262       /* Change is acting on 'src_right' version of the node.
263        *
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;
268     }
269
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);
274
275   if (action && reason)
276     {
277       *desc = apr_psprintf(pool, _("%s, %s %s"),
278                            reason, action, operation);
279     }
280   else
281     {
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,
288                                               conflict->reason),
289                            svn_node_kind_to_word(incoming_kind),
290                            svn_token__to_word(map_conflict_action_xml,
291                                               conflict->action),
292                            operation);
293     }
294   return SVN_NO_ERROR;
295 }
296
297
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".*/
302 static svn_error_t *
303 add_conflict_version_xml(svn_stringbuf_t **pstr,
304                          const char *side,
305                          const svn_wc_conflict_version_t *version,
306                          apr_pool_t *pool)
307 {
308   apr_hash_t *att_hash = apr_hash_make(pool);
309
310
311   svn_hash_sets(att_hash, "side", side);
312
313   if (version->repos_url)
314     svn_hash_sets(att_hash, "repos-url", version->repos_url);
315
316   if (version->path_in_repos)
317     svn_hash_sets(att_hash, "path-in-repos", version->path_in_repos);
318
319   if (SVN_IS_VALID_REVNUM(version->peg_rev))
320     svn_hash_sets(att_hash, "revision", apr_ltoa(pool, version->peg_rev));
321
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));
325
326   svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
327                              "version", att_hash);
328   return SVN_NO_ERROR;
329 }
330
331
332 static svn_error_t *
333 append_tree_conflict_info_xml(svn_stringbuf_t *str,
334                               const svn_wc_conflict_description2_t *conflict,
335                               apr_pool_t *pool)
336 {
337   apr_hash_t *att_hash = apr_hash_make(pool);
338   const char *tmp;
339
340   svn_hash_sets(att_hash, "victim",
341                 svn_dirent_basename(conflict->local_abspath, pool));
342
343   svn_hash_sets(att_hash, "kind",
344                 svn_cl__node_kind_str_xml(conflict->node_kind));
345
346   svn_hash_sets(att_hash, "operation",
347                 svn_cl__operation_str_xml(conflict->operation, pool));
348
349   tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
350   svn_hash_sets(att_hash, "action", tmp);
351
352   tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
353   svn_hash_sets(att_hash, "reason", tmp);
354
355   /* Open the tree-conflict tag. */
356   svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
357                              "tree-conflict", att_hash);
358
359   /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
360
361   if (conflict->src_left_version)
362     SVN_ERR(add_conflict_version_xml(&str,
363                                      "source-left",
364                                      conflict->src_left_version,
365                                      pool));
366
367   if (conflict->src_right_version)
368     SVN_ERR(add_conflict_version_xml(&str,
369                                      "source-right",
370                                      conflict->src_right_version,
371                                      pool));
372
373   svn_xml_make_close_tag(&str, pool, "tree-conflict");
374
375   return SVN_NO_ERROR;
376 }
377
378 svn_error_t *
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)
382 {
383   apr_hash_t *att_hash;
384   const char *kind;
385   if (conflict->kind == svn_wc_conflict_kind_tree)
386     {
387       /* Uses other element type */
388       return svn_error_trace(
389                 append_tree_conflict_info_xml(str, conflict, scratch_pool));
390     }
391
392   att_hash = apr_hash_make(scratch_pool);
393
394   svn_hash_sets(att_hash, "operation",
395                 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
396
397
398   kind = svn_token__to_word(map_conflict_kind_xml, conflict->kind);
399   svn_hash_sets(att_hash, "type", kind);
400
401   svn_hash_sets(att_hash, "operation",
402                 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
403
404
405   /* "<conflict>" */
406   svn_xml_make_open_tag_hash(&str, scratch_pool,
407                              svn_xml_normal, "conflict", att_hash);
408
409   if (conflict->src_left_version)
410     SVN_ERR(add_conflict_version_xml(&str,
411                                      "source-left",
412                                      conflict->src_left_version,
413                                      scratch_pool));
414
415   if (conflict->src_right_version)
416     SVN_ERR(add_conflict_version_xml(&str,
417                                      "source-right",
418                                      conflict->src_right_version,
419                                      scratch_pool));
420
421   switch (conflict->kind)
422     {
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);
427
428         /* "<prev-wc-file> xx </prev-wc-file>" */
429         svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-wc-file",
430                                  conflict->my_abspath);
431
432         /* "<cur-base-file> xx </cur-base-file>" */
433         svn_cl__xml_tagged_cdata(&str, scratch_pool, "cur-base-file",
434                                  conflict->their_abspath);
435
436         break;
437
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);
442         break;
443
444       default:
445       case svn_wc_conflict_kind_tree:
446         SVN_ERR_MALFUNCTION(); /* Handled separately */
447         break;
448     }
449
450   /* "</conflict>" */
451   svn_xml_make_close_tag(&str, scratch_pool, "conflict");
452
453   return SVN_NO_ERROR;
454 }