]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svn/cl-conflicts.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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                  svn_wc_operation_t operation)
74 {
75   switch (kind)
76     {
77       case svn_node_file:
78       case svn_node_symlink:
79         switch (reason)
80           {
81           case svn_wc_conflict_reason_edited:
82             return _("local file edit");
83           case svn_wc_conflict_reason_obstructed:
84             return _("local file obstruction");
85           case svn_wc_conflict_reason_deleted:
86             return _("local file delete");
87           case svn_wc_conflict_reason_missing:
88             if (operation == svn_wc_operation_merge)
89               return _("local file missing or deleted or moved away");
90             else
91               return _("local file missing");
92           case svn_wc_conflict_reason_unversioned:
93             return _("local file unversioned");
94           case svn_wc_conflict_reason_added:
95             return _("local file add");
96           case svn_wc_conflict_reason_replaced:
97             return _("local file replace");
98           case svn_wc_conflict_reason_moved_away:
99             return _("local file moved away");
100           case svn_wc_conflict_reason_moved_here:
101             return _("local file moved here");
102           }
103         break;
104       case svn_node_dir:
105         switch (reason)
106           {
107           case svn_wc_conflict_reason_edited:
108             return _("local dir edit");
109           case svn_wc_conflict_reason_obstructed:
110             return _("local dir obstruction");
111           case svn_wc_conflict_reason_deleted:
112             return _("local dir delete");
113           case svn_wc_conflict_reason_missing:
114             if (operation == svn_wc_operation_merge)
115               return _("local dir missing or deleted or moved away");
116             else
117               return _("local dir missing");
118           case svn_wc_conflict_reason_unversioned:
119             return _("local dir unversioned");
120           case svn_wc_conflict_reason_added:
121             return _("local dir add");
122           case svn_wc_conflict_reason_replaced:
123             return _("local dir replace");
124           case svn_wc_conflict_reason_moved_away:
125             return _("local dir moved away");
126           case svn_wc_conflict_reason_moved_here:
127             return _("local dir moved here");
128           }
129         break;
130       case svn_node_none:
131       case svn_node_unknown:
132         switch (reason)
133           {
134           case svn_wc_conflict_reason_edited:
135             return _("local edit");
136           case svn_wc_conflict_reason_obstructed:
137             return _("local obstruction");
138           case svn_wc_conflict_reason_deleted:
139             return _("local delete");
140           case svn_wc_conflict_reason_missing:
141             if (operation == svn_wc_operation_merge)
142               return _("local missing or deleted or moved away");
143             else
144               return _("local missing");
145           case svn_wc_conflict_reason_unversioned:
146             return _("local unversioned");
147           case svn_wc_conflict_reason_added:
148             return _("local add");
149           case svn_wc_conflict_reason_replaced:
150             return _("local replace");
151           case svn_wc_conflict_reason_moved_away:
152             return _("local moved away");
153           case svn_wc_conflict_reason_moved_here:
154             return _("local moved here");
155           }
156         break;
157     }
158   return NULL;
159 }
160
161 /* Return a localised string representation of the incoming part of a
162    conflict; NULL for non-localised odd cases. */
163 static const char *
164 incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action)
165 {
166   switch (kind)
167     {
168       case svn_node_file:
169       case svn_node_symlink:
170         switch (action)
171           {
172             case svn_wc_conflict_action_edit:
173               return _("incoming file edit");
174             case svn_wc_conflict_action_add:
175               return _("incoming file add");
176             case svn_wc_conflict_action_delete:
177               return _("incoming file delete or move");
178             case svn_wc_conflict_action_replace:
179               return _("incoming replace with file");
180           }
181         break;
182       case svn_node_dir:
183         switch (action)
184           {
185             case svn_wc_conflict_action_edit:
186               return _("incoming dir edit");
187             case svn_wc_conflict_action_add:
188               return _("incoming dir add");
189             case svn_wc_conflict_action_delete:
190               return _("incoming dir delete or move");
191             case svn_wc_conflict_action_replace:
192               return _("incoming replace with dir");
193           }
194         break;
195       case svn_node_none:
196       case svn_node_unknown:
197         switch (action)
198           {
199             case svn_wc_conflict_action_edit:
200               return _("incoming edit");
201             case svn_wc_conflict_action_add:
202               return _("incoming add");
203             case svn_wc_conflict_action_delete:
204               return _("incoming delete or move");
205             case svn_wc_conflict_action_replace:
206               return _("incoming replace");
207           }
208         break;
209     }
210   return NULL;
211 }
212
213 /* Return a localised string representation of the operation part of a
214    conflict. */
215 static const char *
216 operation_str(svn_wc_operation_t operation)
217 {
218   switch (operation)
219     {
220     case svn_wc_operation_update: return _("upon update");
221     case svn_wc_operation_switch: return _("upon switch");
222     case svn_wc_operation_merge:  return _("upon merge");
223     case svn_wc_operation_none:   return _("upon none");
224     }
225   SVN_ERR_MALFUNCTION_NO_RETURN();
226   return NULL;
227 }
228
229 svn_error_t *
230 svn_cl__get_human_readable_prop_conflict_description(
231   const char **desc,
232   const svn_wc_conflict_description2_t *conflict,
233   apr_pool_t *pool)
234 {
235   const char *reason_str, *action_str;
236
237   /* We provide separately translatable strings for the values that we
238    * know about, and a fall-back in case any other values occur. */
239   switch (conflict->reason)
240     {
241       case svn_wc_conflict_reason_edited:
242         reason_str = _("local edit");
243         break;
244       case svn_wc_conflict_reason_added:
245         reason_str = _("local add");
246         break;
247       case svn_wc_conflict_reason_deleted:
248         reason_str = _("local delete");
249         break;
250       case svn_wc_conflict_reason_obstructed:
251         reason_str = _("local obstruction");
252         break;
253       default:
254         reason_str = apr_psprintf(pool, _("local %s"),
255                                   svn_token__to_word(map_conflict_reason_xml,
256                                                      conflict->reason));
257         break;
258     }
259   switch (conflict->action)
260     {
261       case svn_wc_conflict_action_edit:
262         action_str = _("incoming edit");
263         break;
264       case svn_wc_conflict_action_add:
265         action_str = _("incoming add");
266         break;
267       case svn_wc_conflict_action_delete:
268         action_str = _("incoming delete");
269         break;
270       default:
271         action_str = apr_psprintf(pool, _("incoming %s"),
272                                   svn_token__to_word(map_conflict_action_xml,
273                                                      conflict->action));
274         break;
275     }
276   SVN_ERR_ASSERT(reason_str && action_str);
277   *desc = apr_psprintf(pool, _("%s, %s %s"),
278                        reason_str, action_str,
279                        operation_str(conflict->operation));
280   return SVN_NO_ERROR;
281 }
282
283 svn_error_t *
284 svn_cl__get_human_readable_tree_conflict_description(
285   const char **desc,
286   const svn_wc_conflict_description2_t *conflict,
287   apr_pool_t *pool)
288 {
289   const char *action, *reason, *operation;
290   svn_node_kind_t incoming_kind;
291
292   /* Determine the node kind of the incoming change. */
293   incoming_kind = svn_node_unknown;
294   if (conflict->action == svn_wc_conflict_action_edit ||
295       conflict->action == svn_wc_conflict_action_delete)
296     {
297       /* Change is acting on 'src_left' version of the node. */
298       if (conflict->src_left_version)
299         incoming_kind = conflict->src_left_version->node_kind;
300     }
301   else if (conflict->action == svn_wc_conflict_action_add ||
302            conflict->action == svn_wc_conflict_action_replace)
303     {
304       /* Change is acting on 'src_right' version of the node.
305        *
306        * ### For 'replace', the node kind is ambiguous. However, src_left
307        * ### is NULL for replace, so we must use src_right. */
308       if (conflict->src_right_version)
309         incoming_kind = conflict->src_right_version->node_kind;
310     }
311
312   reason = local_reason_str(conflict->node_kind, conflict->reason,
313                             conflict->operation);
314   action = incoming_action_str(incoming_kind, conflict->action);
315   operation = operation_str(conflict->operation);
316   SVN_ERR_ASSERT(operation);
317
318   if (action && reason)
319     {
320       *desc = apr_psprintf(pool, _("%s, %s %s"),
321                            reason, action, operation);
322     }
323   else
324     {
325       /* A catch-all message for very rare or nominally impossible cases.
326          It will not be pretty, but is closer to an internal error than
327          an ordinary user-facing string. */
328       *desc = apr_psprintf(pool, _("local: %s %s incoming: %s %s %s"),
329                            svn_node_kind_to_word(conflict->node_kind),
330                            svn_token__to_word(map_conflict_reason_xml,
331                                               conflict->reason),
332                            svn_node_kind_to_word(incoming_kind),
333                            svn_token__to_word(map_conflict_action_xml,
334                                               conflict->action),
335                            operation);
336     }
337   return SVN_NO_ERROR;
338 }
339
340 svn_error_t *
341 svn_cl__get_human_readable_action_description(
342         const char **desc,
343         svn_wc_conflict_action_t action,
344         svn_wc_operation_t operation,
345         svn_node_kind_t kind,
346         apr_pool_t *pool)
347 {
348   const char *action_s, *operation_s;
349
350   action_s = incoming_action_str(kind, action);
351   operation_s = operation_str(operation);
352
353   SVN_ERR_ASSERT(operation_s);
354
355   *desc = apr_psprintf(pool, _("%s %s"),
356                        action_s, operation_s);
357
358   return SVN_NO_ERROR;
359 }
360
361
362 /* Helper for svn_cl__append_tree_conflict_info_xml().
363  * Appends the attributes of the given VERSION to ATT_HASH.
364  * SIDE is the content of the version tag's side="..." attribute,
365  * currently one of "source-left" or "source-right".*/
366 static svn_error_t *
367 add_conflict_version_xml(svn_stringbuf_t **pstr,
368                          const char *side,
369                          const svn_wc_conflict_version_t *version,
370                          apr_pool_t *pool)
371 {
372   apr_hash_t *att_hash = apr_hash_make(pool);
373
374
375   svn_hash_sets(att_hash, "side", side);
376
377   if (version->repos_url)
378     svn_hash_sets(att_hash, "repos-url", version->repos_url);
379
380   if (version->path_in_repos)
381     svn_hash_sets(att_hash, "path-in-repos", version->path_in_repos);
382
383   if (SVN_IS_VALID_REVNUM(version->peg_rev))
384     svn_hash_sets(att_hash, "revision", apr_ltoa(pool, version->peg_rev));
385
386   if (version->node_kind != svn_node_unknown)
387     svn_hash_sets(att_hash, "kind",
388                   svn_cl__node_kind_str_xml(version->node_kind));
389
390   svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
391                              "version", att_hash);
392   return SVN_NO_ERROR;
393 }
394
395
396 static svn_error_t *
397 append_tree_conflict_info_xml(svn_stringbuf_t *str,
398                               const svn_wc_conflict_description2_t *conflict,
399                               apr_pool_t *pool)
400 {
401   apr_hash_t *att_hash = apr_hash_make(pool);
402   const char *tmp;
403
404   svn_hash_sets(att_hash, "victim",
405                 svn_dirent_basename(conflict->local_abspath, pool));
406
407   svn_hash_sets(att_hash, "kind",
408                 svn_cl__node_kind_str_xml(conflict->node_kind));
409
410   svn_hash_sets(att_hash, "operation",
411                 svn_cl__operation_str_xml(conflict->operation, pool));
412
413   tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
414   svn_hash_sets(att_hash, "action", tmp);
415
416   tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
417   svn_hash_sets(att_hash, "reason", tmp);
418
419   /* Open the tree-conflict tag. */
420   svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
421                              "tree-conflict", att_hash);
422
423   /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
424
425   if (conflict->src_left_version)
426     SVN_ERR(add_conflict_version_xml(&str,
427                                      "source-left",
428                                      conflict->src_left_version,
429                                      pool));
430
431   if (conflict->src_right_version)
432     SVN_ERR(add_conflict_version_xml(&str,
433                                      "source-right",
434                                      conflict->src_right_version,
435                                      pool));
436
437   svn_xml_make_close_tag(&str, pool, "tree-conflict");
438
439   return SVN_NO_ERROR;
440 }
441
442 svn_error_t *
443 svn_cl__append_conflict_info_xml(svn_stringbuf_t *str,
444                                  const svn_wc_conflict_description2_t *conflict,
445                                  apr_pool_t *scratch_pool)
446 {
447   apr_hash_t *att_hash;
448   const char *kind;
449   if (conflict->kind == svn_wc_conflict_kind_tree)
450     {
451       /* Uses other element type */
452       return svn_error_trace(
453                 append_tree_conflict_info_xml(str, conflict, scratch_pool));
454     }
455
456   att_hash = apr_hash_make(scratch_pool);
457
458   svn_hash_sets(att_hash, "operation",
459                 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
460
461
462   kind = svn_token__to_word(map_conflict_kind_xml, conflict->kind);
463   svn_hash_sets(att_hash, "type", kind);
464
465   svn_hash_sets(att_hash, "operation",
466                 svn_cl__operation_str_xml(conflict->operation, scratch_pool));
467
468
469   /* "<conflict>" */
470   svn_xml_make_open_tag_hash(&str, scratch_pool,
471                              svn_xml_normal, "conflict", att_hash);
472
473   if (conflict->src_left_version)
474     SVN_ERR(add_conflict_version_xml(&str,
475                                      "source-left",
476                                      conflict->src_left_version,
477                                      scratch_pool));
478
479   if (conflict->src_right_version)
480     SVN_ERR(add_conflict_version_xml(&str,
481                                      "source-right",
482                                      conflict->src_right_version,
483                                      scratch_pool));
484
485   switch (conflict->kind)
486     {
487       case svn_wc_conflict_kind_text:
488         /* "<prev-base-file> xx </prev-base-file>" */
489         svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-base-file",
490                                  conflict->base_abspath);
491
492         /* "<prev-wc-file> xx </prev-wc-file>" */
493         svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-wc-file",
494                                  conflict->my_abspath);
495
496         /* "<cur-base-file> xx </cur-base-file>" */
497         svn_cl__xml_tagged_cdata(&str, scratch_pool, "cur-base-file",
498                                  conflict->their_abspath);
499
500         break;
501
502       case svn_wc_conflict_kind_property:
503         /* "<prop-file> xx </prop-file>" */
504         svn_cl__xml_tagged_cdata(&str, scratch_pool, "prop-file",
505                                  conflict->their_abspath);
506         break;
507
508       default:
509       case svn_wc_conflict_kind_tree:
510         SVN_ERR_MALFUNCTION(); /* Handled separately */
511         break;
512     }
513
514   /* "</conflict>" */
515   svn_xml_make_close_tag(&str, scratch_pool, "conflict");
516
517   return SVN_NO_ERROR;
518 }