monotone

monotone Mtn Source Tree

Root/src/merge_content.cc

1// Copyright (C) 2008 Nathaniel Smith <njs@pobox.com>
2// 2008, 2010, 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_content.hh"
13
14#include "constants.hh"
15#include "database.hh"
16#include "diff_output.hh"
17#include "file_io.hh"
18#include "lua_hooks.hh"
19#include "revision.hh"
20#include "merge_roster.hh"
21#include "simplestring_xform.hh"
22#include "transforms.hh"
23#include "xdelta.hh"
24
25#include "safe_map.hh"
26#include "vector.hh"
27#include <iostream>
28#include <boost/shared_ptr.hpp>
29
30using std::make_pair;
31using std::map;
32using std::set;
33using std::string;
34using std::vector;
35using boost::shared_ptr;
36
37///////////////////////////////////////////////////////////////////////////
38// content_merge_database_adaptor
39///////////////////////////////////////////////////////////////////////////
40
41content_merge_database_adaptor::content_merge_database_adaptor(database & db,
42 revision_id const & left,
43 revision_id const & right,
44 marking_map const & left_mm,
45 marking_map const & right_mm)
46 : db(db), left_rid (left), right_rid (right), left_mm(left_mm), right_mm(right_mm)
47{
48 // FIXME: possibly refactor to run this lazily, as we don't
49 // need to find common ancestors if we're never actually
50 // called on to do content merging.
51 find_common_ancestor_for_merge(db, left, right, lca);
52}
53
54void
55content_merge_database_adaptor::record_merge(file_id const & left_ident,
56 file_id const & right_ident,
57 file_id const & merged_ident,
58 file_data const & left_data,
59 file_data const & right_data,
60 file_data const & merged_data)
61{
62 L(FL("recording successful merge of %s <-> %s into %s")
63 % left_ident
64 % right_ident
65 % merged_ident);
66
67 transaction_guard guard(db);
68
69 if (!(left_ident == merged_ident))
70 {
71 delta left_delta;
72 diff(left_data.inner(), merged_data.inner(), left_delta);
73 db.put_file_version(left_ident, merged_ident, file_delta(left_delta));
74 }
75 if (!(right_ident == merged_ident))
76 {
77 delta right_delta;
78 diff(right_data.inner(), merged_data.inner(), right_delta);
79 db.put_file_version(right_ident, merged_ident, file_delta(right_delta));
80 }
81 guard.commit();
82}
83
84void
85content_merge_database_adaptor::record_file(file_id const & ident,
86 file_data const & data)
87{
88 L(FL("recording new file %s")
89 % ident);
90
91 transaction_guard guard(db);
92
93 db.put_file(ident, data);
94
95 guard.commit();
96}
97
98void
99content_merge_database_adaptor::record_file(file_id const & parent_ident,
100 file_id const & merged_ident,
101 file_data const & parent_data,
102 file_data const & merged_data)
103{
104 L(FL("recording file %s -> %s")
105 % parent_ident
106 % merged_ident);
107
108 transaction_guard guard(db);
109
110 if (!(parent_ident == merged_ident))
111 {
112 delta parent_delta;
113 diff(parent_data.inner(), merged_data.inner(), parent_delta);
114 db.put_file_version(parent_ident, merged_ident, file_delta(parent_delta));
115 }
116 guard.commit();
117}
118
119void
120content_merge_database_adaptor::cache_roster(revision_id const & rid,
121 boost::shared_ptr<roster_t const> roster)
122{
123 safe_insert(rosters, make_pair(rid, roster));
124};
125
126static void
127load_and_cache_roster(database & db, revision_id const & rid,
128 map<revision_id, shared_ptr<roster_t const> > & rmap,
129 shared_ptr<roster_t const> & rout)
130{
131 map<revision_id, shared_ptr<roster_t const> >::const_iterator i = rmap.find(rid);
132 if (i != rmap.end())
133 rout = i->second;
134 else
135 {
136 cached_roster cr;
137 db.get_roster(rid, cr);
138 safe_insert(rmap, make_pair(rid, cr.first));
139 rout = cr.first;
140 }
141}
142
143void
144content_merge_database_adaptor::get_ancestral_roster(node_id nid,
145 revision_id & rid,
146 shared_ptr<roster_t const> & anc)
147{
148 // Given a file, if the lca is nonzero and its roster contains the file,
149 // then we use its roster. Otherwise we use the roster at the file's
150 // birth revision, which is the "per-file worst case" lca.
151
152 // Begin by loading any non-empty file lca roster
153 rid = lca;
154 if (!lca.inner()().empty())
155 load_and_cache_roster(db, lca, rosters, anc);
156
157 // If there is no LCA, or the LCA's roster doesn't contain the file,
158 // then use the file's birth roster.
159 if (!anc || !anc->has_node(nid))
160 {
161 if (!left_mm.contains(nid))
162 {
163 rid = right_mm.get_marking(nid)->birth_revision;
164 }
165 else if (!right_mm.contains(nid))
166 {
167 rid = left_mm.get_marking(nid)->birth_revision;
168 }
169 else
170 {
171 const_marking_t const & lm = left_mm.get_marking(nid);
172 const_marking_t const & rm = right_mm.get_marking(nid);
173 I(lm->birth_revision == rm->birth_revision);
174 rid = lm->birth_revision;
175 }
176
177 load_and_cache_roster(db, rid, rosters, anc);
178 }
179 I(anc);
180}
181
182void
183content_merge_database_adaptor::get_dropped_details(revision_id & rev_id,
184 node_id nid,
185 revision_id & dropped_rev_id,
186 file_path & dropped_name,
187 file_id & dropped_file_id)
188{
189 set<revision_id> parents;
190 db.get_revision_parents(rev_id, parents);
191
192 while (parents.begin() != parents.end())
193 {
194 set<revision_id>::iterator i = parents.begin();
195 roster_t roster;
196 marking_map marking_map;
197
198 db.get_roster(*i, roster, marking_map);
199 if (roster.has_node(nid))
200 {
201 dropped_rev_id = *i;
202 roster.get_file_details(nid, dropped_file_id, dropped_name);
203 return;
204 }
205 else
206 {
207 parents.erase (i);
208 set<revision_id> more_parents;
209 db.get_revision_parents(*i, more_parents);
210 parents.insert(more_parents.begin(), more_parents.end());
211 }
212 }
213}
214
215void
216content_merge_database_adaptor::get_version(file_id const & ident,
217 file_data & dat) const
218{
219 db.get_file_version(ident, dat);
220}
221
222
223///////////////////////////////////////////////////////////////////////////
224// content_merge_workspace_adaptor
225///////////////////////////////////////////////////////////////////////////
226
227void
228content_merge_workspace_adaptor::cache_roster(revision_id const & rid,
229 boost::shared_ptr<roster_t const> roster)
230{
231 rosters.insert(std::make_pair(rid, roster));
232}
233
234void
235content_merge_workspace_adaptor::record_merge(file_id const & left_id,
236 file_id const & right_id,
237 file_id const & merged_id,
238 file_data const & left_data,
239 file_data const & right_data,
240 file_data const & merged_data)
241{
242 L(FL("temporarily recording merge of %s <-> %s into %s")
243 % left_id
244 % right_id
245 % merged_id);
246 // this is an insert instead of a safe_insert because it is perfectly
247 // legal (though rare) to have multiple merges resolve to the same file
248 // contents.
249 temporary_store.insert(make_pair(merged_id, merged_data));
250}
251
252void
253content_merge_workspace_adaptor::record_file(file_id const & id,
254 file_data const & data)
255{
256 L(FL("temporarily recording file %s")
257 % id);
258 // this is an insert instead of a safe_insert because it is perfectly
259 // legal (though rare) to have multiple merges resolve to the same file
260 // contents.
261 temporary_store.insert(make_pair(id, data));
262}
263
264void
265content_merge_workspace_adaptor::record_file(file_id const & parent_id,
266 file_id const & merged_id,
267 file_data const & parent_data,
268 file_data const & merged_data)
269{
270 L(FL("temporarily recording file %s -> %s")
271 % parent_id
272 % merged_id);
273 // this is an insert instead of a safe_insert because it is perfectly
274 // legal (though rare) to have multiple merges resolve to the same file
275 // contents.
276 temporary_store.insert(make_pair(merged_id, merged_data));
277}
278
279void
280content_merge_workspace_adaptor::get_ancestral_roster(node_id nid,
281 revision_id & rid,
282 shared_ptr<roster_t const> & anc)
283{
284 // Begin by loading any non-empty file lca roster
285 if (base->has_node(nid))
286 {
287 rid = lca;
288 anc = base;
289 }
290 else
291 {
292 if (!left_mm.contains(nid))
293 {
294 rid = right_mm.get_marking(nid)->birth_revision;
295 }
296 else if (!right_mm.contains(nid))
297 {
298 rid = left_mm.get_marking(nid)->birth_revision;
299 }
300 else
301 {
302 const_marking_t const & lm = left_mm.get_marking(nid);
303 const_marking_t const & rm = right_mm.get_marking(nid);
304 I(lm->birth_revision == rm->birth_revision);
305 rid = lm->birth_revision;
306 }
307
308 load_and_cache_roster(db, rid, rosters, anc);
309 }
310 I(anc);
311}
312
313void
314content_merge_workspace_adaptor::get_version(file_id const & ident,
315 file_data & dat) const
316{
317 map<file_id,file_data>::const_iterator i = temporary_store.find(ident);
318 if (i != temporary_store.end())
319 dat = i->second;
320 else if (db.file_version_exists(ident))
321 db.get_file_version(ident, dat);
322 else
323 {
324 data tmp;
325 file_id fid;
326 map<file_id, file_path>::const_iterator i = content_paths.find(ident);
327 I(i != content_paths.end());
328
329 require_path_is_file(i->second,
330 F("file '%s' does not exist in workspace") % i->second,
331 F("'%s' in workspace is a directory, not a file") % i->second);
332 read_data(i->second, tmp);
333 calculate_ident(file_data(tmp), fid);
334 E(fid == ident, origin::system,
335 F("file '%s' in workspace has id %s, wanted %s")
336 % i->second
337 % fid
338 % ident);
339 dat = file_data(tmp);
340 }
341}
342
343
344///////////////////////////////////////////////////////////////////////////
345// content_merge_checkout_adaptor
346///////////////////////////////////////////////////////////////////////////
347
348void
349content_merge_checkout_adaptor::record_merge(file_id const & left_ident,
350 file_id const & right_ident,
351 file_id const & merged_ident,
352 file_data const & left_data,
353 file_data const & right_data,
354 file_data const & merged_data)
355{
356 I(false);
357}
358
359void
360content_merge_checkout_adaptor::record_file(file_id const & parent_ident,
361 file_id const & merged_ident,
362 file_data const & parent_data,
363 file_data const & merged_data)
364{
365 I(false);
366}
367
368void
369content_merge_checkout_adaptor::record_file(file_id const & ident,
370 file_data const & data)
371{
372 I(false);
373}
374
375void
376content_merge_checkout_adaptor::get_ancestral_roster(node_id nid,
377 revision_id & rid,
378 shared_ptr<roster_t const> & anc)
379{
380 I(false);
381}
382
383void
384content_merge_checkout_adaptor::get_version(file_id const & ident,
385 file_data & dat) const
386{
387 db.get_file_version(ident, dat);
388}
389
390///////////////////////////////////////////////////////////////////////////
391// content_merge_empty_adaptor
392///////////////////////////////////////////////////////////////////////////
393
394void
395content_merge_empty_adaptor::record_merge(file_id const & left_ident,
396 file_id const & right_ident,
397 file_id const & merged_ident,
398 file_data const & left_data,
399 file_data const & right_data,
400 file_data const & merged_data)
401{
402 I(false);
403}
404
405void
406content_merge_empty_adaptor::record_file(file_id const & ident,
407 file_data const & data)
408{
409 I(false);
410}
411
412void
413content_merge_empty_adaptor::record_file(file_id const & parent_ident,
414 file_id const & merged_ident,
415 file_data const & parent_data,
416 file_data const & merged_data)
417{
418 I(false);
419}
420
421void
422content_merge_empty_adaptor::get_ancestral_roster(node_id nid,
423 revision_id & rid,
424 shared_ptr<roster_t const> & anc)
425{
426 I(false);
427}
428
429void
430content_merge_empty_adaptor::get_version(file_id const & ident,
431 file_data & dat) const
432{
433 I(false);
434}
435
436///////////////////////////////////////////////////////////////////////////
437// content_merger
438///////////////////////////////////////////////////////////////////////////
439
440string
441content_merger::get_file_encoding(file_path const & path,
442 roster_t const & ros)
443{
444 attr_value v;
445 if (ros.get_attr(path, attr_key(constants::encoding_attribute), v))
446 return v();
447 return constants::default_encoding;
448}
449
450bool
451content_merger::attribute_manual_merge(file_path const & path,
452 roster_t const & ros)
453{
454 attr_value v;
455 if (ros.get_attr(path, attr_key(constants::manual_merge_attribute), v)
456 && v() == "true")
457 return true;
458 return false; // default: enable auto merge
459}
460
461bool
462content_merger::attempt_auto_merge(file_path const & anc_path, // inputs
463 file_path const & left_path,
464 file_path const & right_path,
465 file_id const & ancestor_id,
466 file_id const & left_id,
467 file_id const & right_id,
468 file_data & left_data, // outputs
469 file_data & right_data,
470 file_data & merge_data)
471{
472 I(left_id != right_id);
473
474 if (attribute_manual_merge(left_path, left_ros) ||
475 attribute_manual_merge(right_path, right_ros))
476 {
477 return false;
478 }
479
480 // both files mergeable by monotone internal algorithm, try to merge
481 // note: the ancestor is not considered for manual merging. Forcing the
482 // user to merge manually just because of an ancestor mistakenly marked
483 // manual seems too harsh
484
485 file_data ancestor_data;
486
487 adaptor.get_version(left_id, left_data);
488 adaptor.get_version(ancestor_id, ancestor_data);
489 adaptor.get_version(right_id, right_data);
490
491 data const left_unpacked = left_data.inner();
492 data const ancestor_unpacked = ancestor_data.inner();
493 data const right_unpacked = right_data.inner();
494
495 string const left_encoding(get_file_encoding(left_path, left_ros));
496 string const anc_encoding(get_file_encoding(anc_path, anc_ros));
497 string const right_encoding(get_file_encoding(right_path, right_ros));
498
499 vector<string> left_lines, ancestor_lines, right_lines, merged_lines;
500 split_into_lines(left_unpacked(), left_encoding, left_lines,
501 split_flags::keep_endings);
502 split_into_lines(ancestor_unpacked(), anc_encoding, ancestor_lines,
503 split_flags::keep_endings);
504 split_into_lines(right_unpacked(), right_encoding, right_lines,
505 split_flags::keep_endings);
506
507 if (merge3(ancestor_lines, left_lines, right_lines, merged_lines))
508 {
509 string tmp;
510
511 join_lines(merged_lines, tmp, "");
512 merge_data = file_data(tmp, origin::internal);
513 return true;
514 }
515
516 return false;
517}
518
519bool
520content_merger::try_auto_merge(file_path const & anc_path,
521 file_path const & left_path,
522 file_path const & right_path,
523 file_path const & merged_path,
524 file_id const & ancestor_id,
525 file_id const & left_id,
526 file_id const & right_id,
527 file_id & merged_id)
528{
529 // This version of try_to_merge_files should only be called when there is a
530 // real merge3 to perform.
531 I(!null_id(ancestor_id));
532 I(!null_id(left_id));
533 I(!null_id(right_id));
534
535 L(FL("trying auto merge '%s' %s <-> %s (ancestor: %s)")
536 % merged_path
537 % left_id
538 % right_id
539 % ancestor_id);
540
541 if (left_id == right_id)
542 {
543 L(FL("files are identical"));
544 merged_id = left_id;
545 return true;
546 }
547
548 file_data left_data, right_data, merge_data;
549
550 if (attempt_auto_merge(anc_path, left_path, right_path,
551 ancestor_id, left_id, right_id,
552 left_data, right_data, merge_data))
553 {
554 L(FL("internal 3-way merged ok"));
555 calculate_ident(merge_data, merged_id);
556
557 adaptor.record_merge(left_id, right_id, merged_id,
558 left_data, right_data, merge_data);
559
560 return true;
561 }
562
563 return false;
564}
565
566bool
567content_merger::try_user_merge(file_path const & anc_path,
568 file_path const & left_path,
569 file_path const & right_path,
570 file_path const & merged_path,
571 file_id const & ancestor_id,
572 file_id const & left_id,
573 file_id const & right_id,
574 file_id & merged_id)
575{
576 // This version of try_to_merge_files should only be called when there is a
577 // real merge3 to perform.
578 I(!null_id(ancestor_id));
579 I(!null_id(left_id));
580 I(!null_id(right_id));
581
582 L(FL("trying user merge '%s' %s <-> %s (ancestor: %s)")
583 % merged_path
584 % left_id
585 % right_id
586 % ancestor_id);
587
588 if (left_id == right_id)
589 {
590 L(FL("files are identical"));
591 merged_id = left_id;
592 return true;
593 }
594
595 file_data left_data, right_data, ancestor_data;
596 data left_unpacked, ancestor_unpacked, right_unpacked, merged_unpacked;
597
598 adaptor.get_version(left_id, left_data);
599 adaptor.get_version(ancestor_id, ancestor_data);
600 adaptor.get_version(right_id, right_data);
601
602 left_unpacked = left_data.inner();
603 ancestor_unpacked = ancestor_data.inner();
604 right_unpacked = right_data.inner();
605
606 P(F("help required for 3-way merge\n"
607 "[ancestor] %s\n"
608 "[ left] %s\n"
609 "[ right] %s\n"
610 "[ merged] %s")
611 % anc_path
612 % left_path
613 % right_path
614 % merged_path);
615
616 if (lua.hook_merge3(anc_path, left_path, right_path, merged_path,
617 ancestor_unpacked, left_unpacked,
618 right_unpacked, merged_unpacked))
619 {
620 file_data merge_data(merged_unpacked);
621
622 L(FL("lua merge3 hook merged ok"));
623 calculate_ident(merge_data, merged_id);
624
625 adaptor.record_merge(left_id, right_id, merged_id,
626 left_data, right_data, merge_data);
627 return true;
628 }
629
630 return false;
631}
632
633enum merge_method { auto_merge, user_merge };
634
635static void
636try_to_merge_files(lua_hooks & lua,
637 roster_t const & left_roster, roster_t const & right_roster,
638 roster_merge_result & result, content_merge_adaptor & adaptor,
639 merge_method const method)
640{
641 size_t cnt;
642 size_t total_conflicts = result.file_content_conflicts.size();
643 std::vector<file_content_conflict>::iterator it;
644
645 for (cnt = 1, it = result.file_content_conflicts.begin();
646 it != result.file_content_conflicts.end(); ++cnt)
647 {
648 file_content_conflict const & conflict = *it;
649
650 MM(conflict);
651
652 revision_id rid;
653 shared_ptr<roster_t const> roster_for_file_lca;
654 adaptor.get_ancestral_roster(conflict.nid, rid, roster_for_file_lca);
655
656 // Now we should certainly have a roster, which has the node.
657 I(roster_for_file_lca);
658 I(roster_for_file_lca->has_node(conflict.nid));
659
660 file_id anc_id, left_id, right_id;
661 file_path anc_path, left_path, right_path;
662 roster_for_file_lca->get_file_details(conflict.nid, anc_id, anc_path);
663 left_roster.get_file_details(conflict.nid, left_id, left_path);
664 right_roster.get_file_details(conflict.nid, right_id, right_path);
665
666 file_id merged_id;
667
668 content_merger cm(lua, *roster_for_file_lca,
669 left_roster, right_roster,
670 adaptor);
671
672 bool merged = false;
673
674 switch (method)
675 {
676 case auto_merge:
677 merged = cm.try_auto_merge(anc_path, left_path, right_path,
678 right_path, anc_id, left_id, right_id,
679 merged_id);
680 break;
681
682 case user_merge:
683 merged = cm.try_user_merge(anc_path, left_path, right_path,
684 right_path, anc_id, left_id, right_id,
685 merged_id);
686
687 // If the user merge has failed, there's no point
688 // trying to continue -- we'll only frustrate users by
689 // encouraging them to continue working with their merge
690 // tool on a merge that is now destined to fail.
691 if (!merged)
692 return;
693
694 break;
695 }
696
697 if (merged)
698 {
699 L(FL("resolved content conflict %d / %d on file '%s'")
700 % cnt % total_conflicts % right_path);
701 file_t f = downcast_to_file_t(result.roster.get_node_for_update(conflict.nid));
702 f->content = merged_id;
703
704 it = result.file_content_conflicts.erase(it);
705 }
706 else
707 {
708 ++it;
709 }
710 }
711}
712
713void
714resolve_merge_conflicts(lua_hooks & lua,
715 options const & opts,
716 roster_t const & left_roster,
717 roster_t const & right_roster,
718 roster_merge_result & result,
719 content_merge_adaptor & adaptor,
720 temp_node_id_source & nis,
721 const bool resolutions_given)
722{
723 if (!result.is_clean())
724 {
725 result.log_conflicts();
726
727 if (resolutions_given)
728 {
729 // We require --resolve-conflicts to enable processing attr
730 // mtn:resolve_conflict.
731
732 // If there are any conflicts for which we don't currently support
733 // resolutions, give a nice error message.
734 char const * const msg = "conflict resolution for %s not yet supported";
735
736 E(!result.missing_root_conflict, origin::user,
737 F(msg) % "missing_root_dir");
738 E(result.invalid_name_conflicts.size() == 0, origin::user,
739 F(msg) % "invalid_name_conflicts");
740 E(result.directory_loop_conflicts.size() == 0, origin::user,
741 F(msg) % "directory_loop_conflicts");
742 E(result.multiple_name_conflicts.size() == 0, origin::user,
743 F(msg) % "multiple_name_conflicts");
744 E(result.attribute_conflicts.size() == 0, origin::user,
745 F(msg) % "attribute_conflicts");
746
747 // Resolve the ones we can, if they have resolutions specified. Each
748 // conflict list is deleted once all are resolved.
749 result.resolve_orphaned_node_conflicts(lua, left_roster, right_roster, adaptor);
750 result.resolve_dropped_modified_conflicts(lua, left_roster, right_roster,
751 dynamic_cast <content_merge_database_adaptor&>(adaptor), nis);
752 result.resolve_duplicate_name_conflicts(lua, left_roster, right_roster, adaptor);
753
754 result.resolve_file_content_conflicts (lua, left_roster, right_roster, adaptor);
755 }
756 }
757
758 if (result.has_non_content_conflicts())
759 {
760 result.report_missing_root_conflicts(left_roster, right_roster, adaptor, false, std::cout);
761 result.report_invalid_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
762 result.report_directory_loop_conflicts(left_roster, right_roster, adaptor, false, std::cout);
763
764 result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor, false, std::cout);
765 result.report_multiple_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
766 result.report_dropped_modified_conflicts(left_roster, right_roster, adaptor, false, std::cout);
767 result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
768
769 result.report_attribute_conflicts(left_roster, right_roster, adaptor, false, std::cout);
770 result.report_file_content_conflicts(lua, left_roster, right_roster, adaptor, false, std::cout);
771 }
772 else if (result.has_content_conflicts())
773 {
774 // Attempt to auto-resolve any content conflicts using the line-merger.
775 // To do this requires finding a merge ancestor.
776
777 L(FL("examining content conflicts"));
778
779 try_to_merge_files(lua, left_roster, right_roster,
780 result, adaptor, auto_merge);
781
782 size_t remaining = result.file_content_conflicts.size();
783 if (remaining > 0)
784 {
785 P(FP("%d content conflict requires user intervention",
786 "%d content conflicts require user intervention",
787 remaining) % remaining);
788
789 // We don't spawn the merger here, because some mergers
790 // (such as opendiff) require prompting the user via
791 // stdin/stdout, and that's what 'non_interactive' prevents.
792 // Note that 'automate stdio' sets non_interactive true,
793 // because it doesn't support prompting.
794 //
795 // Another option would be to pass the option to the merger,
796 // and let it decide. We are not doing that, because it is
797 // felt this whole design is already too complicated; we are
798 // working to find a better solution. See thread at
799 // http://lists.gnu.org/archive/html/monotone-devel/2010-04/msg00000.html
800 E(!opts.non_interactive, origin::user,
801 F("can't spawn external merger when non-interactive"));
802
803 result.report_file_content_conflicts(lua, left_roster, right_roster, adaptor, false, std::cout);
804
805 try_to_merge_files(lua, left_roster, right_roster,
806 result, adaptor, user_merge);
807 }
808 }
809
810 E(result.is_clean(), origin::user,
811 F("merge failed due to unresolved conflicts"));
812}
813
814void
815interactive_merge_and_store(lua_hooks & lua,
816 database & db,
817 options const & opts,
818 revision_id const & left_rid,
819 revision_id const & right_rid,
820 revision_id & merged_rid)
821{
822 roster_t left_roster, right_roster;
823 marking_map left_marking_map, right_marking_map;
824 set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
825
826 db.get_roster(left_rid, left_roster, left_marking_map);
827 db.get_roster(right_rid, right_roster, right_marking_map);
828 db.get_uncommon_ancestors(left_rid, right_rid,
829 left_uncommon_ancestors, right_uncommon_ancestors);
830
831 roster_merge_result result;
832
833 roster_merge(left_roster, left_marking_map, left_uncommon_ancestors,
834 right_roster, right_marking_map, right_uncommon_ancestors,
835 result);
836
837 bool resolutions_given;
838 temp_node_id_source nis;
839 content_merge_database_adaptor dba(db, left_rid, right_rid,
840 left_marking_map, right_marking_map);
841
842 parse_resolve_conflicts_opts (opts, left_rid, left_roster, right_rid, right_roster, result, resolutions_given);
843
844 resolve_merge_conflicts(lua, opts, left_roster, right_roster, result, dba, nis, resolutions_given);
845
846 // write new files into the db
847 store_roster_merge_result(db,
848 left_roster, right_roster, result,
849 left_rid, right_rid, merged_rid);
850}
851
852void
853store_roster_merge_result(database & db,
854 roster_t const & left_roster,
855 roster_t const & right_roster,
856 roster_merge_result & result,
857 revision_id const & left_rid,
858 revision_id const & right_rid,
859 revision_id & merged_rid)
860{
861 I(result.is_clean());
862 roster_t & merged_roster = result.roster;
863 merged_roster.check_sane(true); // resolve conflicts can create new nodes
864
865 revision_t merged_rev;
866 merged_rev.made_for = made_for_database;
867
868 calculate_ident(merged_roster, merged_rev.new_manifest);
869
870 shared_ptr<cset> left_to_merged(new cset);
871 make_cset(left_roster, merged_roster, *left_to_merged);
872 safe_insert(merged_rev.edges, make_pair(left_rid, left_to_merged));
873
874 shared_ptr<cset> right_to_merged(new cset);
875 make_cset(right_roster, merged_roster, *right_to_merged);
876 safe_insert(merged_rev.edges, make_pair(right_rid, right_to_merged));
877
878 revision_data merged_data;
879 write_revision(merged_rev, merged_data);
880 calculate_ident(merged_data, merged_rid);
881 {
882 transaction_guard guard(db);
883
884 db.put_revision(merged_rid, merged_rev);
885
886 guard.commit();
887 }
888}
889
890// Local Variables:
891// mode: C++
892// fill-column: 76
893// c-file-style: "gnu"
894// indent-tabs-mode: nil
895// End:
896// 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