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 "database.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(lua_hooks & lua,
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(lua, *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(lua_hooks & lua,
128 roster_t const & left_roster,
129 roster_t const & right_roster,
130 roster_merge_result & result,
131 content_merge_adaptor & adaptor)
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(lua, 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(lua, left_roster, right_roster,
172 result, adaptor, user_merge);
173 }
174 }
175
176 E(result.is_clean(),
177 F("merge failed due to unresolved conflicts"));
178}
179
180void
181interactive_merge_and_store(lua_hooks & lua, database & db,
182 revision_id const & left_rid,
183 revision_id const & right_rid,
184 revision_id & merged_rid)
185{
186 roster_t left_roster, right_roster;
187 marking_map left_marking_map, right_marking_map;
188 set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
189
190 db.get_roster(left_rid, left_roster, left_marking_map);
191 db.get_roster(right_rid, right_roster, right_marking_map);
192 db.get_uncommon_ancestors(left_rid, right_rid,
193 left_uncommon_ancestors, right_uncommon_ancestors);
194
195 roster_merge_result result;
196
197 roster_merge(left_roster, left_marking_map, left_uncommon_ancestors,
198 right_roster, right_marking_map, right_uncommon_ancestors,
199 result);
200
201 content_merge_database_adaptor dba(db, left_rid, right_rid,
202 left_marking_map, right_marking_map);
203 resolve_merge_conflicts(lua, left_roster, right_roster,
204 result, dba);
205
206 // write new files into the db
207 store_roster_merge_result(db,
208 left_roster, right_roster, result,
209 left_rid, right_rid, merged_rid);
210}
211
212void
213store_roster_merge_result(database & db,
214 roster_t const & left_roster,
215 roster_t const & right_roster,
216 roster_merge_result & result,
217 revision_id const & left_rid,
218 revision_id const & right_rid,
219 revision_id & merged_rid)
220{
221 I(result.is_clean());
222 roster_t & merged_roster = result.roster;
223 merged_roster.check_sane();
224
225 revision_t merged_rev;
226 merged_rev.made_for = made_for_database;
227
228 calculate_ident(merged_roster, merged_rev.new_manifest);
229
230 shared_ptr<cset> left_to_merged(new cset);
231 make_cset(left_roster, merged_roster, *left_to_merged);
232 safe_insert(merged_rev.edges, make_pair(left_rid, left_to_merged));
233
234 shared_ptr<cset> right_to_merged(new cset);
235 make_cset(right_roster, merged_roster, *right_to_merged);
236 safe_insert(merged_rev.edges, make_pair(right_rid, right_to_merged));
237
238 revision_data merged_data;
239 write_revision(merged_rev, merged_data);
240 calculate_ident(merged_data, merged_rid);
241 {
242 transaction_guard guard(db);
243
244 db.put_revision(merged_rid, merged_rev);
245
246 guard.commit();
247 }
248}
249
250// Local Variables:
251// mode: C++
252// fill-column: 76
253// c-file-style: "gnu"
254// indent-tabs-mode: nil
255// End:
256// 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