#
#
# patch "ChangeLog"
# from [87867412708f689af35f8d9c36fc397f6b33a52e]
# to [a0f514002071578c34f3ed3b6609a65b90fef85a]
#
# patch "commands.cc"
# from [a10d3cb2745a4e0abf8281668b57204b333e03a6]
# to [df7e5f355a8ac6505b5ea92b90036dc727bc14a9]
#
# patch "merge.cc"
# from [6b32ab65201a1b475b64af515bb8edf6481eca46]
# to [f129963451cc6c8f1562d84852c6d4e31a211eec]
#
# patch "merge.hh"
# from [d8869e646a690f203ff3c85764929d89399f16cd]
# to [b6598ac002b64ca96f130041514d9f6edda74bd8]
#
# patch "monotone.texi"
# from [d62e740d5bf36c317e4ec3fea155132bd7ba702d]
# to [b032e26d6e1fdec14a573061fb411780def43795]
#
# patch "testsuite.at"
# from [d67ef3223629641b02551a0a31cbf4644cd60d82]
# to [a78067f72148532de9792e0923eccead9c1b8d2c]
#
============================================================
--- ChangeLog 87867412708f689af35f8d9c36fc397f6b33a52e
+++ ChangeLog a0f514002071578c34f3ed3b6609a65b90fef85a
@@ -1,3 +1,17 @@
+2006-02-26 Timothy Brownawell
+
+ Add a new command, merge_into_dir, to allow a project to include an
+ unrelated project as a subdir. Also add a show_conflicts command that
+ performs a dry-run merge and prints conflict counts.
+ * commands.cc: new commands, merge_into_dir and show_conflicts
+ * commands.cc (propagate): remove duplicated code; this now calls
+ merge_into_dir with a dir of '' (the empty string).
+ * monotone.texi: Document the new commands.
+ * tests/t_{merge_into_dir,show_conflicts}.at: Test the new commands.
+ * testsuite.at: Use the new tests.
+ * merge.{cc,hh}: Factor a store_roster_merge_result function out
+ of interactive_merge_and_store.
+
2006-02-26 Nathaniel Smith
* roster_merge.cc: Update notes on tests.
============================================================
--- commands.cc a10d3cb2745a4e0abf8281668b57204b333e03a6
+++ commands.cc df7e5f355a8ac6505b5ea92b90036dc727bc14a9
@@ -3157,6 +3157,17 @@
N_("merge from one branch to another asymmetrically"),
OPT_DATE % OPT_AUTHOR % OPT_LCA % OPT_MESSAGE % OPT_MSGFILE)
{
+ if (args.size() != 2)
+ throw usage(name);
+ vector a = args;
+ a.push_back(utf8());
+ process(app, "merge_into_dir", a);
+}
+
+CMD(merge_into_dir, N_("tree"), N_("SOURCE-BRANCH DEST-BRANCH DIR"),
+ N_("merge one branch into a subdirectory in another branch"),
+ OPT_DATE % OPT_AUTHOR % OPT_LCA % OPT_MESSAGE % OPT_MSGFILE)
+{
// this is a special merge operator, but very useful for people maintaining
// "slightly disparate but related" trees. it does a one-way merge; less
// powerful than putting things in the same branch and also more flexible.
@@ -3178,10 +3189,14 @@
// there are also special cases we have to check for where no merge is
// actually necessary, because there hasn't been any divergence since the
// last time propagate was run.
+ //
+ // if dir is not the empty string, rename the root of N1 to have the name
+ // 'dir' in the merged tree. (ie, it has name "basename(dir)", and its
+ // parent node is "N2.get_node(dirname(dir))")
set src_heads, dst_heads;
- if (args.size() != 2)
+ if (args.size() != 3)
throw usage(name);
get_branch_heads(idx(args, 0)(), app, src_heads);
@@ -3220,8 +3235,65 @@
{
revision_id merged;
transaction_guard guard(app.db);
- interactive_merge_and_store(*src_i, *dst_i, merged, app);
+ {
+ revision_id const & left_rid(*src_i), & right_rid(*dst_i);
+ roster_t left_roster, right_roster;
+ MM(left_roster);
+ MM(right_roster);
+ marking_map left_marking_map, right_marking_map;
+ std::set left_uncommon_ancestors, right_uncommon_ancestors;
+
+ app.db.get_roster(left_rid, left_roster, left_marking_map);
+ app.db.get_roster(right_rid, right_roster, right_marking_map);
+ app.db.get_uncommon_ancestors(left_rid, right_rid,
+ left_uncommon_ancestors,
+ right_uncommon_ancestors);
+
+ {
+ dir_t moved_root = left_roster.root();
+ split_path sp, dirname;
+ path_component basename;
+ MM(dirname);
+ if (!idx(args,2)().empty())
+ {
+ file_path_external(idx(args,2)).split(sp);
+ dirname_basename(sp, dirname, basename);
+ N(right_roster.has_node(dirname),
+ F("Path %s not found in destination tree.") % sp);
+ node_t parent = right_roster.get_node(dirname);
+ moved_root->parent = parent->self;
+ moved_root->name = basename;
+ marking_map::iterator i=left_marking_map.find(moved_root->self);
+ I(i != left_marking_map.end());
+ i->second.parent_name.clear();
+ i->second.parent_name.insert(left_rid);
+ }
+ }
+
+ roster_merge_result result;
+ roster_merge(left_roster, left_marking_map, left_uncommon_ancestors,
+ right_roster, right_marking_map, right_uncommon_ancestors,
+ result);
+
+ content_merge_database_adaptor dba(app, left_rid, right_rid, left_marking_map);
+ resolve_merge_conflicts (left_rid, right_rid,
+ left_roster, right_roster,
+ left_marking_map, right_marking_map,
+ result, dba, app);
+
+ {
+ dir_t moved_root = left_roster.root();
+ moved_root->parent = 0;
+ moved_root->name = the_null_component;
+ }
+
+ // write new files into the db
+ store_roster_merge_result(left_roster, right_roster, result,
+ left_rid, right_rid, merged,
+ app);
+ }
+
packet_db_writer dbw(app);
cert_revision_in_branch(merged, idx(args, 1)(), app, dbw);
@@ -3910,4 +3982,37 @@
cout << dat;
}
+CMD(show_conflicts, N_("informative"), N_("REV REV"), N_("Show what conflicts would need to be resolved to merge the given revisions."),
+ OPT_BRANCH_NAME % OPT_DATE % OPT_AUTHOR)
+{
+ if (args.size() != 2)
+ throw usage(name);
+ revision_id l_id, r_id;
+ complete(app, idx(args,0)(), l_id);
+ complete(app, idx(args,1)(), r_id);
+ N(!is_ancestor(l_id, r_id, app),
+ F("%s in an ancestor of %s; no merge is needed.") % l_id % r_id);
+ N(!is_ancestor(r_id, l_id, app),
+ F("%s in an ancestor of %s; no merge is needed.") % r_id % l_id);
+ roster_t l_roster, r_roster;
+ marking_map l_marking, r_marking;
+ app.db.get_roster(l_id, l_roster, l_marking);
+ app.db.get_roster(r_id, r_roster, r_marking);
+ std::set l_uncommon_ancestors, r_uncommon_ancestors;
+ app.db.get_uncommon_ancestors(l_id, r_id,
+ l_uncommon_ancestors,
+ r_uncommon_ancestors);
+ roster_merge_result result;
+ roster_merge(l_roster, l_marking, l_uncommon_ancestors,
+ r_roster, r_marking, r_uncommon_ancestors,
+ result);
+
+ P(F("There are %s node_name_conflicts.") % result.node_name_conflicts.size());
+ P(F("There are %s file_content_conflicts.") % result.file_content_conflicts.size());
+ P(F("There are %s node_attr_conflicts.") % result.node_attr_conflicts.size());
+ P(F("There are %s orphaned_node_conflicts.") % result.orphaned_node_conflicts.size());
+ P(F("There are %s rename_target_conflicts.") % result.rename_target_conflicts.size());
+ P(F("There are %s directory_loop_conflicts.") % result.directory_loop_conflicts.size());
+}
+
}; // namespace commands
============================================================
--- merge.cc 6b32ab65201a1b475b64af515bb8edf6481eca46
+++ merge.cc f129963451cc6c8f1562d84852c6d4e31a211eec
@@ -139,8 +139,6 @@
right_roster, right_marking_map, right_uncommon_ancestors,
result);
- roster_t & merged_roster = result.roster;
-
content_merge_database_adaptor dba(app, left_rid, right_rid, left_marking_map);
resolve_merge_conflicts (left_rid, right_rid,
left_roster, right_roster,
@@ -148,8 +146,22 @@
result, dba, app);
// write new files into the db
+ store_roster_merge_result(left_roster, right_roster, result,
+ left_rid, right_rid, merged_rid,
+ app);
+}
+void
+store_roster_merge_result(roster_t const & left_roster,
+ roster_t const & right_roster,
+ roster_merge_result & result,
+ revision_id const & left_rid,
+ revision_id const & right_rid,
+ revision_id & merged_rid,
+ app_state & app)
+{
I(result.is_clean());
+ roster_t & merged_roster = result.roster;
merged_roster.check_sane();
revision_set merged_rev;
============================================================
--- merge.hh d8869e646a690f203ff3c85764929d89399f16cd
+++ merge.hh b6598ac002b64ca96f130041514d9f6edda74bd8
@@ -46,4 +46,13 @@
interactive_merge_and_store(revision_id const & left, revision_id const & right,
revision_id & merged, app_state & app);
+void
+store_roster_merge_result(roster_t const & left_roster,
+ roster_t const & right_roster,
+ roster_merge_result & result,
+ revision_id const & left_rid,
+ revision_id const & right_rid,
+ revision_id & merged_rid,
+ app_state & app);
+
#endif
============================================================
--- monotone.texi d62e740d5bf36c317e4ec3fea155132bd7ba702d
+++ monotone.texi b032e26d6e1fdec14a573061fb411780def43795
@@ -3785,6 +3785,25 @@
another branch, again, you can use this command. If the optional
@var{ancestor} argument is given, the merge uses that revision as the
common ancestor instead of the default ancestor.
+
address@hidden monotone merge_into_dir @var{sourcebranch} @var{destbranch} @var{dir}
+This command takes a unique head from @var{sourcebranch} and merges it
+into a unique head of @var{destbranch}, as a directory. The resulting
+revision is committed to @var{destbranch}. If either @var{sourcebranch} or
address@hidden has multiple heads, @command{merge_into_dir} aborts, doing
+nothing.
+
+The purpose of @command{merge_into_dir} is to permit a project to contain
+another project in such a way that @command{propagate} can be used to keep
+the contained project up-to-date. It is meant to replace the use of nested
+checkouts in many circumstances.
+
+Note that @command{merge_into_dir} @emph{does not} permit changes made to the
+contained project in @var{destbranch} to be propagated back to
address@hidden Attempting this would lead to @var{sourcebranch} containing
+both projects nested as in @var{destbranch} instead of only the project
+originally in @var{sourcebranch}, which is almost certainly not what would be
+intended.
@end ftable
@@ -4467,6 +4486,10 @@
Specifying only the pathname "." will restrict the search for known
files to the current subdirectory of the workspace.
address@hidden monotone show_conflicts @var{rev} @var{rev}
+
+This command shows what conflicts would need to be resolved in order to merge
+the given revisions.
@end ftable
============================================================
--- testsuite.at d67ef3223629641b02551a0a31cbf4644cd60d82
+++ testsuite.at a78067f72148532de9792e0923eccead9c1b8d2c
@@ -784,3 +784,5 @@
m4_include(tests/t_pivot_root_revert.at)
m4_include(tests/t_pivot_root_update.at)
m4_include(tests/t_rosterify_root_suture.at)
+m4_include(tests/t_show_conflicts.at)
+m4_include(tests/t_merge_into_dir.at)