monotone

monotone Mtn Source Tree

Root/src/merge_roster.hh

1// Copyright (C) 2005, 2010 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#ifndef __ROSTER_MERGE_HH__
12#define __ROSTER_MERGE_HH__
13
14#include <boost/shared_ptr.hpp>
15
16#include "rev_types.hh"
17#include "database.hh"
18#include "merge_content.hh"
19#include "roster.hh" // needs full definition of roster_t available
20
21// interactions between conflict types:
22// node rename conflicts never participate in structural conflicts
23// (e.g., merge <rename a foo; rename b bar>, <rename a bar> could be
24// considered to have two conflicts -- 'a' being renamed to both 'foo' and
25// 'bar', and 'a' and 'b' both being renamed to 'bar'. Only the former
26// occurs; 'b' merges cleanly and will be named 'bar' in the resulting
27// manifest.)
28//
29
30namespace resolve_conflicts
31{
32 enum resolution_t {none, content_user, content_internal, drop, keep, rename, content_user_rename};
33
34 char const * image(resolution_t item);
35
36 enum side_t {left_side, right_side};
37
38 char const * image(side_t item);
39
40 struct file_resolution_t
41 {
42 resolution_t resolution;
43 boost::shared_ptr<any_path> content;
44 file_path rename;
45
46 file_resolution_t() :
47 resolution(none),
48 content(),
49 rename()
50 {}
51 };
52
53 std::string image(file_resolution_t res);
54
55 // For filename read from conflicts file; converts path to utf8. basic_io
56 // parser should return utf8 in the first place.
57 file_path file_path_external(std::string path);
58
59}
60
61// renaming the root dir allows these:
62// -- _MTN in root
63// -- missing root directory
64
65// this is a node that cleanly merged to some name, but that name was somehow
66// forbidden. (Currently, the only forbidden name is "_MTN" in the root
67// directory.)
68struct invalid_name_conflict
69{
70 node_id nid;
71 std::pair<node_id, path_component> parent_name; // renamed from (if any)
72};
73
74struct directory_loop_conflict
75{
76 node_id nid;
77 std::pair<node_id, path_component> parent_name; // renamed from (if any)
78};
79
80// orphaned nodes always merged their name cleanly, so we simply put that name
81// here. the node in the resulting roster is detached.
82struct orphaned_node_conflict
83{
84 node_id nid;
85 // nid is the orphaned node; it exists in one parent, but the directory
86 // containing it does not exist in the other.
87
88 std::pair<node_id, path_component> parent_name;
89 // parent_name is the name of the orphaned node, in the parent revision
90 // where it exists. parent_name.first is the directory containing
91 // parent_name.second
92
93 resolve_conflicts::file_resolution_t resolution;
94
95};
96
97// our general strategy is to return a (possibly insane) roster, and a list of
98// conflicts encountered in that roster. Each conflict encountered in merging
99// the roster creates an entry in this list.
100
101// nodes with multiple name conflicts are left detached in the resulting
102// roster, with null parent and name fields.
103// note that it is possible that the parent node on the left, the right, or
104// both, no longer exist in the merged roster. also note that it is possible
105// that on one or both sides, they do exist, but already have an entry with
106// the given name.
107struct multiple_name_conflict
108{
109 node_id nid;
110 multiple_name_conflict(node_id nid) : nid(nid) {}
111 std::pair<node_id, path_component> left, right;
112};
113
114// nodes with drop/modified conflicts are left detached in the resulting
115// roster, with null parent and name fields.
116struct dropped_modified_conflict
117{
118 // A dropped_modified conflict can be the result of a repeated
119 // duplicate_name conflict (see
120 // ../test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2)
121 //
122 // If the user has recreated the dropped node, that also looks like a
123 // duplicate name conflict.
124 //
125 // In either case both nids are non-null.
126 //
127 // Dropped_modified can also be due to a dropped directory, in which case
128 // this looks like an orphaned_node conflict.
129 //
130 // If the resolution for the dropped node is 'keep', we need the revision
131 // that contains the node id, which is an ancestor of the merge parent.
132 // That information is also useful for an external front end, that wants to
133 // retreive the file contents for a merge tool.
134
135 resolve_conflicts::side_t dropped_side;
136
137 // If nid is set, it is in corresponding merge parent.
138 node_id left_nid, right_nid;
139
140 bool orphaned; // if true, the dropped side is due to a dropped parent directory
141
142 revision_id left_rid, right_rid;
143 resolve_conflicts::file_resolution_t left_resolution, right_resolution;
144
145 dropped_modified_conflict(node_id left_nid, node_id right_nid) :
146 left_nid(left_nid),
147 right_nid(right_nid),
148 orphaned(false),
149 left_rid(),
150 right_rid(),
151 left_resolution(),
152 right_resolution()
153 {dropped_side = (left_nid == the_null_node ? resolve_conflicts::left_side : resolve_conflicts::right_side);}
154
155 dropped_modified_conflict() :
156 left_nid(the_null_node),
157 right_nid(the_null_node),
158 orphaned(false),
159 left_rid(),
160 right_rid(),
161 left_resolution(),
162 right_resolution()
163 {}
164
165 // for find
166 bool operator==(node_id n) {return left_nid == n || right_nid == n;}
167};
168
169// this is when two distinct nodes want to have the same name. these nodes
170// always each merged their names cleanly. the nodes in the resulting roster
171// are both detached.
172// only two nodes are possible, because we
173// -- only merge two rosters at a time
174// -- merge (parent, basename) as a single scalar. If we merged them
175// separately, then it would be possible to have one side of a merge
176// rename a bunch of files in different directories to all have the same
177// basename, and the other side of the merge to move them all into the
178// same directory.
179// a clean *-merge of a scalar always takes on the value of one parent or
180// another, and the requirement here is that each node have a unique (parent,
181// basename) tuple, and since our requirement matches our *-merge scalar,
182// we're okay.
183struct duplicate_name_conflict
184{
185 node_id left_nid, right_nid;
186 std::pair<node_id, path_component> parent_name;
187 // file part of resolution must be a file_path if resolution is 'rename';
188 // it may be a bookkeeping or system path if resolution is 'user'.
189 resolve_conflicts::file_resolution_t left_resolution, right_resolution;
190
191 duplicate_name_conflict() {};
192
193 // for find
194 bool operator==(node_id n) {return left_nid == n || right_nid == n;}
195};
196
197// nodes with attribute conflicts are left attached in the resulting tree (unless
198// detached for some other reason), but with the given attribute left out of
199// their attr_map_t. Note that this doesn't actually leave the resulting
200// roster insane (FIXME: we could put an invalid attr value in instead, like a
201// pair (false, "foo") (since the second value can only be non-null if the
202// first is 'true'). Should we do this?)
203struct attribute_conflict
204{
205 node_id nid;
206 attribute_conflict(node_id nid) : nid(nid) {}
207 attr_key key; // attr_name?
208 std::pair<bool, attr_value> left, right;
209};
210
211// files with content conflicts are left attached in resulting tree (unless
212// detached for some other reason), but with a null content hash.
213struct file_content_conflict
214{
215 node_id nid;
216 file_id ancestor, left, right; // ancestor is set only when reading in a conflicts file
217 resolve_conflicts::file_resolution_t resolution;
218
219 file_content_conflict() :
220 nid(the_null_node),
221 resolution()
222 {};
223
224 file_content_conflict(node_id nid) :
225 nid(nid),
226 resolution()
227 {};
228};
229
230template <> void dump(invalid_name_conflict const & conflict, std::string & out);
231template <> void dump(directory_loop_conflict const & conflict, std::string & out);
232
233template <> void dump(orphaned_node_conflict const & conflict, std::string & out);
234template <> void dump(multiple_name_conflict const & conflict, std::string & out);
235template <> void dump(dropped_modified_conflict const & conflict, std::string & out);
236template <> void dump(duplicate_name_conflict const & conflict, std::string & out);
237
238template <> void dump(attribute_conflict const & conflict, std::string & out);
239template <> void dump(file_content_conflict const & conflict, std::string & out);
240
241struct roster_merge_result
242{
243 // three main types of conflicts
244 // - structural conflicts (which have the following subtypes)
245 // - missing root directory
246 // - invalid name conflicts
247 // - duplicate name conflicts
248 // - orphaned node conflicts
249 // - multiple name conflicts
250 // - drop/modified conflicts
251 // - directory loop conflicts
252 // - attribute conflicts
253 // - file content conflicts
254
255 bool missing_root_conflict; // there can only be one of these
256 std::vector<invalid_name_conflict> invalid_name_conflicts;
257 std::vector<directory_loop_conflict> directory_loop_conflicts;
258
259 std::vector<orphaned_node_conflict> orphaned_node_conflicts;
260 std::vector<multiple_name_conflict> multiple_name_conflicts;
261 std::vector<dropped_modified_conflict> dropped_modified_conflicts;
262 std::vector<duplicate_name_conflict> duplicate_name_conflicts;
263
264 std::vector<attribute_conflict> attribute_conflicts;
265 std::vector<file_content_conflict> file_content_conflicts;
266
267
268 // this roster is sane if is_clean() returns true
269 roster_t roster;
270 bool is_clean() const;
271 bool has_content_conflicts() const;
272 bool has_non_content_conflicts() const;
273 int count_supported_resolution() const;
274 int count_unsupported_resolution() const;
275 void log_conflicts() const;
276
277 void report_missing_root_conflicts(roster_t const & left,
278 roster_t const & right,
279 content_merge_adaptor & adaptor,
280 bool const basic_io,
281 std::ostream & output) const;
282 void report_invalid_name_conflicts(roster_t const & left,
283 roster_t const & right,
284 content_merge_adaptor & adaptor,
285 bool const basic_io,
286 std::ostream & output) const;
287 void report_directory_loop_conflicts(roster_t const & left,
288 roster_t const & right,
289 content_merge_adaptor & adaptor,
290 bool const basic_io,
291 std::ostream & output) const;
292
293 void report_orphaned_node_conflicts(roster_t const & left,
294 roster_t const & right,
295 content_merge_adaptor & adaptor,
296 bool const basic_io,
297 std::ostream & output) const;
298 void resolve_orphaned_node_conflicts(lua_hooks & lua,
299 roster_t const & left_roster,
300 roster_t const & right_roster,
301 content_merge_adaptor & adaptor);
302
303 void report_multiple_name_conflicts(roster_t const & left,
304 roster_t const & right,
305 content_merge_adaptor & adaptor,
306 bool const basic_io,
307 std::ostream & output) const;
308
309 void report_dropped_modified_conflicts(roster_t const & left,
310 roster_t const & right,
311 content_merge_adaptor & adaptor,
312 bool const basic_io,
313 std::ostream & output) const;
314 void resolve_dropped_modified_conflicts(lua_hooks & lua,
315 roster_t const & left_roster,
316 roster_t const & right_roster,
317 content_merge_database_adaptor & adaptor,
318 temp_node_id_source & nis);
319
320 void report_duplicate_name_conflicts(roster_t const & left,
321 roster_t const & right,
322 content_merge_adaptor & adaptor,
323 bool const basic_io,
324 std::ostream & output) const;
325 void resolve_duplicate_name_conflicts(lua_hooks & lua,
326 roster_t const & left_roster,
327 roster_t const & right_roster,
328 content_merge_adaptor & adaptor);
329
330 void report_attribute_conflicts(roster_t const & left,
331 roster_t const & right,
332 content_merge_adaptor & adaptor,
333 bool const basic_io,
334 std::ostream & output) const;
335
336 // not 'const' because this sets resolution to 'resolved_internal' if the
337 // internal merger would succeed.
338 void report_file_content_conflicts(lua_hooks & lua,
339 roster_t const & left_roster,
340 roster_t const & right_roster,
341 content_merge_adaptor & adaptor,
342 bool const basic_io,
343 std::ostream & output);
344 void resolve_file_content_conflicts(lua_hooks & lua,
345 roster_t const & left_roster,
346 roster_t const & right_roster,
347 content_merge_adaptor & adaptor);
348 void clear();
349
350 // Conflict file editing
351
352 // If validate, compare file contents to existing conflicts, and add
353 // resolutions. Otherwise just read into conflicts.
354 void read_conflict_file(database & db,
355 bookkeeping_path const & file_name,
356 revision_id & ancestor_rid,
357 revision_id & left_rid,
358 revision_id & right_rid,
359 roster_t & left_roster,
360 marking_map & left_marking,
361 roster_t & right_roster,
362 marking_map & r_marking);
363
364 void write_conflict_file(database & db,
365 lua_hooks & lua,
366 bookkeeping_path const & file_name,
367 revision_id const & ancestor_rid,
368 revision_id const & left_rid,
369 revision_id const & right_rid,
370 boost::shared_ptr<roster_t> left_roster,
371 marking_map const & left_marking,
372 boost::shared_ptr<roster_t> right_roster,
373 marking_map const & right_marking);
374};
375
376template <> void dump(roster_merge_result const & result, std::string & out);
377
378void
379roster_merge(roster_t const & left_parent,
380 marking_map const & left_markings,
381 std::set<revision_id> const & left_uncommon_ancestors,
382 roster_t const & right_parent,
383 marking_map const & right_markings,
384 std::set<revision_id> const & right_uncommon_ancestors,
385 roster_merge_result & result);
386
387void
388parse_resolve_conflicts_opts (options const & opts,
389 revision_id const & left_rid,
390 roster_t const & left_roster,
391 revision_id const & right_rid,
392 roster_t const & right_roster,
393 roster_merge_result & result,
394 bool & resolutions_given);
395
396#endif
397
398// Local Variables:
399// mode: C++
400// fill-column: 76
401// c-file-style: "gnu"
402// indent-tabs-mode: nil
403// End:
404// 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