monotone

monotone Mtn Source Tree

Root/merge.cc

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