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