monotone

monotone Mtn Source Tree

Root/merge.cc

1// Copyright (C) 2008 Stephen Leake <stephen_leake@stephe-leake.org>
2// Copyright (C) 2008 Nathaniel Smith <njs@pobox.com>
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 <iostream>
13#include <set>
14
15#include <boost/shared_ptr.hpp>
16
17#include "diff_patch.hh"
18#include "merge.hh"
19#include "revision.hh"
20#include "roster_merge.hh"
21#include "safe_map.hh"
22#include "transforms.hh"
23#include "database.hh"
24
25using std::make_pair;
26using std::map;
27using std::set;
28using std::vector;
29
30using boost::shared_ptr;
31
32namespace
33{
34 enum merge_method { auto_merge, user_merge };
35
36 void
37 get_file_details(roster_t const & ros, node_id nid,
38 file_id & fid,
39 file_path & pth)
40 {
41 I(ros.has_node(nid));
42 file_t f = downcast_to_file_t(ros.get_node(nid));
43 fid = f->content;
44 ros.get_name(nid, pth);
45 }
46
47 void
48 try_to_merge_files(lua_hooks & lua,
49 roster_t const & left_roster, roster_t const & right_roster,
50 roster_merge_result & result, content_merge_adaptor & adaptor,
51 merge_method const method)
52 {
53 size_t cnt;
54 size_t total_conflicts = result.file_content_conflicts.size();
55 std::vector<file_content_conflict>::iterator it;
56
57 for (cnt = 1, it = result.file_content_conflicts.begin();
58 it != result.file_content_conflicts.end(); ++cnt)
59 {
60 file_content_conflict const & conflict = *it;
61
62 MM(conflict);
63
64 revision_id rid;
65 shared_ptr<roster_t const> roster_for_file_lca;
66 adaptor.get_ancestral_roster(conflict.nid, rid, roster_for_file_lca);
67
68 // Now we should certainly have a roster, which has the node.
69 I(roster_for_file_lca);
70 I(roster_for_file_lca->has_node(conflict.nid));
71
72 file_id anc_id, left_id, right_id;
73 file_path anc_path, left_path, right_path;
74 get_file_details(*roster_for_file_lca, conflict.nid, anc_id, anc_path);
75 get_file_details(left_roster, conflict.nid, left_id, left_path);
76 get_file_details(right_roster, conflict.nid, right_id, right_path);
77
78 file_id merged_id;
79
80 content_merger cm(lua, *roster_for_file_lca,
81 left_roster, right_roster,
82 adaptor);
83
84 bool merged = false;
85
86 switch (method)
87 {
88 case auto_merge:
89 merged = cm.try_auto_merge(anc_path, left_path, right_path,
90 right_path, anc_id, left_id, right_id,
91 merged_id);
92 break;
93
94 case user_merge:
95 merged = cm.try_user_merge(anc_path, left_path, right_path,
96 right_path, anc_id, left_id, right_id,
97 merged_id);
98
99 // If the user merge has failed, there's no point
100 // trying to continue -- we'll only frustrate users by
101 // encouraging them to continue working with their merge
102 // tool on a merge that is now destined to fail.
103 if (!merged)
104 return;
105
106 break;
107 }
108
109 if (merged)
110 {
111 L(FL("resolved content conflict %d / %d on file '%s'")
112 % cnt % total_conflicts % right_path);
113 file_t f = downcast_to_file_t(result.roster.get_node(conflict.nid));
114 f->content = merged_id;
115
116 it = result.file_content_conflicts.erase(it);
117 }
118 else
119 {
120 ++it;
121 }
122 }
123 }
124
125
126}
127
128void
129resolve_merge_conflicts(lua_hooks & lua,
130 roster_t const & left_roster,
131 roster_t const & right_roster,
132 roster_merge_result & result,
133 content_merge_adaptor & adaptor)
134{
135 // FIXME_ROSTERS: we only have code (below) to invoke the
136 // line-merger on content conflicts. Other classes of conflict will
137 // cause an invariant to trip below. Probably just a bunch of lua
138 // hooks for remaining conflict types will be ok.
139
140 if (!result.is_clean())
141 result.log_conflicts();
142
143
144 if (result.has_non_content_conflicts())
145 {
146 result.report_missing_root_conflicts(left_roster, right_roster, adaptor, false, std::cout);
147 result.report_invalid_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
148 result.report_directory_loop_conflicts(left_roster, right_roster, adaptor, false, std::cout);
149
150 result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor, false, std::cout);
151 result.report_multiple_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
152 result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
153
154 result.report_attribute_conflicts(left_roster, right_roster, adaptor, false, std::cout);
155 result.report_file_content_conflicts(left_roster, right_roster, adaptor, false, std::cout);
156 }
157 else if (result.has_content_conflicts())
158 {
159 // Attempt to auto-resolve any content conflicts using the line-merger.
160 // To do this requires finding a merge ancestor.
161
162 L(FL("examining content conflicts"));
163
164 try_to_merge_files(lua, left_roster, right_roster,
165 result, adaptor, auto_merge);
166
167 size_t remaining = result.file_content_conflicts.size();
168 if (remaining > 0)
169 {
170 P(FP("%d content conflict requires user intervention",
171 "%d content conflicts require user intervention",
172 remaining) % remaining);
173 result.report_file_content_conflicts(left_roster, right_roster, adaptor, false, std::cout);
174
175 try_to_merge_files(lua, left_roster, right_roster,
176 result, adaptor, user_merge);
177 }
178 }
179
180 E(result.is_clean(),
181 F("merge failed due to unresolved conflicts"));
182}
183
184void
185interactive_merge_and_store(lua_hooks & lua, database & db,
186 revision_id const & left_rid,
187 revision_id const & right_rid,
188 revision_id & merged_rid)
189{
190 roster_t left_roster, right_roster;
191 marking_map left_marking_map, right_marking_map;
192 set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
193
194 db.get_roster(left_rid, left_roster, left_marking_map);
195 db.get_roster(right_rid, right_roster, right_marking_map);
196 db.get_uncommon_ancestors(left_rid, right_rid,
197 left_uncommon_ancestors, right_uncommon_ancestors);
198
199 roster_merge_result result;
200
201 roster_merge(left_roster, left_marking_map, left_uncommon_ancestors,
202 right_roster, right_marking_map, right_uncommon_ancestors,
203 result);
204
205 content_merge_database_adaptor dba(db, left_rid, right_rid,
206 left_marking_map, right_marking_map);
207 resolve_merge_conflicts(lua, left_roster, right_roster,
208 result, dba);
209
210 // write new files into the db
211 store_roster_merge_result(db,
212 left_roster, right_roster, result,
213 left_rid, right_rid, merged_rid);
214}
215
216void
217store_roster_merge_result(database & db,
218 roster_t const & left_roster,
219 roster_t const & right_roster,
220 roster_merge_result & result,
221 revision_id const & left_rid,
222 revision_id const & right_rid,
223 revision_id & merged_rid)
224{
225 I(result.is_clean());
226 roster_t & merged_roster = result.roster;
227 merged_roster.check_sane();
228
229 revision_t merged_rev;
230 merged_rev.made_for = made_for_database;
231
232 calculate_ident(merged_roster, merged_rev.new_manifest);
233
234 shared_ptr<cset> left_to_merged(new cset);
235 make_cset(left_roster, merged_roster, *left_to_merged);
236 safe_insert(merged_rev.edges, make_pair(left_rid, left_to_merged));
237
238 shared_ptr<cset> right_to_merged(new cset);
239 make_cset(right_roster, merged_roster, *right_to_merged);
240 safe_insert(merged_rev.edges, make_pair(right_rid, right_to_merged));
241
242 revision_data merged_data;
243 write_revision(merged_rev, merged_data);
244 calculate_ident(merged_data, merged_rid);
245 {
246 transaction_guard guard(db);
247
248 db.put_revision(merged_rid, merged_rev);
249
250 guard.commit();
251 }
252}
253
254// Local Variables:
255// mode: C++
256// fill-column: 76
257// c-file-style: "gnu"
258// indent-tabs-mode: nil
259// End:
260// 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