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