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