#
#
# add_file "cmd_conflicts.cc"
# content [c4100c05922de53abdd3312733827efd0c45e6ec]
#
# add_file "tests/resolve_duplicate_name_conflict/conflicts-resolved"
# content [65a583cc0d3a3f50cc7e24bac786ef88027671c6]
#
# patch "Makefile.am"
# from [4eba20c4f0654b122564fe6845edbc7f5cd55f18]
# to [658abc8efdc2afc5eb7ceded3a8d8279dd2b0056]
#
# patch "cmd.hh"
# from [f0e4d46973929a8c7b70b0e12593de40e7aec203]
# to [dd969e8f9644c72b50fb94c56cd5655d095f160d]
#
# patch "cmd_merging.cc"
# from [1641d6a228ca07cc5b747ed7586b72cfab7ead48]
# to [d4113f1b227d26e48ca23fce88ee1b9fd929bedf]
#
# patch "options_list.hh"
# from [154daf8d9f4ab4673441dc8e30bf7531fb7fe3eb]
# to [872b69d45a0ac08b4f8cc04d9fc7c294a132d579]
#
# patch "roster_merge.cc"
# from [07e0797d91191e7eb2004e4720ac710a02f2df02]
# to [5c4a8791df3ca572589f2c7e91a565ed8f99dd32]
#
# patch "roster_merge.hh"
# from [cc362cd4a0b539c2fbdfd892a9a171586fc84d5c]
# to [3bbdacaa0de545b88f71e178ca7931616ea0eb83]
#
# patch "tests/resolve_conflict_all_resolutions/__driver__.lua"
# from [709ab05fab5b76ebcbe76b2a9fdf12307f690522]
# to [2c013201e50e953df1ca9b55ad726b4c633c9610]
#
# patch "tests/resolve_duplicate_name_conflict/__driver__.lua"
# from [37b9d15331a7f470419dbc20e416c0142c45237c]
# to [320c07fc8141d2eac97f2f6a71e6b9d4754799c9]
#
============================================================
--- cmd_conflicts.cc c4100c05922de53abdd3312733827efd0c45e6ec
+++ cmd_conflicts.cc c4100c05922de53abdd3312733827efd0c45e6ec
@@ -0,0 +1,290 @@
+// Copyright (C) 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+
+#include "app_state.hh"
+#include "cmd.hh"
+#include "database.hh"
+#include "roster_merge.hh"
+
+CMD_GROUP(conflicts, "conflicts", "", CMD_REF(tree),
+ N_("Commands for conflict resolutions."),
+ "");
+
+struct conflicts_t
+{
+ roster_merge_result result;
+ revision_id ancestor_rid, left_rid, right_rid;
+ boost::shared_ptr left_roster;
+ boost::shared_ptr right_roster;
+ marking_map left_marking, right_marking;
+
+ conflicts_t(database & db, system_path file):
+ left_roster(boost::shared_ptr(new roster_t())),
+ right_roster(boost::shared_ptr(new roster_t()))
+ {
+ result.clear(); // default constructor doesn't do this.
+
+ result.read_conflict_file(db, file, ancestor_rid, left_rid, right_rid,
+ *left_roster, left_marking,
+ *right_roster, right_marking);
+ };
+
+ void write (database & db, lua_hooks & lua, system_path file)
+ {
+ result.write_conflict_file
+ (db, lua, file, ancestor_rid, left_rid, right_rid,
+ left_roster, left_marking, right_roster, right_marking);
+ };
+};
+
+static void
+show_first_conflict(conflicts_t conflicts)
+{
+ // To find the first conflict, go thru the conflicts we know how to
+ // resolve in the same order we output them.
+ for (std::vector::iterator i = conflicts.result.duplicate_name_conflicts.begin();
+ i != conflicts.result.duplicate_name_conflicts.end();
+ ++i)
+ {
+ duplicate_name_conflict & conflict = *i;
+
+ if (conflict.left_resolution.first == resolve_conflicts::none ||
+ conflict.right_resolution.first == resolve_conflicts::none)
+ {
+ file_path left_name;
+ conflicts.left_roster->get_name(conflict.left_nid, left_name);
+ P(F("duplicate_name %s") % left_name);
+ P(F("possible resolutions:"));
+
+ if (conflict.left_resolution.first == resolve_conflicts::none)
+ {
+ P(F("resolve_first_left drop"));
+ P(F("resolve_first_left rename \"name\""));
+ P(F("resolve_first_left user \"name\""));
+ }
+
+ if (conflict.right_resolution.first == resolve_conflicts::none)
+ {
+ P(F("resolve_first_right drop"));
+ P(F("resolve_first_right rename \"name\""));
+ P(F("resolve_first_right user \"name\""));
+ }
+ return;
+ }
+ }
+
+ for (std::vector::iterator i = conflicts.result.file_content_conflicts.begin();
+ i != conflicts.result.file_content_conflicts.end();
+ ++i)
+ {
+ file_content_conflict & conflict = *i;
+
+ if (conflict.resolution.first == resolve_conflicts::none)
+ {
+ file_path name;
+ conflicts.left_roster->get_name(conflict.nid, name);
+ P(F("content %s") % name);
+ P(F("possible resolutions:"));
+ P(F("resolve user \"file_name\""));
+ return;
+ }
+ }
+
+ N(false, F("no resolvable yet unresolved conflicts"));
+
+} // show_first_conflict
+
+enum side_t {left, right, neither};
+static char const * const conflict_resolution_not_supported_msg = "%s is not a supported conflict resolution for %s";
+
+static void
+set_duplicate_name_conflict(resolve_conflicts::file_resolution_t & resolution,
+ args_vector const & args)
+{
+ if ("drop" == idx(args, 0)())
+ {
+ N(args.size() == 1, F("too many arguments"));
+ resolution.first = resolve_conflicts::drop;
+ }
+ else if ("rename" == idx(args, 0)())
+ {
+ N(args.size() == 2, F("wrong number of arguments"));
+ resolution.first = resolve_conflicts::rename;
+ resolution.second = resolve_conflicts::new_file_path(idx(args,1)());
+ }
+ else if ("user" == idx(args, 0)())
+ {
+ N(args.size() == 2, F("wrong number of arguments"));
+ resolution.first = resolve_conflicts::content_user;
+ resolution.second = resolve_conflicts::new_optimal_path(idx(args,1)());
+ }
+ else
+ N(false, F(conflict_resolution_not_supported_msg) % idx(args,0) % "duplicate_name");
+
+} //set_duplicate_name_conflict
+
+static void
+set_first_conflict(side_t side, conflicts_t & conflicts, args_vector const & args)
+{
+ if (side != neither)
+ {
+ for (std::vector::iterator i = conflicts.result.duplicate_name_conflicts.begin();
+ i != conflicts.result.duplicate_name_conflicts.end();
+ ++i)
+ {
+ duplicate_name_conflict & conflict = *i;
+
+ switch (side)
+ {
+ case left:
+ if (conflict.left_resolution.first == resolve_conflicts::none)
+ {
+ set_duplicate_name_conflict(conflict.left_resolution, args);
+ return;
+ }
+ break;
+
+ case right:
+ if (conflict.right_resolution.first == resolve_conflicts::none)
+ {
+ set_duplicate_name_conflict(conflict.right_resolution, args);
+ return;
+ }
+ break;
+
+ case neither:
+ I(false);
+ }
+ }
+ }
+
+ if (side == neither)
+ {
+ for (std::vector::iterator i = conflicts.result.file_content_conflicts.begin();
+ i != conflicts.result.file_content_conflicts.end();
+ ++i)
+ {
+ file_content_conflict & conflict = *i;
+
+ if (conflict.resolution.first == resolve_conflicts::none)
+ {
+ if ("user" == idx(args,0)())
+ {
+ N(args.size() == 2, F("wrong number of arguments"));
+
+ conflict.resolution.first = resolve_conflicts::content_user;
+ conflict.resolution.second = resolve_conflicts::new_optimal_path(idx(args,1)());
+ }
+ else
+ {
+ // We don't allow the user to specify 'resolved_internal'; that
+ // is only done by automate show_conflicts.
+ N(false, F(conflict_resolution_not_supported_msg) % idx(args,0) % "file_content");
+ }
+ return;
+ }
+ }
+ }
+
+ switch (side)
+ {
+ case left:
+ N(false, F("no resolvable yet unresolved left side conflicts"));
+ break;
+
+ case right:
+ N(false, F("no resolvable yet unresolved right side conflicts"));
+ break;
+
+ case neither:
+ N(false, F("no resolvable yet unresolved single-file conflicts"));
+ break;
+ }
+
+} // set_first_conflict
+
+
+/// commands
+
+// CMD(store) is in cmd_merging, since it needs access to
+// show_conflicts_core, and doesn't need conflicts_t.
+
+CMD(show_first, "show_first", "", CMD_REF(conflicts),
+ "",
+ N_("Show the first conflict in the conflicts file, and possible resolutions."),
+ "",
+ options::opts::conflicts_opts)
+{
+ database db(app);
+ conflicts_t conflicts (db, app.opts.conflicts_file);
+
+ show_first_conflict(conflicts);
+}
+
+CMD(resolve_first, "resolve_first", "", CMD_REF(conflicts),
+ N_("RESOLUTION"),
+ N_("Set the resolution for the first single-file conflict."),
+ "",
+ options::opts::conflicts_opts)
+{
+ database db(app);
+ conflicts_t conflicts (db, app.opts.conflicts_file);
+
+ set_first_conflict(neither, conflicts, args);
+
+ conflicts.write (db, app.lua, app.opts.conflicts_file);
+}
+
+CMD(resolve_first_left, "resolve_first_left", "", CMD_REF(conflicts),
+ N_("RESOLUTION"),
+ N_("Set the left resolution for the first two-file conflict."),
+ "",
+ options::opts::conflicts_opts)
+{
+ database db(app);
+ conflicts_t conflicts (db, app.opts.conflicts_file);
+
+ set_first_conflict(left, conflicts, args);
+
+ conflicts.write (db, app.lua, app.opts.conflicts_file);
+}
+
+CMD(resolve_first_right, "resolve_first_right", "", CMD_REF(conflicts),
+ N_("RESOLUTION"),
+ N_("Set the right resolution for the first two-file conflict."),
+ "",
+ options::opts::conflicts_opts)
+{
+ database db(app);
+ conflicts_t conflicts (db, app.opts.conflicts_file);
+
+ set_first_conflict(right, conflicts, args);
+
+ conflicts.write (db, app.lua, app.opts.conflicts_file);
+}
+
+CMD(clean, "clean", "", CMD_REF(conflicts),
+ N_(""),
+ N_("Delete any bookkeeping files related to conflict resolution."),
+ "",
+ options::opts::none)
+{
+ bookkeeping_path conflicts_file("conflicts");
+ bookkeeping_path resolutions_dir("resolutions");
+
+ if (path_exists(conflicts_file))
+ delete_file(conflicts_file);
+
+ if (path_exists(resolutions_dir))
+ delete_dir_recursive(resolutions_dir);
+}
+
+// end of file
============================================================
--- tests/resolve_duplicate_name_conflict/conflicts-resolved 65a583cc0d3a3f50cc7e24bac786ef88027671c6
+++ tests/resolve_duplicate_name_conflict/conflicts-resolved 65a583cc0d3a3f50cc7e24bac786ef88027671c6
@@ -0,0 +1,23 @@
+ left [1337cb1059c4bc3e376b14381b43e9383c654da1]
+ right [d5f1dd136c86b5bbd5e71b0c3365667e328af492]
+ancestor [b3ac8a77cee78263b66800c635441ecb1f259a42]
+
+ conflict duplicate_name
+ left_type "added file"
+ left_name "checkout.sh"
+ left_file_id [61b8d4fb0e5d78be111f691b955d523c782fa92e]
+ right_type "added file"
+ right_name "checkout.sh"
+ right_file_id [dd6805ae36432d6edcbdff6ea578ea981ffa2144]
+ resolved_drop_left
+resolved_user_right "_MTN/resolutions/checkout.sh"
+
+ conflict duplicate_name
+ left_type "added file"
+ left_name "thermostat.c"
+ left_file_id [4cdcec6fa2f9d5c075d5b80d03c708c8e4801196]
+ right_type "added file"
+ right_name "thermostat.c"
+ right_file_id [7e9f2712c5d3570815f1546772d9119474d32afc]
+ resolved_rename_left "thermostat-westinghouse.c"
+resolved_rename_right "thermostat-honeywell.c"
============================================================
--- Makefile.am 4eba20c4f0654b122564fe6845edbc7f5cd55f18
+++ Makefile.am 658abc8efdc2afc5eb7ceded3a8d8279dd2b0056
@@ -4,7 +4,7 @@ CMD_SOURCES = \
CMD_SOURCES = \
cmd.hh cmd_netsync.cc cmd_list.cc cmd_packet.cc cmd_key_cert.cc \
cmd_merging.cc cmd_db.cc cmd_diff_log.cc cmd_ws_commit.cc \
- cmd_othervcs.cc cmd_automate.cc cmd_files.cc
+ cmd_othervcs.cc cmd_automate.cc cmd_files.cc cmd_conflicts.cc
SANITY_CORE_SOURCES = \
sanity.cc sanity.hh quick_alloc.hh vector.hh base.hh \
============================================================
--- cmd.hh f0e4d46973929a8c7b70b0e12593de40e7aec203
+++ cmd.hh dd969e8f9644c72b50fb94c56cd5655d095f160d
@@ -279,6 +279,7 @@ CMD_FWD_DECL(automation);
CMD_FWD_DECL(__root__);
CMD_FWD_DECL(automation);
+CMD_FWD_DECL(conflicts);
CMD_FWD_DECL(database);
CMD_FWD_DECL(debug);
CMD_FWD_DECL(informative);
============================================================
--- cmd_merging.cc 1641d6a228ca07cc5b747ed7586b72cfab7ead48
+++ cmd_merging.cc d4113f1b227d26e48ca23fce88ee1b9fd929bedf
@@ -947,6 +947,40 @@ CMD(show_conflicts, "show_conflicts", ""
show_conflicts_core(db, app.lua, l_id, r_id, false, std::cout);
}
+static void get_conflicts_rids(args_vector const & args,
+ database & db,
+ project_t & project,
+ app_state & app,
+ revision_id & left_rid,
+ revision_id & right_rid)
+{
+ if (args.size() == 0)
+ {
+ // get ids from heads
+ N(app.opts.branchname() != "",
+ F("please specify a branch, with --branch=BRANCH"));
+
+ set heads;
+ project.get_branch_heads(app.opts.branchname, heads,
+ app.opts.ignore_suspend_certs);
+
+ N(heads.size() >= 2,
+ F("branch '%s' has only 1 head; must be at least 2 for conflicts") % app.opts.branchname);
+
+ revpair p = find_heads_to_merge (db, heads);
+ left_rid = p.first;
+ right_rid = p.second;
+ }
+ else if (args.size() == 2)
+ {
+ // get ids from args
+ complete(app.opts, app.lua, project, idx(args,0)(), left_rid);
+ complete(app.opts, app.lua, project, idx(args,1)(), right_rid);
+ }
+ else
+ N(false, F("wrong argument count"));
+}
+
// Name: show_conflicts
// Arguments:
// Two revision ids (optional, determined from the workspace if not given; there must be exactly two heads)
@@ -967,61 +1001,34 @@ CMD_AUTOMATE(show_conflicts, N_("[LEFT_R
N_("Shows the conflicts between two revisions."),
N_("If no arguments are given, LEFT_REVID and RIGHT_REVID default to the "
"first two heads that would be chosen by the 'merge' command."),
- options::opts::branch)
+ options::opts::branch | options::opts::ignore_suspend_certs)
{
database db(app);
project_t project(db);
revision_id l_id, r_id;
- if (args.size() == 0)
- {
- // get ids from heads
- N(app.opts.branchname() != "",
- F("please specify a branch, with --branch=BRANCH"));
-
- set heads;
- project.get_branch_heads(app.opts.branchname, heads,
- app.opts.ignore_suspend_certs);
-
- N(heads.size() >= 2,
- F("branch '%s' has %d heads; must be at least 2 for show_conflicts") % app.opts.branchname % heads.size());
-
- revpair p = find_heads_to_merge (db, heads);
- l_id = p.first;
- r_id = p.second;
- }
- else if (args.size() == 2)
- {
- // get ids from args
- complete(app.opts, app.lua, project, idx(args,0)(), l_id);
- complete(app.opts, app.lua, project, idx(args,1)(), r_id);
- }
- else
- N(false, F("wrong argument count"));
-
+ get_conflicts_rids(args, db, project, app, l_id, r_id);
show_conflicts_core(db, app.lua, l_id, r_id, true, output);
}
-CMD(resolve_conflict, "resolve_conflict", "", CMD_REF(tree),
- N_("CONFLICTS-FILE CONFLICT"),
- N_("Set the resolution for the first conflict in the conflicts file."),
- "",
- options::opts::none)
+CMD(store, "store", "", CMD_REF(conflicts),
+ "[LEFT_REVID RIGHT_REVID]",
+ N_("Store the conflicts from merging two revisions."),
+ N_("If no arguments are given, LEFT_REVID and RIGHT_REVID default to the "
+ "first two heads that would be chosen by the 'merge' command."),
+ options::opts::branch | options::opts::conflicts_opts)
{
- database db(app);
- roster_merge_result roster;
- revision_id ancestor_rid, left_rid, right_rid;
- boost::shared_ptr left_roster = shared_ptr(new roster_t());
- boost::shared_ptr right_roster = shared_ptr(new roster_t());
- marking_map left_marking, right_marking;
+ database db(app);
+ project_t project(db);
+ revision_id left_id, right_id;
- roster.clear(); // default constructor doesn't do this.
-
- roster.read_conflict_file(db, idx(args,0)(), ancestor_rid, left_rid, right_rid,
- *left_roster, left_marking, *right_roster, right_marking);
- roster.set_first_conflict(idx(args,1)());
- roster.write_conflict_file(db, app.lua, idx(args,0)(), ancestor_rid, left_rid, right_rid,
- left_roster, left_marking, right_roster, right_marking);
+ get_conflicts_rids(args, db, project, app, left_id, right_id);
+
+ std::ostringstream output;
+ show_conflicts_core(db, app.lua, left_id, right_id, true, output);
+
+ data dat(output.str());
+ write_data(system_path(app.opts.conflicts_file), dat, system_path("_MTN"));
}
CMD_AUTOMATE(file_merge, N_("LEFT_REVID LEFT_FILENAME RIGHT_REVID RIGHT_FILENAME"),
============================================================
--- options_list.hh 154daf8d9f4ab4673441dc8e30bf7531fb7fe3eb
+++ options_list.hh 872b69d45a0ac08b4f8cc04d9fc7c294a132d579
@@ -658,8 +658,20 @@ OPTION(resolve_conflicts_opts, resolve_c
F("only one of --resolve-conflicts or --resolve-conflicts-file may be given"));
resolve_conflicts = arg;
}
+
+OPTSET(conflicts_opts)
+OPTVAR(conflicts_opts, system_path, conflicts_file, system_path("_MTN/conflicts"))
+
+OPTION(conflicts_opts, conflicts_file, true, "conflicts-file",
+ gettext_noop("file in which to store conflicts"))
+#ifdef option_bodies
+{
+ conflicts_file = system_path(utf8(arg));
+}
#endif
+#endif
+
// Local Variables:
// mode: C++
// fill-column: 76
============================================================
--- roster_merge.cc 07e0797d91191e7eb2004e4720ac710a02f2df02
+++ roster_merge.cc 5c4a8791df3ca572589f2c7e91a565ed8f99dd32
@@ -29,22 +29,48 @@ using std::string;
using std::set;
using std::string;
-static char * const
-image(resolve_conflicts::resolution_t resolution)
+namespace resolve_conflicts
{
- switch (resolution)
- {
- case resolve_conflicts::none:
- return "none";
- case resolve_conflicts::content_user:
- return "content_user";
- case resolve_conflicts::content_internal:
- return "content_internal";
- case resolve_conflicts::rename:
- return "rename";
- default:
- I(false);
- }
+ boost::shared_ptr
+ new_file_path(std::string path)
+ {
+ return boost::shared_ptr(new file_path(file_path_external(utf8(path))));
+ };
+
+ boost::shared_ptr
+ new_optimal_path(std::string path)
+ {
+ if (bookkeeping_path::external_string_is_bookkeeping_path(utf8(path)))
+ return boost::shared_ptr(new bookkeeping_path(path));
+ else
+ try
+ {
+ return new_file_path(path);
+ }
+ catch (informative_failure &)
+ {
+ return boost::shared_ptr(new system_path(path));
+ }
+ };
+
+ static char * const
+ image(resolve_conflicts::resolution_t resolution)
+ {
+ switch (resolution)
+ {
+ case resolve_conflicts::none:
+ return "none";
+ case resolve_conflicts::content_user:
+ return "content_user";
+ case resolve_conflicts::content_internal:
+ return "content_internal";
+ case resolve_conflicts::rename:
+ return "rename";
+ case resolve_conflicts::drop:
+ return "drop";
+ }
+ I(false); // keep compiler happy
+ }
}
template <> void
@@ -238,10 +264,14 @@ namespace
symbol const node_type("node_type");
symbol const orphaned_directory("orphaned_directory");
symbol const orphaned_file("orphaned_file");
+ symbol const resolved_drop_right("resolved_drop_right");
+ symbol const resolved_drop_left("resolved_drop_left");
symbol const resolved_internal("resolved_internal");
symbol const resolved_rename_left("resolved_rename_left");
symbol const resolved_rename_right("resolved_rename_right");
symbol const resolved_user("resolved_user");
+ symbol const resolved_user_left("resolved_user_left");
+ symbol const resolved_user_right("resolved_user_right");
symbol const right("right");
symbol const right_attr_state("right_attr_state");
symbol const right_attr_value("right_attr_value");
@@ -461,7 +491,63 @@ put_attr_conflict (basic_io::stanza & st
}
}
+enum side_t {left_side, right_side};
+
static void
+put_duplicate_name_resolution(basic_io::stanza & st,
+ side_t side,
+ resolve_conflicts::file_resolution_t const & resolution)
+{
+ switch (resolution.first)
+ {
+ case resolve_conflicts::none:
+ break;
+
+ case resolve_conflicts::content_user:
+ switch (side)
+ {
+ case left_side:
+ st.push_str_pair(syms::resolved_user_left, resolution.second->as_external());
+ break;
+
+ case right_side:
+ st.push_str_pair(syms::resolved_user_right, resolution.second->as_external());
+ break;
+ }
+ break;
+
+ case resolve_conflicts::rename:
+ switch (side)
+ {
+ case left_side:
+ st.push_str_pair(syms::resolved_rename_left, resolution.second->as_external());
+ break;
+
+ case right_side:
+ st.push_str_pair(syms::resolved_rename_right, resolution.second->as_external());
+ break;
+ }
+ break;
+
+ case resolve_conflicts::drop:
+ switch (side)
+ {
+ case left_side:
+ st.push_symbol(syms::resolved_drop_left);
+ break;
+
+ case right_side:
+ st.push_symbol(syms::resolved_drop_right);
+ break;
+ }
+ break;
+
+ default:
+ I(false);
+ }
+}
+
+static void
put_content_conflict (basic_io::stanza & st,
roster_t const & left_roster,
roster_t const & right_roster,
@@ -510,7 +596,7 @@ put_content_conflict (basic_io::stanza &
break;
case resolve_conflicts::content_user:
- st.push_str_pair(syms::resolved_user, conflict.resolution.second);
+ st.push_str_pair(syms::resolved_user, conflict.resolution.second->as_external());
break;
default:
@@ -1216,7 +1302,11 @@ roster_merge_result::report_duplicate_na
I(false);
if (basic_io)
- put_stanza(st, output);
+ {
+ put_duplicate_name_resolution (st, left_side, conflict.left_resolution);
+ put_duplicate_name_resolution (st, right_side, conflict.right_resolution);
+ put_stanza(st, output);
+ }
}
}
@@ -1388,7 +1478,7 @@ roster_merge_result::report_file_content
basic_io::stanza st;
if (auto_merge_succeeds(lua, conflict, adaptor, left_roster, right_roster))
- conflict.resolution = make_pair(resolve_conflicts::content_internal, std::string());
+ conflict.resolution.first = resolve_conflicts::content_internal;
st.push_str_pair(syms::conflict, syms::content);
put_content_conflict (st, left_roster, right_roster, adaptor, conflict);
@@ -1497,21 +1587,44 @@ read_duplicate_name_conflict(basic_io::p
// check for a resolution
while ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
{
- if (pars.symp (syms::resolved_rename_left))
+ if (pars.symp (syms::resolved_drop_left))
{
+ conflict.left_resolution.first = resolve_conflicts::drop;
+ pars.sym();
+ }
+ else if (pars.symp (syms::resolved_drop_right))
+ {
+ conflict.right_resolution.first = resolve_conflicts::drop;
+ pars.sym();
+ }
+ else if (pars.symp (syms::resolved_rename_left))
+ {
conflict.left_resolution.first = resolve_conflicts::rename;
pars.sym();
- // File path is specified by the user, so it's an external string.
- conflict.left_resolution.second = file_path_external (utf8(pars.token));
+ conflict.left_resolution.second = resolve_conflicts::new_file_path(pars.token);
pars.str();
}
else if (pars.symp (syms::resolved_rename_right))
{
conflict.right_resolution.first = resolve_conflicts::rename;
pars.sym();
- conflict.right_resolution.second = file_path_external (utf8(pars.token));
+ conflict.right_resolution.second = resolve_conflicts::new_file_path(pars.token);
pars.str();
}
+ else if (pars.symp (syms::resolved_user_left))
+ {
+ conflict.left_resolution.first = resolve_conflicts::content_user;
+ pars.sym();
+ conflict.left_resolution.second = resolve_conflicts::new_optimal_path(pars.token);
+ pars.str();
+ }
+ else if (pars.symp (syms::resolved_user_right))
+ {
+ conflict.right_resolution.first = resolve_conflicts::content_user;
+ pars.sym();
+ conflict.right_resolution.second = resolve_conflicts::new_optimal_path(pars.token);
+ pars.str();
+ }
else
N(false, F(conflict_resolution_not_supported_msg) % pars.token % "duplicate_name");
}
@@ -1609,7 +1722,7 @@ read_file_content_conflict(basic_io::par
{
conflict.resolution.first = resolve_conflicts::content_user;
pars.sym();
- conflict.resolution.second = pars.token;
+ conflict.resolution.second = resolve_conflicts::new_optimal_path(pars.token);
pars.str();
}
else
@@ -1713,7 +1826,7 @@ roster_merge_result::read_conflict_file(
void
roster_merge_result::read_conflict_file(database & db,
- std::string const file_name,
+ system_path const file_name,
revision_id & ancestor_rid,
revision_id & left_rid,
revision_id & right_rid,
@@ -1724,9 +1837,9 @@ roster_merge_result::read_conflict_file(
{
data dat;
- read_data (system_path(utf8(file_name)), dat);
+ read_data (file_name, dat);
- basic_io::input_source src(dat(), file_name);
+ basic_io::input_source src(dat(), file_name.as_external());
basic_io::tokenizer tok(src);
basic_io::parser pars(tok);
std::string temp;
@@ -1750,83 +1863,9 @@ void
} // roster_merge_result::read_conflict_file
void
-roster_merge_result::set_first_conflict(std::string conflict)
-{
- basic_io::input_source src(conflict, "resolve_conflicts string");
- basic_io::tokenizer tok(src);
- basic_io::parser pars(tok);
-
- // To find the first conflict, go thru the conflicts we know how to
- // resolve in the same order we output them.
- for (std::vector::iterator i = duplicate_name_conflicts.begin();
- i != duplicate_name_conflicts.end();
- ++i)
- {
- duplicate_name_conflict & conflict = *i;
-
- if (conflict.left_resolution.first == resolve_conflicts::none)
- {
- for (int i = 1; i <= 2; i++)
- {
- if (pars.symp (syms::resolved_rename_left))
- {
- conflict.left_resolution.first = resolve_conflicts::rename;
- pars.sym();
- conflict.left_resolution.second = file_path_external (utf8(pars.token));
- pars.str();
- }
- else if (pars.symp (syms::resolved_rename_right))
- {
- conflict.right_resolution.first = resolve_conflicts::rename;
- pars.sym();
- conflict.right_resolution.second = file_path_external (utf8(pars.token));
- pars.str();
- }
- else
- N(false, F(conflict_resolution_not_supported_msg) % pars.token % "duplicate_name");
- }
-
- N(pars.tok.in.lookahead == EOF, F(conflict_extra));
- return;
- }
- }
-
- for (std::vector::iterator i = file_content_conflicts.begin();
- i != file_content_conflicts.end();
- ++i)
- {
- file_content_conflict & conflict = *i;
-
- if (conflict.resolution.first == resolve_conflicts::none)
- {
- if (pars.symp (syms::resolved_user))
- {
- conflict.resolution.first = resolve_conflicts::content_user;
- pars.sym();
- conflict.resolution.second = pars.token;
- pars.str();
- }
- else
- {
- // We don't allow the user to specify 'resolved_internal'; that
- // is only done by automate show_conflicts.
- N(false, F(conflict_resolution_not_supported_msg) % pars.token % "file_content");
- }
-
- N(pars.tok.in.lookahead == EOF, F(conflict_extra));
- return;
- }
- }
-
- N(false, F("no resolvable yet unresolved conflicts"));
-
-} // roster_merge_result::set_first_conflict
-
-
-void
roster_merge_result::write_conflict_file(database & db,
lua_hooks & lua,
- std::string file_name,
+ system_path const file_name,
revision_id const & ancestor_rid,
revision_id const & left_rid,
revision_id const & right_rid,
@@ -1853,11 +1892,11 @@ roster_merge_result::write_conflict_file
output.write(pr.buf.data(), pr.buf.size());
}
- report_duplicate_name_conflicts(*left_roster, *right_roster, adaptor, false, output);
- report_file_content_conflicts(lua, *left_roster, *right_roster, adaptor, false, output);
+ report_duplicate_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
+ report_file_content_conflicts(lua, *left_roster, *right_roster, adaptor, true, output);
data dat(output.str());
- write_data(system_path(file_name), dat, system_path("_MTN"));
+ write_data(file_name, dat, system_path("_MTN"));
} // roster_merge_result::write_conflict_file
@@ -1884,7 +1923,7 @@ parse_resolve_conflicts_str(basic_io::pa
conflict.left_resolution.first = resolve_conflicts::rename;
pars.sym();
- conflict.left_resolution.second = file_path_external (utf8(pars.token));
+ conflict.left_resolution.second = resolve_conflicts::new_file_path(pars.token);
pars.str();
}
else if (pars.symp (syms::resolved_rename_right))
@@ -1896,7 +1935,7 @@ parse_resolve_conflicts_str(basic_io::pa
conflict.right_resolution.first = resolve_conflicts::rename;
pars.sym();
- conflict.right_resolution.second = file_path_external (utf8(pars.token));
+ conflict.right_resolution.second = resolve_conflicts::new_file_path(pars.token);
pars.str();
}
else if (pars.symp (syms::resolved_user))
@@ -1908,7 +1947,7 @@ parse_resolve_conflicts_str(basic_io::pa
conflict.resolution.first = resolve_conflicts::content_user;
pars.sym();
- conflict.resolution.second = pars.token;
+ conflict.resolution.second = resolve_conflicts::new_optimal_path(pars.token);
pars.str();
}
else
@@ -2019,9 +2058,22 @@ roster_merge_result::resolve_duplicate_n
switch (conflict.left_resolution.first)
{
+ case resolve_conflicts::content_user:
+ P(F("replacing content of %s with %s") % left_name % conflict.left_resolution.second->as_external());
+ I(false); // FIXME_RESOLVE_CONFLICTS: not implemented
+ // something like adaptor.record_file();
+ break;
+
+ case resolve_conflicts::drop:
+ P(F("dropping %s") % left_name);
+ I(false); // FIXME_RESOLVE_CONFLICTS: not implemented
+ // something like drop_detached_node();
+ break;
+
case resolve_conflicts::rename:
P(F("renaming %s to %s") % left_name % conflict.left_resolution.second);
- attach_node (lua, this->roster, left_nid, conflict.left_resolution.second);
+ attach_node
+ (lua, this->roster, left_nid, file_path_internal (conflict.left_resolution.second->as_internal()));
break;
case resolve_conflicts::none:
@@ -2036,7 +2088,8 @@ roster_merge_result::resolve_duplicate_n
{
case resolve_conflicts::rename:
P(F("renaming %s to %s") % right_name % conflict.right_resolution.second);
- attach_node (lua, this->roster, right_nid, conflict.right_resolution.second);
+ attach_node
+ (lua, this->roster, right_nid, file_path_internal (conflict.right_resolution.second->as_internal()));
break;
case resolve_conflicts::none:
@@ -2099,7 +2152,8 @@ roster_merge_result::resolve_file_conten
case resolve_conflicts::content_user:
{
- P(F("replacing content of %s, %s with %s") % left_name % right_name % conflict.resolution.second);
+ P(F("replacing content of %s, %s with %s") %
+ left_name % right_name % conflict.resolution.second->as_external());
file_id result_id;
file_data left_data, right_data, result_data;
@@ -2107,7 +2161,7 @@ roster_merge_result::resolve_file_conten
adaptor.get_version(conflict.left, left_data);
adaptor.get_version(conflict.right, right_data);
- read_data(system_path(conflict.resolution.second), result_raw_data);
+ read_data(*conflict.resolution.second, result_raw_data);
result_data = file_data(result_raw_data);
calculate_ident(result_data, result_id);
@@ -2277,8 +2331,6 @@ namespace
return false;
}
- enum side_t { left_side, right_side };
-
void
assign_name(roster_merge_result & result, node_id nid,
node_id parent, path_component name, side_t side)
============================================================
--- roster_merge.hh cc362cd4a0b539c2fbdfd892a9a171586fc84d5c
+++ roster_merge.hh 3bbdacaa0de545b88f71e178ca7931616ea0eb83
@@ -29,7 +29,15 @@ namespace resolve_conflicts
namespace resolve_conflicts
{
- enum resolution_t {none, content_user, content_internal, rename};
+ enum resolution_t {none, content_user, content_internal, rename, drop};
+
+ typedef std::pair > file_resolution_t;
+
+ boost::shared_ptr new_file_path(std::string path);
+
+ // Return a file_path, bookkeeping_path, or system_path, as appropriate.
+ // This keeps the file names in the conflict file relative if possible.
+ boost::shared_ptr new_optimal_path(std::string path);
}
// renaming the root dir allows these:
@@ -94,7 +102,9 @@ struct duplicate_name_conflict
{
node_id left_nid, right_nid;
std::pair parent_name;
- std::pair left_resolution, right_resolution;
+ // file part of resolution must be a file_path if resolution is 'rename';
+ // it may be a bookkeeping or system path if resolution is 'user'.
+ resolve_conflicts::file_resolution_t left_resolution, right_resolution;
duplicate_name_conflict ()
{left_resolution.first = resolve_conflicts::none;
@@ -121,18 +131,14 @@ struct file_content_conflict
{
node_id nid;
file_id left, right;
- // The second item is a file path. We don't use type 'file_path' because
- // that can't be in _MTN. We don't specify 'bookkeeping_path' because that
- // _must_ be in _MTN. We want users to have a choice of workflow. This is
- // local data only, so it is in system encoding.
- std::pair resolution;
+ resolve_conflicts::file_resolution_t resolution;
file_content_conflict () :
- nid(the_null_node),
- resolution(std::make_pair(resolve_conflicts::none, std::string())) {};
+ nid(the_null_node)
+ {resolution.first = resolve_conflicts::none;};
file_content_conflict(node_id nid) :
- nid(nid), resolution(std::make_pair(resolve_conflicts::none, std::string())) {};
+ nid(nid) {resolution.first = resolve_conflicts::none;};
};
template <> void dump(invalid_name_conflict const & conflict, std::string & out);
@@ -239,7 +245,7 @@ struct roster_merge_result
// If validate, compare file contents to existing conflicts, and add
// resolutions. Otherwise just read into conflicts.
void read_conflict_file(database & db,
- std::string file_name,
+ system_path const file_name,
revision_id & ancestor_rid,
revision_id & left_rid,
revision_id & right_rid,
@@ -248,11 +254,9 @@ struct roster_merge_result
roster_t & right_roster,
marking_map & r_marking);
- void set_first_conflict(std::string conflict);
-
void write_conflict_file(database & db,
lua_hooks & lua,
- std::string file_name,
+ system_path const file_name,
revision_id const & ancestor_rid,
revision_id const & left_rid,
revision_id const & right_rid,
============================================================
--- tests/resolve_conflict_all_resolutions/__driver__.lua 709ab05fab5b76ebcbe76b2a9fdf12307f690522
+++ tests/resolve_conflict_all_resolutions/__driver__.lua 2c013201e50e953df1ca9b55ad726b4c633c9610
@@ -1,4 +1,5 @@
--- Test setting conflict resolutions in a conflict file
+-- Test showing and setting all possible conflict resolutions in a
+-- conflict file.
mtn_setup()
@@ -37,31 +38,45 @@ beth_1 = base_revision()
commit("testbranch", "beth_1")
beth_1 = base_revision()
-check (mtn("automate", "show_conflicts"), 0, true, nil)
-canonicalize("stdout")
-check(samefilestd("conflicts-1", "stdout"))
+-- Don't use _MTN/conflicts, to test that capability
+mkdir("resolutions")
+check (mtn("conflicts", "--conflict_file=resolutions/conflicts", "store", abe_1, beth_1), 0, true, nil)
+check(samefilestd("conflicts-1", "resolutions/conflicts"))
--- Save the conflicts file so we can edit it
-mkdir("resolutions")
rename("stdout", "resolutions/conflicts")
--- 'resolve_conflict' specifies a resolution for the first unresolved
--- conflict in the file.
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "show_first", resolution), 0, true, nil)
+canonicalize("stdout")
+check(samefilestd("show_first-checkout_left"), "stdout")
+
resolution = "resolved_drop_left\n resolved_user_right \"resolutions/checkout_left.sh\""
-check(mtn("resolve_conflict", "resolutions/conflicts", resolution), 0, nil, nil)
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "resolve_first", resolution), 0, nil, nil)
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "show_first", resolution), 0, true, nil)
+canonicalize("stdout")
+check(samefilestd("show_first-checkout_right"), "stdout")
+
+resolution = "resolved_drop_rightt\n resolved_user_left \"resolutions/checkout_right.sh\""
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "resolve_first", resolution), 0, nil, nil)
+
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "show_first", resolution), 0, true, nil)
+canonicalize("stdout")
+check(samefilestd("show_first-thermostat"), "stdout")
+
resolution = "resolved_rename_left \"thermostat-westinghouse.c\"\n resolved_rename_right \"thermostat-honeywell.c\""
-check(mtn("resolve_conflict", "resolutions/conflicts", resolution), 0, nil, nil)
+check(mtn("conflicts", "--conflict_file=resolutions/conflicts", "resolve_first", resolution), 0, nil, nil)
-check(samefilestd("conflicts-2", "resolutions/conflicts"))
+check(samefilestd("conflicts-resolved", "resolutions/conflicts"))
-- This succeeds
check(mtn("merge", "--resolve-conflicts-file", "resolutions/conflicts"), 0, true, nil)
canonicalize("stdout")
check(samefilestd("merge-1", "stdout"))
+-- Verify user specified resolution files
check(mtn("update"), 0, nil, false)
check("checkout_left.sh beth 2" == readfile("checkout_left.sh"))
+check("checkout_right.sh beth 2" == readfile("checkout_right.sh"))
-- end of file
============================================================
--- tests/resolve_duplicate_name_conflict/__driver__.lua 37b9d15331a7f470419dbc20e416c0142c45237c
+++ tests/resolve_duplicate_name_conflict/__driver__.lua 320c07fc8141d2eac97f2f6a71e6b9d4754799c9
@@ -44,35 +44,39 @@ check(mtn("merge"), 1, nil, false)
-- For thermostat.c, she specifies a conflict resolution that renames
-- both versions.
-check (mtn("automate", "show_conflicts"), 0, true, nil)
-canonicalize("stdout")
-check(samefilestd("conflicts-1", "stdout"))
+check(mtn("conflicts", "store"), 0, true, nil)
+check(samefilestd("conflicts-1", "_MTN/conflicts"))
--- Save the conflicts file so we can edit it later
-mkdir("resolutions")
-rename("stdout", "resolutions/conflicts")
+-- Find out what the first unresolved conflict is
+check(mtn("conflicts", "show_first"), 0, nil, true)
-- Retrieve Abe's version of checkout.sh, and pretend we did a manual
--- merge, using our favorite merge tool. We put the files outside the
--- workspace, so 'update' doesn't complain.
+-- merge, using our favorite merge tool. We put the files in the
+-- bookkeeping area, so mtn doesn't see them.
+mkdir("_MTN/resolutions")
check(mtn("automate", "get_file", "61b8d4fb0e5d78be111f691b955d523c782fa92e"), 0, true, nil)
-rename("stdout", "resolutions/checkout.sh-abe")
-check(readfile("resolutions/checkout.sh-abe") == "checkout.sh abe 1")
+rename("stdout", "_MTN/resolutions/checkout.sh-abe")
+check(readfile("_MTN/resolutions/checkout.sh-abe") == "checkout.sh abe 1")
-writefile("resolutions/checkout.sh", "checkout.sh beth 2")
+writefile("_MTN/resolutions/checkout.sh", "checkout.sh beth 2")
--- 'resolve_conflict' specifies a resolution for the first unresolved
--- conflict in the file.
-resolution = "resolved_drop_left\n resolved_user_right \"resolutions/checkout.sh\""
-check(mtn("resolve_conflict", "resolutions/conflicts", resolution), 0, nil, nil)
+-- specify a part of the resolution for the first unresolved conflict in the file.
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, nil)
-resolution = "resolved_rename_left \"thermostat-westinghouse.c\"\n resolved_rename_right \"thermostat-honeywell.c\""
-check(mtn("resolve_conflict", "resolutions/conflicts", resolution), 0, nil, nil)
+-- and now the other part
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(mtn("conflicts", "resolve_first_right", "user", "_MTN/resolutions/checkout.sh"), 0, nil, nil)
-check(samefilestd("conflicts-2", "resolutions/conflicts"))
+-- Find out what the next unresolved conflict is
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(mtn("conflicts", "resolve_first_left", "rename", "thermostat-westinghouse.c"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "rename", "thermostat-honeywell.c"), 0, nil, nil)
+
+check(samefilestd("conflicts-resolved", "_MTN/conflicts"))
+
-- This succeeds
-check(mtn("merge", "--resolve-conflicts-file", "resolutions/conflicts"), 0, true, nil)
+check(mtn("merge", "--resolve-conflicts-file", "_MTN/conflicts"), 0, true, nil)
canonicalize("stdout")
check(samefilestd("merge-1", "stdout"))