monotone

monotone Mtn Source Tree

Root/src/cmd_conflicts.cc

1// Copyright (C) 2008 - 2010, 2012 Stephen Leake <stephen_leake@stephe-leake.org>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <iostream>
12
13#include "app_state.hh"
14#include "cmd.hh"
15#include "database.hh"
16#include "merge_roster.hh"
17
18CMD_GROUP(conflicts, "conflicts", "", CMD_REF(tree),
19 N_("Commands for conflict resolutions"),
20 "");
21
22struct conflicts_t
23{
24 roster_merge_result result;
25 revision_id ancestor_rid, left_rid, right_rid;
26 boost::shared_ptr<roster_t> ancestor_roster;
27 boost::shared_ptr<roster_t> left_roster;
28 boost::shared_ptr<roster_t> right_roster;
29 marking_map left_marking, right_marking;
30
31 conflicts_t(database & db, bookkeeping_path const & file):
32 left_roster(boost::shared_ptr<roster_t>(new roster_t())),
33 right_roster(boost::shared_ptr<roster_t>(new roster_t()))
34 {
35 result.clear(); // default constructor doesn't do this.
36
37 result.read_conflict_file(db, file, ancestor_rid, left_rid, right_rid,
38 *left_roster, left_marking,
39 *right_roster, right_marking);
40 };
41
42 void write (database & db, lua_hooks & lua, bookkeeping_path const & file)
43 {
44 result.write_conflict_file
45 (db, lua, file, ancestor_rid, left_rid, right_rid,
46 left_roster, left_marking, right_roster, right_marking);
47 };
48};
49
50typedef enum {first, remaining} show_conflicts_case_t;
51
52static void
53show_resolution(resolve_conflicts::file_resolution_t resolution, char const * const prefix)
54{
55
56 if (resolution.resolution != resolve_conflicts::none)
57 {
58 P(F(string(prefix).append(image(resolution)).c_str()));
59 }
60}
61
62static void
63show_conflicts(database & db, conflicts_t conflicts, show_conflicts_case_t show_case)
64{
65 // Go thru the conflicts we know how to resolve in the same order
66 // merge.cc resolve_merge_conflicts outputs them.
67 for (std::vector<orphaned_node_conflict>::iterator i = conflicts.result.orphaned_node_conflicts.begin();
68 i != conflicts.result.orphaned_node_conflicts.end();
69 ++i)
70 {
71 orphaned_node_conflict & conflict = *i;
72
73 if (conflict.resolution.resolution == resolve_conflicts::none)
74 {
75 file_path name;
76 if (conflicts.left_roster->has_node(conflict.nid))
77 conflicts.left_roster->get_name(conflict.nid, name);
78 else
79 conflicts.right_roster->get_name(conflict.nid, name);
80
81 P(F("orphaned node '%s'") % name);
82
83 switch (show_case)
84 {
85 case first:
86 P(F("possible resolutions:"));
87 P(F("resolve_first drop"));
88 P(F("resolve_first rename \"file_name\""));
89 return;
90
91 case remaining:
92 break;
93 }
94 }
95 }
96
97 for (std::vector<dropped_modified_conflict>::iterator i = conflicts.result.dropped_modified_conflicts.begin();
98 i != conflicts.result.dropped_modified_conflicts.end();
99 ++i)
100 {
101 dropped_modified_conflict & conflict = *i;
102
103 if ((conflict.left_nid != the_null_node &&
104 conflict.left_resolution.resolution == resolve_conflicts::none) ||
105 (conflict.right_nid != the_null_node &&
106 conflict.right_resolution.resolution == resolve_conflicts::none))
107 {
108 file_path modified_name;
109
110 switch (conflict.dropped_side)
111 {
112 case resolve_conflicts::left_side:
113 conflicts.right_roster->get_name(conflict.right_nid, modified_name);
114 break;
115
116 case resolve_conflicts::right_side:
117 conflicts.left_roster->get_name(conflict.left_nid, modified_name);
118 break;
119 }
120
121 P(F("conflict: file '%s'") % modified_name);
122 if (conflict.orphaned)
123 {
124 switch (conflict.dropped_side)
125 {
126 case resolve_conflicts::left_side:
127 P(F("orphaned on the left"));
128 P(F("modified on the right"));
129 break;
130
131 case resolve_conflicts::right_side:
132 P(F("modified on the left"));
133 P(F("orphaned on the right"));
134 }
135 }
136 else
137 {
138 switch (conflict.dropped_side)
139 {
140 case resolve_conflicts::left_side:
141 if (conflict.left_nid == the_null_node)
142 P(F("dropped on the left"));
143 else
144 {
145 // we can't distinguish duplicate name from recreated
146 P(F("dropped and recreated on the left"));
147 }
148
149 P(F("modified on the right"));
150 break;
151
152 case resolve_conflicts::right_side:
153 P(F("modified on the left"));
154
155 if (conflict.right_nid == the_null_node)
156 P(F("dropped on the right"));
157 else
158 {
159 P(F("dropped and recreated on the right"));
160 }
161 }
162 }
163
164 show_resolution(conflict.left_resolution, "left_");
165 show_resolution(conflict.right_resolution, "right_");
166
167 if (show_case == remaining) return;
168
169 if (conflict.left_nid == the_null_node || conflict.right_nid == the_null_node)
170 {
171 // only one file involved; only need one resolution
172 P(F("possible resolutions:"));
173 P(F("resolve_first drop"));
174 P(F("resolve_first rename"));
175 P(F("resolve_first user_rename \"new_content_name\" \"new_file_name\""));
176
177 if (!conflict.orphaned)
178 {
179 P(F("resolve_first keep"));
180 P(F("resolve_first user \"name\""));
181 }
182 return;
183 }
184 else
185 {
186 // recreated or repeated duplicate name; need two resolutions
187
188 P(F("possible resolutions:"));
189
190 if (conflict.left_nid != the_null_node &&
191 conflict.left_resolution.resolution == resolve_conflicts::none)
192 {
193 P(F("resolve_first_left drop"));
194 P(F("resolve_first_left rename"));
195 P(F("resolve_first_left user_rename \"new_content_name\" \"new_file_name\""));
196
197 if (!conflict.orphaned &&
198 conflict.right_resolution.resolution != resolve_conflicts::keep &&
199 conflict.right_resolution.resolution != resolve_conflicts::content_user)
200 {
201 P(F("resolve_first_left keep"));
202 P(F("resolve_first_left user \"name\""));
203 }
204 }
205
206 if (conflict.right_nid != the_null_node &&
207 conflict.right_resolution.resolution == resolve_conflicts::none)
208 {
209 P(F("resolve_first_right drop"));
210 P(F("resolve_first_right rename"));
211 P(F("resolve_first_right user_rename \"new_content_name\" \"new_file_name\""));
212 if (!conflict.orphaned &&
213 conflict.left_resolution.resolution != resolve_conflicts::keep &&
214 conflict.left_resolution.resolution != resolve_conflicts::content_user)
215 {
216 P(F("resolve_first_right keep"));
217 P(F("resolve_first_right user \"name\""));
218 }
219 }
220 return;
221 }
222 }
223 }
224
225 for (std::vector<duplicate_name_conflict>::iterator i = conflicts.result.duplicate_name_conflicts.begin();
226 i != conflicts.result.duplicate_name_conflicts.end();
227 ++i)
228 {
229 duplicate_name_conflict & conflict = *i;
230
231 if (conflict.left_resolution.resolution == resolve_conflicts::none ||
232 conflict.right_resolution.resolution == resolve_conflicts::none)
233 {
234 file_path left_name;
235 conflicts.left_roster->get_name(conflict.left_nid, left_name);
236 P(F("duplicate_name %s") % left_name);
237
238 switch (show_case)
239 {
240 case first:
241 P(F("possible resolutions:"));
242
243 if (conflict.left_resolution.resolution == resolve_conflicts::none)
244 {
245 P(F("resolve_first_left drop"));
246 P(F("resolve_first_left keep"));
247 P(F("resolve_first_left rename \"name\""));
248 P(F("resolve_first_left user \"name\""));
249 }
250
251 if (conflict.right_resolution.resolution == resolve_conflicts::none)
252 {
253 P(F("resolve_first_right drop"));
254 P(F("resolve_first_right keep"));
255 P(F("resolve_first_right rename \"name\""));
256 P(F("resolve_first_right user \"name\""));
257 }
258 return;
259
260 case remaining:
261 break;
262 }
263 }
264 }
265
266 for (std::vector<file_content_conflict>::iterator i = conflicts.result.file_content_conflicts.begin();
267 i != conflicts.result.file_content_conflicts.end();
268 ++i)
269 {
270 file_content_conflict & conflict = *i;
271
272 if (conflict.resolution.resolution == resolve_conflicts::none)
273 {
274 file_path name;
275 conflicts.left_roster->get_name(conflict.nid, name);
276 P(F("content %s") % name);
277
278 switch (show_case)
279 {
280 case first:
281 P(F("possible resolutions:"));
282 P(F("resolve_first interactive \"file_name\""));
283 P(F("resolve_first user \"file_name\""));
284 return;
285
286 case remaining:
287 break;
288 }
289 }
290 }
291
292 switch (show_case)
293 {
294 case first:
295 {
296 int const count = conflicts.result.count_unsupported_resolution();
297 if (count > 0)
298 P(FP("warning: %d conflict with no supported resolutions.",
299 "warning: %d conflicts with no supported resolutions.",
300 count) % count);
301 else
302 P(F("all conflicts resolved"));
303 }
304 break;
305
306 case remaining:
307 {
308 int const count = conflicts.result.count_unsupported_resolution();
309 if (count > 0)
310 {
311 P(FP("warning: %d conflict with no supported resolutions.",
312 "warning: %d conflicts with no supported resolutions.",
313 count) % count);
314
315 content_merge_database_adaptor adaptor
316 (db, conflicts.left_rid, conflicts.right_rid, conflicts.left_marking, conflicts.right_marking);
317
318 conflicts.result.report_missing_root_conflicts
319 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
320 conflicts.result.report_invalid_name_conflicts
321 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
322 conflicts.result.report_directory_loop_conflicts
323 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
324 conflicts.result.report_orphaned_node_conflicts
325 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
326 conflicts.result.report_multiple_name_conflicts
327 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
328 conflicts.result.report_dropped_modified_conflicts
329 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
330 conflicts.result.report_attribute_conflicts
331 (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
332 }
333 }
334 break;
335 }
336
337} // show_conflicts
338
339enum side_t {left, right, neither};
340static char const * const conflict_resolution_not_supported_msg = N_("'%s' is not a supported conflict resolution for %s");
341
342// Call Lua merge3 hook to merge left_fid, right_fid, store result in result_path
343static bool
344do_interactive_merge(database & db,
345 lua_hooks & lua,
346 conflicts_t & conflicts,
347 node_id const nid,
348 file_id const & ancestor_fid,
349 file_id const & left_fid,
350 file_id const & right_fid,
351 bookkeeping_path const & result_path)
352{
353 file_path ancestor_path, left_path, right_path;
354
355 if (!conflicts.ancestor_roster)
356 {
357 conflicts.ancestor_roster = boost::shared_ptr<roster_t>(new roster_t());
358 db.get_roster(conflicts.ancestor_rid, *conflicts.ancestor_roster);
359 }
360
361 conflicts.ancestor_roster->get_name(nid, ancestor_path);
362 conflicts.left_roster->get_name(nid, left_path);
363 conflicts.right_roster->get_name(nid, right_path);
364
365 file_data left_data, right_data, ancestor_data;
366 data merged_unpacked;
367
368 db.get_file_version(left_fid, left_data);
369 db.get_file_version(ancestor_fid, ancestor_data);
370 db.get_file_version(right_fid, right_data);
371
372 if (lua.hook_merge3(ancestor_path, left_path, right_path, file_path(),
373 ancestor_data.inner(), left_data.inner(),
374 right_data.inner(), merged_unpacked))
375 {
376 write_data(result_path, merged_unpacked);
377 return true;
378 }
379
380 return false;
381} // do_interactive_merge
382
383static void
384set_resolution(resolve_conflicts::file_resolution_t & resolution,
385 resolve_conflicts::file_resolution_t const & other_resolution,
386 args_vector const & args)
387{
388 if ("drop" == idx(args, 0)())
389 {
390 E(args.size() == 1, origin::user, F("too many arguments"));
391 resolution.resolution = resolve_conflicts::drop;
392 }
393 else if ("keep" == idx(args, 0)())
394 {
395 E(args.size() == 1, origin::user, F("too many arguments"));
396 E(other_resolution.resolution == resolve_conflicts::none ||
397 other_resolution.resolution == resolve_conflicts::drop ||
398 other_resolution.resolution == resolve_conflicts::rename ||
399 other_resolution.resolution == resolve_conflicts::content_user_rename,
400 origin::user,
401 F("other resolution is %s; specify 'drop', 'rename', or 'user_rename'") %
402 image(other_resolution.resolution));
403 resolution.resolution = resolve_conflicts::keep;
404 }
405 else if ("rename" == idx(args, 0)())
406 {
407 E(args.size() == 2, origin::user, F("wrong number of arguments"));
408 resolution.resolution = resolve_conflicts::rename;
409 resolution.rename = file_path_external(idx(args,1));
410 }
411 else if ("user" == idx(args, 0)())
412 {
413 E(args.size() == 2, origin::user, F("wrong number of arguments"));
414 E(other_resolution.resolution == resolve_conflicts::none ||
415 other_resolution.resolution == resolve_conflicts::drop ||
416 other_resolution.resolution == resolve_conflicts::rename ||
417 other_resolution.resolution == resolve_conflicts::content_user_rename,
418 origin::user,
419 F("other resolution is %s; specify 'drop', 'rename', or 'user_rename'") %
420 image(other_resolution.resolution));
421
422 resolution.resolution = resolve_conflicts::content_user;
423 resolution.content = new_optimal_path(idx(args,1)(), false);
424 }
425 else if ("user_rename" == idx(args,0)())
426 {
427 E(args.size() == 3, origin::user, F("wrong number of arguments"));
428
429 resolution.resolution = resolve_conflicts::content_user_rename;
430 resolution.content = new_optimal_path(idx(args,1)(), false);
431 resolution.rename = file_path_external(idx(args,2));
432 }
433 else
434 E(false, origin::user,
435 F(conflict_resolution_not_supported_msg) % idx(args,0) % "duplicate_name");
436
437} // set_resolution
438
439static void
440set_first_conflict(database & db,
441 lua_hooks & lua,
442 conflicts_t & conflicts,
443 args_vector const & args,
444 side_t side)
445{
446 E(args.size() > 0, origin::user, F("wrong number of arguments"));
447
448 if (side != neither)
449 {
450 for (std::vector<dropped_modified_conflict>::iterator i = conflicts.result.dropped_modified_conflicts.begin();
451 i != conflicts.result.dropped_modified_conflicts.end();
452 ++i)
453 {
454 dropped_modified_conflict & conflict = *i;
455
456 // here we only allow two resolutions; single resolutions are handled below
457
458 switch (side)
459 {
460 case left:
461 if (conflict.left_resolution.resolution == resolve_conflicts::none)
462 {
463 E(conflict.left_nid != the_null_node, origin::user,
464 F("must specify resolve_first (not _left or _right)"));
465
466 if ("keep" == idx(args,0)())
467 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
468
469 set_resolution(conflict.left_resolution, conflict.right_resolution, args);
470 return;
471 }
472 break;
473 case right:
474 if (conflict.right_resolution.resolution == resolve_conflicts::none)
475 {
476 E(conflict.right_nid != the_null_node, origin::user,
477 F("must specify resolve_first (not _left or _right)"));
478
479 if ("keep" == idx(args,0)())
480 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
481
482 set_resolution(conflict.right_resolution, conflict.left_resolution, args);
483 return;
484 }
485 break;
486 case neither:
487 // can't get here
488 break;
489 }
490 }
491
492 for (std::vector<duplicate_name_conflict>::iterator i = conflicts.result.duplicate_name_conflicts.begin();
493 i != conflicts.result.duplicate_name_conflicts.end();
494 ++i)
495 {
496 duplicate_name_conflict & conflict = *i;
497
498 switch (side)
499 {
500 case left:
501 if (conflict.left_resolution.resolution == resolve_conflicts::none)
502 {
503 set_resolution(conflict.left_resolution, conflict.right_resolution, args);
504 return;
505 }
506 break;
507
508 case right:
509 if (conflict.right_resolution.resolution == resolve_conflicts::none)
510 {
511 set_resolution(conflict.right_resolution, conflict.left_resolution, args);
512 return;
513 }
514 break;
515
516 case neither:
517 I(false);
518 }
519 }
520 }
521
522 if (side == neither)
523 {
524 for (std::vector<orphaned_node_conflict>::iterator i = conflicts.result.orphaned_node_conflicts.begin();
525 i != conflicts.result.orphaned_node_conflicts.end();
526 ++i)
527 {
528 orphaned_node_conflict & conflict = *i;
529
530 if (conflict.resolution.resolution == resolve_conflicts::none)
531 {
532 if ("drop" == idx(args,0)())
533 {
534 E(args.size() == 1, origin::user, F("wrong number of arguments"));
535
536 conflict.resolution.resolution = resolve_conflicts::drop;
537 }
538 else if ("rename" == idx(args,0)())
539 {
540 E(args.size() == 2, origin::user, F("wrong number of arguments"));
541
542 conflict.resolution.resolution = resolve_conflicts::rename;
543 conflict.resolution.rename = file_path_external(idx(args,1));
544 }
545 else
546 {
547 E(false, origin::user,
548 F(conflict_resolution_not_supported_msg) % idx(args,0) % "orphaned_node");
549 }
550 return;
551 }
552 }
553
554 for (std::vector<dropped_modified_conflict>::iterator i = conflicts.result.dropped_modified_conflicts.begin();
555 i != conflicts.result.dropped_modified_conflicts.end();
556 ++i)
557 {
558 dropped_modified_conflict & conflict = *i;
559
560 // Here we only allow single resolutions; two resolutions are handled above
561
562 switch (conflict.dropped_side)
563 {
564 case resolve_conflicts::left_side:
565 E(conflict.left_nid == the_null_node, origin::user,
566 F("must specify 'resolve_first_left' or 'resolve_first_right' (not just 'resolve_first')"));
567
568 // the left side stays dropped; we either drop, keep or replace the right side
569 if (conflict.right_resolution.resolution == resolve_conflicts::none)
570 {
571 if ("drop" == idx(args,0)())
572 {
573 E(args.size() == 1, origin::user, F("wrong number of arguments"));
574
575 conflict.right_resolution.resolution = resolve_conflicts::drop;
576 }
577 else if ("keep" == idx(args,0)())
578 {
579 E(args.size() == 1, origin::user, F("wrong number of arguments"));
580 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
581
582 conflict.right_resolution.resolution = resolve_conflicts::keep;
583 }
584 else if ("user" == idx(args,0)())
585 {
586 E(args.size() == 2, origin::user, F("wrong number of arguments"));
587 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
588
589 conflict.right_resolution.resolution = resolve_conflicts::content_user;
590 conflict.right_resolution.content = new_optimal_path(idx(args,1)(), false);
591 }
592 else if ("rename" == idx(args,0)())
593 {
594 E(args.size() == 2, origin::user, F("wrong number of arguments"));
595
596 conflict.right_resolution.resolution = resolve_conflicts::rename;
597 conflict.right_resolution.rename = file_path_external(idx(args,1));
598 }
599 else if ("user_rename" == idx(args,0)())
600 {
601 E(args.size() == 3, origin::user, F("wrong number of arguments"));
602
603 conflict.right_resolution.resolution = resolve_conflicts::content_user_rename;
604 conflict.right_resolution.content = new_optimal_path(idx(args,1)(), false);
605 conflict.right_resolution.rename = file_path_external(idx(args,2));
606 }
607 else
608 {
609 E(false, origin::user,
610 F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
611 }
612 return;
613 }
614 break;
615
616 case resolve_conflicts::right_side:
617 E(conflict.right_nid == the_null_node, origin::user,
618 F("must specify 'resolve_first_left' or 'resolve_first_right' (not just 'resolve_first')"));
619
620 // the right side stays dropped; we either drop, keep or replace the left side
621 if (conflict.left_resolution.resolution == resolve_conflicts::none)
622 {
623 if ("drop" == idx(args,0)())
624 {
625 E(args.size() == 1, origin::user, F("wrong number of arguments"));
626
627 conflict.left_resolution.resolution = resolve_conflicts::drop;
628 }
629 else if ("keep" == idx(args,0)())
630 {
631 E(args.size() == 1, origin::user, F("wrong number of arguments"));
632 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
633
634 conflict.left_resolution.resolution = resolve_conflicts::keep;
635 }
636 else if ("user" == idx(args,0)())
637 {
638 E(args.size() == 2, origin::user, F("wrong number of arguments"));
639 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
640
641 conflict.left_resolution.resolution = resolve_conflicts::content_user;
642 conflict.left_resolution.content = new_optimal_path(idx(args,1)(), false);
643 }
644 else if ("rename" == idx(args,0)())
645 {
646 E(args.size() == 2, origin::user, F("wrong number of arguments"));
647
648 conflict.left_resolution.resolution = resolve_conflicts::rename;
649 conflict.left_resolution.rename = file_path_external(idx(args,1));
650 }
651 else if ("user_rename" == idx(args,0)())
652 {
653 E(args.size() == 3, origin::user, F("wrong number of arguments"));
654
655 conflict.left_resolution.resolution = resolve_conflicts::content_user_rename;
656 conflict.left_resolution.content = new_optimal_path(idx(args,1)(), false);
657 conflict.left_resolution.rename = file_path_external(idx(args,2));
658 }
659 else
660 {
661 E(false, origin::user,
662 F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
663 }
664 return;
665 }
666 break;
667 }
668 }
669
670 for (std::vector<file_content_conflict>::iterator i = conflicts.result.file_content_conflicts.begin();
671 i != conflicts.result.file_content_conflicts.end();
672 ++i)
673 {
674 file_content_conflict & conflict = *i;
675
676 if (conflict.resolution.resolution == resolve_conflicts::none)
677 {
678 if ("interactive" == idx(args,0)())
679 {
680 bookkeeping_path result_path;
681
682 switch (args.size())
683 {
684 case 1:
685 // use default path for resolution file
686 {
687 file_path left_path;
688 conflicts.left_roster->get_name(conflict.nid, left_path);
689 result_path = bookkeeping_resolutions_dir / left_path;
690 }
691 break;
692
693 case 2:
694 // user path for resolution file
695 {
696 string normalized;
697 normalize_external_path(idx(args,1)(),
698 normalized,
699 false); // to_workspace_root
700 result_path = bookkeeping_path(normalized, origin::user);
701 }
702 break;
703
704 default:
705 E(false, origin::user, F("wrong number of arguments"));
706 }
707
708 if (do_interactive_merge(db, lua, conflicts, conflict.nid,
709 conflict.ancestor, conflict.left, conflict.right, result_path))
710 {
711 conflict.resolution.resolution = resolve_conflicts::content_user;
712 conflict.resolution.content = boost::shared_ptr<any_path>(new bookkeeping_path(result_path));
713 P(F("interactive merge result saved in '%s'") % result_path.as_internal());
714 }
715 else
716 P(F("interactive merge failed."));
717 }
718 else if ("user" == idx(args,0)())
719 {
720 E(args.size() == 2, origin::user, F("wrong number of arguments"));
721
722 conflict.resolution.resolution = resolve_conflicts::content_user;
723 conflict.resolution.content = new_optimal_path(idx(args,1)(), false);
724 }
725 else
726 {
727 // We don't allow the user to specify 'resolved_internal'; that
728 // is only done by automate show_conflicts.
729 E(false, origin::user,
730 F(conflict_resolution_not_supported_msg) % idx(args,0) % "file_content");
731 }
732 return;
733 }
734 }
735 }
736
737 switch (side)
738 {
739 case left:
740 E(false, origin::user, F("no resolvable yet unresolved left side conflicts"));
741 break;
742
743 case right:
744 E(false, origin::user, F("no resolvable yet unresolved right side conflicts"));
745 break;
746
747 case neither:
748 E(false, origin::user, F("no resolvable yet unresolved single-file conflicts"));
749 break;
750 }
751
752} // set_first_conflict
753
754
755/// commands
756
757// CMD(store) is in cmd_merging.cc, since it needs access to
758// show_conflicts_core, and doesn't need conflicts_t.
759
760CMD(show_first, "show_first", "", CMD_REF(conflicts),
761 "",
762 N_("Show the first unresolved conflict in the conflicts file, and possible resolutions"),
763 "",
764 options::opts::conflicts_opts)
765{
766 database db(app);
767 conflicts_t conflicts (db, app.opts.conflicts_file);
768
769 E(args.size() == 0, origin::user, F("wrong number of arguments"));
770 show_conflicts(db, conflicts, first);
771}
772
773CMD(show_remaining, "show_remaining", "", CMD_REF(conflicts),
774 "",
775 N_("Show the remaining unresolved conflicts in the conflicts file"),
776 "",
777 options::opts::conflicts_opts)
778{
779 database db(app);
780 conflicts_t conflicts (db, app.opts.conflicts_file);
781
782 E(args.size() == 0, origin::user, F("wrong number of arguments"));
783 show_conflicts(db, conflicts, remaining);
784}
785
786CMD(resolve_first, "resolve_first", "", CMD_REF(conflicts),
787 N_("RESOLUTION"),
788 N_("Set the resolution for the first unresolved single-file conflict."),
789 "Use 'mtn conflicts show_first' to see possible resolutions.",
790 options::opts::conflicts_opts)
791{
792 database db(app);
793 conflicts_t conflicts (db, app.opts.conflicts_file);
794
795 set_first_conflict(db, app.lua, conflicts, args, neither);
796
797 conflicts.write (db, app.lua, app.opts.conflicts_file);
798}
799
800CMD(resolve_first_left, "resolve_first_left", "", CMD_REF(conflicts),
801 N_("RESOLUTION"),
802 N_("Set the left resolution for the first unresolved two-file conflict"),
803 "",
804 options::opts::conflicts_opts)
805{
806 database db(app);
807 conflicts_t conflicts (db, app.opts.conflicts_file);
808
809 set_first_conflict(db, app.lua, conflicts, args, left);
810
811 conflicts.write (db, app.lua, app.opts.conflicts_file);
812}
813
814CMD(resolve_first_right, "resolve_first_right", "", CMD_REF(conflicts),
815 N_("RESOLUTION"),
816 N_("Set the right resolution for the first unresolved two-file conflict"),
817 "",
818 options::opts::conflicts_opts)
819{
820 database db(app);
821 conflicts_t conflicts (db, app.opts.conflicts_file);
822
823 set_first_conflict(db, app.lua, conflicts, args, right);
824
825 conflicts.write (db, app.lua, app.opts.conflicts_file);
826}
827
828CMD(clean, "clean", "", CMD_REF(conflicts),
829 "",
830 N_("Delete any bookkeeping files related to conflict resolution"),
831 "",
832 options::opts::none)
833{
834 if (path_exists(bookkeeping_conflicts_file))
835 delete_file(bookkeeping_conflicts_file);
836
837 if (path_exists(bookkeeping_resolutions_dir))
838 delete_dir_recursive(bookkeeping_resolutions_dir);
839}
840
841// Local Variables:
842// mode: C++
843// fill-column: 76
844// c-file-style: "gnu"
845// indent-tabs-mode: nil
846// End:
847// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status