]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svn/cl-conflicts.c
Merge ^/vendor/lvm-project/master up to its last change (upstream commit
[FreeBSD/FreeBSD.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 /* Return a localised string representation of the local part of a conflict;
62    NULL for non-localised odd cases. */
63 static const char *
64 local_reason_str(svn_node_kind_t kind, svn_wc_conflict_reason_t reason,
65                  svn_wc_operation_t operation)
66 {
67   switch (kind)
68     {
69       case svn_node_file:
70       case svn_node_symlink:
71         switch (reason)
72           {
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");
82             else
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");
94           }
95         break;
96       case svn_node_dir:
97         switch (reason)
98           {
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");
108             else
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_none:
123       case svn_node_unknown:
124         switch (reason)
125           {
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");
135             else
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");
147           }
148         break;
149     }
150   return NULL;
151 }
152
153 /* Return a localised string representation of the incoming part of a
154    conflict; NULL for non-localised odd cases. */
155 static const char *
156 incoming_action_str(svn_node_kind_t kind, svn_wc_conflict_action_t action)
157 {
158   switch (kind)
159     {
160       case svn_node_file:
161       case svn_node_symlink:
162         switch (action)
163           {
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");
172           }
173         break;
174       case svn_node_dir:
175         switch (action)
176           {
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");
185           }
186         break;
187       case svn_node_none:
188       case svn_node_unknown:
189         switch (action)
190           {
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");
199           }
200         break;
201     }
202   return NULL;
203 }
204
205 /* Return a localised string representation of the operation part of a
206    conflict. */
207 static const char *
208 operation_str(svn_wc_operation_t operation)
209 {
210   switch (operation)
211     {
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");
216     }
217   SVN_ERR_MALFUNCTION_NO_RETURN();
218   return NULL;
219 }
220
221 svn_error_t *
222 svn_cl__get_human_readable_prop_conflict_description(
223   const char **desc,
224   svn_client_conflict_t *conflict,
225   apr_pool_t *pool)
226 {
227   const char *reason_str, *action_str;
228
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))
232     {
233       case svn_wc_conflict_reason_edited:
234         reason_str = _("local edit");
235         break;
236       case svn_wc_conflict_reason_added:
237         reason_str = _("local add");
238         break;
239       case svn_wc_conflict_reason_deleted:
240         reason_str = _("local delete");
241         break;
242       case svn_wc_conflict_reason_obstructed:
243         reason_str = _("local obstruction");
244         break;
245       default:
246         reason_str = apr_psprintf(
247                        pool, _("local %s"),
248                        svn_token__to_word(
249                          map_conflict_reason_xml,
250                          svn_client_conflict_get_local_change(conflict)));
251         break;
252     }
253   switch (svn_client_conflict_get_incoming_change(conflict))
254     {
255       case svn_wc_conflict_action_edit:
256         action_str = _("incoming edit");
257         break;
258       case svn_wc_conflict_action_add:
259         action_str = _("incoming add");
260         break;
261       case svn_wc_conflict_action_delete:
262         action_str = _("incoming delete");
263         break;
264       default:
265         action_str = apr_psprintf(
266                        pool, _("incoming %s"),
267                        svn_token__to_word(
268                          map_conflict_action_xml,
269                          svn_client_conflict_get_incoming_change(conflict)));
270         break;
271     }
272   SVN_ERR_ASSERT(reason_str && action_str);
273   *desc = apr_psprintf(pool, _("%s, %s %s"),
274                        reason_str, action_str,
275                        operation_str(
276                          svn_client_conflict_get_operation(conflict)));
277   return SVN_NO_ERROR;
278 }
279
280 svn_error_t *
281 svn_cl__get_human_readable_tree_conflict_description(
282   const char **desc,
283   svn_client_conflict_t *conflict,
284   apr_pool_t *pool)
285 {
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;
292
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);
297
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)
302     {
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));
306     }
307   else if (conflict_action == svn_wc_conflict_action_add ||
308            conflict_action == svn_wc_conflict_action_replace)
309     {
310       /* Change is acting on 'src_right' version of the node.
311        *
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));
316     }
317
318   reason = local_reason_str(conflict_node_kind, conflict_reason,
319                             conflict_operation);
320   action = incoming_action_str(incoming_kind, conflict_action);
321   operation = operation_str(conflict_operation);
322   SVN_ERR_ASSERT(operation);
323
324   if (action && reason)
325     {
326       *desc = apr_psprintf(pool, _("%s, %s %s"),
327                            reason, action, operation);
328     }
329   else
330     {
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,
337                                               conflict_reason),
338                            svn_node_kind_to_word(incoming_kind),
339                            svn_token__to_word(map_conflict_action_xml,
340                                               conflict_action),
341                            operation);
342     }
343   return SVN_NO_ERROR;
344 }
345
346 svn_error_t *
347 svn_cl__get_human_readable_action_description(
348         const char **desc,
349         svn_wc_conflict_action_t action,
350         svn_wc_operation_t operation,
351         svn_node_kind_t kind,
352         apr_pool_t *pool)
353 {
354   const char *action_s, *operation_s;
355
356   action_s = incoming_action_str(kind, action);
357   operation_s = operation_str(operation);
358
359   SVN_ERR_ASSERT(operation_s);
360
361   *desc = apr_psprintf(pool, _("%s %s"),
362                        action_s, operation_s);
363
364   return SVN_NO_ERROR;
365 }
366
367
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".*/
372 static svn_error_t *
373 add_conflict_version_xml(svn_stringbuf_t **pstr,
374                          const char *side,
375                          const char *repos_root_url,
376                          const char *repos_relpath,
377                          svn_revnum_t peg_rev,
378                          svn_node_kind_t node_kind,
379                          apr_pool_t *pool)
380 {
381   apr_hash_t *att_hash = apr_hash_make(pool);
382
383
384   svn_hash_sets(att_hash, "side", side);
385
386   if (repos_root_url)
387     svn_hash_sets(att_hash, "repos-url", repos_root_url);
388
389   if (repos_relpath)
390     svn_hash_sets(att_hash, "path-in-repos", repos_relpath);
391
392   if (SVN_IS_VALID_REVNUM(peg_rev))
393     svn_hash_sets(att_hash, "revision", apr_ltoa(pool, peg_rev));
394
395   if (node_kind != svn_node_unknown)
396     svn_hash_sets(att_hash, "kind", svn_cl__node_kind_str_xml(node_kind));
397
398   svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
399                              "version", att_hash);
400   return SVN_NO_ERROR;
401 }
402
403
404 static svn_error_t *
405 append_tree_conflict_info_xml(svn_stringbuf_t *str,
406                               svn_client_conflict_t *conflict,
407                               apr_pool_t *pool)
408 {
409   apr_hash_t *att_hash = apr_hash_make(pool);
410   const char *tmp;
411   const char *repos_root_url;
412   const char *repos_relpath;
413   svn_revnum_t peg_rev;
414   svn_node_kind_t node_kind;
415
416   svn_hash_sets(att_hash, "victim",
417                 svn_dirent_basename(
418                   svn_client_conflict_get_local_abspath(conflict), pool));
419
420   svn_hash_sets(att_hash, "kind",
421                 svn_cl__node_kind_str_xml(
422                   svn_client_conflict_tree_get_victim_node_kind(conflict)));
423
424   svn_hash_sets(att_hash, "operation",
425                 svn_cl__operation_str_xml(
426                   svn_client_conflict_get_operation(conflict), pool));
427
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);
431
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);
435
436   /* Open the tree-conflict tag. */
437   svn_xml_make_open_tag_hash(&str, pool, svn_xml_normal,
438                              "tree-conflict", att_hash);
439
440   /* Add child tags for OLDER_VERSION and THEIR_VERSION. */
441
442   SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL, conflict,
443                                              pool, pool));
444   SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
445                                                               &peg_rev,
446                                                               &node_kind,
447                                                               conflict,
448                                                               pool,
449                                                               pool));
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,
453                                      node_kind, pool));
454
455   SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
456                                                               &peg_rev,
457                                                               &node_kind,
458                                                               conflict,
459                                                               pool,
460                                                               pool));
461   if (repos_root_url && repos_relpath)
462     SVN_ERR(add_conflict_version_xml(&str,
463                                      "source-right",
464                                      repos_root_url, repos_relpath, peg_rev,
465                                      node_kind, pool));
466
467   svn_xml_make_close_tag(&str, pool, "tree-conflict");
468
469   return SVN_NO_ERROR;
470 }
471
472 svn_error_t *
473 svn_cl__append_conflict_info_xml(svn_stringbuf_t *str,
474                                  svn_client_conflict_t *conflict,
475                                  apr_pool_t *scratch_pool)
476 {
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;
486
487   conflict_operation = svn_client_conflict_get_operation(conflict);
488
489   SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
490                                              &props_conflicted,
491                                              &tree_conflicted,
492                                              conflict,
493                                              scratch_pool, scratch_pool));
494   if (tree_conflicted)
495     {
496       /* Uses other element type */
497       return svn_error_trace(
498                 append_tree_conflict_info_xml(str, conflict, scratch_pool));
499     }
500
501   SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
502                                              conflict,
503                                              scratch_pool, scratch_pool));
504   att_hash = apr_hash_make(scratch_pool);
505
506   svn_hash_sets(att_hash, "operation",
507                 svn_cl__operation_str_xml(conflict_operation, scratch_pool));
508
509   svn_hash_sets(att_hash, "operation",
510                 svn_cl__operation_str_xml(conflict_operation, scratch_pool));
511
512   if (text_conflicted)
513     {
514       const char *base_abspath;
515       const char *my_abspath;
516       const char *their_abspath;
517
518       svn_hash_sets(att_hash, "type", "text");
519
520       /* "<conflict>" */
521       svn_xml_make_open_tag_hash(&str, scratch_pool,
522                                  svn_xml_normal, "conflict", att_hash);
523
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));
531
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));
539
540       SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
541                                                     &base_abspath,
542                                                     &their_abspath,
543                                                     conflict, scratch_pool,
544                                                     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);
548
549       /* "<prev-wc-file> xx </prev-wc-file>" */
550       svn_cl__xml_tagged_cdata(
551         &str, scratch_pool, "prev-wc-file", my_abspath);
552
553       /* "<cur-base-file> xx </cur-base-file>" */
554       svn_cl__xml_tagged_cdata(
555         &str, scratch_pool, "cur-base-file", their_abspath);
556
557       /* "</conflict>" */
558       svn_xml_make_close_tag(&str, scratch_pool, "conflict");
559     }
560
561   if (props_conflicted->nelts > 0)
562     {
563       const char *reject_abspath;
564
565       svn_hash_sets(att_hash, "type", "property");
566
567       /* "<conflict>" */
568       svn_xml_make_open_tag_hash(&str, scratch_pool,
569                                  svn_xml_normal, "conflict", att_hash);
570
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));
578
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));
586
587       /* "<prop-file> xx </prop-file>" */
588       reject_abspath =
589         svn_client_conflict_prop_get_reject_abspath(conflict);
590       svn_cl__xml_tagged_cdata(
591         &str, scratch_pool, "prop-file", reject_abspath);
592
593       /* "</conflict>" */
594       svn_xml_make_close_tag(&str, scratch_pool, "conflict");
595     }
596
597   return SVN_NO_ERROR;
598 }