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