# # # add_dir "tests/automate_show_conflicts" # # add_dir "tests/resolve_duplicate_name_conflict" # # add_file "cmd_merging.hh" # content [8fdbae1fdc78bf36f86efb904d8e068684743ce6] # # add_file "tests/automate_show_conflicts/__driver__.lua" # content [1702dae2d13bb2b959fb7c1a9b9b669ae0d70c05] # # add_file "tests/automate_show_conflicts/expected-1.stdout" # content [f1d2d2f924e986ac86fdf7b36c94bcdf32beec15] # # add_file "tests/automate_show_conflicts/expected-2.stdout" # content [e242ed3bffccdf271b7fbaf34ed72d089537b42f] # # add_file "tests/resolve_duplicate_name_conflict/__driver__.lua" # content [d876aec0f26b52b9f8016aa8580d5fcfd4fae293] # # patch "automate.cc" # from [be3c9252738e13b5a931b2cb063c30e7565ef203] # to [ef2ff038b98144c19686fc37f49222a4c5e9152e] # # patch "cmd_automate.cc" # from [fdad3e113ea4f077db08c1e789c1f2bf39abe2c0] # to [aa644eac27e978e1f51203db320206272a98f8a1] # # patch "cmd_merging.cc" # from [9a1dacae8931c700a6f7acd0b71c50082f663117] # to [fb2f2037a464226d7e99feb91ea119b6f90c76b2] # # patch "merge.cc" # from [5110775ef0a938c14246f95abd33a6b97118baba] # to [3085e923fba02976219da4889f0a71892a91ca74] # # patch "monotone.texi" # from [5fab7072154208ae616663e4579cef644987b6bd] # to [5179a73b50747a8bf47b842250caf0917bfe341b] # # patch "roster_merge.cc" # from [d565f9299ca85d77bd1d7f2a29196d7161f0ea08] # to [13c48d6355d0a7521acaeb841383395a3740119a] # # patch "roster_merge.hh" # from [fb803cd207c21b77a69115552a6820664b607ba5] # to [528467e96d5195c9639b3c3b48799391c3fb2d7f] # ============================================================ --- cmd_merging.hh 8fdbae1fdc78bf36f86efb904d8e068684743ce6 +++ cmd_merging.hh 8fdbae1fdc78bf36f86efb904d8e068684743ce6 @@ -0,0 +1,21 @@ +#ifndef __CMD_MERGING_HH__ +#define __CMD_MERGING__HH__ + +// 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. + +// Functions in cmd_merging.cc that are shared between command-line +// and automate interfaces + +// If basic_io, display results in basic_io stanzas. Otherwise use +// command-line format. +void +show_conflicts_core (database & db, revision_id const & l_id, revision_id const & r_id, bool const basic_io, std::ostream & output); + +#endif ============================================================ --- tests/automate_show_conflicts/__driver__.lua 1702dae2d13bb2b959fb7c1a9b9b669ae0d70c05 +++ tests/automate_show_conflicts/__driver__.lua 1702dae2d13bb2b959fb7c1a9b9b669ae0d70c05 @@ -0,0 +1,39 @@ +-- Test 'automate show_conflicts' + +mtn_setup() + +check(get("expected-1.stdout")) +check(get("expected-2.stdout")) + +-- Get a non-empty base revision, then create conflicts +addfile("randomfile", "blah blah blah") +commit() +base = base_revision() + +-- Abe adds conflict files +addfile("checkout.sh", "checkout.sh abe 1") +addfile("thermostat.c", "thermostat westinghouse") +commit() +abe_1 = base_revision() + +revert_to(base) + +-- Beth adds conflict files +addfile("checkout.sh", "checkout.sh beth 1") +addfile("thermostat.c", "thermostat honeywell") +commit() +beth_1 = base_revision() + +check(mtn("automate", "show_conflicts"), 0, true, false) +canonicalize("stdout") +check(readfile("expected-1.stdout") == readfile("stdout")) + +-- Now specify revisions, in an order that reverses left/right from +-- the previous, to show the arguments are used. + +check(mtn("automate", "show_conflicts", abe_1, beth_1), 0, true, false) +canonicalize("stdout") +check(readfile("expected-2.stdout") == readfile("stdout")) + + +-- end of file ============================================================ --- tests/automate_show_conflicts/expected-1.stdout f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 +++ tests/automate_show_conflicts/expected-1.stdout f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 @@ -0,0 +1 @@ +foo ============================================================ --- tests/automate_show_conflicts/expected-2.stdout e242ed3bffccdf271b7fbaf34ed72d089537b42f +++ tests/automate_show_conflicts/expected-2.stdout e242ed3bffccdf271b7fbaf34ed72d089537b42f @@ -0,0 +1 @@ +bar ============================================================ --- tests/resolve_duplicate_name_conflict/__driver__.lua d876aec0f26b52b9f8016aa8580d5fcfd4fae293 +++ tests/resolve_duplicate_name_conflict/__driver__.lua d876aec0f26b52b9f8016aa8580d5fcfd4fae293 @@ -0,0 +1,52 @@ +-- Test/demonstrate handling of a duplicate name conflict; Abe and +-- Beth add files with the same names. +-- +-- For checkout.sh, the user intent is that there be +-- one file with that name; the contents should be merged. +-- +-- For thermostat.c, there should be two files; +-- thermostat-westinghouse.c and thermostat-honeywell.c + +mtn_setup() + +-- Get a non-empty base revision +addfile("randomfile", "blah blah blah") +commit() +base = base_revision() + +-- Abe adds conflict files +addfile("checkout.sh", "checkout.sh abe 1") +addfile("thermostat.c", "thermostat westinghouse") +commit() +abe_1 = base_revision() + +revert_to(base) + +-- Beth adds files, and attempts to merge +addfile("checkout.sh", "checkout.sh beth 1") +addfile("thermostat.c", "thermostat honeywell") +commit() +beth_1 = base_revision() + +-- This fails due to duplicate name conflicts +check(mtn("merge"), 1, false, false) + +-- Beth fixes the conflicts. +-- +-- For checkout.sh, she retrieves Abe's version to merge with hers, +-- using 'automate get_file_of'. This requires knowing the revision id +-- of Abe's commit, which we get from 'automate show_conflicts'. +-- +-- For thermostat.c, she renames her version, letting Abe rename his. + +check (mtn("automate show_conflicts"), 0, false, false) + +check (mtn ("drop", "checkout.sh"), 0, false, false) +check (mtn ("rename", "thermostat.c", "thermostat-honeywell.c"), 0, false, false) +commit() + +-- This succeeds +check(mtn("merge"), 0, false, false) + +-- end of file + ============================================================ --- automate.cc be3c9252738e13b5a931b2cb063c30e7565ef203 +++ automate.cc ef2ff038b98144c19686fc37f49222a4c5e9152e @@ -1,5 +1,5 @@ +// Copyright (C) 2007 - 2008 Stephen Leake // Copyright (C) 2004, 2007 Nathaniel Smith -// Copyright (C) 2007 - 2008 Stephen Leake // // This program is made available under the GNU GPL version 2.0 or // greater. See the accompanying file COPYING for details. @@ -25,6 +25,7 @@ #include "basic_io.hh" #include "cert.hh" #include "cmd.hh" +#include "cmd_merging.hh" #include "commands.hh" #include "constants.hh" #include "inodeprint.hh" @@ -1249,7 +1250,7 @@ CMD_AUTOMATE(get_current_revision, N_("[ excluded, join_words(execid)); rev.check_sane(); N(rev.is_nontrivial(), F("no changes to commit")); - + calculate_ident(rev, ident); write_revision(rev, dat); @@ -2251,6 +2252,54 @@ CMD_AUTOMATE(drop_db_variables, N_("DOMA } } +// Name: show_conflicts +// Arguments: +// Two revision ids (optional, determined from the workspace if not given; there must be exactly two heads) +// Added in: 7.1 +// Purpose: Prints the conflicts between two revisions, to aid in merging them. +// +// Output format: see monotone.texi +// +// Error conditions: +// +// If the revision IDs are unknown or invalid prints an error message to +// stderr and exits with status 1. +// +// If revision ids are not given, and the current workspace does not have +// two heads, prints an error message to stderr and exits with status 1. +// +CMD_AUTOMATE(show_conflicts, N_("[REVID, REVID]"), + N_("Shows the conflicts between two revisions (default two heads of current workspace)"), + "", + options::opts::none) +{ + database db(app); + project_t project(db); + revision_id l_id, r_id; + + if (args.size() == 0) + { + // get ids from heads + 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 exactly 2 for show_conflicts") % app.opts.branchname % heads.size()); + l_id = *heads.begin(); + r_id = *heads.end(); + } + 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 + throw usage(execid); + + show_conflicts_core(db, l_id, r_id, true, output); +} + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- cmd_automate.cc fdad3e113ea4f077db08c1e789c1f2bf39abe2c0 +++ cmd_automate.cc aa644eac27e978e1f51203db320206272a98f8a1 @@ -63,7 +63,7 @@ namespace commands { } } -static string const interface_version = "7.0"; +static string const interface_version = "7.1"; // Major or minor number only increments once for each monotone release; // check the most recent release before incrementing this. ============================================================ --- cmd_merging.cc 9a1dacae8931c700a6f7acd0b71c50082f663117 +++ cmd_merging.cc fb2f2037a464226d7e99feb91ea119b6f90c76b2 @@ -1,3 +1,4 @@ +// Copyright (C) 2008 Stephen Leake // Copyright (C) 2002 Graydon Hoare // // This program is made available under the GNU GPL version 2.0 or @@ -12,7 +13,9 @@ #include #include +#include "basic_io.hh" #include "cmd.hh" +#include "cmd_merging.hh" #include "diff_patch.hh" #include "merge.hh" #include "restrictions.hh" @@ -818,6 +821,22 @@ CMD(show_conflicts, "show_conflicts", "" revision_id l_id, r_id; complete(app.opts, app.lua, project, idx(args,0)(), l_id); complete(app.opts, app.lua, project, idx(args,1)(), r_id); + + show_conflicts_core(db, l_id, r_id, false, std::cout); +} + +namespace +{ + namespace syms + { + symbol const left("left"); + symbol const right("right"); + } +} + +void +show_conflicts_core (database & db, revision_id const & l_id, revision_id const & r_id, bool const basic_io, std::ostream & output) +{ N(!is_ancestor(db, l_id, r_id), F("%s is an ancestor of %s; no merge is needed.") % l_id % r_id); N(!is_ancestor(db, r_id, l_id), @@ -827,41 +846,52 @@ CMD(show_conflicts, "show_conflicts", "" db.get_roster(l_id, l_roster, l_marking); db.get_roster(r_id, r_roster, r_marking); set l_uncommon_ancestors, r_uncommon_ancestors; - db.get_uncommon_ancestors(l_id, r_id, - l_uncommon_ancestors, - r_uncommon_ancestors); + 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); // note that left and right are in the order specified on the command line - // they are not in lexical order as they are with other merge commands - // so they may appear swapped here. perhaps we should sort left and right - // before using them? + // they are not in lexical order as they are with other merge commands so + // they may appear swapped here. The user may have done that deliberately, + // especially via automate, so we don't sort them here. - P(F("[left] %s") % l_id); - P(F("[right] %s") % r_id); + if (basic_io) + { + basic_io::stanza st; + basic_io::printer pr; + st.push_hex_pair(syms::left, l_id.inner()); + st.push_hex_pair(syms::right, r_id.inner()); + pr.print_stanza(st); + output.write(pr.buf.data(), pr.buf.size()); + } + else + { + P(F("[left] %s") % l_id); + P(F("[right] %s") % r_id); + } if (result.is_clean()) { - P(F("no conflicts detected")); + if (not basic_io) + P(F("no conflicts detected")); } else { content_merge_database_adaptor adaptor(db, l_id, r_id, l_marking, r_marking); - result.report_missing_root_conflicts(l_roster, r_roster, adaptor); - result.report_invalid_name_conflicts(l_roster, r_roster, adaptor); - result.report_directory_loop_conflicts(l_roster, r_roster, adaptor); + result.report_missing_root_conflicts(l_roster, r_roster, adaptor, basic_io, output); + result.report_invalid_name_conflicts(l_roster, r_roster, adaptor, basic_io, output); + result.report_directory_loop_conflicts(l_roster, r_roster, adaptor, basic_io, output); - result.report_orphaned_node_conflicts(l_roster, r_roster, adaptor); - result.report_multiple_name_conflicts(l_roster, r_roster, adaptor); - result.report_duplicate_name_conflicts(l_roster, r_roster, adaptor); + result.report_orphaned_node_conflicts(l_roster, r_roster, adaptor, basic_io, output); + result.report_multiple_name_conflicts(l_roster, r_roster, adaptor, basic_io, output); + result.report_duplicate_name_conflicts(l_roster, r_roster, adaptor, basic_io, output); - result.report_attribute_conflicts(l_roster, r_roster, adaptor); - result.report_file_content_conflicts(l_roster, r_roster, adaptor); + result.report_attribute_conflicts(l_roster, r_roster, adaptor, basic_io, output); + result.report_file_content_conflicts(l_roster, r_roster, adaptor, basic_io, output); } } ============================================================ --- merge.cc 5110775ef0a938c14246f95abd33a6b97118baba +++ merge.cc 3085e923fba02976219da4889f0a71892a91ca74 @@ -1,4 +1,5 @@ -// Copyright (C) 2005 Nathaniel Smith +// Copyright (C) 2008 Stephen Leake +// Copyright (C) 2008 Nathaniel Smith // // This program is made available under the GNU GPL version 2.0 or // greater. See the accompanying file COPYING for details. @@ -8,6 +9,7 @@ // PURPOSE. #include "base.hh" +#include #include #include @@ -141,16 +143,16 @@ resolve_merge_conflicts(lua_hooks & lua, if (result.has_non_content_conflicts()) { - result.report_missing_root_conflicts(left_roster, right_roster, adaptor); - result.report_invalid_name_conflicts(left_roster, right_roster, adaptor); - result.report_directory_loop_conflicts(left_roster, right_roster, adaptor); + result.report_missing_root_conflicts(left_roster, right_roster, adaptor, false, std::cout); + result.report_invalid_name_conflicts(left_roster, right_roster, adaptor, false, std::cout); + result.report_directory_loop_conflicts(left_roster, right_roster, adaptor, false, std::cout); - result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor); - result.report_multiple_name_conflicts(left_roster, right_roster, adaptor); - result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor); + result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor, false, std::cout); + result.report_multiple_name_conflicts(left_roster, right_roster, adaptor, false, std::cout); + result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor, false, std::cout); - result.report_attribute_conflicts(left_roster, right_roster, adaptor); - result.report_file_content_conflicts(left_roster, right_roster, adaptor); + result.report_attribute_conflicts(left_roster, right_roster, adaptor, false, std::cout); + result.report_file_content_conflicts(left_roster, right_roster, adaptor, false, std::cout); } else if (result.has_content_conflicts()) { @@ -166,7 +168,7 @@ resolve_merge_conflicts(lua_hooks & lua, if (remaining > 0) { P(F("%d content conflicts require user intervention") % remaining); - result.report_file_content_conflicts(left_roster, right_roster, adaptor); + result.report_file_content_conflicts(left_roster, right_roster, adaptor, false, std::cout); try_to_merge_files(lua, left_roster, right_roster, result, adaptor, user_merge); ============================================================ --- monotone.texi 5fab7072154208ae616663e4579cef644987b6bd +++ monotone.texi 5179a73b50747a8bf47b842250caf0917bfe341b @@ -8524,6 +8524,63 @@ @section Automation @end table address@hidden mtn automate show_conflicts + address@hidden @strong address@hidden Arguments: + +Optional left and right revision ids. They default to the two heads of +the current workspace. + address@hidden Added in: + +7.1 + address@hidden Purpose: + +Show all conflicts between two revisions. + address@hidden Sample output: address@hidden address@hidden + left "1337cb1059c4bc3e376b14381b43e9383c654da1" + right "d5f1dd136c86b5bbd5e71b0c3365667e328af492" + + conflict "duplicate name" + name "checkout.sh" + left_id "FIXME:left id" + left_type file + right_id "FIXME:right id" +right_type file + +FIXME: show all conflicts + address@hidden group address@hidden smallexample + address@hidden Output format: + +First the revision ids of the left and right revisions are printed in +one basic_io stanza. + +Then each conflict is listed in a basic_io stanza. Stanzas are +separated by blank lines. + +Each conflict stanza starts with a @code{conflict} line, and contains +up to FIXME: lines. The order of the lines is not important, and may +change in future revisions, except that the first line will always be address@hidden + address@hidden Error conditions: + +If the revision IDs are gvien, but either is unknown or +invalid, prints an error message to stderr and exits with status 1. + +If revision ids are not given, and the current workspace does not have +two heads, prints an error message to stderr and exits with status 1. + address@hidden table + @end ftable @page ============================================================ --- roster_merge.cc d565f9299ca85d77bd1d7f2a29196d7161f0ea08 +++ roster_merge.cc 13c48d6355d0a7521acaeb841383395a3740119a @@ -1,3 +1,4 @@ +// Copyright (C) 2008 Stephen Leake // Copyright (C) 2005 Nathaniel Smith // // This program is made available under the GNU GPL version 2.0 or @@ -12,6 +13,7 @@ #include +#include "basic_io.hh" #include "vocab.hh" #include "roster_merge.hh" #include "parallel_iter.hh" @@ -177,10 +179,27 @@ namespace } } +namespace +{ + namespace syms + { + symbol const conflict("conflict"); + symbol const name("name"); + symbol const left_id("left_id"); + symbol const right_id("right_id"); + symbol const left_type("left_type"); + symbol const right_type("right_type"); + symbol const left_name("left_name"); + symbol const right_name("right_name"); + } +} + void roster_merge_result::report_missing_root_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -243,7 +262,9 @@ roster_merge_result::report_invalid_name void roster_merge_result::report_invalid_name_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -312,7 +333,9 @@ roster_merge_result::report_directory_lo void roster_merge_result::report_directory_loop_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -363,7 +386,9 @@ roster_merge_result::report_orphaned_nod void roster_merge_result::report_orphaned_node_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -461,7 +486,9 @@ roster_merge_result::report_multiple_nam void roster_merge_result::report_multiple_name_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -502,7 +529,9 @@ roster_merge_result::report_duplicate_na void roster_merge_result::report_duplicate_name_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -514,6 +543,9 @@ roster_merge_result::report_duplicate_na node_id left_nid, right_nid; + basic_io::stanza st; + basic_io::printer pr; + left_nid = conflict.left_nid; right_nid = conflict.right_nid; @@ -533,8 +565,17 @@ roster_merge_result::report_duplicate_na adaptor.get_ancestral_roster(left_nid, left_lca_rid, left_lca_roster); adaptor.get_ancestral_roster(right_nid, right_lca_rid, right_lca_roster); - P(F("conflict: duplicate name '%s'") % left_name); - + if (basic_io) + { + st.push_str_pair(syms::conflict, "duplicate_name"); + st.push_str_pair(syms::name, left_name.as_external()); + st.push_hex_pair(syms::left_id, left_lca_rid.inner()); // FIXME: get file revision, not ancestor revision? + st.push_hex_pair(syms::right_id, right_lca_rid.inner()); + } + else + { + P(F("conflict: duplicate name '%s'") % left_name); + } node_type left_type = get_type(left_roster, left_nid); node_type right_type = get_type(right_roster, right_nid); @@ -542,14 +583,26 @@ roster_merge_result::report_duplicate_na !right_lca_roster->has_node(left_nid)) { if (left_type == file_type) - P(F("added as a new file on the left")); + if (basic_io) + st.push_str_pair(syms::left_type, "added file"); + else + P(F("added as a new file on the left")); else - P(F("added as a new directory on the left")); + if (basic_io) + st.push_str_pair(syms::left_type, "added directory"); + else + P(F("added as a new directory on the left")); if (right_type == file_type) - P(F("added as a new file on the right")); + if (basic_io) + st.push_str_pair(syms::right_type, "added file"); + else + P(F("added as a new file on the right")); else - P(F("added as a new directory on the right")); + if (basic_io) + st.push_str_pair(syms::right_type, "added directory"); + else + P(F("added as a new directory on the right")); } else if (!left_lca_roster->has_node(right_nid) && right_lca_roster->has_node(left_nid)) @@ -558,14 +611,32 @@ roster_merge_result::report_duplicate_na left_lca_roster->get_name(left_nid, left_lca_name); if (left_type == file_type) - P(F("renamed from file '%s' on the left") % left_lca_name); + if (basic_io) + { + st.push_str_pair(syms::left_type, "renamed file"); + st.push_str_pair(syms::left_name, left_lca_name.as_external()); + } + else + P(F("renamed from file '%s' on the left") % left_lca_name); else - P(F("renamed from directory '%s' on the left") % left_lca_name); + if (basic_io) + { + st.push_str_pair(syms::left_type, "renamed directory"); + st.push_str_pair(syms::left_name, left_lca_name.as_external()); + } + else + P(F("renamed from directory '%s' on the left") % left_lca_name); if (right_type == file_type) - P(F("added as a new file on the right")); + if (basic_io) + st.push_str_pair(syms::right_type, "added file"); + else + P(F("added as a new file on the right")); else - P(F("added as a new directory on the right")); + if (basic_io) + st.push_str_pair(syms::right_type, "added directory"); + else + P(F("added as a new directory on the right")); } else if (left_lca_roster->has_node(right_nid) && !right_lca_roster->has_node(left_nid)) @@ -574,14 +645,32 @@ roster_merge_result::report_duplicate_na right_lca_roster->get_name(right_nid, right_lca_name); if (left_type == file_type) - P(F("added as a new file on the left")); + if (basic_io) + st.push_str_pair(syms::left_type, "added file"); + else + P(F("added as a new file on the left")); else - P(F("added as a new directory on the left")); + if (basic_io) + st.push_str_pair(syms::right_type, "added directory"); + else + P(F("added as a new directory on the left")); if (right_type == file_type) - P(F("renamed from file '%s' on the right") % right_lca_name); + if (basic_io) + { + st.push_str_pair(syms::right_type, "renamed file"); + st.push_str_pair(syms::right_name, right_lca_name.as_external()); + } + else + P(F("renamed from file '%s' on the right") % right_lca_name); else - P(F("renamed from directory '%s' on the right") % right_lca_name); + if (basic_io) + { + st.push_str_pair(syms::right_type, "renamed directory"); + st.push_str_pair(syms::right_name, right_lca_name.as_external()); + } + else + P(F("renamed from directory '%s' on the right") % right_lca_name); } else if (left_lca_roster->has_node(right_nid) && right_lca_roster->has_node(left_nid)) @@ -591,24 +680,53 @@ roster_merge_result::report_duplicate_na right_lca_roster->get_name(right_nid, right_lca_name); if (left_type == file_type) - P(F("renamed from file '%s' on the left") % left_lca_name); + if (basic_io) + { + st.push_str_pair(syms::left_type, "renamed file"); + st.push_str_pair(syms::left_name, left_lca_name.as_external()); + } + else + P(F("renamed from file '%s' on the left") % left_lca_name); else - P(F("renamed from directory '%s' on the left") % left_lca_name); + if (basic_io) + { + st.push_str_pair(syms::left_type, "renamed directory"); + st.push_str_pair(syms::left_name, left_lca_name.as_external()); + } + else + P(F("renamed from directory '%s' on the left") % left_lca_name); if (right_type == file_type) - P(F("renamed from file '%s' on the right") % right_lca_name); + if (basic_io) + { + st.push_str_pair(syms::right_type, "renamed file"); + st.push_str_pair(syms::right_name, right_lca_name.as_external()); + } + else + P(F("renamed from file '%s' on the right") % right_lca_name); else - P(F("renamed from directory '%s' on the right") % right_lca_name); + if (basic_io) + { + st.push_str_pair(syms::right_type, "renamed directory"); + st.push_str_pair(syms::right_name, right_lca_name.as_external()); + } + else + P(F("renamed from directory '%s' on the right") % right_lca_name); } else I(false); + + if (basic_io) + output.write(pr.buf.data(), pr.buf.size()); } } void roster_merge_result::report_attribute_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); @@ -710,7 +828,9 @@ roster_merge_result::report_file_content void roster_merge_result::report_file_content_conflicts(roster_t const & left_roster, roster_t const & right_roster, - content_merge_adaptor & adaptor) const + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const { MM(left_roster); MM(right_roster); ============================================================ --- roster_merge.hh fb803cd207c21b77a69115552a6820664b607ba5 +++ roster_merge.hh 528467e96d5195c9639b3c3b48799391c3fb2d7f @@ -155,30 +155,46 @@ struct roster_merge_result void report_missing_root_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_invalid_name_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_directory_loop_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_orphaned_node_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_multiple_name_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_duplicate_name_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_attribute_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void report_file_content_conflicts(roster_t const & left, roster_t const & right, - content_merge_adaptor & adaptor) const; + content_merge_adaptor & adaptor, + bool basic_io, + std::ostream & output) const; void clear(); };