monotone

monotone Mtn Source Tree

Root/cmd_merging.cc

1// Copyright (C) 2008 Stephen Leake <stephen_leake@stephe-leake.org>
2// Copyright (C) 2002 Graydon Hoare <graydon@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 <cstring>
13#include <iostream>
14#include <iomanip>
15
16#include "basic_io.hh"
17#include "cmd.hh"
18#include "diff_patch.hh"
19#include "merge.hh"
20#include "restrictions.hh"
21#include "revision.hh"
22#include "roster_merge.hh"
23#include "transforms.hh"
24#include "update.hh"
25#include "work.hh"
26#include "safe_map.hh"
27#include "ui.hh"
28#include "app_state.hh"
29#include "project.hh"
30#include "simplestring_xform.hh"
31#include "keys.hh"
32#include "key_store.hh"
33#include "database.hh"
34
35using std::cout;
36using std::make_pair;
37using std::map;
38using std::set;
39using std::string;
40using std::vector;
41using std::strlen;
42
43using boost::shared_ptr;
44
45static void
46three_way_merge(revision_id const & ancestor_rid, roster_t const & ancestor_roster,
47 revision_id const & left_rid, roster_t const & left_roster,
48 revision_id const & right_rid, roster_t const & right_roster,
49 roster_merge_result & result,
50 marking_map & left_markings,
51 marking_map & right_markings)
52{
53 MM(ancestor_roster);
54 MM(left_roster);
55 MM(right_roster);
56
57 MM(ancestor_rid);
58 MM(left_rid);
59 MM(right_rid);
60
61 // Mark up the ANCESTOR
62 marking_map ancestor_markings; MM(ancestor_markings);
63 mark_roster_with_no_parents(ancestor_rid, ancestor_roster, ancestor_markings);
64
65 // Mark up the LEFT roster
66 left_markings.clear();
67 MM(left_markings);
68 mark_roster_with_one_parent(ancestor_roster, ancestor_markings,
69 left_rid, left_roster, left_markings);
70
71 // Mark up the RIGHT roster
72 right_markings.clear();
73 MM(right_markings);
74 mark_roster_with_one_parent(ancestor_roster, ancestor_markings,
75 right_rid, right_roster, right_markings);
76
77 // Make the synthetic graph, by creating uncommon ancestor sets
78 std::set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
79 safe_insert(left_uncommon_ancestors, left_rid);
80 safe_insert(right_uncommon_ancestors, right_rid);
81
82 P(F("[left] %s") % left_rid);
83 P(F("[right] %s") % right_rid);
84
85 // And do the merge
86 roster_merge(left_roster, left_markings, left_uncommon_ancestors,
87 right_roster, right_markings, right_uncommon_ancestors,
88 result);
89}
90
91static bool
92pick_branch_for_update(options & opts, database & db, revision_id chosen_rid)
93{
94 bool switched_branch = false;
95
96 // figure out which branches the target is in
97 vector< revision<cert> > certs;
98 db.get_revision_certs(chosen_rid, branch_cert_name, certs);
99 erase_bogus_certs(db, certs);
100
101 set< branch_name > branches;
102 for (vector< revision<cert> >::const_iterator i = certs.begin();
103 i != certs.end(); i++)
104 branches.insert(branch_name(i->inner().value()));
105
106 if (branches.find(opts.branchname) != branches.end())
107 {
108 L(FL("using existing branch %s") % opts.branchname());
109 }
110 else
111 {
112 P(F("target revision is not in current branch"));
113 if (branches.size() > 1)
114 {
115 // multiple non-matching branchnames
116 string branch_list;
117 for (set<branch_name>::const_iterator i = branches.begin();
118 i != branches.end(); i++)
119 branch_list += "\n " + (*i)();
120 N(false, F("target revision is in multiple branches:%s\n\n"
121 "try again with explicit --branch") % branch_list);
122 }
123 else if (branches.size() == 1)
124 {
125 // one non-matching, inform and update
126 opts.branchname = *(branches.begin());
127 switched_branch = true;
128 }
129 else
130 {
131 I(branches.empty());
132 W(F("target revision not in any branch\n"
133 "next commit will use branch %s")
134 % opts.branchname);
135 }
136 }
137 return switched_branch;
138}
139
140CMD(update, "update", "", CMD_REF(workspace), "",
141 N_("Updates the workspace"),
142 N_("This command modifies your workspace to be based off of a "
143 "different revision, preserving uncommitted changes as it does so. "
144 "If a revision is given, update the workspace to that revision. "
145 "If not, update the workspace to the head of the branch."),
146 options::opts::branch | options::opts::revision)
147{
148 if (!args.empty())
149 throw usage(execid);
150
151 if (app.opts.revision_selectors.size() > 1)
152 throw usage(execid);
153
154 database db(app);
155 workspace work(app);
156 project_t project(db);
157
158 // Figure out where we are
159 parent_map parents;
160 work.get_parent_rosters(db, parents);
161
162 N(parents.size() == 1,
163 F("this command can only be used in a single-parent workspace"));
164
165 revision_id old_rid = parent_id(parents.begin());
166 N(!null_id(old_rid),
167 F("this workspace is a new project; cannot update"));
168
169 // Figure out where we're going
170 N(!app.opts.branchname().empty(),
171 F("cannot determine branch for update"));
172
173 revision_id chosen_rid;
174 if (app.opts.revision_selectors.empty())
175 {
176 P(F("updating along branch '%s'") % app.opts.branchname);
177 set<revision_id> candidates;
178 pick_update_candidates(app.lua, project, candidates, old_rid,
179 app.opts.branchname,
180 app.opts.ignore_suspend_certs);
181 N(!candidates.empty(),
182 F("your request matches no descendents of the current revision\n"
183 "in fact, it doesn't even match the current revision\n"
184 "maybe you want something like --revision=h:%s")
185 % app.opts.branchname);
186 if (candidates.size() != 1)
187 {
188 P(F("multiple update candidates:"));
189 for (set<revision_id>::const_iterator i = candidates.begin();
190 i != candidates.end(); ++i)
191 P(i18n_format(" %s")
192 % describe_revision(project, *i));
193 P(F("choose one with '%s update -r<id>'") % ui.prog_name);
194 E(false, F("multiple update candidates remain after selection"));
195 }
196 chosen_rid = *(candidates.begin());
197 }
198 else
199 {
200 complete(app.opts, app.lua, project, app.opts.revision_selectors[0](), chosen_rid);
201 }
202 I(!null_id(chosen_rid));
203
204 // do this notification before checking to see if we can bail out early,
205 // because when you are at one of several heads, and you hit update, you
206 // want to know that merging would let you update further.
207 notify_if_multiple_heads(project,
208 app.opts.branchname, app.opts.ignore_suspend_certs);
209
210 if (old_rid == chosen_rid)
211 {
212 P(F("already up to date at %s") % old_rid);
213 // do still switch the workspace branch, in case they have used
214 // update to switch branches.
215 work.set_ws_options(app.opts, true);
216 return;
217 }
218
219 P(F("selected update target %s") % chosen_rid);
220
221 // Fiddle around with branches, in an attempt to guess what the user
222 // wants.
223 bool switched_branch = pick_branch_for_update(app.opts, db, chosen_rid);
224 if (switched_branch)
225 P(F("switching to branch %s") % app.opts.branchname());
226
227 // Okay, we have a target, we have a branch, let's do this merge!
228
229 // We have:
230 //
231 // old --> working
232 // | |
233 // V V
234 // chosen --> merged
235 //
236 // - old is the revision specified in _MTN/revision
237 // - working is based on old and includes the workspace's changes
238 // - chosen is the revision we're updating to and will end up in _MTN/revision
239 // - merged is the merge of working and chosen, that will become the new
240 // workspace
241 //
242 // we apply the working to merged cset to the workspace
243 // and write the cset from chosen to merged changeset in _MTN/work
244
245 temp_node_id_source nis;
246
247 // Get the OLD and WORKING rosters
248 roster_t_cp old_roster
249 = parent_cached_roster(parents.begin()).first;
250 MM(*old_roster);
251
252 shared_ptr<roster_t> working_roster = shared_ptr<roster_t>(new roster_t());
253
254 MM(*working_roster);
255 work.get_current_roster_shape(db, nis, *working_roster);
256 work.update_current_roster_from_filesystem(*working_roster);
257
258 revision_t working_rev;
259 revision_id working_rid;
260 make_revision_for_workspace(parents, *working_roster, working_rev);
261 calculate_ident(working_rev, working_rid);
262
263 // Get the CHOSEN roster
264 roster_t chosen_roster; MM(chosen_roster);
265 db.get_roster(chosen_rid, chosen_roster);
266
267
268 // And finally do the merge
269 roster_merge_result result;
270 marking_map left_markings, right_markings;
271 three_way_merge(old_rid, *old_roster,
272 working_rid, *working_roster,
273 chosen_rid, chosen_roster,
274 result, left_markings, right_markings);
275
276 roster_t & merged_roster = result.roster;
277
278 map<file_id, file_path> paths;
279 get_content_paths(*working_roster, paths);
280
281 content_merge_workspace_adaptor wca(db, old_rid, old_roster,
282 left_markings, right_markings, paths);
283 wca.cache_roster(working_rid, working_roster);
284 resolve_merge_conflicts(app.lua, *working_roster, chosen_roster,
285 result, wca);
286
287 // Make sure it worked...
288 I(result.is_clean());
289 merged_roster.check_sane(true);
290
291 // Now finally modify the workspace
292 cset update;
293 make_cset(*working_roster, merged_roster, update);
294 work.perform_content_update(db, update, wca);
295
296 revision_t remaining;
297 make_revision_for_workspace(chosen_rid, chosen_roster,
298 merged_roster, remaining);
299
300 // small race condition here...
301 work.put_work_rev(remaining);
302 work.update_any_attrs(db);
303 work.maybe_update_inodeprints(db);
304 work.set_ws_options(app.opts, true);
305
306 if (switched_branch)
307 P(F("switched branch; next commit will use branch %s") % app.opts.branchname());
308 P(F("updated to base revision %s") % chosen_rid);
309}
310
311// Subroutine of CMD(merge) and CMD(explicit_merge). Merge LEFT with RIGHT,
312// placing results onto BRANCH. Note that interactive_merge_and_store may
313// bomb out, and therefore so may this.
314static void
315merge_two(options & opts, lua_hooks & lua, project_t & project,
316 key_store & keys,
317 revision_id const & left, revision_id const & right,
318 branch_name const & branch, string const & caller,
319 std::ostream & output, bool automate)
320{
321 // The following mess constructs a neatly formatted log message that looks
322 // like this:
323 // CALLER of 'LEFT'
324 // and 'RIGHT'
325 // to branch 'BRANCH'
326 // where the last line is left out if we're merging onto the current branch.
327 // We use a stringstream because boost::format does not support %-*s.
328 using std::ostringstream;
329 using std::setw;
330 using std::max;
331
332 ostringstream log;
333 size_t fieldwidth = max(caller.size() + strlen(" of '"), strlen("and '"));
334
335 if (branch != opts.branchname)
336 fieldwidth = max(fieldwidth, strlen("to branch '"));
337
338 log << setw(fieldwidth - strlen(" of '")) << caller << " of '" << left
339 << "'\n" << setw(fieldwidth) << "and '" << right
340 << "'\n";
341
342 if (branch != opts.branchname)
343 log << setw(fieldwidth) << "to branch '" << branch << "'\n";
344
345 // Now it's time for the real work.
346 if (automate)
347 {
348 output << left << " " << right << " ";
349 }
350 else
351 {
352 P(F("[left] %s") % left);
353 P(F("[right] %s") % right);
354 }
355
356 revision_id merged;
357 transaction_guard guard(project.db);
358 interactive_merge_and_store(lua, project.db, left, right, merged);
359
360 project.put_standard_certs_from_options(opts, lua, keys, merged, branch,
361 utf8(log.str()));
362
363 guard.commit();
364 if (automate)
365 output << merged << "\n";
366 else
367 P(F("[merged] %s") % merged);
368}
369
370typedef std::pair<revision_id, revision_id> revpair;
371typedef set<revision_id>::const_iterator rid_set_iter;
372
373// Subroutine of 'merge' and 'automate show_conflicts'; find first pair of
374// heads to merge.
375static revpair
376find_heads_to_merge(database & db, set<revision_id> const heads)
377{
378 I(heads.size() > 2);
379 map<revision_id, revpair> heads_for_ancestor;
380 set<revision_id> ancestors;
381
382 // For every pair of heads, determine their merge ancestor, and
383 // remember the ancestor->head mapping.
384 for (rid_set_iter i = heads.begin(); i != heads.end(); ++i)
385 for (rid_set_iter j = i; j != heads.end(); ++j)
386 {
387 // It is not possible to initialize j to i+1 (set iterators
388 // expose neither operator+ nor a nondestructive next() method)
389 if (j == i)
390 continue;
391
392 revision_id ancestor;
393 find_common_ancestor_for_merge(db, *i, *j, ancestor);
394
395 // More than one pair might have the same ancestor (e.g. if we
396 // have three heads all with the same parent); as this table
397 // will be recalculated on every pass, we just take the first
398 // one we find.
399 if (ancestors.insert(ancestor).second)
400 safe_insert(heads_for_ancestor, std::make_pair(ancestor, revpair(*i, *j)));
401 }
402
403 // Erasing ancestors from ANCESTORS will now produce a set of merge
404 // ancestors each of which is not itself an ancestor of any other
405 // merge ancestor.
406 erase_ancestors(db, ancestors);
407 I(!ancestors.empty());
408
409 // Take the first ancestor from the above set.
410 return heads_for_ancestor[*ancestors.begin()];
411}
412
413// should merge support --message, --message-file? It seems somewhat weird,
414// since a single 'merge' command may perform arbitrarily many actual merges.
415// (Possibility: append the --message/--message-file text to the synthetic
416// log message constructed in merge_two().)
417CMD(merge, "merge", "", CMD_REF(tree), "",
418 N_("Merges unmerged heads of a branch"),
419 "",
420 options::opts::branch | options::opts::date | options::opts::author)
421{
422 database db(app);
423 key_store keys(app);
424 project_t project(db);
425
426 if (!args.empty())
427 throw usage(execid);
428
429 N(app.opts.branchname() != "",
430 F("please specify a branch, with --branch=BRANCH"));
431
432 set<revision_id> heads;
433 project.get_branch_heads(app.opts.branchname, heads,
434 app.opts.ignore_suspend_certs);
435
436 N(!heads.empty(), F("branch '%s' is empty") % app.opts.branchname);
437 if (heads.size() == 1)
438 {
439 P(F("branch '%s' is already merged") % app.opts.branchname);
440 return;
441 }
442
443 P(FP("%d head on branch '%s'", "%d heads on branch '%s'", heads.size())
444 % heads.size() % app.opts.branchname);
445
446 // avoid failure after lots of work
447 cache_user_key(app.opts, app.lua, db, keys);
448
449 size_t pass = 1, todo = heads.size() - 1;
450
451 // If there are more than two heads to be merged, on each iteration we
452 // merge a pair whose least common ancestor is not an ancestor of any
453 // other pair's least common ancestor. For example, if the history graph
454 // looks like this:
455 //
456 // X
457 // / \. (periods to prevent multi-line
458 // Y C comment warnings)
459 // / \.
460 // A B
461 //
462 // A and B will be merged first, and then the result will be merged with C.
463 while (heads.size() > 2)
464 {
465 P(F("merge %d / %d:") % pass % todo);
466 P(F("calculating best pair of heads to merge next"));
467
468 revpair p = find_heads_to_merge(db, heads);
469
470 merge_two(app.opts, app.lua, project, keys,
471 p.first, p.second, app.opts.branchname, string("merge"),
472 std::cout, false);
473
474 project.get_branch_heads(app.opts.branchname, heads,
475 app.opts.ignore_suspend_certs);
476 pass++;
477 }
478
479 // Last one.
480 I(pass == todo);
481 if (todo > 1)
482 P(F("merge %d / %d:") % pass % todo);
483
484 rid_set_iter i = heads.begin();
485 revision_id left = *i++;
486 revision_id right = *i++;
487 I(i == heads.end());
488
489 merge_two(app.opts, app.lua, project, keys,
490 left, right, app.opts.branchname, string("merge"),
491 std::cout, false);
492 P(F("note: your workspaces have not been updated"));
493}
494
495CMD(propagate, "propagate", "", CMD_REF(tree),
496 N_("SOURCE-BRANCH DEST-BRANCH"),
497 N_("Merges from one branch to another asymmetrically"),
498 "",
499 options::opts::date | options::opts::author | options::opts::message | options::opts::msgfile)
500{
501 if (args.size() != 2)
502 throw usage(execid);
503 args_vector a = args;
504 a.push_back(arg_type());
505 process(app, make_command_id("tree merge_into_dir"), a);
506}
507
508// This is a special merge operator, but very useful for people
509// maintaining "slightly disparate but related" trees. It does a one-way
510// merge; less powerful than putting things in the same branch and also
511// more flexible.
512//
513// 1. Check to see if src and dst branches are merged, if not abort, if so
514// call heads N1 and N2 respectively.
515//
516// 2. (FIXME: not yet present) Run the hook propagate ("src-branch",
517// "dst-branch", N1, N2) which gives the user a chance to massage N1 into
518// a state which is likely to "merge nicely" with N2, eg. edit pathnames,
519// omit optional files of no interest.
520//
521// 3. Do a normal 2 or 3-way merge on N1 and N2, depending on the
522// existence of common ancestors.
523//
524// 4. Save the results as the delta (N2,M), the ancestry edges (N1,M)
525// and (N2,M), and the cert (N2,dst).
526//
527// There are also special cases we have to check for where no merge is
528// actually necessary, because there hasn't been any divergence since the
529// last time propagate was run.
530//
531// If dir is not the empty string, rename the root of N1 to have the name
532// 'dir' in the merged tree. (ie, it has name "basename(dir)", and its
533// parent node is "N2.get_node(dirname(dir))")
534CMD(merge_into_dir, "merge_into_dir", "", CMD_REF(tree),
535 N_("SOURCE-BRANCH DEST-BRANCH DIR"),
536 N_("Merges one branch into a subdirectory in another branch"),
537 "",
538 options::opts::date | options::opts::author | options::opts::message | options::opts::msgfile)
539{
540 database db(app);
541 key_store keys(app);
542 project_t project(db);
543 set<revision_id> src_heads, dst_heads;
544
545 if (args.size() != 3)
546 throw usage(execid);
547
548 project.get_branch_heads(branch_name(idx(args, 0)()), src_heads,
549 app.opts.ignore_suspend_certs);
550 project.get_branch_heads(branch_name(idx(args, 1)()), dst_heads,
551 app.opts.ignore_suspend_certs);
552
553 N(src_heads.size() != 0, F("branch '%s' is empty") % idx(args, 0)());
554 N(src_heads.size() == 1, F("branch '%s' is not merged") % idx(args, 0)());
555
556 N(dst_heads.size() != 0, F("branch '%s' is empty") % idx(args, 1)());
557 N(dst_heads.size() == 1, F("branch '%s' is not merged") % idx(args, 1)());
558
559 set<revision_id>::const_iterator src_i = src_heads.begin();
560 set<revision_id>::const_iterator dst_i = dst_heads.begin();
561
562 if (*src_i == *dst_i || is_ancestor(db, *src_i, *dst_i))
563 {
564 P(F("branch '%s' is up-to-date with respect to branch '%s'")
565 % idx(args, 1)() % idx(args, 0)());
566 P(F("no action taken"));
567 return;
568 }
569
570 cache_user_key(app.opts, app.lua, db, keys);
571
572 P(F("propagating %s -> %s") % idx(args,0) % idx(args,1));
573 P(F("[left] %s") % *src_i);
574 P(F("[right] %s") % *dst_i);
575
576 // check for special cases
577 if (is_ancestor(db, *dst_i, *src_i))
578 {
579 P(F("no merge necessary; putting %s in branch '%s'")
580 % *src_i % idx(args, 1)());
581 transaction_guard guard(db);
582 project.put_revision_in_branch(keys, *src_i,
583 branch_name(idx(args, 1)()));
584 guard.commit();
585 }
586 else
587 {
588 revision_id merged;
589 transaction_guard guard(db);
590
591 {
592 revision_id const & left_rid(*src_i), & right_rid(*dst_i);
593 roster_t left_roster, right_roster;
594 MM(left_roster);
595 MM(right_roster);
596 marking_map left_marking_map, right_marking_map;
597 set<revision_id>
598 left_uncommon_ancestors,
599 right_uncommon_ancestors;
600
601 db.get_roster(left_rid, left_roster, left_marking_map);
602 db.get_roster(right_rid, right_roster, right_marking_map);
603 db.get_uncommon_ancestors(left_rid, right_rid,
604 left_uncommon_ancestors,
605 right_uncommon_ancestors);
606
607 if (!idx(args,2)().empty())
608 {
609 dir_t moved_root = left_roster.root();
610 file_path pth = file_path_external(idx(args, 2));
611 file_path dir;
612 path_component base;
613 MM(dir);
614 pth.dirname_basename(dir, base);
615
616 N(right_roster.has_node(dir),
617 F("Path %s not found in destination tree.") % pth);
618 node_t parent = right_roster.get_node(dir);
619 moved_root->parent = parent->self;
620 moved_root->name = base;
621 marking_map::iterator
622 i = left_marking_map.find(moved_root->self);
623 I(i != left_marking_map.end());
624 i->second.parent_name.clear();
625 i->second.parent_name.insert(left_rid);
626 }
627
628 roster_merge_result result;
629 roster_merge(left_roster,
630 left_marking_map,
631 left_uncommon_ancestors,
632 right_roster,
633 right_marking_map,
634 right_uncommon_ancestors,
635 result);
636
637 content_merge_database_adaptor
638 dba(db, left_rid, right_rid, left_marking_map, right_marking_map);
639
640 resolve_merge_conflicts(app.lua, left_roster, right_roster,
641 result, dba);
642
643 {
644 dir_t moved_root = left_roster.root();
645 moved_root->parent = the_null_node;
646 moved_root->name = path_component();
647 }
648
649 // Write new files into the db.
650 store_roster_merge_result(db, left_roster, right_roster, result,
651 left_rid, right_rid, merged);
652 }
653
654 bool log_message_given;
655 utf8 log_message;
656 process_commit_message_args(app.opts, log_message_given, log_message);
657 if (!log_message_given)
658 log_message = utf8((FL("propagate from branch '%s' (head %s)\n"
659 " to branch '%s' (head %s)\n")
660 % idx(args, 0)
661 % *src_i
662 % idx(args, 1)
663 % *dst_i).str());
664
665 project.put_standard_certs_from_options(app.opts, app.lua,
666 keys,
667 merged,
668 branch_name(idx(args, 1)()),
669 log_message);
670
671 guard.commit();
672 P(F("[merged] %s") % merged);
673 }
674}
675
676CMD(merge_into_workspace, "merge_into_workspace", "", CMD_REF(tree),
677 N_("OTHER-REVISION"),
678 N_("Merges a revision into the current workspace's base revision"),
679 N_("Merges OTHER-REVISION into the current workspace's base revision, "
680 "and update the current workspace with the result. There can be no "
681 "pending changes in the current workspace. Both OTHER-REVISION and "
682 "the workspace's base revision will be recorded as parents on commit. "
683 "The workspace's selected branch is not changed."),
684 options::opts::none)
685{
686 revision_id left_id, right_id;
687 cached_roster left, right;
688 shared_ptr<roster_t> working_roster = shared_ptr<roster_t>(new roster_t());
689
690 if (args.size() != 1)
691 throw usage(execid);
692
693 database db(app);
694 workspace work(app);
695 project_t project(db);
696
697 // Get the current state of the workspace.
698
699 // This command cannot be applied to a workspace with more than one parent
700 // (revs can have no more than two parents).
701 revision_id working_rid;
702
703 {
704 parent_map parents;
705 work.get_parent_rosters(db, parents);
706 N(parents.size() == 1,
707 F("this command can only be used in a single-parent workspace"));
708
709 temp_node_id_source nis;
710 work.get_current_roster_shape(db, nis, *working_roster);
711 work.update_current_roster_from_filesystem(*working_roster);
712
713 N(parent_roster(parents.begin()) == *working_roster,
714 F("'%s' can only be used in a workspace with no pending changes") %
715 join_words(execid)());
716
717 left_id = parent_id(parents.begin());
718 left = parent_cached_roster(parents.begin());
719
720 revision_t working_rev;
721 make_revision_for_workspace(parents, *working_roster, working_rev);
722 calculate_ident(working_rev, working_rid);
723 }
724
725 complete(app.opts, app.lua, project, idx(args, 0)(), right_id);
726 db.get_roster(right_id, right);
727 N(!(left_id == right_id),
728 F("workspace is already at revision %s") % left_id);
729
730 P(F("[left] %s") % left_id);
731 P(F("[right] %s") % right_id);
732
733 set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
734 db.get_uncommon_ancestors(left_id, right_id,
735 left_uncommon_ancestors,
736 right_uncommon_ancestors);
737
738 roster_merge_result merge_result;
739 MM(merge_result);
740 roster_merge(*left.first, *left.second, left_uncommon_ancestors,
741 *right.first, *right.second, right_uncommon_ancestors,
742 merge_result);
743
744 revision_id lca_id;
745 cached_roster lca;
746 find_common_ancestor_for_merge(db, left_id, right_id, lca_id);
747 db.get_roster(lca_id, lca);
748
749 map<file_id, file_path> paths;
750 get_content_paths(*working_roster, paths);
751
752 content_merge_workspace_adaptor wca(db, lca_id, lca.first,
753 *left.second, *right.second, paths);
754 wca.cache_roster(working_rid, working_roster);
755 resolve_merge_conflicts(app.lua, *left.first, *right.first, merge_result, wca);
756
757 // Make sure it worked...
758 I(merge_result.is_clean());
759 merge_result.roster.check_sane(true);
760
761 // Construct the workspace revision.
762 parent_map parents;
763 safe_insert(parents, std::make_pair(left_id, left));
764 safe_insert(parents, std::make_pair(right_id, right));
765
766 revision_t merged_rev;
767 make_revision_for_workspace(parents, merge_result.roster, merged_rev);
768
769 // Note: the csets in merged_rev are _not_ suitable for submission to
770 // perform_content_update, because content changes have been dropped.
771 cset update;
772 make_cset(*left.first, merge_result.roster, update);
773
774 // small race condition here...
775 work.perform_content_update(db, update, wca);
776 work.put_work_rev(merged_rev);
777 work.update_any_attrs(db);
778 work.maybe_update_inodeprints(db);
779
780 P(F("updated to result of merge\n"
781 " [left] %s\n"
782 "[right] %s\n")
783 % left_id
784 % right_id);
785}
786
787CMD(explicit_merge, "explicit_merge", "", CMD_REF(tree),
788 N_("LEFT-REVISION RIGHT-REVISION DEST-BRANCH"),
789 N_("Merges two explicitly given revisions"),
790 N_("The results of the merge are placed on the branch specified by "
791 "DEST-BRANCH."),
792 options::opts::date | options::opts::author)
793{
794 database db(app);
795 key_store keys(app);
796 project_t project(db);
797 revision_id left, right;
798 branch_name branch;
799
800 if (args.size() != 3)
801 throw usage(execid);
802
803 complete(app.opts, app.lua, project, idx(args, 0)(), left);
804 complete(app.opts, app.lua, project, idx(args, 1)(), right);
805 branch = branch_name(idx(args, 2)());
806
807 N(!(left == right),
808 F("%s and %s are the same revision, aborting")
809 % left % right);
810 N(!is_ancestor(db, left, right),
811 F("%s is already an ancestor of %s")
812 % left % right);
813 N(!is_ancestor(db, right, left),
814 F("%s is already an ancestor of %s")
815 % right % left);
816
817 // avoid failure after lots of work
818 cache_user_key(app.opts, app.lua, db, keys);
819 merge_two(app.opts, app.lua, project, keys,
820 left, right, branch, string("explicit merge"),
821 std::cout, false);
822}
823
824namespace
825{
826 namespace syms
827 {
828 symbol const ancestor("ancestor");
829 symbol const left("left");
830 symbol const right("right");
831 }
832}
833
834static void
835show_conflicts_core (database & db, revision_id const & l_id, revision_id const & r_id, bool const basic_io, std::ostream & output)
836{
837 N(!is_ancestor(db, l_id, r_id),
838 F("%s is an ancestor of %s; no merge is needed.")
839 % l_id % r_id);
840 N(!is_ancestor(db, r_id, l_id),
841 F("%s is an ancestor of %s; no merge is needed.")
842 % r_id % l_id);
843 shared_ptr<roster_t> l_roster = shared_ptr<roster_t>(new roster_t());
844 shared_ptr<roster_t> r_roster = shared_ptr<roster_t>(new roster_t());
845 marking_map l_marking, r_marking;
846 db.get_roster(l_id, *l_roster, l_marking);
847 db.get_roster(r_id, *r_roster, r_marking);
848 set<revision_id> l_uncommon_ancestors, r_uncommon_ancestors;
849 db.get_uncommon_ancestors(l_id, r_id, l_uncommon_ancestors, r_uncommon_ancestors);
850 roster_merge_result result;
851 roster_merge(*l_roster, l_marking, l_uncommon_ancestors,
852 *r_roster, r_marking, r_uncommon_ancestors,
853 result);
854
855 // note that left and right are in the order specified on the command line
856 // they are not in lexical order as they are with other merge commands so
857 // they may appear swapped here. The user may have done that deliberately,
858 // especially via automate, so we don't sort them here.
859
860 basic_io::stanza st;
861
862 if (basic_io)
863 {
864 st.push_binary_pair(syms::left, l_id.inner());
865 st.push_binary_pair(syms::right, r_id.inner());
866 }
867 else
868 {
869 P(F("[left] %s") % l_id);
870 P(F("[right] %s") % r_id);
871 }
872
873 if (result.is_clean())
874 {
875 if (basic_io)
876 {
877 basic_io::printer pr;
878 pr.print_stanza(st);
879 output.write(pr.buf.data(), pr.buf.size());
880 }
881 else
882 P(F("no conflicts detected"));
883 }
884 else
885 {
886 content_merge_database_adaptor adaptor(db, l_id, r_id,
887 l_marking, r_marking);
888
889 {
890 basic_io::printer pr;
891 st.push_binary_pair(syms::ancestor, adaptor.lca.inner());
892 pr.print_stanza(st);
893 output.write(pr.buf.data(), pr.buf.size());
894 }
895
896 // The basic_io routines in roster_merge.cc access these rosters via
897 // the adaptor.
898 adaptor.cache_roster (l_id, l_roster);
899 adaptor.cache_roster (r_id, r_roster);
900
901 result.report_missing_root_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
902 result.report_invalid_name_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
903 result.report_directory_loop_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
904
905 result.report_orphaned_node_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
906 result.report_multiple_name_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
907 result.report_duplicate_name_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
908
909 result.report_attribute_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
910 result.report_file_content_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
911 }
912}
913
914CMD(show_conflicts, "show_conflicts", "", CMD_REF(informative), N_("REV REV"),
915 N_("Shows what conflicts need resolution between two revisions"),
916 N_("The conflicts are calculated based on the two revisions given in "
917 "the REV parameters."),
918 options::opts::none)
919{
920 database db(app);
921 project_t project(db);
922
923 if (args.size() != 2)
924 throw usage(execid);
925 revision_id l_id, r_id;
926 complete(app.opts, app.lua, project, idx(args,0)(), l_id);
927 complete(app.opts, app.lua, project, idx(args,1)(), r_id);
928
929 show_conflicts_core(db, l_id, r_id, false, std::cout);
930}
931
932// Name: show_conflicts
933// Arguments:
934// Two revision ids (optional, determined from the workspace if not given; there must be exactly two heads)
935// Added in: 7.1
936// Purpose: Prints the conflicts between two revisions, to aid in merging them.
937//
938// Output format: see monotone.texi
939//
940// Error conditions:
941//
942// If the revision IDs are unknown or invalid prints an error message to
943// stderr and exits with status 1.
944//
945// If revision ids are not given, and the current workspace does not have
946// two heads, prints an error message to stderr and exits with status 1.
947//
948CMD_AUTOMATE(show_conflicts, N_("[LEFT_REVID RIGHT_REVID]"),
949 N_("Shows the conflicts between two revisions."),
950 N_("If no arguments are given, LEFT_REVID and RIGHT_REVID default to the "
951 "first two heads that would be chosen by the 'merge' command."),
952 options::opts::branch)
953{
954 database db(app);
955 project_t project(db);
956 revision_id l_id, r_id;
957
958 if (args.empty())
959 {
960 // get ids from heads
961 N(app.opts.branchname() != "",
962 F("please specify a branch, with --branch=BRANCH"));
963
964 set<revision_id> heads;
965 project.get_branch_heads(app.opts.branchname, heads,
966 app.opts.ignore_suspend_certs);
967
968 N(heads.size() >= 2,
969 F("branch '%s' has %d heads; must be at least 2 for show_conflicts") % app.opts.branchname % heads.size());
970
971 if (heads.size() == 2)
972 {
973 set<revision_id>::const_iterator i = heads.begin();
974 l_id = *i;
975 ++i;
976 r_id = *i;
977 }
978 else
979 {
980 revpair p = find_heads_to_merge (db, heads);
981 l_id = p.first;
982 r_id = p.second;
983 }
984 }
985 else if (args.size() == 2)
986 {
987 // get ids from args
988 complete(app.opts, app.lua, project, idx(args,0)(), l_id);
989 complete(app.opts, app.lua, project, idx(args,1)(), r_id);
990 }
991 else
992 N(false, F("wrong argument count"));
993
994 show_conflicts_core(db, l_id, r_id, true, output);
995}
996
997CMD(pluck, "pluck", "", CMD_REF(workspace), N_("[-r FROM] -r TO [PATH...]"),
998 N_("Applies changes made at arbitrary places in history"),
999 N_("This command takes changes made at any point in history, and "
1000 "edits your current workspace to include those changes. The end result "
1001 "is identical to 'mtn diff -r FROM -r TO | patch -p0', except that "
1002 "this command uses monotone's merger, and thus intelligently handles "
1003 "renames, conflicts, and so on.\n"
1004 "If one revision is given, applies the changes made in that revision "
1005 "compared to its parent.\n"
1006 "If two revisions are given, applies the changes made to get from the "
1007 "first revision to the second."),
1008 options::opts::revision | options::opts::depth | options::opts::exclude)
1009{
1010 database db(app);
1011 workspace work(app);
1012 project_t project(db);
1013
1014 // Work out our arguments
1015 revision_id from_rid, to_rid;
1016 if (app.opts.revision_selectors.size() == 1)
1017 {
1018 complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), to_rid);
1019 std::set<revision_id> parents;
1020 db.get_revision_parents(to_rid, parents);
1021 N(parents.size() == 1,
1022 F("revision %s is a merge\n"
1023 "to apply the changes relative to one of its parents, use:\n"
1024 " %s pluck -r PARENT -r %s")
1025 % to_rid
1026 % ui.prog_name
1027 % to_rid);
1028 from_rid = *parents.begin();
1029 }
1030 else if (app.opts.revision_selectors.size() == 2)
1031 {
1032 complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), from_rid);
1033 complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 1)(), to_rid);
1034 }
1035 else
1036 throw usage(execid);
1037
1038 N(!(from_rid == to_rid), F("no changes to apply"));
1039
1040 // notionally, we have the situation
1041 //
1042 // from --> working
1043 // | |
1044 // V V
1045 // to --> merged
1046 //
1047 // - from is the revision we start plucking from
1048 // - to is the revision we stop plucking at
1049 // - working is the current contents of the workspace
1050 // - merged is the result of the plucking, and achieved by running a
1051 // merge in the fictional graph seen above
1052 //
1053 // To perform the merge, we use the real from roster, and the real working
1054 // roster, but synthesize a temporary 'to' roster. This ensures that the
1055 // 'from', 'working' and 'base' rosters all use the same nid namespace,
1056 // while any additions that happened between 'from' and 'to' should be
1057 // considered as new nodes, even if the file that was added is in fact in
1058 // 'working' already -- so 'to' needs its own namespace. (Among other
1059 // things, it is impossible with our merge formalism to have the above
1060 // graph with a node that exists in 'to' and 'working', but not 'from'.)
1061 //
1062 // finally, we take the cset from working -> merged, and apply that to the
1063 // workspace
1064 // and take the cset from the workspace's base, and write that to _MTN/work
1065
1066 // The node id source we'll use for the 'working' and 'to' rosters.
1067 temp_node_id_source nis;
1068
1069 // Get the FROM roster
1070 shared_ptr<roster_t> from_roster = shared_ptr<roster_t>(new roster_t());
1071 MM(*from_roster);
1072 db.get_roster(from_rid, *from_roster);
1073
1074 // Get the WORKING roster
1075 shared_ptr<roster_t> working_roster = shared_ptr<roster_t>(new roster_t());
1076 MM(*working_roster);
1077 work.get_current_roster_shape(db, nis, *working_roster);
1078
1079 work.update_current_roster_from_filesystem(*working_roster);
1080
1081 // Get the FROM->TO cset...
1082 cset from_to_to; MM(from_to_to);
1083 cset from_to_to_excluded; MM(from_to_to_excluded);
1084 {
1085 roster_t to_true_roster;
1086 db.get_roster(to_rid, to_true_roster);
1087 node_restriction mask(work, args_to_paths(args),
1088 args_to_paths(app.opts.exclude_patterns),
1089 app.opts.depth,
1090 *from_roster, to_true_roster);
1091
1092 roster_t restricted_roster;
1093 make_restricted_roster(*from_roster, to_true_roster,
1094 restricted_roster, mask);
1095
1096 make_cset(*from_roster, restricted_roster, from_to_to);
1097 make_cset(restricted_roster, to_true_roster, from_to_to_excluded);
1098 }
1099 N(!from_to_to.empty(), F("no changes to be applied"));
1100 // ...and use it to create the TO roster
1101 shared_ptr<roster_t> to_roster = shared_ptr<roster_t>(new roster_t());
1102 MM(*to_roster);
1103 {
1104 *to_roster = *from_roster;
1105 editable_roster_base editable_to_roster(*to_roster, nis);
1106 from_to_to.apply_to(editable_to_roster);
1107 }
1108
1109 parent_map parents;
1110 work.get_parent_rosters(db, parents);
1111
1112 revision_t working_rev;
1113 revision_id working_rid;
1114 make_revision_for_workspace(parents, *working_roster, working_rev);
1115 calculate_ident(working_rev, working_rid);
1116
1117 // Now do the merge
1118 roster_merge_result result;
1119 marking_map left_markings, right_markings;
1120 three_way_merge(from_rid, *from_roster,
1121 working_rid, *working_roster,
1122 to_rid, *to_roster,
1123 result, left_markings, right_markings);
1124
1125 roster_t & merged_roster = result.roster;
1126
1127 map<file_id, file_path> paths;
1128 get_content_paths(*working_roster, paths);
1129
1130 content_merge_workspace_adaptor wca(db, from_rid, from_roster,
1131 left_markings, right_markings, paths);
1132
1133 wca.cache_roster(working_rid, working_roster);
1134 // cache the synthetic to_roster under the to_rid so that the real
1135 // to_roster is not fetched from the db which does not have temporary nids
1136 wca.cache_roster(to_rid, to_roster);
1137
1138 resolve_merge_conflicts(app.lua, *working_roster, *to_roster,
1139 result, wca);
1140
1141 I(result.is_clean());
1142 // temporary node ids may appear
1143 merged_roster.check_sane(true);
1144
1145 // we apply the working to merged cset to the workspace
1146 cset update;
1147 MM(update);
1148 make_cset(*working_roster, merged_roster, update);
1149 E(!update.empty(), F("no changes were applied"));
1150 work.perform_content_update(db, update, wca);
1151
1152 P(F("applied changes to workspace"));
1153
1154 // and record any remaining changes in _MTN/revision
1155 revision_t remaining;
1156 MM(remaining);
1157 make_revision_for_workspace(parents, merged_roster, remaining);
1158
1159 // small race condition here...
1160 work.put_work_rev(remaining);
1161 work.update_any_attrs(db);
1162
1163 // add a note to the user log file about what we did
1164 {
1165 utf8 log;
1166 work.read_user_log(log);
1167 std::string log_str = log();
1168 if (!log_str.empty())
1169 log_str += "\n";
1170 if (from_to_to_excluded.empty())
1171 log_str += (FL("applied changes from %s\n"
1172 " through %s\n")
1173 % from_rid
1174 % to_rid).str();
1175 else
1176 log_str += (FL("applied partial changes from %s\n"
1177 " through %s\n")
1178 % from_rid
1179 % to_rid).str();
1180 work.write_user_log(utf8(log_str));
1181 }
1182}
1183
1184CMD(heads, "heads", "", CMD_REF(tree), "",
1185 N_("Shows unmerged head revisions of a branch"),
1186 "",
1187 options::opts::branch)
1188{
1189 set<revision_id> heads;
1190 if (!args.empty())
1191 throw usage(execid);
1192
1193 N(app.opts.branchname() != "",
1194 F("please specify a branch, with --branch=BRANCH"));
1195
1196 database db(app);
1197 project_t project(db);
1198
1199 project.get_branch_heads(app.opts.branchname, heads,
1200 app.opts.ignore_suspend_certs);
1201
1202 if (heads.empty())
1203 P(F("branch '%s' is empty") % app.opts.branchname);
1204 else if (heads.size() == 1)
1205 P(F("branch '%s' is currently merged:") % app.opts.branchname);
1206 else
1207 P(F("branch '%s' is currently unmerged:") % app.opts.branchname);
1208
1209 for (set<revision_id>::const_iterator i = heads.begin();
1210 i != heads.end(); ++i)
1211 cout << describe_revision(project, *i) << '\n';
1212}
1213
1214CMD(get_roster, "get_roster", "", CMD_REF(debug), N_("[REVID]"),
1215 N_("Dumps the roster associated with a given identifier"),
1216 N_("If no REVID is given, the workspace is used."),
1217 options::opts::none)
1218{
1219 database db(app);
1220 roster_t roster;
1221 marking_map mm;
1222
1223 if (args.empty())
1224 {
1225 parent_map parents;
1226 temp_node_id_source nis;
1227 revision_id rid(fake_id());
1228
1229 workspace work(app);
1230 work.get_parent_rosters(db, parents);
1231 work.get_current_roster_shape(db, nis, roster);
1232 work.update_current_roster_from_filesystem(roster);
1233
1234 if (parents.empty())
1235 {
1236 mark_roster_with_no_parents(rid, roster, mm);
1237 }
1238 else if (parents.size() == 1)
1239 {
1240 roster_t parent = parent_roster(parents.begin());
1241 marking_map parent_mm = parent_marking(parents.begin());
1242 mark_roster_with_one_parent(parent, parent_mm, rid, roster, mm);
1243 }
1244 else
1245 {
1246 parent_map::const_iterator i = parents.begin();
1247 revision_id left_id = parent_id(i);
1248 roster_t const & left_roster = parent_roster(i);
1249 marking_map const & left_markings = parent_marking(i);
1250
1251 i++;
1252 revision_id right_id = parent_id(i);
1253 roster_t const & right_roster = parent_roster(i);
1254 marking_map const & right_markings = parent_marking(i);
1255
1256 i++; I(i == parents.end());
1257
1258 set<revision_id> left_uncommon_ancestors, right_uncommon_ancestors;
1259 db.get_uncommon_ancestors(left_id, right_id,
1260 left_uncommon_ancestors,
1261 right_uncommon_ancestors);
1262
1263 mark_merge_roster(left_roster, left_markings,
1264 left_uncommon_ancestors,
1265 right_roster, right_markings,
1266 right_uncommon_ancestors,
1267 rid, roster, mm);
1268 }
1269 }
1270 else if (args.size() == 1)
1271 {
1272 database db(app);
1273 project_t project(db);
1274 revision_id rid;
1275 complete(app.opts, app.lua, project, idx(args, 0)(), rid);
1276 I(!null_id(rid));
1277 db.get_roster(rid, roster, mm);
1278 }
1279 else
1280 throw usage(execid);
1281
1282 roster_data dat;
1283 write_roster_and_marking(roster, mm, dat);
1284 cout << dat;
1285}
1286
1287
1288// Local Variables:
1289// mode: C++
1290// fill-column: 76
1291// c-file-style: "gnu"
1292// indent-tabs-mode: nil
1293// End:
1294// 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