monotone

monotone Mtn Source Tree

Root/src/merge_conflict.cc

1// Copyright (C) 2005, 2012 Nathaniel Smith <njs@pobox.com>
2// 2008, 2009, 2012 Stephen Leake <stephen_leake@stephe-leake.org>
3//
4// This program is made available under the GNU GPL version 2.0 or
5// greater. See the accompanying file COPYING for details.
6//
7// This program is distributed WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9// PURPOSE.
10
11#include "base.hh"
12#include "merge_roster.hh"
13
14#include "basic_io.hh"
15#include "file_io.hh"
16#include "lua_hooks.hh"
17#include "options.hh"
18#include "transforms.hh"
19
20#include <sstream>
21
22using std::make_pair;
23using std::string;
24using boost::shared_ptr;
25
26namespace
27{
28 enum node_type { file_type, dir_type };
29
30 node_type
31 get_type(roster_t const & roster, node_id const nid)
32 {
33 const_node_t n = roster.get_node(nid);
34
35 if (is_file_t(n))
36 return file_type;
37 else if (is_dir_t(n))
38 return dir_type;
39 else
40 I(false);
41 }
42
43 namespace syms
44 {
45 symbol const ancestor("ancestor");
46 symbol const ancestor_file_id("ancestor_file_id");
47 symbol const ancestor_name("ancestor_name");
48 symbol const attr_name("attr_name");
49 symbol const attribute("attribute");
50 symbol const conflict("conflict");
51 symbol const content("content");
52 symbol const directory_loop("directory_loop");
53 symbol const dropped_modified("dropped_modified");
54 symbol const duplicate_name("duplicate_name");
55 symbol const invalid_name("invalid_name");
56 symbol const left("left");
57 symbol const left_attr_state("left_attr_state");
58 symbol const left_attr_value("left_attr_value");
59 symbol const left_file_id("left_file_id");
60 symbol const left_name("left_name");
61 symbol const left_rev("left_rev");
62 symbol const left_type("left_type");
63 symbol const missing_root("missing_root");
64 symbol const multiple_names("multiple_names");
65 symbol const node_type("node_type");
66 symbol const orphaned_directory("orphaned_directory");
67 symbol const orphaned_file("orphaned_file");
68 symbol const resolved_drop_left("resolved_drop_left");
69 symbol const resolved_drop_right("resolved_drop_right");
70 symbol const resolved_keep_left("resolved_keep_left");
71 symbol const resolved_keep_right("resolved_keep_right");
72 symbol const resolved_internal("resolved_internal");
73 symbol const resolved_rename_left("resolved_rename_left");
74 symbol const resolved_rename_right("resolved_rename_right");
75 symbol const resolved_user_left("resolved_user_left");
76 symbol const resolved_user_right("resolved_user_right");
77 symbol const right("right");
78 symbol const right_attr_state("right_attr_state");
79 symbol const right_attr_value("right_attr_value");
80 symbol const right_file_id("right_file_id");
81 symbol const right_name("right_name");
82 symbol const right_rev("right_rev");
83 symbol const right_type("right_type");
84 }
85}
86
87namespace resolve_conflicts
88{
89 file_path file_path_external(string path)
90 {
91 return file_path_external(utf8(path, origin::user));
92 };
93}
94
95
96static void
97put_added_conflict_left(basic_io::stanza & st,
98 content_merge_adaptor & adaptor,
99 node_id const nid)
100{
101 // We access the roster via the adaptor, to be sure we use the left
102 // roster; avoids typos in long parameter lists.
103
104 // If we get a workspace adaptor here someday, we should add the required
105 // access functions to content_merge_adaptor.
106
107 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
108 boost::shared_ptr<roster_t const> roster(db_adaptor.rosters[db_adaptor.left_rid]);
109 file_path name;
110
111 roster->get_name (nid, name);
112
113 if (file_type == get_type (*roster, nid))
114 {
115 file_id fid;
116 db_adaptor.db.get_file_content (db_adaptor.left_rid, nid, fid);
117 st.push_str_pair(syms::left_type, "added file");
118 st.push_file_pair(syms::left_name, name);
119 st.push_binary_pair(syms::left_file_id, fid.inner());
120 }
121 else
122 {
123 st.push_str_pair(syms::left_type, "added directory");
124 st.push_file_pair(syms::left_name, name);
125 }
126}
127
128static void
129put_added_conflict_right(basic_io::stanza & st,
130 content_merge_adaptor & adaptor,
131 node_id const nid)
132{
133 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
134 boost::shared_ptr<roster_t const> roster(db_adaptor.rosters[db_adaptor.right_rid]);
135 I(0 != roster);
136
137 file_path name;
138
139 roster->get_name (nid, name);
140
141 if (file_type == get_type (*roster, nid))
142 {
143 file_id fid;
144 db_adaptor.db.get_file_content (db_adaptor.right_rid, nid, fid);
145
146 st.push_str_pair(syms::right_type, "added file");
147 st.push_file_pair(syms::right_name, name);
148 st.push_binary_pair(syms::right_file_id, fid.inner());
149 }
150 else
151 {
152 st.push_str_pair(syms::right_type, "added directory");
153 st.push_file_pair(syms::right_name, name);
154 }
155}
156
157static void
158put_rename_conflict_left(basic_io::stanza & st,
159 content_merge_adaptor & adaptor,
160 node_id const nid)
161{
162 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
163 boost::shared_ptr<roster_t const> ancestor_roster(db_adaptor.rosters[db_adaptor.lca]);
164 I(0 != ancestor_roster);
165 boost::shared_ptr<roster_t const> left_roster(db_adaptor.rosters[db_adaptor.left_rid]);
166
167 file_path ancestor_name;
168 file_path left_name;
169
170 ancestor_roster->get_name (nid, ancestor_name);
171 left_roster->get_name (nid, left_name);
172
173 if (file_type == get_type (*left_roster, nid))
174 {
175 st.push_str_pair(syms::left_type, "renamed file");
176 file_id ancestor_fid;
177 db_adaptor.db.get_file_content (db_adaptor.lca, nid, ancestor_fid);
178 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
179 st.push_binary_pair(syms::ancestor_file_id, ancestor_fid.inner());
180 file_id left_fid;
181 db_adaptor.db.get_file_content (db_adaptor.left_rid, nid, left_fid);
182 st.push_file_pair(syms::left_name, left_name);
183 st.push_binary_pair(syms::left_file_id, left_fid.inner());
184 }
185 else
186 {
187 st.push_str_pair(syms::left_type, "renamed directory");
188 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
189 st.push_file_pair(syms::left_name, left_name);
190 }
191}
192
193static void
194get_nid_name_pair(roster_t const & roster,
195 string const & path,
196 node_id & nid,
197 std::pair<node_id, path_component> & name)
198{
199 const_node_t node = roster.get_node(file_path_external(utf8(path, origin::internal)));
200 nid = node->self;
201 name = make_pair (node->parent, node->name);
202}
203
204static void
205read_added_rename_conflict_left(basic_io::parser & pars,
206 roster_t const & roster,
207 node_id & left_nid,
208 std::pair<node_id, path_component> & left_name)
209{
210 string tmp;
211
212 pars.esym(syms::left_type);
213
214 pars.str(tmp);
215
216 if (tmp == "renamed file")
217 {
218 pars.esym(syms::ancestor_name); pars.str();
219 pars.esym(syms::ancestor_file_id); pars.hex();
220
221 pars.esym(syms::left_name); pars.str(tmp);
222 get_nid_name_pair(roster, tmp, left_nid, left_name);
223 pars.esym(syms::left_file_id); pars.hex();
224 }
225 else if (tmp == "renamed directory")
226 {
227 pars.esym(syms::ancestor_name); pars.str();
228 pars.esym(syms::left_name); pars.str(tmp);
229 get_nid_name_pair(roster, tmp, left_nid, left_name);
230 }
231 else if (tmp == "added file")
232 {
233 pars.esym(syms::left_name); pars.str(tmp);
234 get_nid_name_pair(roster, tmp, left_nid, left_name);
235 pars.esym(syms::left_file_id); pars.hex();
236 }
237 else if (tmp == "added directory")
238 {
239 pars.esym(syms::left_name); pars.str(tmp);
240 get_nid_name_pair(roster, tmp, left_nid, left_name);
241 }
242} // read_added_rename_conflict_left
243
244static void
245read_added_rename_conflict_right(basic_io::parser & pars,
246 roster_t const & roster,
247 node_id & right_nid,
248 std::pair<node_id, path_component> & right_name)
249{
250 string tmp;
251
252 pars.esym(syms::right_type);
253
254 pars.str(tmp);
255
256 if (tmp == "renamed file")
257 {
258 pars.esym(syms::ancestor_name); pars.str();
259 pars.esym(syms::ancestor_file_id); pars.hex();
260
261 pars.esym(syms::right_name); pars.str(tmp);
262 get_nid_name_pair(roster, tmp, right_nid, right_name);
263 pars.esym(syms::right_file_id); pars.hex();
264 }
265 else if (tmp == "renamed directory")
266 {
267 pars.esym(syms::ancestor_name); pars.str();
268 pars.esym(syms::right_name); pars.str(tmp);
269 get_nid_name_pair(roster, tmp, right_nid, right_name);
270 }
271 else if (tmp == "added file")
272 {
273 pars.esym(syms::right_name); pars.str(tmp);
274 get_nid_name_pair(roster, tmp, right_nid, right_name);
275 pars.esym(syms::right_file_id); pars.hex();
276 }
277 else if (tmp == "added directory")
278 {
279 pars.esym(syms::right_name); pars.str(tmp);
280 get_nid_name_pair(roster, tmp, right_nid, right_name);
281 }
282}
283
284static void
285put_rename_conflict_right (basic_io::stanza & st,
286 content_merge_adaptor & adaptor,
287 node_id const nid)
288{
289 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
290 boost::shared_ptr<roster_t const> ancestor_roster(db_adaptor.rosters[db_adaptor.lca]);
291 I(0 != ancestor_roster);
292 boost::shared_ptr<roster_t const> right_roster(db_adaptor.rosters[db_adaptor.right_rid]);
293 I(0 != right_roster);
294
295 file_path ancestor_name;
296 file_path right_name;
297
298 ancestor_roster->get_name (nid, ancestor_name);
299 right_roster->get_name (nid, right_name);
300
301 if (file_type == get_type (*right_roster, nid))
302 {
303 st.push_str_pair(syms::right_type, "renamed file");
304 file_id ancestor_fid;
305 db_adaptor.db.get_file_content (db_adaptor.lca, nid, ancestor_fid);
306 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
307 st.push_binary_pair(syms::ancestor_file_id, ancestor_fid.inner());
308 file_id right_fid;
309 db_adaptor.db.get_file_content (db_adaptor.right_rid, nid, right_fid);
310 st.push_file_pair(syms::right_name, right_name);
311 st.push_binary_pair(syms::right_file_id, right_fid.inner());
312 }
313 else
314 {
315 st.push_str_pair(syms::right_type, "renamed directory");
316 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
317 st.push_file_pair(syms::right_name, right_name);
318 }
319}
320
321static void
322put_attr_state_left (basic_io::stanza & st, attribute_conflict const & conflict)
323{
324 if (conflict.left.first)
325 st.push_str_pair(syms::left_attr_value, conflict.left.second());
326 else
327 st.push_str_pair(syms::left_attr_state, "dropped");
328}
329
330static void
331put_attr_state_right (basic_io::stanza & st, attribute_conflict const & conflict)
332{
333 if (conflict.right.first)
334 st.push_str_pair(syms::right_attr_value, conflict.right.second());
335 else
336 st.push_str_pair(syms::right_attr_state, "dropped");
337}
338
339static void
340put_attr_conflict (basic_io::stanza & st,
341 content_merge_adaptor & adaptor,
342 attribute_conflict const & conflict)
343{
344 // Always report ancestor, left, and right information, for completeness
345
346 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
347
348 // This ensures that the ancestor roster is computed
349 boost::shared_ptr<roster_t const> ancestor_roster;
350 revision_id ancestor_rid;
351 db_adaptor.get_ancestral_roster (conflict.nid, ancestor_rid, ancestor_roster);
352
353 boost::shared_ptr<roster_t const> left_roster(db_adaptor.rosters[db_adaptor.left_rid]);
354 I(0 != left_roster);
355 boost::shared_ptr<roster_t const> right_roster(db_adaptor.rosters[db_adaptor.right_rid]);
356 I(0 != right_roster);
357
358 file_path ancestor_name;
359 file_path left_name;
360 file_path right_name;
361
362 ancestor_roster->get_name (conflict.nid, ancestor_name);
363 left_roster->get_name (conflict.nid, left_name);
364 right_roster->get_name (conflict.nid, right_name);
365
366 if (file_type == get_type (*ancestor_roster, conflict.nid))
367 {
368 st.push_str_pair(syms::node_type, "file");
369 st.push_str_pair(syms::attr_name, conflict.key());
370 file_id ancestor_fid;
371 db_adaptor.db.get_file_content (db_adaptor.lca, conflict.nid, ancestor_fid);
372 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
373 st.push_binary_pair(syms::ancestor_file_id, ancestor_fid.inner());
374 file_id left_fid;
375 db_adaptor.db.get_file_content (db_adaptor.left_rid, conflict.nid, left_fid);
376 st.push_file_pair(syms::left_name, left_name);
377 st.push_binary_pair(syms::left_file_id, left_fid.inner());
378 put_attr_state_left (st, conflict);
379 file_id right_fid;
380 db_adaptor.db.get_file_content (db_adaptor.right_rid, conflict.nid, right_fid);
381 st.push_file_pair(syms::right_name, right_name);
382 st.push_binary_pair(syms::right_file_id, right_fid.inner());
383 put_attr_state_right (st, conflict);
384 }
385 else
386 {
387 st.push_str_pair(syms::node_type, "directory");
388 st.push_str_pair(syms::attr_name, conflict.key());
389 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
390 st.push_file_pair(syms::left_name, left_name);
391 put_attr_state_left (st, conflict);
392 st.push_file_pair(syms::right_name, right_name);
393 put_attr_state_right (st, conflict);
394 }
395}
396
397static void
398put_file_resolution(basic_io::stanza & st,
399 resolve_conflicts::side_t side,
400 resolve_conflicts::file_resolution_t const & resolution)
401{
402 // We output any resolution for any conflict; only valid resolutions
403 // should get into the data structures. To enforce that, when reading
404 // resolutions from files we check that the resolution is valid for the
405 // conflict. Hence there is no read_resolution.
406
407 switch (resolution.resolution)
408 {
409 case resolve_conflicts::none:
410 break;
411
412 case resolve_conflicts::content_user:
413 switch (side)
414 {
415 case resolve_conflicts::left_side:
416 st.push_str_pair(syms::resolved_user_left, resolution.content->as_external());
417 break;
418
419 case resolve_conflicts::right_side:
420 st.push_str_pair(syms::resolved_user_right, resolution.content->as_external());
421 break;
422 }
423 break;
424
425 case resolve_conflicts::content_user_rename:
426 switch (side)
427 {
428 case resolve_conflicts::left_side:
429 st.push_str_pair(syms::resolved_user_left, resolution.content->as_external());
430 st.push_str_pair(syms::resolved_rename_left, resolution.rename.as_external());
431 break;
432
433 case resolve_conflicts::right_side:
434 st.push_str_pair(syms::resolved_user_right, resolution.content->as_external());
435 st.push_str_pair(syms::resolved_rename_right, resolution.rename.as_external());
436 break;
437 }
438 break;
439
440 case resolve_conflicts::content_internal:
441 st.push_symbol(syms::resolved_internal);
442 break;
443
444 case resolve_conflicts::rename:
445 switch (side)
446 {
447 case resolve_conflicts::left_side:
448 st.push_str_pair(syms::resolved_rename_left, resolution.rename.as_external());
449 break;
450
451 case resolve_conflicts::right_side:
452 st.push_str_pair(syms::resolved_rename_right, resolution.rename.as_external());
453 break;
454 }
455 break;
456
457 case resolve_conflicts::drop:
458 switch (side)
459 {
460 case resolve_conflicts::left_side:
461 st.push_symbol(syms::resolved_drop_left);
462 break;
463
464 case resolve_conflicts::right_side:
465 st.push_symbol(syms::resolved_drop_right);
466 break;
467 }
468 break;
469
470 case resolve_conflicts::keep:
471 switch (side)
472 {
473 case resolve_conflicts::left_side:
474 st.push_symbol(syms::resolved_keep_left);
475 break;
476
477 case resolve_conflicts::right_side:
478 st.push_symbol(syms::resolved_keep_right);
479 break;
480 }
481 break;
482
483 default:
484 I(false);
485 }
486}
487
488static void
489put_content_conflict (basic_io::stanza & st,
490 roster_t const & left_roster,
491 roster_t const & right_roster,
492 content_merge_adaptor & adaptor,
493 file_content_conflict const & conflict)
494{
495 // Always report ancestor, left, and right information, for completeness
496
497 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
498
499 // This ensures that the ancestor roster is computed
500 boost::shared_ptr<roster_t const> ancestor_roster;
501 revision_id ancestor_rid;
502 db_adaptor.get_ancestral_roster (conflict.nid, ancestor_rid, ancestor_roster);
503
504 file_path ancestor_name;
505 file_path left_name;
506 file_path right_name;
507
508 ancestor_roster->get_name (conflict.nid, ancestor_name);
509 left_roster.get_name (conflict.nid, left_name);
510 right_roster.get_name (conflict.nid, right_name);
511
512 if (file_type == get_type (*ancestor_roster, conflict.nid))
513 {
514 st.push_str_pair(syms::node_type, "file");
515 file_id ancestor_fid;
516 db_adaptor.db.get_file_content (db_adaptor.lca, conflict.nid, ancestor_fid);
517 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
518 st.push_binary_pair(syms::ancestor_file_id, ancestor_fid.inner());
519 st.push_file_pair(syms::left_name, left_name);
520 file_id left_fid;
521 db_adaptor.db.get_file_content (db_adaptor.left_rid, conflict.nid, left_fid);
522 st.push_binary_pair(syms::left_file_id, left_fid.inner());
523 st.push_file_pair(syms::right_name, right_name);
524 file_id right_fid;
525 db_adaptor.db.get_file_content (db_adaptor.right_rid, conflict.nid, right_fid);
526 st.push_binary_pair(syms::right_file_id, right_fid.inner());
527 }
528 else
529 {
530 st.push_str_pair(syms::node_type, "directory");
531 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
532 st.push_file_pair(syms::left_name, left_name);
533 st.push_file_pair(syms::right_name, right_name);
534 }
535 put_file_resolution (st, resolve_conflicts::left_side, conflict.resolution);
536}
537
538static void
539put_stanza (basic_io::stanza & st,
540 std::ostream & output)
541{
542 // We have to declare the printer here, rather than more globally,
543 // because adaptor.get_ancestral_roster uses a basic_io::printer
544 // internally, and there can only be one active at a time.
545 basic_io::printer pr;
546 output << "\n";
547 pr.print_stanza(st);
548 output.write(pr.buf.data(), pr.buf.size());
549}
550
551void
552roster_merge_result::report_missing_root_conflicts(roster_t const & left_roster,
553 roster_t const & right_roster,
554 content_merge_adaptor & adaptor,
555 bool const basic_io,
556 std::ostream & output) const
557{
558 MM(left_roster);
559 MM(right_roster);
560
561 if (missing_root_conflict)
562 {
563 node_id left_root, right_root;
564 left_root = left_roster.root()->self;
565 right_root = right_roster.root()->self;
566
567 // these must be different for this conflict to happen
568 I(left_root != right_root);
569
570 shared_ptr<roster_t const> left_lca_roster, right_lca_roster;
571 revision_id left_lca_rid, right_lca_rid;
572 file_path left_lca_name, right_lca_name;
573
574 adaptor.get_ancestral_roster(left_root, left_lca_rid,
575 left_lca_roster);
576 adaptor.get_ancestral_roster(right_root, right_lca_rid,
577 right_lca_roster);
578
579 left_lca_roster->get_name(left_root, left_lca_name);
580 right_lca_roster->get_name(right_root, right_lca_name);
581
582 node_id left_lca_root = left_lca_roster->root()->self;
583 node_id right_lca_root = right_lca_roster->root()->self;
584
585 basic_io::stanza st;
586
587 if (basic_io)
588 st.push_str_pair(syms::conflict, syms::missing_root);
589 else
590 P(F("conflict: missing root directory"));
591
592 if (left_root != left_lca_root && right_root == right_lca_root)
593 {
594 if (basic_io)
595 {
596 st.push_str_pair(syms::left_type, "pivoted root");
597 st.push_str_pair(syms::ancestor_name, left_lca_name.as_external());
598 }
599 else
600 P(F("directory '%s' pivoted to root on the left") % left_lca_name);
601
602 if (!right_roster.has_node(left_root))
603 {
604 if (basic_io)
605 {
606 st.push_str_pair(syms::right_type, "deleted directory");
607 st.push_str_pair(syms::ancestor_name, left_lca_name.as_external());
608 }
609 else
610 P(F("directory '%s' deleted on the right") % left_lca_name);
611 }
612 }
613 else if (left_root == left_lca_root && right_root != right_lca_root)
614 {
615 if (!left_roster.has_node(right_root))
616 {
617 if (basic_io)
618 {
619 st.push_str_pair(syms::left_type, "deleted directory");
620 st.push_str_pair(syms::ancestor_name, right_lca_name.as_external());
621 }
622 else
623 P(F("directory '%s' deleted on the left") % right_lca_name);
624 }
625
626 if (basic_io)
627 {
628 st.push_str_pair(syms::right_type, "pivoted root");
629 st.push_str_pair(syms::ancestor_name, right_lca_name.as_external());
630 }
631 else
632 P(F("directory '%s' pivoted to root on the right") % right_lca_name);
633 }
634 else if (left_root != left_lca_root && right_root != right_lca_root)
635 {
636 if (basic_io)
637 {
638 st.push_str_pair(syms::left_type, "pivoted root");
639 st.push_str_pair(syms::ancestor_name, left_lca_name.as_external());
640 }
641 else
642 P(F("directory '%s' pivoted to root on the left") % left_lca_name);
643
644 if (!right_roster.has_node(left_root))
645 {
646 if (basic_io)
647 {
648 st.push_str_pair(syms::right_type, "deleted directory");
649 st.push_str_pair(syms::ancestor_name, left_lca_name.as_external());
650 }
651 else
652 P(F("directory '%s' deleted on the right") % left_lca_name);
653 }
654
655 if (!left_roster.has_node(right_root))
656 {
657 if (basic_io)
658 {
659 st.push_str_pair(syms::left_type, "deleted directory");
660 st.push_str_pair(syms::ancestor_name, right_lca_name.as_external());
661 }
662 else
663 P(F("directory '%s' deleted on the left") % right_lca_name);
664 }
665
666 if (basic_io)
667 {
668 st.push_str_pair(syms::right_type, "pivoted root");
669 st.push_str_pair(syms::ancestor_name, right_lca_name.as_external());
670 }
671 else
672 P(F("directory '%s' pivoted to root on the right") % right_lca_name);
673 }
674 // else
675 // other conflicts can cause the root dir to be left detached
676 // for example, merging two independently created projects
677 // in these cases don't report anything about pivot_root
678
679 if (basic_io)
680 put_stanza (st, output);
681 }
682}
683
684void
685roster_merge_result::report_invalid_name_conflicts(roster_t const & left_roster,
686 roster_t const & right_roster,
687 content_merge_adaptor & adaptor,
688 const bool basic_io,
689 std::ostream & output) const
690{
691 MM(left_roster);
692 MM(right_roster);
693
694 for (size_t i = 0; i < invalid_name_conflicts.size(); ++i)
695 {
696 invalid_name_conflict const & conflict = invalid_name_conflicts[i];
697 MM(conflict);
698
699 I(!roster.is_attached(conflict.nid));
700
701 shared_ptr<roster_t const> lca_roster, parent_lca_roster;
702 revision_id lca_rid, parent_lca_rid;
703 file_path lca_name, lca_parent_name;
704 basic_io::stanza st;
705
706 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
707 lca_roster->get_name(conflict.nid, lca_name);
708 lca_roster->get_name(conflict.parent_name.first, lca_parent_name);
709
710 adaptor.get_ancestral_roster(conflict.parent_name.first,
711 parent_lca_rid, parent_lca_roster);
712
713 if (basic_io)
714 st.push_str_pair(syms::conflict, syms::invalid_name);
715 else
716 P(F("conflict: invalid name '_MTN' in root directory"));
717
718 if (left_roster.root()->self == conflict.parent_name.first)
719 {
720 if (basic_io)
721 {
722 st.push_str_pair(syms::left_type, "pivoted root");
723 st.push_str_pair(syms::ancestor_name, lca_parent_name.as_external());
724 }
725 else
726 P(F("'%s' pivoted to root on the left")
727 % lca_parent_name);
728
729 file_path right_name;
730 right_roster.get_name(conflict.nid, right_name);
731 if (parent_lca_roster->has_node(conflict.nid))
732 {
733 if (basic_io)
734 put_rename_conflict_right (st, adaptor, conflict.nid);
735 else
736 P(F("'%s' renamed to '%s' on the right")
737 % lca_name % right_name);
738 }
739 else
740 {
741 if (basic_io)
742 put_added_conflict_right (st, adaptor, conflict.nid);
743 else
744 P(F("'%s' added in revision %s on the right")
745 % right_name % lca_rid);
746 }
747 }
748 else if (right_roster.root()->self == conflict.parent_name.first)
749 {
750 if (basic_io)
751 {
752 st.push_str_pair(syms::right_type, "pivoted root");
753 st.push_str_pair(syms::ancestor_name, lca_parent_name.as_external());
754 }
755 else
756 P(F("'%s' pivoted to root on the right")
757 % lca_parent_name);
758
759 file_path left_name;
760 left_roster.get_name(conflict.nid, left_name);
761 if (parent_lca_roster->has_node(conflict.nid))
762 {
763 if (basic_io)
764 put_rename_conflict_left (st, adaptor, conflict.nid);
765 else
766 P(F("'%s' renamed to '%s' on the left")
767 % lca_name % left_name);
768 }
769 else
770 {
771 if (basic_io)
772 put_added_conflict_left (st, adaptor, conflict.nid);
773 else
774 P(F("'%s' added in revision %s on the left")
775 % left_name % lca_rid);
776 }
777 }
778 else
779 I(false);
780
781 if (basic_io)
782 put_stanza(st, output);
783 }
784}
785
786void
787roster_merge_result::report_directory_loop_conflicts(roster_t const & left_roster,
788 roster_t const & right_roster,
789 content_merge_adaptor & adaptor,
790 const bool basic_io,
791 std::ostream & output) const
792{
793 MM(left_roster);
794 MM(right_roster);
795
796 for (size_t i = 0; i < directory_loop_conflicts.size(); ++i)
797 {
798 directory_loop_conflict const & conflict = directory_loop_conflicts[i];
799 MM(conflict);
800
801 I(!roster.is_attached(conflict.nid));
802
803 file_path left_name, right_name, left_parent_name, right_parent_name;
804
805 left_roster.get_name(conflict.nid, left_name);
806 right_roster.get_name(conflict.nid, right_name);
807
808 left_roster.get_name(conflict.parent_name.first, left_parent_name);
809 right_roster.get_name(conflict.parent_name.first, right_parent_name);
810
811 shared_ptr<roster_t const> lca_roster;
812 revision_id lca_rid;
813 file_path lca_name, lca_parent_name;
814 basic_io::stanza st;
815
816 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
817 lca_roster->get_name(conflict.nid, lca_name);
818 lca_roster->get_name(conflict.parent_name.first, lca_parent_name);
819
820 if (basic_io)
821 st.push_str_pair(syms::conflict, syms::directory_loop);
822 else
823 P(F("conflict: directory loop created"));
824
825 if (left_name != lca_name)
826 {
827 if (basic_io)
828 put_rename_conflict_left (st, adaptor, conflict.nid);
829 else
830 P(F("'%s' renamed to '%s' on the left")
831 % lca_name % left_name);
832 }
833
834 if (right_name != lca_name)
835 {
836 if (basic_io)
837 put_rename_conflict_right (st, adaptor, conflict.nid);
838 else
839 P(F("'%s' renamed to '%s' on the right")
840 % lca_name % right_name);
841 }
842
843 if (left_parent_name != lca_parent_name)
844 {
845 if (basic_io)
846 put_rename_conflict_left (st, adaptor, conflict.parent_name.first);
847 else
848 P(F("'%s' renamed to '%s' on the left")
849 % lca_parent_name % left_parent_name);
850 }
851
852 if (right_parent_name != lca_parent_name)
853 {
854 if (basic_io)
855 put_rename_conflict_right (st, adaptor, conflict.parent_name.first);
856 else
857 P(F("'%s' renamed to '%s' on the right")
858 % lca_parent_name % right_parent_name);
859 }
860
861 if (basic_io)
862 put_stanza(st, output);
863 }
864}
865
866void
867roster_merge_result::report_orphaned_node_conflicts(roster_t const & left_roster,
868 roster_t const & right_roster,
869 content_merge_adaptor & adaptor,
870 const bool basic_io,
871 std::ostream & output) const
872{
873 MM(left_roster);
874 MM(right_roster);
875
876 for (size_t i = 0; i < orphaned_node_conflicts.size(); ++i)
877 {
878 orphaned_node_conflict const & conflict = orphaned_node_conflicts[i];
879 MM(conflict);
880
881 I(!roster.is_attached(conflict.nid));
882
883 shared_ptr<roster_t const> lca_roster, parent_lca_roster;
884 revision_id lca_rid, parent_lca_rid;
885 file_path lca_name;
886
887 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
888 adaptor.get_ancestral_roster(conflict.parent_name.first,
889 parent_lca_rid, parent_lca_roster);
890
891 lca_roster->get_name(conflict.nid, lca_name);
892
893 node_type type = get_type(*lca_roster, conflict.nid);
894
895 basic_io::stanza st;
896
897 if (type == file_type)
898 if (basic_io)
899 st.push_str_pair(syms::conflict, syms::orphaned_file);
900 else
901 P(F("conflict: orphaned file '%s' from revision %s")
902 % lca_name % lca_rid);
903 else
904 {
905 if (basic_io)
906 st.push_str_pair(syms::conflict, syms::orphaned_directory);
907 else
908 P(F("conflict: orphaned directory '%s' from revision %s")
909 % lca_name % lca_rid);
910 }
911
912 if (left_roster.has_node(conflict.parent_name.first) &&
913 !right_roster.has_node(conflict.parent_name.first))
914 {
915 file_path orphan_name, parent_name;
916 left_roster.get_name(conflict.nid, orphan_name);
917 left_roster.get_name(conflict.parent_name.first, parent_name);
918
919 if (basic_io)
920 {
921 st.push_str_pair(syms::right_type, "deleted directory");
922 st.push_str_pair(syms::ancestor_name, parent_name.as_external());
923 }
924 else
925 P(F("parent directory '%s' was deleted on the right")
926 % parent_name);
927
928 if (parent_lca_roster->has_node(conflict.nid))
929 {
930 if (basic_io)
931 put_rename_conflict_left (st, adaptor, conflict.nid);
932 else
933 if (type == file_type)
934 P(F("file '%s' was renamed from '%s' on the left")
935 % orphan_name % lca_name);
936 else
937 P(F("directory '%s' was renamed from '%s' on the left")
938 % orphan_name % lca_name);
939 }
940 else
941 {
942 if (basic_io)
943 put_added_conflict_left (st, adaptor, conflict.nid);
944 else
945 {
946 if (type == file_type)
947 P(F("file '%s' was added on the left")
948 % orphan_name);
949 else
950 P(F("directory '%s' was added on the left")
951 % orphan_name);
952 }
953 }
954 }
955 else if (!left_roster.has_node(conflict.parent_name.first) &&
956 right_roster.has_node(conflict.parent_name.first))
957 {
958 file_path orphan_name, parent_name;
959 right_roster.get_name(conflict.nid, orphan_name);
960 right_roster.get_name(conflict.parent_name.first, parent_name);
961
962 if (basic_io)
963 {
964 st.push_str_pair(syms::left_type, "deleted directory");
965 st.push_str_pair(syms::ancestor_name, parent_name.as_external());
966 }
967 else
968 P(F("parent directory '%s' was deleted on the left")
969 % parent_name);
970
971 if (parent_lca_roster->has_node(conflict.nid))
972 {
973 if (basic_io)
974 put_rename_conflict_right (st, adaptor, conflict.nid);
975 else
976 if (type == file_type)
977 P(F("file '%s' was renamed from '%s' on the right")
978 % orphan_name % lca_name);
979 else
980 P(F("directory '%s' was renamed from '%s' on the right")
981 % orphan_name % lca_name);
982 }
983 else
984 {
985 if (basic_io)
986 put_added_conflict_right (st, adaptor, conflict.nid);
987 else
988 if (type == file_type)
989 P(F("file '%s' was added on the right")
990 % orphan_name);
991 else
992 P(F("directory '%s' was added on the right")
993 % orphan_name);
994 }
995 }
996 else
997 I(false);
998
999 if (basic_io)
1000 {
1001 put_file_resolution (st, resolve_conflicts::left_side, conflict.resolution);
1002 put_stanza (st, output);
1003 }
1004 }
1005}
1006
1007void
1008roster_merge_result::report_multiple_name_conflicts(roster_t const & left_roster,
1009 roster_t const & right_roster,
1010 content_merge_adaptor & adaptor,
1011 const bool basic_io,
1012 std::ostream & output) const
1013{
1014 MM(left_roster);
1015 MM(right_roster);
1016
1017 for (size_t i = 0; i < multiple_name_conflicts.size(); ++i)
1018 {
1019 multiple_name_conflict const & conflict = multiple_name_conflicts[i];
1020 MM(conflict);
1021
1022 I(!roster.is_attached(conflict.nid));
1023
1024 file_path left_name, right_name;
1025
1026 left_roster.get_name(conflict.nid, left_name);
1027 right_roster.get_name(conflict.nid, right_name);
1028
1029 shared_ptr<roster_t const> lca_roster;
1030 revision_id lca_rid;
1031 file_path lca_name;
1032
1033 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
1034 lca_roster->get_name(conflict.nid, lca_name);
1035
1036 node_type type = get_type(*lca_roster, conflict.nid);
1037
1038 basic_io::stanza st;
1039
1040 if (basic_io)
1041 {
1042 st.push_str_pair(syms::conflict, syms::multiple_names);
1043 put_rename_conflict_left (st, adaptor, conflict.nid);
1044 put_rename_conflict_right (st, adaptor, conflict.nid);
1045 }
1046 else
1047 {
1048 if (type == file_type)
1049 P(F("conflict: multiple names for file '%s' from revision %s")
1050 % lca_name % lca_rid);
1051 else
1052 P(F("conflict: multiple names for directory '%s' from revision %s")
1053 % lca_name % lca_rid);
1054
1055 P(F("renamed to '%s' on the left") % left_name);
1056 P(F("renamed to '%s' on the right") % right_name);
1057 }
1058
1059 if (basic_io)
1060 put_stanza(st, output);
1061 }
1062}
1063
1064void static
1065push_dropped_details(content_merge_database_adaptor & db_adaptor,
1066 symbol rev_sym,
1067 symbol name_sym,
1068 symbol file_id_sym,
1069 revision_id rev_id,
1070 node_id nid,
1071 basic_io::stanza & st)
1072{
1073 revision_id dropped_rev_id;
1074 file_path dropped_name;
1075 file_id dropped_file_id;
1076 db_adaptor.get_dropped_details(rev_id, nid, dropped_rev_id, dropped_name, dropped_file_id);
1077
1078 st.push_binary_pair(rev_sym, dropped_rev_id.inner());
1079 st.push_str_pair(name_sym, dropped_name.as_external());
1080 st.push_binary_pair(file_id_sym, dropped_file_id.inner());
1081}
1082
1083void
1084roster_merge_result::report_dropped_modified_conflicts(roster_t const & left_roster,
1085 roster_t const & right_roster,
1086 content_merge_adaptor & adaptor,
1087 const bool basic_io,
1088 std::ostream & output) const
1089{
1090 MM(left_roster);
1091 MM(right_roster);
1092
1093 for (size_t i = 0; i < dropped_modified_conflicts.size(); ++i)
1094 {
1095 dropped_modified_conflict const & conflict = dropped_modified_conflicts[i];
1096 MM(conflict);
1097
1098 node_id nid;
1099 file_path modified_name;
1100
1101 switch (conflict.dropped_side)
1102 {
1103 case resolve_conflicts::left_side:
1104 I(!roster.is_attached(conflict.right_nid));
1105
1106 nid = conflict.right_nid;
1107 right_roster.get_name(conflict.right_nid, modified_name);
1108 break;
1109
1110 case resolve_conflicts::right_side:
1111 I(!roster.is_attached(conflict.left_nid));
1112
1113 nid = conflict.left_nid;
1114 left_roster.get_name(conflict.left_nid, modified_name);
1115 break;
1116 }
1117
1118 shared_ptr<roster_t const> lca_roster;
1119 revision_id lca_rid;
1120 file_path ancestor_name;
1121
1122 adaptor.get_ancestral_roster(nid, lca_rid, lca_roster);
1123 lca_roster->get_name(nid, ancestor_name);
1124
1125 if (basic_io)
1126 {
1127 basic_io::stanza st;
1128
1129 content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
1130 file_id fid;
1131
1132 st.push_str_pair(syms::conflict, syms::dropped_modified);
1133 st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
1134 db_adaptor.db.get_file_content (db_adaptor.lca, nid, fid);
1135 st.push_binary_pair(syms::ancestor_file_id, fid.inner());
1136
1137 switch (conflict.dropped_side)
1138 {
1139 case resolve_conflicts::left_side:
1140 if (conflict.orphaned)
1141 {
1142 st.push_str_pair(syms::left_type, "orphaned file");
1143 push_dropped_details(db_adaptor, syms::left_rev, syms::left_name, syms::left_file_id,
1144 db_adaptor.left_rid, nid, st);
1145 }
1146 else
1147 {
1148 if (conflict.left_nid == the_null_node)
1149 {
1150 st.push_str_pair(syms::left_type, "dropped file");
1151 push_dropped_details(db_adaptor, syms::left_rev, syms::left_name, syms::left_file_id,
1152 db_adaptor.left_rid, nid, st);
1153 }
1154 else
1155 {
1156 st.push_str_pair(syms::left_type, "recreated file");
1157 st.push_str_pair(syms::left_name, modified_name.as_external());
1158 db_adaptor.db.get_file_content (db_adaptor.left_rid, conflict.left_nid, fid);
1159 st.push_binary_pair(syms::left_file_id, fid.inner());
1160 }
1161 }
1162
1163 st.push_str_pair(syms::right_type, "modified file");
1164 st.push_str_pair(syms::right_name, modified_name.as_external());
1165 db_adaptor.db.get_file_content (db_adaptor.right_rid, nid, fid);
1166 st.push_binary_pair(syms::right_file_id, fid.inner());
1167 break;
1168
1169 case resolve_conflicts::right_side:
1170 st.push_str_pair(syms::left_type, "modified file");
1171 st.push_str_pair(syms::left_name, modified_name.as_external());
1172 db_adaptor.db.get_file_content (db_adaptor.left_rid, nid, fid);
1173 st.push_binary_pair(syms::left_file_id, fid.inner());
1174
1175 if (conflict.orphaned)
1176 {
1177 st.push_str_pair(syms::right_type, "orphaned file");
1178 push_dropped_details(db_adaptor, syms::right_rev, syms::right_name, syms::right_file_id,
1179 db_adaptor.right_rid, nid, st);
1180 }
1181 else
1182 {
1183 if (conflict.right_nid == the_null_node)
1184 {
1185 st.push_str_pair(syms::right_type, "dropped file");
1186 push_dropped_details(db_adaptor, syms::right_rev, syms::right_name, syms::right_file_id,
1187 db_adaptor.right_rid, nid, st);
1188 }
1189 else
1190 {
1191 st.push_str_pair(syms::right_type, "recreated file");
1192 st.push_str_pair(syms::right_name, modified_name.as_external());
1193 db_adaptor.db.get_file_content (db_adaptor.right_rid, conflict.right_nid, fid);
1194 st.push_binary_pair(syms::right_file_id, fid.inner());
1195 }
1196 }
1197 break;
1198 }
1199
1200 put_file_resolution (st, resolve_conflicts::left_side, conflict.left_resolution);
1201 put_file_resolution (st, resolve_conflicts::right_side, conflict.right_resolution);
1202
1203 put_stanza(st, output);
1204 }
1205 else
1206 {
1207 P(F("conflict: file '%s'") % ancestor_name);
1208 switch (conflict.dropped_side)
1209 {
1210 case resolve_conflicts::left_side:
1211 if (conflict.orphaned)
1212 {
1213 P(F("orphaned on the left"));
1214 }
1215 else
1216 {
1217 if (conflict.left_nid == the_null_node)
1218 P(F("dropped on the left"));
1219 else
1220 P(F("dropped and recreated on the left"));
1221 }
1222 P(F("modified on the right, named %s") % modified_name);
1223 break;
1224
1225 case resolve_conflicts::right_side:
1226 P(F("modified on the left, named %s") % modified_name);
1227 if (conflict.orphaned)
1228 {
1229 P(F("orphaned on the right"));
1230 }
1231 else
1232 {
1233 if (conflict.right_nid == the_null_node)
1234 P(F("dropped on the right"));
1235 else
1236 P(F("dropped and recreated on the right"));
1237 }
1238 break;
1239 }
1240
1241 // We can have a resolution from a mtn:resolve_conflict attribute
1242 // (so far, that only supports drop).
1243 switch (conflict.left_resolution.resolution)
1244 {
1245 case resolve_conflicts::none:
1246 break;
1247
1248 case resolve_conflicts::content_user:
1249 case resolve_conflicts::content_user_rename:
1250 case resolve_conflicts::rename:
1251 case resolve_conflicts::keep:
1252 case resolve_conflicts::content_internal:
1253 I(false);
1254 break;
1255
1256 case resolve_conflicts::drop:
1257 P(F("left_resolution: drop"));
1258 break;
1259 }
1260 switch (conflict.right_resolution.resolution)
1261 {
1262 case resolve_conflicts::none:
1263 break;
1264
1265 case resolve_conflicts::content_user:
1266 case resolve_conflicts::content_user_rename:
1267 case resolve_conflicts::rename:
1268 case resolve_conflicts::keep:
1269 case resolve_conflicts::content_internal:
1270 I(false);
1271 break;
1272
1273 case resolve_conflicts::drop:
1274 P(F("right_resolution: drop"));
1275 break;
1276 }
1277 }
1278 }
1279}
1280
1281void
1282roster_merge_result::report_duplicate_name_conflicts(roster_t const & left_roster,
1283 roster_t const & right_roster,
1284 content_merge_adaptor & adaptor,
1285 bool const basic_io,
1286 std::ostream & output) const
1287{
1288 MM(left_roster);
1289 MM(right_roster);
1290
1291 for (size_t i = 0; i < duplicate_name_conflicts.size(); ++i)
1292 {
1293 duplicate_name_conflict const & conflict = duplicate_name_conflicts[i];
1294 MM(conflict);
1295
1296 node_id left_nid, right_nid;
1297
1298 left_nid = conflict.left_nid;
1299 right_nid = conflict.right_nid;
1300
1301 I(!roster.is_attached(left_nid));
1302 I(!roster.is_attached(right_nid));
1303
1304 file_path left_name, right_name;
1305
1306 left_roster.get_name(left_nid, left_name);
1307 right_roster.get_name(right_nid, right_name);
1308
1309 shared_ptr<roster_t const> left_lca_roster, right_lca_roster;
1310 revision_id left_lca_rid, right_lca_rid;
1311
1312 adaptor.get_ancestral_roster(left_nid, left_lca_rid, left_lca_roster);
1313 adaptor.get_ancestral_roster(right_nid, right_lca_rid, right_lca_roster);
1314
1315 // In most cases, the left_name equals the right_name. However, maybe
1316 // a parent directory got renamed on one side. In that case, the names
1317 // don't match, but it's still the same directory (by node id), to
1318 // which we want to add the same file (by name).
1319
1320 basic_io::stanza st;
1321
1322 if (basic_io)
1323 st.push_str_pair(syms::conflict, syms::duplicate_name);
1324 else
1325 {
1326 if (left_name == right_name)
1327 {
1328 file_path dir;
1329 path_component basename;
1330 left_name.dirname_basename(dir, basename);
1331 P(F("conflict: duplicate name '%s' for the directory '%s'") % basename % dir);
1332 }
1333 else
1334 {
1335 file_path left_dir, right_dir;
1336 path_component left_basename, right_basename;
1337 left_name.dirname_basename(left_dir, left_basename);
1338 right_name.dirname_basename(right_dir, right_basename);
1339 I(left_basename == right_basename);
1340 P(F("conflict: duplicate name '%s' for the directory\n"
1341 " named '%s' on the left and\n"
1342 " named '%s' on the right.")
1343 % left_basename % left_dir % right_dir);
1344 }
1345 }
1346
1347 node_type left_type = get_type(left_roster, left_nid);
1348 node_type right_type = get_type(right_roster, right_nid);
1349
1350 if (!left_lca_roster->has_node(right_nid) &&
1351 !right_lca_roster->has_node(left_nid))
1352 {
1353 if (basic_io)
1354 put_added_conflict_left (st, adaptor, left_nid);
1355 else
1356 {
1357 if (left_type == file_type)
1358 P(F("added as a new file on the left"));
1359 else
1360 P(F("added as a new directory on the left"));
1361 }
1362
1363 if (basic_io)
1364 put_added_conflict_right (st, adaptor, right_nid);
1365 else
1366 {
1367 if (right_type == file_type)
1368 P(F("added as a new file on the right"));
1369 else
1370 P(F("added as a new directory on the right"));
1371 }
1372 }
1373 else if (!left_lca_roster->has_node(right_nid) &&
1374 right_lca_roster->has_node(left_nid))
1375 {
1376 file_path left_lca_name;
1377 left_lca_roster->get_name(left_nid, left_lca_name);
1378
1379 if (basic_io)
1380 put_rename_conflict_left (st, adaptor, left_nid);
1381 else
1382 if (left_type == file_type)
1383 P(F("renamed from file '%s' on the left") % left_lca_name);
1384 else
1385 P(F("renamed from directory '%s' on the left") % left_lca_name);
1386
1387 if (basic_io)
1388 put_added_conflict_right (st, adaptor, right_nid);
1389 else
1390 {
1391 if (right_type == file_type)
1392 P(F("added as a new file on the right"));
1393 else
1394 P(F("added as a new directory on the right"));
1395 }
1396 }
1397 else if (left_lca_roster->has_node(right_nid) &&
1398 !right_lca_roster->has_node(left_nid))
1399 {
1400 file_path right_lca_name;
1401 right_lca_roster->get_name(right_nid, right_lca_name);
1402
1403 if (basic_io)
1404 put_added_conflict_left (st, adaptor, left_nid);
1405 else
1406 {
1407 if (left_type == file_type)
1408 P(F("added as a new file on the left"));
1409 else
1410 P(F("added as a new directory on the left"));
1411 }
1412
1413 if (basic_io)
1414 put_rename_conflict_right (st, adaptor, right_nid);
1415 else
1416 {
1417 if (right_type == file_type)
1418 P(F("renamed from file '%s' on the right") % right_lca_name);
1419 else
1420 P(F("renamed from directory '%s' on the right") % right_lca_name);
1421 }
1422 }
1423 else if (left_lca_roster->has_node(right_nid) &&
1424 right_lca_roster->has_node(left_nid))
1425 {
1426 file_path left_lca_name, right_lca_name;
1427 left_lca_roster->get_name(left_nid, left_lca_name);
1428 right_lca_roster->get_name(right_nid, right_lca_name);
1429
1430 if (basic_io)
1431 put_rename_conflict_left (st, adaptor, left_nid);
1432 else
1433 {
1434 if (left_type == file_type)
1435 P(F("renamed from file '%s' on the left") % left_lca_name);
1436 else
1437 P(F("renamed from directory '%s' on the left") % left_lca_name);
1438 }
1439
1440 if (basic_io)
1441 put_rename_conflict_right (st, adaptor, right_nid);
1442 else
1443 {
1444 if (right_type == file_type)
1445 P(F("renamed from file '%s' on the right") % right_lca_name);
1446 else
1447 P(F("renamed from directory '%s' on the right") % right_lca_name);
1448 }
1449 }
1450 else
1451 I(false);
1452
1453 if (basic_io)
1454 {
1455 put_file_resolution (st, resolve_conflicts::left_side, conflict.left_resolution);
1456 put_file_resolution (st, resolve_conflicts::right_side, conflict.right_resolution);
1457 put_stanza(st, output);
1458 }
1459 }
1460}
1461
1462void
1463roster_merge_result::report_attribute_conflicts(roster_t const & left_roster,
1464 roster_t const & right_roster,
1465 content_merge_adaptor & adaptor,
1466 const bool basic_io,
1467 std::ostream & output) const
1468{
1469 MM(left_roster);
1470 MM(right_roster);
1471 MM(roster);
1472
1473 for (size_t i = 0; i < attribute_conflicts.size(); ++i)
1474 {
1475 attribute_conflict const & conflict = attribute_conflicts[i];
1476 MM(conflict);
1477
1478 if (basic_io)
1479 {
1480 basic_io::stanza st;
1481
1482 st.push_str_pair(syms::conflict, syms::attribute);
1483 put_attr_conflict (st, adaptor, conflict);
1484 put_stanza (st, output);
1485 }
1486 else
1487 {
1488 // this->roster is null when we are called from 'conflicts
1489 // show_remaining'; treat as unattached in that case.
1490 node_type type = get_type(left_roster, conflict.nid);
1491
1492 if (roster.all_nodes().size() > 0 && roster.is_attached(conflict.nid))
1493 {
1494 file_path name;
1495 roster.get_name(conflict.nid, name);
1496
1497 if (type == file_type)
1498 P(F("conflict: multiple values for attribute '%s' on file '%s'")
1499 % conflict.key % name);
1500 else
1501 P(F("conflict: multiple values for attribute '%s' on directory '%s'")
1502 % conflict.key % name);
1503
1504 if (conflict.left.first)
1505 P(F("set to '%s' on the left") % conflict.left.second);
1506 else
1507 P(F("deleted on the left"));
1508
1509 if (conflict.right.first)
1510 P(F("set to '%s' on the right") % conflict.right.second);
1511 else
1512 P(F("deleted on the right"));
1513 }
1514 else
1515 {
1516 // This node isn't attached in the merged roster, due to another
1517 // conflict (ie renamed to different names). So report the
1518 // ancestor name and the left and right names.
1519
1520 file_path left_name, right_name;
1521 left_roster.get_name(conflict.nid, left_name);
1522 right_roster.get_name(conflict.nid, right_name);
1523
1524 shared_ptr<roster_t const> lca_roster;
1525 revision_id lca_rid;
1526 file_path lca_name;
1527
1528 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
1529 lca_roster->get_name(conflict.nid, lca_name);
1530
1531 if (type == file_type)
1532 P(F("conflict: multiple values for attribute '%s' on file '%s' from revision %s")
1533 % conflict.key % lca_name % lca_rid);
1534 else
1535 P(F("conflict: multiple values for attribute '%s' on directory '%s' from revision %s")
1536 % conflict.key % lca_name % lca_rid);
1537
1538 if (conflict.left.first)
1539 {
1540 if (type == file_type)
1541 P(F("set to '%s' on left file '%s'")
1542 % conflict.left.second % left_name);
1543 else
1544 P(F("set to '%s' on left directory '%s'")
1545 % conflict.left.second % left_name);
1546 }
1547 else
1548 {
1549 if (type == file_type)
1550 P(F("deleted from left file '%s'")
1551 % left_name);
1552 else
1553 P(F("deleted from left directory '%s'")
1554 % left_name);
1555 }
1556
1557 if (conflict.right.first)
1558 {
1559 if (type == file_type)
1560 P(F("set to '%s' on right file '%s'")
1561 % conflict.right.second % right_name);
1562 else
1563 P(F("set to '%s' on right directory '%s'")
1564 % conflict.right.second % right_name);
1565 }
1566 else
1567 {
1568 if (type == file_type)
1569 P(F("deleted from right file '%s'")
1570 % right_name);
1571 else
1572 P(F("deleted from right directory '%s'")
1573 % right_name);
1574 }
1575 }
1576 }
1577 }
1578}
1579
1580namespace
1581{
1582 bool
1583 auto_merge_succeeds(lua_hooks & lua,
1584 file_content_conflict conflict,
1585 content_merge_adaptor & adaptor,
1586 roster_t const & left_roster,
1587 roster_t const & right_roster)
1588 {
1589 revision_id ancestor_rid;
1590 shared_ptr<roster_t const> ancestor_roster;
1591 adaptor.get_ancestral_roster(conflict.nid, ancestor_rid, ancestor_roster);
1592
1593 I(ancestor_roster);
1594 I(ancestor_roster->has_node(conflict.nid)); // this fails if there is no least common ancestor
1595
1596 file_id anc_id, left_id, right_id;
1597 file_path anc_path, left_path, right_path;
1598 ancestor_roster->get_file_details(conflict.nid, anc_id, anc_path);
1599 left_roster.get_file_details(conflict.nid, left_id, left_path);
1600 right_roster.get_file_details(conflict.nid, right_id, right_path);
1601
1602 content_merger cm(lua, *ancestor_roster, left_roster, right_roster, adaptor);
1603
1604 file_data left_data, right_data, merge_data;
1605
1606 return cm.attempt_auto_merge(anc_path, left_path, right_path,
1607 anc_id, left_id, right_id,
1608 left_data, right_data, merge_data);
1609 }
1610}
1611
1612void
1613roster_merge_result::report_file_content_conflicts(lua_hooks & lua,
1614 roster_t const & left_roster,
1615 roster_t const & right_roster,
1616 content_merge_adaptor & adaptor,
1617 const bool basic_io,
1618 std::ostream & output)
1619{
1620 MM(left_roster);
1621 MM(right_roster);
1622
1623 for (size_t i = 0; i < file_content_conflicts.size(); ++i)
1624 {
1625 file_content_conflict & conflict = file_content_conflicts[i];
1626 MM(conflict);
1627
1628 if (basic_io)
1629 {
1630 basic_io::stanza st;
1631
1632 if (conflict.resolution.resolution == resolve_conflicts::none)
1633 if (auto_merge_succeeds(lua, conflict, adaptor, left_roster, right_roster))
1634 conflict.resolution.resolution = resolve_conflicts::content_internal;
1635
1636 st.push_str_pair(syms::conflict, syms::content);
1637 put_content_conflict (st, left_roster, right_roster, adaptor, conflict);
1638 put_stanza (st, output);
1639 }
1640 else
1641 {
1642 if (roster.is_attached(conflict.nid))
1643 {
1644 file_path name;
1645 roster.get_name(conflict.nid, name);
1646
1647 P(F("conflict: content conflict on file '%s'")
1648 % name);
1649 P(F("content hash is %s on the left") % conflict.left);
1650 P(F("content hash is %s on the right") % conflict.right);
1651 }
1652 else
1653 {
1654 // this node isn't attached in the merged roster and there
1655 // isn't really a good name for it so report both the left
1656 // and right names using a slightly different format
1657
1658 file_path left_name, right_name;
1659 left_roster.get_name(conflict.nid, left_name);
1660 right_roster.get_name(conflict.nid, right_name);
1661
1662 shared_ptr<roster_t const> lca_roster;
1663 revision_id lca_rid;
1664 file_path lca_name;
1665
1666 adaptor.get_ancestral_roster(conflict.nid, lca_rid, lca_roster);
1667 lca_roster->get_name(conflict.nid, lca_name);
1668
1669 P(F("conflict: content conflict on file '%s' from revision %s")
1670 % lca_name % lca_rid);
1671 P(F("content hash is %s on the left in file '%s'")
1672 % conflict.left % left_name);
1673 P(F("content hash is %s on the right in file '%s'")
1674 % conflict.right % right_name);
1675 }
1676 }
1677 }
1678}
1679
1680// Resolving non-content conflicts
1681
1682namespace resolve_conflicts
1683{
1684 bool
1685 do_auto_merge(lua_hooks & lua,
1686 file_content_conflict const & conflict,
1687 content_merge_adaptor & adaptor,
1688 roster_t const & left_roster,
1689 roster_t const & right_roster,
1690 roster_t const & result_roster,
1691 file_id & merged_id)
1692 {
1693 revision_id ancestor_rid;
1694 shared_ptr<roster_t const> ancestor_roster;
1695 adaptor.get_ancestral_roster(conflict.nid, ancestor_rid, ancestor_roster);
1696
1697 I(ancestor_roster);
1698 I(ancestor_roster->has_node(conflict.nid)); // this fails if there is no least common ancestor
1699
1700 file_id anc_id, left_id, right_id;
1701 file_path anc_path, left_path, right_path, merged_path;
1702 ancestor_roster->get_file_details(conflict.nid, anc_id, anc_path);
1703 left_roster.get_file_details(conflict.nid, left_id, left_path);
1704 right_roster.get_file_details(conflict.nid, right_id, right_path);
1705 result_roster.get_file_details(conflict.nid, merged_id, merged_path);
1706
1707 content_merger cm(lua, *ancestor_roster, left_roster, right_roster, adaptor);
1708
1709 return cm.try_auto_merge(anc_path, left_path, right_path, merged_path,
1710 anc_id, left_id, right_id, merged_id);
1711 }
1712}
1713
1714static char const * const conflicts_mismatch_msg = N_("conflicts file does not match current conflicts");
1715static char const * const conflict_resolution_not_supported_msg = N_("%s is not a supported conflict resolution for %s");
1716static char const * const history_lost_msg = N_("history for '%s' from %s will be lost; see user manual Merge Conflicts section");
1717static char const * const conflict_extra = N_("extra chars at end of conflict");
1718
1719static void
1720read_missing_root_conflicts(basic_io::parser & pars,
1721 bool & missing_root_conflict,
1722 roster_t const & left_roster,
1723 roster_t const & right_roster)
1724{
1725 // There can be only one of these
1726 if (pars.tok.in.lookahead != EOF && pars.symp(syms::missing_root))
1727 {
1728 pars.sym();
1729
1730 if (pars.symp(syms::left_type))
1731 {
1732 pars.sym(); pars.str();
1733 pars.esym(syms::ancestor_name); pars.str();
1734 pars.esym(syms::right_type); pars.str();
1735 pars.esym(syms::ancestor_name); pars.str();
1736 }
1737 // else unrelated projects (branches); nothing else output
1738
1739 missing_root_conflict = true;
1740
1741 if (pars.tok.in.lookahead != EOF)
1742 pars.esym (syms::conflict);
1743 }
1744 else
1745 {
1746 missing_root_conflict = false;
1747 }
1748} // read_missing_root_conflicts
1749
1750static void
1751read_invalid_name_conflict(basic_io::parser & pars,
1752 invalid_name_conflict & conflict,
1753 roster_t const & left_roster,
1754 roster_t const & right_roster)
1755{
1756 if (pars.symp(syms::left_type))
1757 {
1758 pars.sym();
1759 pars.str(); // "pivoted root"
1760 pars.esym(syms::ancestor_name); pars.str(); // lca_parent_name
1761 read_added_rename_conflict_right (pars, right_roster, conflict.nid, conflict.parent_name);
1762 }
1763 else
1764 {
1765 pars.esym(syms::right_type);
1766 pars.str(); // "pivoted root"
1767 pars.esym(syms::ancestor_name); pars.str(); // lca_parent_name
1768 read_added_rename_conflict_left (pars, left_roster, conflict.nid, conflict.parent_name);
1769 }
1770} // read_invalid_name_conflict
1771
1772static void
1773read_invalid_name_conflicts(basic_io::parser & pars,
1774 std::vector<invalid_name_conflict> & conflicts,
1775 roster_t const & left_roster,
1776 roster_t const & right_roster)
1777{
1778 while (pars.tok.in.lookahead != EOF && pars.symp(syms::invalid_name))
1779 {
1780 invalid_name_conflict conflict;
1781
1782 pars.sym();
1783
1784 read_invalid_name_conflict(pars, conflict, left_roster, right_roster);
1785
1786 conflicts.push_back(conflict);
1787
1788 if (pars.tok.in.lookahead != EOF)
1789 pars.esym (syms::conflict);
1790 }
1791} // read_invalid_name_conflicts
1792
1793static void
1794read_directory_loop_conflict(basic_io::parser & pars,
1795 directory_loop_conflict & conflict,
1796 roster_t const & left_roster,
1797 roster_t const & right_roster)
1798{
1799 string tmp;
1800
1801 // syms::directory_loop has been read
1802
1803 if (pars.symp(syms::left_type))
1804 {
1805 read_added_rename_conflict_left(pars, left_roster, conflict.nid, conflict.parent_name);
1806 }
1807 if (pars.symp(syms::right_type))
1808 {
1809 read_added_rename_conflict_right(pars, right_roster, conflict.nid, conflict.parent_name);
1810 }
1811
1812 if (pars.symp(syms::left_type))
1813 {
1814 pars.sym();
1815 pars.str(); // "renamed directory"
1816 pars.esym(syms::ancestor_name); pars.str();
1817 pars.esym(syms::left_name); pars.str();
1818 }
1819 if (pars.symp(syms::right_type))
1820 {
1821 pars.sym();
1822 pars.str(); // "renamed directory"
1823 pars.esym(syms::ancestor_name); pars.str();
1824 pars.esym(syms::right_name); pars.str();
1825 }
1826
1827} // read_directory_loop_conflict
1828
1829static void
1830read_directory_loop_conflicts(basic_io::parser & pars,
1831 std::vector<directory_loop_conflict> & conflicts,
1832 roster_t const & left_roster,
1833 roster_t const & right_roster)
1834{
1835 while (pars.tok.in.lookahead != EOF && pars.symp(syms::directory_loop))
1836 {
1837 directory_loop_conflict conflict;
1838
1839 pars.sym();
1840
1841 read_directory_loop_conflict(pars, conflict, left_roster, right_roster);
1842
1843 conflicts.push_back(conflict);
1844
1845 if (pars.tok.in.lookahead != EOF)
1846 pars.esym (syms::conflict);
1847 }
1848} // read_directory_loop_conflicts
1849
1850
1851static void
1852read_orphaned_node_conflict(basic_io::parser & pars,
1853 orphaned_node_conflict & conflict,
1854 roster_t const & left_roster,
1855 roster_t const & right_roster)
1856{
1857 if (pars.symp(syms::left_type))
1858 {
1859 pars.sym(); pars.str(); // "deleted directory | file"
1860 pars.esym(syms::ancestor_name); pars.str();
1861 read_added_rename_conflict_right(pars, right_roster, conflict.nid, conflict.parent_name);
1862 }
1863 else
1864 {
1865 pars.esym(syms::right_type);
1866 pars.str(); // "deleted directory | file"
1867 pars.esym(syms::ancestor_name); pars.str();
1868 read_added_rename_conflict_left(pars, left_roster, conflict.nid, conflict.parent_name);
1869 }
1870
1871 // check for a resolution
1872 if ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
1873 {
1874 if (pars.symp (syms::resolved_drop_left))
1875 {
1876 conflict.resolution.resolution = resolve_conflicts::drop;
1877 pars.sym();
1878 }
1879 else if (pars.symp (syms::resolved_rename_left))
1880 {
1881 conflict.resolution.resolution = resolve_conflicts::rename;
1882 pars.sym();
1883 conflict.resolution.rename = resolve_conflicts::file_path_external(pars.token);
1884 pars.str();
1885 }
1886 else
1887 E(false, origin::user,
1888 F(conflict_resolution_not_supported_msg) % pars.token % "orphaned_node");
1889 }
1890
1891} // read_orphaned_node_conflict
1892
1893static void
1894read_orphaned_node_conflicts(basic_io::parser & pars,
1895 std::vector<orphaned_node_conflict> & conflicts,
1896 roster_t const & left_roster,
1897 roster_t const & right_roster)
1898{
1899 while (pars.tok.in.lookahead != EOF && (pars.symp(syms::orphaned_directory) || pars.symp(syms::orphaned_file)))
1900 {
1901 orphaned_node_conflict conflict;
1902
1903 pars.sym();
1904
1905 read_orphaned_node_conflict(pars, conflict, left_roster, right_roster);
1906
1907 conflicts.push_back(conflict);
1908
1909 if (pars.tok.in.lookahead != EOF)
1910 pars.esym (syms::conflict);
1911 }
1912} // read_orphaned_node_conflicts
1913
1914static void
1915validate_orphaned_node_conflicts(basic_io::parser & pars,
1916 std::vector<orphaned_node_conflict> & conflicts,
1917 roster_t const & left_roster,
1918 roster_t const & right_roster)
1919{
1920 for (std::vector<orphaned_node_conflict>::iterator i = conflicts.begin();
1921 i != conflicts.end();
1922 ++i)
1923 {
1924 orphaned_node_conflict & merge_conflict = *i;
1925 orphaned_node_conflict file_conflict;
1926
1927 if (pars.symp (syms::orphaned_directory) || pars.symp (syms::orphaned_file))
1928 {
1929 pars.sym();
1930 read_orphaned_node_conflict(pars, file_conflict, left_roster, right_roster);
1931 }
1932 else
1933 E(false, origin::user, F("expected orphaned_directory or orphaned_file, found %s") % pars.token);
1934
1935 E(merge_conflict.nid == file_conflict.nid, origin::user,
1936 F(conflicts_mismatch_msg));
1937
1938 merge_conflict.resolution = file_conflict.resolution;
1939
1940 if (pars.tok.in.lookahead != EOF)
1941 pars.esym (syms::conflict);
1942 else
1943 {
1944 std::vector<orphaned_node_conflict>::iterator tmp = i;
1945 E(++tmp == conflicts.end(), origin::user,
1946 F("conflicts file does not match current conflicts"));
1947 }
1948 }
1949} // validate_orphaned_node_conflicts
1950
1951
1952static void
1953read_multiple_name_conflict(basic_io::parser & pars,
1954 multiple_name_conflict & conflict,
1955 roster_t const & left_roster,
1956 roster_t const & right_roster)
1957{
1958 read_added_rename_conflict_left(pars, left_roster, conflict.nid, conflict.left);
1959 read_added_rename_conflict_right(pars, right_roster, conflict.nid, conflict.right);
1960} // read_multiple_name_conflict
1961
1962static void
1963read_multiple_name_conflicts(basic_io::parser & pars,
1964 std::vector<multiple_name_conflict> & conflicts,
1965 roster_t const & left_roster,
1966 roster_t const & right_roster)
1967{
1968 while (pars.tok.in.lookahead != EOF && pars.symp(syms::multiple_names))
1969 {
1970 multiple_name_conflict conflict(the_null_node);
1971
1972 pars.sym();
1973
1974 read_multiple_name_conflict(pars, conflict, left_roster, right_roster);
1975
1976 conflicts.push_back(conflict);
1977
1978 if (pars.tok.in.lookahead != EOF)
1979 pars.esym (syms::conflict);
1980 }
1981} // read_multiple_name_conflicts
1982
1983static void
1984read_dropped_modified_conflict(basic_io::parser & pars,
1985 dropped_modified_conflict & conflict,
1986 revision_id left_rid,
1987 roster_t const & left_roster,
1988 revision_id right_rid,
1989 roster_t const & right_roster)
1990{
1991 string tmp;
1992
1993 pars.esym(syms::ancestor_name); pars.str();
1994 pars.esym(syms::ancestor_file_id); pars.hex();
1995
1996 pars.esym(syms::left_type);
1997 pars.str(tmp);
1998
1999 if (tmp == "dropped file")
2000 {
2001 conflict.dropped_side = resolve_conflicts::left_side;
2002
2003 pars.esym(syms::left_rev); pars.hex(tmp);
2004 conflict.left_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
2005 pars.esym(syms::left_name); pars.str();
2006 pars.esym(syms::left_file_id); pars.hex();
2007 }
2008 else if (tmp == "orphaned file")
2009 {
2010 conflict.dropped_side = resolve_conflicts::left_side;
2011 conflict.orphaned = true;
2012
2013 pars.esym(syms::left_rev); pars.hex(tmp);
2014 conflict.left_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
2015 pars.esym(syms::left_name); pars.str();
2016 pars.esym(syms::left_file_id); pars.hex();
2017 }
2018 else if (tmp == "recreated file")
2019 {
2020 conflict.dropped_side = resolve_conflicts::left_side;
2021 conflict.left_rid = left_rid;
2022
2023 pars.esym(syms::left_name); pars.str(tmp);
2024 conflict.left_nid = left_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
2025 pars.esym(syms::left_file_id); pars.hex();
2026 }
2027 else if (tmp == "modified file")
2028 {
2029 conflict.left_rid = left_rid;
2030
2031 pars.esym(syms::left_name); pars.str(tmp);
2032 conflict.left_nid = left_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
2033 pars.esym(syms::left_file_id); pars.hex();
2034 }
2035 else
2036 I(false);
2037
2038 pars.esym(syms::right_type);
2039 pars.str(tmp);
2040
2041 if (tmp == "dropped file")
2042 {
2043 conflict.dropped_side = resolve_conflicts::right_side;
2044
2045 pars.esym(syms::right_rev); pars.hex(tmp);
2046 conflict.right_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
2047 pars.esym(syms::right_name); pars.str();
2048 pars.esym(syms::right_file_id); pars.hex();
2049 }
2050 else if (tmp == "orphaned file")
2051 {
2052 conflict.dropped_side = resolve_conflicts::right_side;
2053 conflict.orphaned = true;
2054
2055 pars.esym(syms::right_rev); pars.hex(tmp);
2056 conflict.right_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
2057 pars.esym(syms::right_name); pars.str();
2058 pars.esym(syms::right_file_id); pars.hex();
2059 }
2060 else if (tmp == "recreated file")
2061 {
2062 conflict.dropped_side = resolve_conflicts::right_side;
2063 conflict.right_rid = right_rid;
2064
2065 pars.esym(syms::right_name); pars.str(tmp);
2066 conflict.right_nid = right_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
2067 pars.esym(syms::right_file_id); pars.hex();
2068 }
2069 else if (tmp == "modified file")
2070 {
2071 conflict.right_rid = right_rid;
2072
2073 pars.esym(syms::right_name); pars.str(tmp);
2074 conflict.right_nid = right_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
2075 pars.esym(syms::right_file_id); pars.hex();
2076 }
2077 else
2078 I(false);
2079
2080 // check for resolutions
2081 while ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
2082 {
2083 if (pars.symp (syms::resolved_drop_left))
2084 {
2085 conflict.left_resolution.resolution = resolve_conflicts::drop;
2086 pars.sym();
2087 }
2088 else if (pars.symp (syms::resolved_drop_right))
2089 {
2090 conflict.right_resolution.resolution = resolve_conflicts::drop;
2091 pars.sym();
2092 }
2093 else if (pars.symp (syms::resolved_keep_left))
2094 {
2095 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
2096
2097 conflict.left_resolution.resolution = resolve_conflicts::keep;
2098 pars.sym();
2099 }
2100 else if (pars.symp (syms::resolved_keep_right))
2101 {
2102 E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
2103
2104 conflict.right_resolution.resolution = resolve_conflicts::keep;
2105 pars.sym();
2106 }
2107 else if (pars.symp (syms::resolved_rename_left))
2108 {
2109 if (conflict.left_resolution.resolution == resolve_conflicts::content_user)
2110 conflict.left_resolution.resolution = resolve_conflicts::content_user_rename;
2111 else
2112 conflict.left_resolution.resolution = resolve_conflicts::rename;
2113 pars.sym();
2114 conflict.left_resolution.rename = resolve_conflicts::file_path_external(pars.token);
2115 pars.str();
2116 }
2117 else if (pars.symp (syms::resolved_rename_right))
2118 {
2119 if (conflict.right_resolution.resolution == resolve_conflicts::content_user)
2120 conflict.right_resolution.resolution = resolve_conflicts::content_user_rename;
2121 else
2122 conflict.right_resolution.resolution = resolve_conflicts::rename;
2123 pars.sym();
2124 conflict.right_resolution.rename = resolve_conflicts::file_path_external(pars.token);
2125 pars.str();
2126 }
2127 else if (pars.symp (syms::resolved_user_left))
2128 {
2129 if (conflict.left_resolution.resolution == resolve_conflicts::rename)
2130 conflict.left_resolution.resolution = resolve_conflicts::content_user_rename;
2131 else
2132 conflict.left_resolution.resolution = resolve_conflicts::content_user;
2133 pars.sym();
2134 conflict.left_resolution.content = new_optimal_path(pars.token, false);
2135 pars.str();
2136 }
2137 else if (pars.symp (syms::resolved_user_right))
2138 {
2139 if (conflict.right_resolution.resolution == resolve_conflicts::rename)
2140 conflict.right_resolution.resolution = resolve_conflicts::content_user_rename;
2141 else
2142 conflict.right_resolution.resolution = resolve_conflicts::content_user;
2143 pars.sym();
2144 conflict.right_resolution.content = new_optimal_path(pars.token, false);
2145 pars.str();
2146 }
2147 else
2148 E(false, origin::user,
2149 F(conflict_resolution_not_supported_msg) % pars.token % "dropped_modified");
2150 }
2151} // read_dropped_modified_conflict
2152
2153static void
2154read_dropped_modified_conflicts(basic_io::parser & pars,
2155 std::vector<dropped_modified_conflict> & conflicts,
2156 revision_id left_rid,
2157 roster_t const & left_roster,
2158 revision_id right_rid,
2159 roster_t const & right_roster)
2160{
2161 while (pars.tok.in.lookahead != EOF && pars.symp(syms::dropped_modified))
2162 {
2163 dropped_modified_conflict conflict;
2164
2165 pars.sym();
2166
2167 read_dropped_modified_conflict(pars, conflict, left_rid, left_roster, right_rid, right_roster);
2168
2169 conflicts.push_back(conflict);
2170
2171 if (pars.tok.in.lookahead != EOF)
2172 pars.esym (syms::conflict);
2173 }
2174} // read_dropped_modified_conflicts
2175
2176static void
2177validate_dropped_modified_conflicts(basic_io::parser & pars,
2178 std::vector<dropped_modified_conflict> & conflicts,
2179 revision_id left_rid,
2180 roster_t const & left_roster,
2181 revision_id right_rid,
2182 roster_t const & right_roster)
2183{
2184 for (std::vector<dropped_modified_conflict>::iterator i = conflicts.begin();
2185 i != conflicts.end();
2186 ++i)
2187 {
2188 dropped_modified_conflict & merge_conflict = *i;
2189 dropped_modified_conflict file_conflict (the_null_node, the_null_node);
2190
2191 pars.esym(syms::dropped_modified);
2192
2193 read_dropped_modified_conflict(pars, file_conflict, left_rid, left_roster, right_rid, right_roster);
2194
2195 // Note that we do not confirm the file ids.
2196 E(merge_conflict.dropped_side == file_conflict.dropped_side &&
2197 merge_conflict.left_nid == file_conflict.left_nid &&
2198 merge_conflict.right_nid == file_conflict.right_nid,
2199 origin::user,
2200 F(conflicts_mismatch_msg));
2201
2202 merge_conflict.left_rid = file_conflict.left_rid;
2203 merge_conflict.right_rid = file_conflict.right_rid;
2204 merge_conflict.left_resolution = file_conflict.left_resolution;
2205 merge_conflict.right_resolution = file_conflict.right_resolution;
2206
2207 if (pars.tok.in.lookahead != EOF)
2208 pars.esym (syms::conflict);
2209 else
2210 {
2211 std::vector<dropped_modified_conflict>::iterator tmp = i;
2212 E(++tmp == conflicts.end(), origin::user,
2213 F(conflicts_mismatch_msg));
2214 }
2215 }
2216} // validate_dropped_modified_conflicts
2217
2218static void
2219read_duplicate_name_conflict(basic_io::parser & pars,
2220 duplicate_name_conflict & conflict,
2221 roster_t const & left_roster,
2222 roster_t const & right_roster)
2223{
2224 read_added_rename_conflict_left(pars, left_roster, conflict.left_nid, conflict.parent_name);
2225 read_added_rename_conflict_right(pars, right_roster, conflict.right_nid, conflict.parent_name);
2226
2227 // check for a resolution
2228 while ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
2229 {
2230 if (pars.symp (syms::resolved_drop_left))
2231 {
2232 conflict.left_resolution.resolution = resolve_conflicts::drop;
2233 pars.sym();
2234 }
2235 else if (pars.symp (syms::resolved_drop_right))
2236 {
2237 conflict.right_resolution.resolution = resolve_conflicts::drop;
2238 pars.sym();
2239 }
2240 else if (pars.symp (syms::resolved_keep_left))
2241 {
2242 conflict.left_resolution.resolution = resolve_conflicts::keep;
2243 pars.sym();
2244 }
2245 else if (pars.symp (syms::resolved_keep_right))
2246 {
2247 conflict.right_resolution.resolution = resolve_conflicts::keep;
2248 pars.sym();
2249 }
2250 else if (pars.symp (syms::resolved_rename_left))
2251 {
2252 conflict.left_resolution.resolution = resolve_conflicts::rename;
2253 pars.sym();
2254 conflict.left_resolution.rename = resolve_conflicts::file_path_external(pars.token);
2255 pars.str();
2256 }
2257 else if (pars.symp (syms::resolved_rename_right))
2258 {
2259 conflict.right_resolution.resolution = resolve_conflicts::rename;
2260 pars.sym();
2261 conflict.right_resolution.rename = resolve_conflicts::file_path_external(pars.token);
2262 pars.str();
2263 }
2264 else if (pars.symp (syms::resolved_user_left))
2265 {
2266 conflict.left_resolution.resolution = resolve_conflicts::content_user;
2267 pars.sym();
2268 conflict.left_resolution.content = new_optimal_path(pars.token, true);
2269 pars.str();
2270 }
2271 else if (pars.symp (syms::resolved_user_right))
2272 {
2273 conflict.right_resolution.resolution = resolve_conflicts::content_user;
2274 pars.sym();
2275 conflict.right_resolution.content = new_optimal_path(pars.token, true);
2276 pars.str();
2277 }
2278 else
2279 E(false, origin::user,
2280 F(conflict_resolution_not_supported_msg) % pars.token % "duplicate_name");
2281 }
2282
2283} // read_duplicate_name_conflict
2284
2285static void
2286read_duplicate_name_conflicts(basic_io::parser & pars,
2287 std::vector<duplicate_name_conflict> & conflicts,
2288 roster_t const & left_roster,
2289 roster_t const & right_roster)
2290{
2291 while (pars.tok.in.lookahead != EOF && pars.symp(syms::duplicate_name))
2292 {
2293 duplicate_name_conflict conflict;
2294
2295 pars.sym();
2296
2297 read_duplicate_name_conflict(pars, conflict, left_roster, right_roster);
2298
2299 conflicts.push_back(conflict);
2300
2301 if (pars.tok.in.lookahead != EOF)
2302 pars.esym (syms::conflict);
2303 }
2304} // read_duplicate_name_conflicts
2305static void
2306validate_duplicate_name_conflicts(basic_io::parser & pars,
2307 std::vector<duplicate_name_conflict> & conflicts,
2308 roster_t const & left_roster,
2309 roster_t const & right_roster)
2310{
2311 for (std::vector<duplicate_name_conflict>::iterator i = conflicts.begin();
2312 i != conflicts.end();
2313 ++i)
2314 {
2315 duplicate_name_conflict & merge_conflict = *i;
2316 duplicate_name_conflict file_conflict;
2317
2318 pars.esym(syms::duplicate_name);
2319
2320 read_duplicate_name_conflict(pars, file_conflict, left_roster, right_roster);
2321
2322 // Note that we do not confirm the file ids.
2323 E(merge_conflict.left_nid == file_conflict.left_nid &&
2324 merge_conflict.right_nid == file_conflict.right_nid,
2325 origin::user,
2326 F(conflicts_mismatch_msg));
2327
2328 merge_conflict.left_resolution = file_conflict.left_resolution;
2329 merge_conflict.right_resolution = file_conflict.right_resolution;
2330
2331 if (pars.tok.in.lookahead != EOF)
2332 pars.esym (syms::conflict);
2333 else
2334 {
2335 std::vector<duplicate_name_conflict>::iterator tmp = i;
2336 E(++tmp == conflicts.end(), origin::user,
2337 F(conflicts_mismatch_msg));
2338 }
2339 }
2340} // validate_duplicate_name_conflicts
2341
2342static void
2343read_attr_state_left(basic_io::parser & pars,
2344 std::pair<bool, attr_value> & value)
2345{
2346 string tmp;
2347
2348 if (pars.symp(syms::left_attr_value))
2349 {
2350 pars.sym();
2351 value.first = true;
2352 pars.str(tmp);
2353 value.second = attr_value(tmp, pars.tok.in.made_from);
2354 }
2355 else
2356 {
2357 pars.esym(syms::left_attr_state);
2358 pars.str(tmp);
2359 I(tmp == "dropped");
2360 value.first = false;
2361 }
2362} // read_attr_state_left
2363
2364static void
2365read_attr_state_right(basic_io::parser & pars,
2366 std::pair<bool, attr_value> & value)
2367{
2368 string tmp;
2369
2370 if (pars.symp(syms::right_attr_value))
2371 {
2372 pars.sym();
2373 value.first = true;
2374 pars.str(tmp);
2375 value.second = attr_value(tmp, pars.tok.in.made_from);
2376 }
2377 else
2378 {
2379 pars.esym(syms::right_attr_state);
2380 pars.str(tmp);
2381 I(tmp == "dropped");
2382 value.first = false;
2383 }
2384} // read_attr_state_right
2385
2386static void
2387read_attribute_conflict(basic_io::parser & pars,
2388 attribute_conflict & conflict,
2389 roster_t const & left_roster,
2390 roster_t const & right_roster)
2391{
2392 string tmp;
2393
2394 pars.esym(syms::node_type);
2395
2396 pars.str(tmp);
2397
2398 if (tmp == "file")
2399 {
2400 pars.esym(syms::attr_name); pars.str(tmp);
2401 conflict.key = attr_key(tmp, pars.tok.in.made_from);
2402 pars.esym(syms::ancestor_name); pars.str();
2403 pars.esym(syms::ancestor_file_id); pars.hex();
2404 pars.esym(syms::left_name); pars.str(tmp);
2405 conflict.nid = left_roster.get_node(file_path_external(utf8(tmp, pars.tok.in.made_from)))->self;
2406 pars.esym(syms::left_file_id); pars.hex();
2407 read_attr_state_left(pars, conflict.left);
2408 pars.esym(syms::right_name); pars.str();
2409 pars.esym(syms::right_file_id); pars.hex();
2410 read_attr_state_right(pars, conflict.right);
2411 }
2412 else if (tmp == "directory")
2413 {
2414 pars.esym(syms::attr_name); pars.str(tmp);
2415 conflict.key = attr_key(tmp, pars.tok.in.made_from);
2416 pars.esym(syms::ancestor_name); pars.str();
2417 pars.esym(syms::left_name); pars.str(tmp);
2418 conflict.nid = left_roster.get_node(file_path_external(utf8(tmp, pars.tok.in.made_from)))->self;
2419 read_attr_state_left(pars, conflict.left);
2420 pars.esym(syms::right_name); pars.str();
2421 read_attr_state_right(pars, conflict.right);
2422 }
2423 else
2424 I(false);
2425
2426} // read_attribute_conflict
2427
2428static void
2429read_attribute_conflicts(basic_io::parser & pars,
2430 std::vector<attribute_conflict> & conflicts,
2431 roster_t const & left_roster,
2432 roster_t const & right_roster)
2433{
2434 while (pars.tok.in.lookahead != EOF && pars.symp(syms::attribute))
2435 {
2436 attribute_conflict conflict(the_null_node);
2437
2438 pars.sym();
2439
2440 read_attribute_conflict(pars, conflict, left_roster, right_roster);
2441
2442 conflicts.push_back(conflict);
2443
2444 if (pars.tok.in.lookahead != EOF)
2445 pars.esym (syms::conflict);
2446 }
2447} // read_attribute_conflicts
2448
2449static void
2450read_file_content_conflict(basic_io::parser & pars,
2451 file_content_conflict & conflict,
2452 roster_t const & left_roster,
2453 roster_t const & right_roster)
2454{
2455 string tmp;
2456 string left_name, right_name, result_name;
2457
2458 pars.esym(syms::node_type); pars.str(tmp); I(tmp == "file");
2459
2460 pars.esym (syms::ancestor_name); pars.str();
2461 pars.esym (syms::ancestor_file_id); pars.hex(tmp);
2462 conflict.ancestor = decode_hexenc_as<file_id>(tmp, pars.tok.in.made_from);
2463
2464 pars.esym (syms::left_name); pars.str(left_name);
2465 pars.esym(syms::left_file_id); pars.hex(tmp);
2466 conflict.left = decode_hexenc_as<file_id>(tmp, pars.tok.in.made_from);
2467
2468 pars.esym (syms::right_name); pars.str(right_name);
2469 pars.esym(syms::right_file_id); pars.hex(tmp);
2470 conflict.right = decode_hexenc_as<file_id>(tmp, pars.tok.in.made_from);
2471
2472 conflict.nid = left_roster.get_node (file_path_internal (left_name))->self;
2473 I(conflict.nid = right_roster.get_node (file_path_internal (right_name))->self);
2474
2475 // check for a resolution
2476 if ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
2477 {
2478 if (pars.symp (syms::resolved_internal))
2479 {
2480 conflict.resolution.resolution = resolve_conflicts::content_internal;
2481 pars.sym();
2482 }
2483 else if (pars.symp (syms::resolved_user_left))
2484 {
2485 conflict.resolution.resolution = resolve_conflicts::content_user;
2486 pars.sym();
2487 conflict.resolution.content = new_optimal_path(pars.token, true);
2488 pars.str();
2489 }
2490 else
2491 E(false, origin::user,
2492 F(conflict_resolution_not_supported_msg) % pars.token % "file_content");
2493 }
2494
2495} // read_file_content_conflict
2496
2497static void
2498read_file_content_conflicts(basic_io::parser & pars,
2499 std::vector<file_content_conflict> & conflicts,
2500 roster_t const & left_roster,
2501 roster_t const & right_roster)
2502{
2503 while (pars.tok.in.lookahead != EOF && pars.symp(syms::content))
2504 {
2505 file_content_conflict conflict;
2506
2507 pars.sym();
2508
2509 read_file_content_conflict(pars, conflict, left_roster, right_roster);
2510
2511 conflicts.push_back(conflict);
2512
2513 if (pars.tok.in.lookahead != EOF)
2514 pars.esym (syms::conflict);
2515 }
2516} // read_file_content_conflicts
2517
2518static void
2519validate_file_content_conflicts(basic_io::parser & pars,
2520 std::vector<file_content_conflict> & conflicts,
2521 roster_t const & left_roster,
2522 roster_t const & right_roster)
2523{
2524 for (std::vector<file_content_conflict>::iterator i = conflicts.begin();
2525 i != conflicts.end();
2526 ++i)
2527 {
2528 file_content_conflict & merge_conflict = *i;
2529 file_content_conflict file_conflict;
2530
2531 pars.esym(syms::content);
2532
2533 read_file_content_conflict(pars, file_conflict, left_roster, right_roster);
2534
2535 E(merge_conflict.nid == file_conflict.nid, origin::user,
2536 F(conflicts_mismatch_msg));
2537
2538 merge_conflict.resolution = file_conflict.resolution;
2539
2540 if (pars.tok.in.lookahead != EOF)
2541 pars.esym (syms::conflict);
2542 else
2543 {
2544 std::vector<file_content_conflict>::iterator tmp = i;
2545 E(++tmp == conflicts.end(), origin::user,
2546 F("conflicts file does not match current conflicts"));
2547 }
2548 }
2549} // validate_file_content_conflicts
2550
2551static void
2552read_conflict_file_core(basic_io::parser pars,
2553 revision_id left_rid,
2554 roster_t const & left_roster,
2555 revision_id right_rid,
2556 roster_t const & right_roster,
2557 roster_merge_result & result,
2558 bool validate)
2559{
2560 pars.esym (syms::conflict);
2561
2562 // If we are validating, there must be one stanza in the file for each
2563 // conflict; otherwise something has changed since the file was
2564 // regenerated. So we go thru the conflicts in the same order they are
2565 // generated; see merge_content.cc resolve_merge_conflicts.
2566
2567 if (validate)
2568 {
2569 // resolve_merge_conflicts should not call us if there are any conflicts
2570 // for which we don't currently support resolutions; assert that.
2571
2572 I(!result.missing_root_conflict);
2573 I(result.invalid_name_conflicts.size() == 0);
2574 I(result.directory_loop_conflicts.size() == 0);
2575 I(result.multiple_name_conflicts.size() == 0);
2576 I(result.attribute_conflicts.size() == 0);
2577
2578 // These are the ones we know how to resolve. They must be in the same
2579 // order as non-validate, below.
2580
2581 validate_orphaned_node_conflicts(pars, result.orphaned_node_conflicts, left_roster, right_roster);
2582 validate_dropped_modified_conflicts
2583 (pars, result.dropped_modified_conflicts, left_rid, left_roster, right_rid, right_roster);
2584 validate_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster);
2585 validate_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster);
2586 }
2587 else
2588 {
2589 // Read in the ones we know how to resolve. Also read in the ones we
2590 // don't know how to resolve, so we can report them.
2591 read_missing_root_conflicts(pars, result.missing_root_conflict, left_roster, right_roster);
2592 read_invalid_name_conflicts(pars, result.invalid_name_conflicts, left_roster, right_roster);
2593 read_directory_loop_conflicts(pars, result.directory_loop_conflicts, left_roster, right_roster);
2594 read_orphaned_node_conflicts(pars, result.orphaned_node_conflicts, left_roster, right_roster);
2595 read_multiple_name_conflicts(pars, result.multiple_name_conflicts, left_roster, right_roster);
2596 read_dropped_modified_conflicts
2597 (pars, result.dropped_modified_conflicts, left_rid, left_roster, right_rid, right_roster);
2598 read_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster);
2599 read_attribute_conflicts(pars, result.attribute_conflicts, left_roster, right_roster);
2600 read_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster);
2601 }
2602
2603 E(pars.tok.in.lookahead == EOF, pars.tok.in.made_from,
2604 F("extra data in file"));
2605} // read_conflict_file_core
2606
2607void
2608roster_merge_result::read_conflict_file(database & db,
2609 bookkeeping_path const & file_name,
2610 revision_id & ancestor_rid,
2611 revision_id & left_rid,
2612 revision_id & right_rid,
2613 roster_t & left_roster,
2614 marking_map & left_marking,
2615 roster_t & right_roster,
2616 marking_map & right_marking)
2617{
2618 data dat;
2619
2620 read_data (file_name, dat);
2621
2622 basic_io::input_source src(dat(), file_name.as_external());
2623 src.made_from = origin::user;
2624 basic_io::tokenizer tok(src);
2625 basic_io::parser pars(tok);
2626 std::string temp;
2627
2628 // Read left, right, ancestor.
2629 pars.esym(syms::left);
2630 pars.hex(temp);
2631 left_rid = decode_hexenc_as<revision_id>(temp, src.made_from);
2632 pars.esym(syms::right);
2633 pars.hex(temp);
2634 right_rid = decode_hexenc_as<revision_id>(temp, src.made_from);
2635
2636 if (pars.symp(syms::ancestor))
2637 {
2638 pars.sym();
2639 pars.hex(temp);
2640 ancestor_rid = decode_hexenc_as<revision_id>(temp, src.made_from);
2641
2642 // we don't fetch the ancestor roster here, because not every function
2643 // needs it.
2644 db.get_roster(left_rid, left_roster, left_marking);
2645 db.get_roster(right_rid, right_roster, right_marking);
2646
2647 read_conflict_file_core(pars, left_rid, left_roster, right_rid, right_roster, *this, false);
2648 }
2649 // else no conflicts
2650
2651} // roster_merge_result::read_conflict_file
2652
2653void
2654roster_merge_result::write_conflict_file(database & db,
2655 lua_hooks & lua,
2656 bookkeeping_path const & file_name,
2657 revision_id const & ancestor_rid,
2658 revision_id const & left_rid,
2659 revision_id const & right_rid,
2660 boost::shared_ptr<roster_t> left_roster,
2661 marking_map const & left_marking,
2662 boost::shared_ptr<roster_t> right_roster,
2663 marking_map const & right_marking)
2664{
2665 std::ostringstream output;
2666
2667 content_merge_database_adaptor adaptor(db, left_rid, right_rid,
2668 left_marking, right_marking);
2669
2670 adaptor.cache_roster (left_rid, left_roster);
2671 adaptor.cache_roster (right_rid, right_roster);
2672 {
2673 // match format in cmd_merging.cc show_conflicts_core
2674 basic_io::stanza st;
2675 basic_io::printer pr;
2676 st.push_binary_pair(syms::left, left_rid.inner());
2677 st.push_binary_pair(syms::right, right_rid.inner());
2678 st.push_binary_pair(syms::ancestor, adaptor.lca.inner());
2679 pr.print_stanza(st);
2680 output.write(pr.buf.data(), pr.buf.size());
2681 }
2682
2683 report_missing_root_conflicts(*left_roster, *right_roster, adaptor, true, output);
2684 report_invalid_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
2685 report_directory_loop_conflicts(*left_roster, *right_roster, adaptor, true, output);
2686 report_orphaned_node_conflicts(*left_roster, *right_roster, adaptor, true, output);
2687 report_multiple_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
2688 report_dropped_modified_conflicts(*left_roster, *right_roster, adaptor, true, output);
2689 report_duplicate_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
2690 report_attribute_conflicts(*left_roster, *right_roster, adaptor, true, output);
2691 report_file_content_conflicts(lua, *left_roster, *right_roster, adaptor, true, output);
2692
2693 data dat(output.str(), origin::internal);
2694 write_data(file_name, dat);
2695
2696} // roster_merge_result::write_conflict_file
2697
2698void
2699parse_resolve_conflicts_opts (options const & opts,
2700 revision_id const & left_rid,
2701 roster_t const & left_roster,
2702 revision_id const & right_rid,
2703 roster_t const & right_roster,
2704 roster_merge_result & result,
2705 bool & resolutions_given)
2706{
2707 if (opts.resolve_conflicts)
2708 {
2709 resolutions_given = true;
2710
2711 if (!file_exists(system_path(opts.resolve_conflicts_file)))
2712 {
2713 // user may specify --resolve-conflicts to enable attr
2714 // mtn:resolve_conflict, without _MTN/conflicts.
2715 return;
2716 }
2717
2718 data dat;
2719
2720 read_data (system_path(opts.resolve_conflicts_file), dat);
2721
2722 basic_io::input_source src(dat(), opts.resolve_conflicts_file.as_external());
2723 src.made_from = origin::user;
2724 basic_io::tokenizer tok(src);
2725 basic_io::parser pars(tok);
2726 std::string temp;
2727
2728 pars.esym(syms::left);
2729 pars.hex(temp);
2730 E(left_rid == decode_hexenc_as<revision_id>(temp, src.made_from),
2731 origin::user,
2732 F("left revision id does not match conflict file"));
2733
2734 pars.esym(syms::right);
2735 pars.hex(temp);
2736 E(right_rid == decode_hexenc_as<revision_id>(temp, src.made_from),
2737 origin::user,
2738 F("right revision id does not match conflict file"));
2739
2740 if (pars.symp(syms::ancestor))
2741 {
2742 pars.sym();
2743 pars.hex(temp);
2744
2745 read_conflict_file_core (pars, left_rid, left_roster, right_rid, right_roster, result, true);
2746 }
2747 else
2748 {
2749 // if there is no ancestor revision, then left is an ancestor of
2750 // right, or vice versa, and there can be no conflicts.
2751 }
2752 }
2753 else
2754 resolutions_given = false;
2755
2756} // parse_resolve_conflicts_opts
2757
2758static void
2759attach_node (lua_hooks & lua,
2760 roster_t & new_roster,
2761 node_id nid,
2762 file_path const target_path)
2763{
2764 // Simplified from workspace::perform_rename in work.cc
2765
2766 I(!target_path.empty());
2767
2768 E(!new_roster.has_node(target_path), origin::user,
2769 F("'%s' already exists") % target_path.as_external());
2770 E(new_roster.has_node(target_path.dirname()), origin::user,
2771 F("directory '%s' does not exist or is unknown") % target_path.dirname());
2772
2773 new_roster.attach_node (nid, target_path);
2774
2775 const_node_t node = new_roster.get_node (nid);
2776 for (attr_map_t::const_iterator attr = node->attrs.begin();
2777 attr != node->attrs.end();
2778 ++attr)
2779 lua.hook_set_attribute(attr->first(), target_path, attr->second.second());
2780
2781} // attach_node
2782
2783void
2784roster_merge_result::resolve_orphaned_node_conflicts(lua_hooks & lua,
2785 roster_t const & left_roster,
2786 roster_t const & right_roster,
2787 content_merge_adaptor & adaptor)
2788{
2789 MM(left_roster);
2790 MM(right_roster);
2791 MM(this->roster); // New roster
2792
2793 // Conflict nodes are present but detached (without filenames) in the new
2794 // roster. The resolution is either to suture the two files together, or to
2795 // rename one or both.
2796
2797 for (std::vector<orphaned_node_conflict>::const_iterator i = orphaned_node_conflicts.begin();
2798 i != orphaned_node_conflicts.end();
2799 ++i)
2800 {
2801 orphaned_node_conflict const & conflict = *i;
2802 MM(conflict);
2803
2804 file_path name;
2805
2806 if (left_roster.has_node(conflict.nid))
2807 {
2808 left_roster.get_name(conflict.nid, name);
2809 }
2810 else
2811 {
2812 right_roster.get_name(conflict.nid, name);
2813 }
2814
2815 switch (conflict.resolution.resolution)
2816 {
2817 case resolve_conflicts::drop:
2818 if (is_dir_t(roster.get_node(conflict.nid)))
2819 {
2820 E(downcast_to_dir_t(roster.get_node(conflict.nid))->children.empty(), origin::user,
2821 F("can't drop directory '%s'; it is not empty") % name);
2822 }
2823
2824 P(F("dropping '%s'") % name);
2825 roster.drop_detached_node(conflict.nid);
2826 break;
2827
2828 case resolve_conflicts::rename:
2829 P(F("renaming '%s' to '%s'") % name % conflict.resolution.rename);
2830 attach_node (lua, roster, conflict.nid, conflict.resolution.rename);
2831 break;
2832
2833 case resolve_conflicts::none:
2834 E(false, origin::user,
2835 F("no resolution provided for orphaned_node '%s'") % name);
2836 break;
2837
2838 default:
2839 E(false, origin::user,
2840 F("invalid resolution for orphaned_node '%s'") % name);
2841 }
2842 } // end for
2843
2844 orphaned_node_conflicts.clear();
2845}
2846
2847static node_id
2848create_new_node(roster_t const & parent_roster,
2849 string const & side_image,
2850 node_id const & parent_nid,
2851 roster_t & result_roster,
2852 boost::shared_ptr<any_path> new_content,
2853 content_merge_adaptor & adaptor,
2854 temp_node_id_source & nis)
2855{
2856 file_path parent_name;
2857 file_id parent_fid;
2858 file_data parent_data;
2859
2860 parent_roster.get_file_details(parent_nid, parent_fid, parent_name);
2861 adaptor.get_version(parent_fid, parent_data);
2862
2863 P(F("replacing content of '%s' from %s with '%s'") % parent_name % side_image % new_content->as_external());
2864
2865 P(F(history_lost_msg) % parent_name % side_image);
2866
2867 data result_raw_data;
2868 read_data(*new_content, result_raw_data);
2869
2870 file_data result_data = file_data(result_raw_data);
2871 file_id result_fid;
2872 calculate_ident(result_data, result_fid);
2873
2874 // User could specify no changes in content
2875 if (result_fid != parent_fid)
2876 {
2877 adaptor.record_file(parent_fid, result_fid, parent_data, result_data);
2878 }
2879
2880 return result_roster.create_file_node(result_fid, nis);
2881}
2882
2883static void
2884replace_content(roster_t const & parent_roster,
2885 string const & side_image,
2886 node_id const & nid,
2887 roster_t & result_roster,
2888 boost::shared_ptr<any_path> new_content,
2889 content_merge_adaptor & adaptor)
2890{
2891 file_path parent_name;
2892 file_id parent_fid;
2893
2894 parent_roster.get_file_details(nid, parent_fid, parent_name);
2895
2896 P(F("replacing content of '%s' from %s with '%s'") % parent_name % side_image % new_content->as_external());
2897
2898 file_data parent_data;
2899 adaptor.get_version(parent_fid, parent_data);
2900
2901 data result_raw_data;
2902 read_data(*new_content, result_raw_data);
2903
2904 file_data result_data = file_data(result_raw_data);
2905 file_id result_fid;
2906 calculate_ident(result_data, result_fid);
2907
2908 file_t result_node = downcast_to_file_t(result_roster.get_node_for_update(nid));
2909 result_node->content = result_fid;
2910
2911 // User could specify no changes in content
2912 if (result_fid != parent_fid)
2913 {
2914 adaptor.record_file(parent_fid, result_fid, parent_data, result_data);
2915 }
2916}
2917
2918static void
2919resolve_dropped_modified_one(lua_hooks & lua,
2920 string side_image,
2921 bool handling_dropped_side,
2922 resolve_conflicts::file_resolution_t const & resolution,
2923 resolve_conflicts::file_resolution_t const & other_resolution,
2924 roster_t const & side_roster,
2925 file_path const & name,
2926 file_id const & fid,
2927 node_id const nid,
2928 content_merge_database_adaptor & adaptor,
2929 temp_node_id_source & nis,
2930 roster_t & result_roster)
2931{
2932 if (nid == the_null_node)
2933 {
2934 E(resolution.resolution == resolve_conflicts::none, origin::user,
2935 F("extra %s_resolution provided for dropped_modified '%s'") % side_image % name);
2936 return;
2937 }
2938 else
2939 {
2940 E(resolution.resolution != resolve_conflicts::none, origin::user,
2941 (other_resolution.resolution == resolve_conflicts::none) ?
2942 F("no resolution provided for dropped_modified '%s'") % name :
2943 F("no %s_resolution provided for dropped_modified '%s'") % side_image % name);
2944 }
2945
2946 switch (resolution.resolution)
2947 {
2948 case resolve_conflicts::none:
2949 // handled above; can't get here
2950 break;
2951
2952 case resolve_conflicts::content_user:
2953 // FIXME: check other_resolution for consistency
2954 if (handling_dropped_side)
2955 {
2956 // recreated; replace the contents of the recreated node
2957 replace_content(side_roster, side_image, nid, result_roster, resolution.content, adaptor);
2958 attach_node(lua, result_roster, nid, name);
2959 }
2960 else
2961 {
2962 // modified; drop and create a new node
2963 // See comments in keep below on why we drop first
2964 result_roster.drop_detached_node(nid);
2965
2966 node_id new_nid = create_new_node
2967 (side_roster, side_image, nid, result_roster, resolution.content, adaptor, nis);
2968
2969 attach_node(lua, result_roster, new_nid, name);
2970 }
2971 break;
2972
2973 case resolve_conflicts::content_internal:
2974 // not valid for dropped_modified
2975 I(false);
2976
2977 case resolve_conflicts::drop:
2978 // The node is either modified, recreated or duplicate name; in
2979 // any case, it is present but detached in the result roster, so drop it
2980 P(F("dropping '%s' from %s") % name % side_image);
2981 result_roster.drop_detached_node(nid);
2982 break;
2983
2984 case resolve_conflicts::keep:
2985 if (handling_dropped_side)
2986 {
2987 // recreated; keep the recreated contents
2988 P(F("keeping '%s' from %s") % name % side_image);
2989 attach_node(lua, result_roster, nid, name);
2990 }
2991 else
2992 {
2993 // modified; keep the modified contents
2994
2995 P(F("keeping '%s' from %s") % name % side_image);
2996 P(F(history_lost_msg) % name % side_image);
2997
2998 // We'd like to just attach_node here, but that violates a
2999 // fundamental design principle of mtn; nodes are born once,
3000 // and die once. If we attach here, the node is born, died,
3001 // and then born again.
3002 //
3003 // So we have to drop the old node, and create a new node with
3004 // the same contents. That loses history; 'mtn log <path>'
3005 // will end here, not showing the history of the original
3006 // node.
3007 result_roster.drop_detached_node(nid);
3008 node_id nid = result_roster.create_file_node(fid, nis);
3009 attach_node (lua, result_roster, nid, name);
3010 }
3011 break;
3012
3013 case resolve_conflicts::rename:
3014 if (handling_dropped_side)
3015 {
3016 // recreated; rename the recreated contents
3017 P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
3018 attach_node(lua, result_roster, nid, resolution.rename);
3019 }
3020 else
3021 {
3022 // modified; drop, create new node with the modified contents, rename
3023 // See comment in keep above on why we drop first.
3024 result_roster.drop_detached_node(nid);
3025
3026 P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
3027 P(F(history_lost_msg) % name % side_image);
3028
3029 node_id new_nid = result_roster.create_file_node(fid, nis);
3030 attach_node (lua, result_roster, new_nid, resolution.rename);
3031 }
3032 break;
3033
3034 case resolve_conflicts::content_user_rename:
3035 if (handling_dropped_side)
3036 {
3037 // recreated; rename and replace the recreated contents
3038 replace_content(side_roster, side_image, nid, result_roster, resolution.content, adaptor);
3039
3040 P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
3041
3042 attach_node (lua, result_roster, nid, resolution.rename);
3043 }
3044 else
3045 {
3046 // modified; drop, rename and replace the modified contents
3047 result_roster.drop_detached_node(nid);
3048
3049 node_id new_nid = create_new_node
3050 (side_roster, side_image, nid, result_roster, resolution.content, adaptor, nis);
3051
3052 P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
3053
3054 attach_node(lua, result_roster, new_nid, resolution.rename);
3055 }
3056 break;
3057 }
3058} // resolve_dropped_modified_one
3059
3060void
3061roster_merge_result::resolve_dropped_modified_conflicts(lua_hooks & lua,
3062 roster_t const & left_roster,
3063 roster_t const & right_roster,
3064 content_merge_database_adaptor & adaptor,
3065 temp_node_id_source & nis)
3066{
3067 MM(left_roster);
3068 MM(right_roster);
3069 MM(this->roster); // New roster
3070
3071 for (std::vector<dropped_modified_conflict>::iterator i = dropped_modified_conflicts.begin();
3072 i != dropped_modified_conflicts.end();
3073 ++i)
3074 {
3075 dropped_modified_conflict & conflict = *i;
3076 MM(conflict);
3077
3078 file_path left_name;
3079 file_id left_fid;
3080 file_path right_name;
3081 file_id right_fid;
3082
3083 if (conflict.left_nid != the_null_node)
3084 {
3085 if (conflict.left_rid == adaptor.left_rid)
3086 {
3087 left_roster.get_file_details(conflict.left_nid, left_fid, left_name);
3088 }
3089 else
3090 {
3091 if (null_id(conflict.left_rid))
3092 {
3093 // attr mtn::resolve_conflict drop does not set rid; find it now
3094 adaptor.get_dropped_details
3095 (adaptor.left_rid, conflict.left_nid, conflict.left_rid, left_name, left_fid);
3096 }
3097 else
3098 {
3099 roster_t tmp;
3100 adaptor.db.get_roster(conflict.left_rid, tmp);
3101 tmp.get_file_details(conflict.left_nid, left_fid, left_name);
3102 }
3103 }
3104 }
3105
3106 if (conflict.right_nid != the_null_node)
3107 {
3108 if (conflict.right_rid == adaptor.right_rid)
3109 {
3110 right_roster.get_file_details(conflict.right_nid, right_fid, right_name);
3111 }
3112 else
3113 {
3114 if (null_id(conflict.left_rid))
3115 {
3116 adaptor.get_dropped_details
3117 (adaptor.right_rid, conflict.right_nid, conflict.right_rid, right_name, right_fid);
3118 }
3119 else
3120 {
3121 roster_t tmp;
3122 adaptor.db.get_roster(conflict.right_rid, tmp);
3123 tmp.get_file_details(conflict.right_nid, right_fid, right_name);
3124 }
3125 }
3126 }
3127
3128 resolve_dropped_modified_one (lua,
3129 string("left"),
3130 conflict.dropped_side == resolve_conflicts::left_side,
3131 conflict.left_resolution,
3132 conflict.right_resolution,
3133 left_roster,
3134 left_name,
3135 left_fid,
3136 conflict.left_nid,
3137 adaptor,
3138 nis,
3139 roster);
3140
3141 resolve_dropped_modified_one (lua,
3142 string("right"),
3143 conflict.dropped_side == resolve_conflicts::right_side,
3144 conflict.right_resolution,
3145 conflict.left_resolution,
3146 right_roster,
3147 right_name,
3148 right_fid,
3149 conflict.right_nid,
3150 adaptor,
3151 nis,
3152 roster);
3153
3154 } // end for
3155
3156 dropped_modified_conflicts.clear();
3157}
3158
3159static void
3160resolve_duplicate_name_one_side(lua_hooks & lua,
3161 resolve_conflicts::file_resolution_t const & resolution,
3162 resolve_conflicts::file_resolution_t const & other_resolution,
3163 file_path const & name,
3164 file_id const & fid,
3165 node_id const nid,
3166 content_merge_adaptor & adaptor,
3167 roster_t & result_roster)
3168{
3169 switch (resolution.resolution)
3170 {
3171 case resolve_conflicts::content_user:
3172 {
3173 E(other_resolution.resolution == resolve_conflicts::drop ||
3174 other_resolution.resolution == resolve_conflicts::rename,
3175 origin::user,
3176 F("inconsistent left/right resolutions for '%s'") % name);
3177
3178 P(F("replacing content of '%s' with '%s'") % name % resolution.content->as_external());
3179
3180 file_id result_fid;
3181 file_data parent_data, result_data;
3182 data result_raw_data;
3183 adaptor.get_version(fid, parent_data);
3184
3185 read_data(*resolution.content, result_raw_data);
3186
3187 result_data = file_data(result_raw_data);
3188 calculate_ident(result_data, result_fid);
3189
3190 file_t result_node = downcast_to_file_t(result_roster.get_node_for_update(nid));
3191 result_node->content = result_fid;
3192
3193 adaptor.record_file(fid, result_fid, parent_data, result_data);
3194
3195 attach_node(lua, result_roster, nid, name);
3196 }
3197 break;
3198
3199 case resolve_conflicts::drop:
3200 P(F("dropping '%s'") % name);
3201
3202 if (is_dir_t(result_roster.get_node(nid)))
3203 {
3204 const_dir_t n = downcast_to_dir_t(result_roster.get_node(nid));
3205 E(n->children.empty(), origin::user, F("can't drop '%s'; not empty") % name);
3206 }
3207 result_roster.drop_detached_node(nid);
3208 break;
3209
3210 case resolve_conflicts::keep:
3211 E(other_resolution.resolution == resolve_conflicts::drop ||
3212 other_resolution.resolution == resolve_conflicts::rename,
3213 origin::user,
3214 F("inconsistent left/right resolutions for '%s'") % name);
3215
3216 P(F("keeping '%s'") % name);
3217 attach_node (lua, result_roster, nid, name);
3218 break;
3219
3220 case resolve_conflicts::rename:
3221 P(F("renaming '%s' to '%s'") % name % resolution.rename);
3222 attach_node (lua, result_roster, nid, resolution.rename);
3223 break;
3224
3225 case resolve_conflicts::none:
3226 E(false, origin::user,
3227 F("no resolution provided for duplicate_name '%s'") % name);
3228 break;
3229
3230 default:
3231 I(false);
3232 }
3233} // resolve_duplicate_name_one_side
3234
3235void
3236roster_merge_result::resolve_duplicate_name_conflicts(lua_hooks & lua,
3237 roster_t const & left_roster,
3238 roster_t const & right_roster,
3239 content_merge_adaptor & adaptor)
3240{
3241 MM(left_roster);
3242 MM(right_roster);
3243 MM(this->roster); // New roster
3244
3245 // Conflict nodes are present but detached (without filenames) in the new
3246 // roster. The resolution is either to suture the two files together, or to
3247 // rename one or both.
3248
3249 for (std::vector<duplicate_name_conflict>::const_iterator i = duplicate_name_conflicts.begin();
3250 i != duplicate_name_conflicts.end();
3251 ++i)
3252 {
3253 duplicate_name_conflict const & conflict = *i;
3254 MM(conflict);
3255
3256 node_id left_nid = conflict.left_nid;
3257 node_id right_nid= conflict.right_nid;
3258
3259 file_path left_name, right_name;
3260 file_id left_fid, right_fid;
3261
3262 if (is_file_t(left_roster.get_node(left_nid)))
3263 {
3264 left_roster.get_file_details(left_nid, left_fid, left_name);
3265 }
3266 else
3267 {
3268 left_roster.get_name(left_nid, left_name);
3269 }
3270
3271 if (is_file_t(right_roster.get_node(right_nid)))
3272 {
3273 right_roster.get_file_details(right_nid, right_fid, right_name);
3274 }
3275 else
3276 {
3277 right_roster.get_name(right_nid, right_name);
3278 }
3279
3280 resolve_duplicate_name_one_side
3281 (lua, conflict.left_resolution, conflict.right_resolution, left_name, left_fid, left_nid, adaptor, roster);
3282
3283 resolve_duplicate_name_one_side
3284 (lua, conflict.right_resolution, conflict.left_resolution, right_name, right_fid, right_nid, adaptor, roster);
3285 } // end for
3286
3287 duplicate_name_conflicts.clear();
3288}
3289
3290void
3291roster_merge_result::resolve_file_content_conflicts(lua_hooks & lua,
3292 roster_t const & left_roster,
3293 roster_t const & right_roster,
3294 content_merge_adaptor & adaptor)
3295{
3296 MM(left_roster);
3297 MM(right_roster);
3298 MM(this->roster); // New roster
3299
3300 // Conflict node is present and attached in the new roster, with a null
3301 // file content id. The resolution is to enter the user specified file
3302 // content in the database and roster, or let the internal line merger
3303 // handle it.
3304
3305 for (std::vector<file_content_conflict>::const_iterator i = file_content_conflicts.begin();
3306 i != file_content_conflicts.end();
3307 ++i)
3308 {
3309 file_content_conflict const & conflict = *i;
3310 MM(conflict);
3311
3312 file_path left_name, right_name;
3313
3314 left_roster.get_name(conflict.nid, left_name);
3315 right_roster.get_name(conflict.nid, right_name);
3316
3317 switch (conflict.resolution.resolution)
3318 {
3319 case resolve_conflicts::content_internal:
3320 case resolve_conflicts::none:
3321 {
3322 file_id merged_id;
3323
3324 E(resolve_conflicts::do_auto_merge(lua, conflict, adaptor, left_roster,
3325 right_roster, this->roster, merged_id),
3326 origin::user,
3327 F("merge of '%s', '%s' failed") % left_name % right_name);
3328
3329 P(F("merged '%s', '%s'") % left_name % right_name);
3330
3331 file_t result_node = downcast_to_file_t(roster.get_node_for_update(conflict.nid));
3332 result_node->content = merged_id;
3333 }
3334 break;
3335
3336 case resolve_conflicts::content_user:
3337 {
3338 P(F("replacing content of '%s', '%s' with '%s'") %
3339 left_name % right_name % conflict.resolution.content->as_external());
3340
3341 file_id result_id;
3342 file_data left_data, right_data, result_data;
3343 data result_raw_data;
3344 adaptor.get_version(conflict.left, left_data);
3345 adaptor.get_version(conflict.right, right_data);
3346
3347 read_data(*conflict.resolution.content, result_raw_data);
3348
3349 result_data = file_data(result_raw_data);
3350 calculate_ident(result_data, result_id);
3351
3352 file_t result_node = downcast_to_file_t(roster.get_node_for_update(conflict.nid));
3353 result_node->content = result_id;
3354
3355 adaptor.record_merge(conflict.left, conflict.right, result_id,
3356 left_data, right_data, result_data);
3357
3358 }
3359 break;
3360
3361 default:
3362 I(false);
3363 }
3364
3365 } // end for
3366
3367 file_content_conflicts.clear();
3368}
3369
3370// Local Variables:
3371// mode: C++
3372// fill-column: 76
3373// c-file-style: "gnu"
3374// indent-tabs-mode: nil
3375// End:
3376// 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