monotone

monotone Mtn Source Tree

Root/cmd_merging.cc

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