# # # add_dir "tests/automate_show_conflicts" # # add_file "tests/automate_show_conflicts/__driver__.lua" # content [ad6ea7ae925699a9a1e3be4339fa8a17078d5ae4] # # add_file "tests/automate_show_conflicts/expected-missing-root.stdout" # content [d975884f1defedb384b194f9ab1f4e4e0f5f492f] # # patch "cmd_merging.cc" # from [d88f56759e102e0e0f4bba4730d14cbbb637307b] # to [6a81e714d8098bf221652f4f634dee0da6055035] # # patch "merge.cc" # from [a9e8c3a5f704d0fc64e8c28e23c4a3ea376cb322] # to [a2c3037bc94027b942ffbcbded5a3fdfc588f363] # # patch "monotone.texi" # from [615e8a075b5e30913dd84b73a9499dd77ce9346f] # to [636e21c2b85ab832e50dd2f29dbd2f327c314849] # # patch "roster_merge.cc" # from [0e3b1be78c8073f29014b3533914adbdaed510b1] # to [66aa7c730cb7780c93615df48d0ec9d617d75e6d] # # patch "roster_merge.hh" # from [61dc2b4c870575223b62bca5b1edc56bf4589485] # to [34abb44e0b7f62ef5fff648ced4d43984511ccd6] # ============================================================ --- tests/automate_show_conflicts/__driver__.lua ad6ea7ae925699a9a1e3be4339fa8a17078d5ae4 +++ tests/automate_show_conflicts/__driver__.lua ad6ea7ae925699a9a1e3be4339fa8a17078d5ae4 @@ -0,0 +1,539 @@ +-- Create the various non-content conflict cases, check that +-- 'automate show_conflict' displays them properly. +-- +-- Cases are created in the same way as in conflict_messages/__driver__.lua + +mtn_setup() + +function setup(branch) + remove("_MTN") + remove("foo") + remove("bar") + remove("baz") + remove(branch) + check(mtn("setup", ".", "--branch", branch), 0, false, false) +end + +function create_second() + commit(branch .. "-propagate") + second = base_revision() + check(mtn("cert", second, "branch", branch)) +end + +function check_asc() + expected = "expected-" .. branch .. ".stdout" + check(get(expected)) + check(mtn("automate", "show_conflicts", first, second), 0, true, false) + canonicalize("stdout") + check(readfile(expected) == readfile("stdout")) +end + +-- missing root conflict + +branch = "missing-root" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +commit(branch) + +base = base_revision() + +check(mtn("co", "--branch", branch, branch), 0, false, false) +check(indir(branch, mtn("pivot_root", "foo", "bar")), 0, true, true) +check(indir(branch, mtn("commit", "--message", "commit")), 0, false, false) + +first = indir(branch, {base_revision})[1]() + +check(mtn("drop", "--recursive", "foo"), 0, false, false) + +create_second() +check_asc() + +-- invalid name add + +branch = "invalid-add" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +commit(branch) + +base = base_revision() + +check(mtn("co", "--branch", branch, branch), 0, false, false) +check(indir(branch, mtn("pivot_root", "foo", "bar")), 0, true, true) +check(indir(branch, mtn("commit", "--message", "commit")), 0, false, false) + +first = indir(branch, {base_revision})[1]() + +mkdir("foo/_MTN") +addfile("foo/_MTN/foo", branch .. "-foo") +addfile("foo/_MTN/bar", branch .. "-bar") + +create_second() +check_asc() + + +-- invalid name rename + +branch = "invalid-rename" +setup(branch) + +mkdir("foo") +mkdir("bad") +addfile("foo/foo", branch .. "-foofoo") +addfile("bad/_MTN", branch .. "--bar") +commit(branch) + +base = base_revision() + +check(mtn("co", "--branch", branch, branch), 0, false, false) +check(indir(branch, mtn("pivot_root", "foo", "bar")), 0, true, true) +check(indir(branch, mtn("commit", "--message", "commit")), 0, false, false) +first = indir(branch, {base_revision})[1]() + +check(mtn("mv", "bad/_MTN", "foo/_MTN"), 0, false, false) + +create_second() +check_asc() + + +-- directory loop conflict + +branch = "directory-loop" +setup(branch) + +mkdir("foo") +mkdir("bar") +addfile("foo/foo", branch .. "-foofoo") +addfile("bar/bar", branch .. "-barbar") +commit(branch) + +base = base_revision() + +check(mtn("mv", "foo", "bar"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("mv", "bar", "foo"), 0, false, false) + +create_second() +check_asc() + + + +-- orphaned add + +branch = "orphaned-add" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +commit(branch) + +base = base_revision() + +addfile("foo/bar", branch .. "-foobar") +commit(branch) + +check(mtn("mv", "foo/bar", "foo/baz"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +remove("foo") +check(mtn("drop", "--recursive", "foo"), 0, false, false) + +create_second() +check_asc() + + +-- orphaned rename + +branch = "orphaned-rename" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +addfile("bar", branch .. "-bar") +commit(branch) + +base = base_revision() + +check(mtn("mv", "bar", "foo/bar"), 0, false, false) +commit(branch) +check(mtn("mv", "foo/bar", "foo/baz"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +remove("foo") +check(mtn("drop", "--recursive", "foo"), 0, false, false) + +create_second() +check_asc() + + + +-- multiple name conflict + +branch = "multiple-names" +setup(branch) + +addfile("foo", branch .. "-foo") +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "bar"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("mv", "foo", "baz"), 0, false, false) + +create_second() +check_asc() + + + +-- duplicate name conflict (adds) + +branch = "duplicate-adds" +setup(branch) + +addfile("foo", branch .. "-foo") +commit(branch) +base = base_revision() + +addfile("xxx", branch .. "-xxx") +commit(branch) + +check(mtn("mv", "xxx", "bar"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +addfile("bar", branch .. "-bar2") + +create_second() +check_asc() + + +-- duplicate name conflict (renames) + +branch = "duplicate-renames" +setup(branch) + +addfile("foo", branch .. "-foo") +addfile("bar", branch .. "-bar") + +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "abc"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("mv", "bar", "abc"), 0, false, false) + +create_second() +check_asc() + + +-- duplicate name conflict (add-rename) + +branch = "duplicate-add-rename" +setup(branch) + +addfile("foo", branch .. "-foo") + +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "bar"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +addfile("bar", branch .. "-bar") + +create_second() +check_asc() + + + +-- attribute conflict on attached node + +branch = "attribute-attached" +setup(branch) + +addfile("foo", branch .. "-foo") +check(mtn("attr", "set", "foo", "attr1", "value1"), 0, false, false) +check(mtn("attr", "set", "foo", "attr2", "value2"), 0, false, false) +commit(branch) +base = base_revision() + +check(mtn("attr", "set", "foo", "attr1", "valueX"), 0, false, false) +check(mtn("attr", "set", "foo", "attr2", "valueY"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("attr", "set", "foo", "attr1", "valueZ"), 0, false, false) +check(mtn("attr", "drop", "foo", "attr2"), 0, false, false) + +create_second() +check_asc() + + + +-- attribute conflict on detached node + +branch = "attribute-detached" +setup(branch) + +addfile("foo", branch .. "-foo") +check(mtn("attr", "set", "foo", "attr1", "value1"), 0, false, false) +check(mtn("attr", "set", "foo", "attr2", "value2"), 0, false, false) +commit(branch) +base = base_revision() + +check(mtn("attr", "set", "foo", "attr1", "valueX"), 0, false, false) +check(mtn("attr", "set", "foo", "attr2", "valueY"), 0, false, false) +check(mtn("mv", "foo", "bar"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("attr", "set", "foo", "attr1", "valueZ"), 0, false, false) +check(mtn("attr", "drop", "foo", "attr2"), 0, false, false) +check(mtn("mv", "foo", "baz"), 0, false, false) + +create_second() +check_asc() + + + +-- content conflict on attached node + +branch = "content-attached" +setup(branch) + +addfile("foo", branch .. "-foo") +addfile("bar", branch .. "-bar\none\ntwo\nthree") +addfile("baz", branch .. "-baz\naaa\nbbb\nccc") +commit(branch) +base = base_revision() + +writefile("foo", branch .. "-foo first revision") +writefile("bar", branch .. "-bar\nzero\none\ntwo\nthree") +writefile("baz", branch .. "-baz\nAAA\nbbb\nccc") +commit(branch) +first = base_revision() + +revert_to(base) + +writefile("foo", branch .. "-foo second revision") +writefile("bar", branch .. "-bar\none\ntwo\nthree\nfour") +writefile("baz", branch .. "-baz\naaa\nbbb\nCCC") + +create_second() +check_asc() + + +-- content conflict on detached node + +branch = "content-detached" +setup(branch) + +addfile("foo", branch .. "-foo") +commit(branch) +base = base_revision() + +writefile("foo", "foo first revision") +check(mtn("mv", "foo", "bar"), 0, false, false) + +commit(branch) +first = base_revision() + +revert_to(base) + +writefile("foo", "foo second revision") +check(mtn("mv", "foo", "baz"), 0, false, false) + +create_second() +check_asc() + + +-- multiple name plus duplicate name + +branch = "multiple-name-plus-duplicate-name" +setup(branch) + +addfile("foo", branch .. "-foo") +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "aaa"), 0, false, false) +addfile("bbb", branch .. "-bbb") + +commit(branch) +first = base_revision() + +revert_to(base) +remove("aaa") +remove("bbb") + +check(mtn("mv", "foo", "bbb"), 0, false, false) +addfile("aaa", branch .. "-aaa") + +-- this doesn't result in a duplicate name conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +create_second() +check_asc() + + +-- multiple name plus orphan + +branch = "multiple-name-plus-orphan" +setup(branch) + +mkdir("a") +mkdir("b") +check(mtn("add", "a"), 0, false, false) +check(mtn("add", "b"), 0, false, false) +addfile("foo", branch .. "-foo") +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "a"), 0, false, false) +check(mtn("rm", "b"), 0, false, false) +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("mv", "foo", "b"), 0, false, false) +check(mtn("rm", "a"), 0, false, false) + +-- this doesn't result in a directory loop conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +create_second() +check_asc() + + +-- multiple name plus directory loop + +branch = "multiple-name-plus-directory-loop" +setup(branch) + +mkdir("a") +mkdir("b") +mkdir("foo") +check(mtn("add", "a"), 0, false, false) +check(mtn("add", "b"), 0, false, false) +check(mtn("add", "foo"), 0, false, false) +commit(branch) +base = base_revision() + +check(mtn("mv", "foo", "a"), 0, false, false) +check(mtn("mv", "b", "a/foo"), 0, false, false) + +commit(branch) +first = base_revision() + +revert_to(base) + +check(mtn("mv", "foo", "b"), 0, false, false) +check(mtn("mv", "a", "b/foo"), 0, false, false) + +-- this doesn't result in a directory loop conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +create_second() +check_asc() + + + +-- duplicate name plus multiple name plus missing root + +-- the old root directory is pivoted out to aaa on one side and bbb on the other +-- causing a multiple name conflict + +-- a new root directory is pivoted in from foo on one side and bar on the other +-- causing a duplicate name conflict on "" + +-- these conflicts leave the root dir detached causing a missing root conflict + +branch = "duplicate-name-multiple-name-missing-root" +setup(branch) + +mkdir("foo") +mkdir("bar") +addfile("foo/foo", branch .. "-foofoo") +addfile("bar/bar", branch .. "-barbar") +commit(branch) + +base = base_revision() + +dir1 = branch .. "-1" +remove(dir1) + +check(mtn("co", "--revision", base, "--branch", branch, dir1), 0, false, false) +check(indir(dir1, mtn("pivot_root", "foo", "aaa")), 0, true, true) +check(indir(dir1, mtn("commit", "--message", "commit")), 0, false, false) + +first = indir(dir1, {base_revision})[1]() + +dir2 = branch .. "-2" +remove(dir2) + +check(mtn("co", "--revision", base, "--branch", branch, dir2), 0, false, false) +check(indir(dir2, mtn("pivot_root", "bar", "bbb")), 0, true, true) + +second = indir(dir2, {base_revision})[1]() + +check(mtn("cert", second, "branch", branch)) + +check_asc() + + +-- unrelated projects + +branch = "unrelated-projects" +setup(branch) + +copy("_MTN/revision", "clean") + +addfile("foo", branch .. "-foo first") +commit(branch) + +first = base_revision() + +copy("clean", "_MTN/revision") + +addfile("foo", branch .. "-foo second") + +check(mtn("commit", "--message", "commit", "--branch", branch .. "-propagate"), 0, false, false) + +second = base_revision() + +check(mtn("cert", second, "branch", branch)) + +check_asc() + +-- end of file ============================================================ --- tests/automate_show_conflicts/expected-missing-root.stdout d975884f1defedb384b194f9ab1f4e4e0f5f492f +++ tests/automate_show_conflicts/expected-missing-root.stdout d975884f1defedb384b194f9ab1f4e4e0f5f492f @@ -0,0 +1,14 @@ + left [532ab5011ea9e64aa212d4ea52363b1b8133d5ba] +right [b94a03a922c2c281a88d8988db64e76a32edb6a1] + + conflict "missing root" + name "foo" + left_type "pivoted root" +right_type "deleted directory" + + conflict "orphaned directory" + name "" +right_rev_id [ead03530f5fefe50c9010157c42c0ebe18086559] + right_type "deleted directory" + left_type "renamed directory" + left_name "bar" ============================================================ --- cmd_merging.cc d88f56759e102e0e0f4bba4730d14cbbb637307b +++ cmd_merging.cc 6a81e714d8098bf221652f4f634dee0da6055035 @@ -863,16 +863,16 @@ show_conflicts_core (database & db, revi content_merge_database_adaptor adaptor(db, l_id, r_id, l_marking, r_marking); - 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_missing_root_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); + result.report_invalid_name_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); + result.report_directory_loop_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); - 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_orphaned_node_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); + result.report_multiple_name_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); result.report_duplicate_name_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); - 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); + result.report_attribute_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); + result.report_file_content_conflicts(db, l_roster, r_roster, adaptor, basic_io, output); } } @@ -910,9 +910,10 @@ CMD(show_conflicts, "show_conflicts", "" // 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)"), - "", +CMD_AUTOMATE(show_conflicts, N_("[LEFT_REVID RIGHT_REVID]"), + 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::none) { database db(app); @@ -926,9 +927,14 @@ CMD_AUTOMATE(show_conflicts, N_("[REVID, 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.rbegin(); + N(heads.size() >= 2, + F("branch '%s' has %d heads; must be at least 2 for show_conflicts") % app.opts.branchname % heads.size()); + + // FIXME: factor out head choosing algorithm from merge above + set::const_iterator i = heads.begin(); + l_id = *i; + ++i; + r_id = *i; } else if (args.size() == 2) { ============================================================ --- merge.cc a9e8c3a5f704d0fc64e8c28e23c4a3ea376cb322 +++ merge.cc a2c3037bc94027b942ffbcbded5a3fdfc588f363 @@ -143,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, 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_missing_root_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); + result.report_invalid_name_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); + result.report_directory_loop_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); - 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_orphaned_node_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); + result.report_multiple_name_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); result.report_duplicate_name_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); - 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); + result.report_attribute_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); + result.report_file_content_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); } else if (result.has_content_conflicts()) { @@ -168,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, false, std::cout); + result.report_file_content_conflicts(db, left_roster, right_roster, adaptor, false, std::cout); try_to_merge_files(lua, left_roster, right_roster, result, adaptor, user_merge); ============================================================ --- monotone.texi 615e8a075b5e30913dd84b73a9499dd77ce9346f +++ monotone.texi 636e21c2b85ab832e50dd2f29dbd2f327c314849 @@ -3011,12 +3011,13 @@ @section Merge Conflicts @command{explicit_merge}, @command{propagate} and @command{merge_into_dir} or when using the workspace merge commands @command{update}, @command{pluck} and @command{merge_into_workspace}. -The @command{show_conflicts} command can be used to list conflicts -between database revisions which would be encountered by the database -merge commands. Unfortunately, this command can't yet list conflicts -between a database revision and the current workspace revision which -would be encountered by the workspace merge commands. +The @command{show_conflicts} and @command{automate show_conflicts} +commands can be used to list conflicts between database revisions +which would be encountered by the database merge commands. +Unfortunately, these commands can't yet list conflicts between a +database revision and the current workspace. + @subsection Conflict Types Monotone versions both files and directories explicitly and it tracks @@ -5409,6 +5410,9 @@ @section Informative This command shows what conflicts would need to be resolved in order to merge the given revisions. + +Note that this does not show conflicts due to update commands, since +in that case one revision is the workspace. @end ftable @@ -8524,14 +8528,17 @@ @section Automation @end table address@hidden mtn automate show_conflicts address@hidden mtn automate show_conflicts @var{[left_rev right_rev]} @table @strong @item Arguments: -Optional left and right revision ids. They default to the two heads of -the current workspace. +Optional left and right revision ids. +If no revs are given, @var{left_rev} and @var{right_rev} default to +the first two heads that would be chosen by the @command{merge} +command. + @item Added in: 7.1 @@ -8540,6 +8547,15 @@ @section Automation Show all conflicts between two revisions. +This is intented to be used when an attempted merge has failed due to +conflicts; an external tool can guide the user thru resolving each +conflict in turn, then redo the original command. + +For more information on conflicts, @ref{Merge Conflicts}. + +Note that this does not show conflicts due to update commands, since +in that case one revision is the workspace. + @item Sample output: @smallexample @group @@ -8548,10 +8564,6 @@ @section Automation 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 ============================================================ --- roster_merge.cc 0e3b1be78c8073f29014b3533914adbdaed510b1 +++ roster_merge.cc 66aa7c730cb7780c93615df48d0ec9d617d75e6d @@ -184,18 +184,20 @@ namespace namespace syms { symbol const conflict("conflict"); + symbol const left_id("left_id"); + symbol const left_name("left_name"); + symbol const left_type("left_type"); 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_name("right_name"); + symbol const right_rev_id("right_rev_id"); 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_merge_result::report_missing_root_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -228,13 +230,30 @@ roster_merge_result::report_missing_root node_id left_lca_root = left_lca_roster->root()->self; node_id right_lca_root = right_lca_roster->root()->self; - P(F("conflict: missing root directory")); + basic_io::stanza st; + if (basic_io) + { + st.push_str_pair(syms::conflict, "missing root"); + st.push_str_pair(syms::name, left_lca_name.as_external()); + } + else + { + P(F("conflict: missing root directory")); + } + if (left_root != left_lca_root && right_root == right_lca_root) { - P(F("directory '%s' pivoted to root on the left") % left_lca_name); + if (basic_io) + st.push_str_pair(syms::left_type, "pivoted root"); + else + P(F("directory '%s' pivoted to root on the left") % left_lca_name); + if (!right_roster.has_node(left_root)) - P(F("directory '%s' deleted on the right") % left_lca_name); + if (basic_io) + st.push_str_pair(syms::right_type, "deleted directory"); + else + P(F("directory '%s' deleted on the right") % left_lca_name); } else if (left_root == left_lca_root && right_root != right_lca_root) { @@ -256,11 +275,20 @@ roster_merge_result::report_missing_root // other conflicts can cause the root dir to be left detached // for example, merging two independently created projects // in these cases don't report anything about pivot_root + + if (basic_io) + { + basic_io::printer pr; + output << "\n"; + pr.print_stanza(st); + output.write(pr.buf.data(), pr.buf.size()); + } } } void -roster_merge_result::report_invalid_name_conflicts(roster_t const & left_roster, +roster_merge_result::report_invalid_name_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -331,7 +359,8 @@ void } void -roster_merge_result::report_directory_loop_conflicts(roster_t const & left_roster, +roster_merge_result::report_directory_loop_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -384,7 +413,8 @@ void } void -roster_merge_result::report_orphaned_node_conflicts(roster_t const & left_roster, +roster_merge_result::report_orphaned_node_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -412,12 +442,25 @@ roster_merge_result::report_orphaned_nod node_type type = get_type(*lca_roster, conflict.nid); + basic_io::stanza st; + if (type == file_type) P(F("conflict: orphaned file '%s' from revision %s") % lca_name % lca_rid); else - P(F("conflict: orphaned directory '%s' from revision %s") - % lca_name % lca_rid); + { + if (basic_io) + { + st.push_str_pair(syms::conflict, "orphaned directory"); + st.push_str_pair(syms::name, lca_name.as_external()); + st.push_hex_pair(syms::right_rev_id, lca_rid.inner()); + } + else + { + P(F("conflict: orphaned directory '%s' from revision %s") + % lca_name % lca_rid); + } + } if (left_roster.has_node(conflict.parent_name.first) && !right_roster.has_node(conflict.parent_name.first)) @@ -426,8 +469,11 @@ roster_merge_result::report_orphaned_nod left_roster.get_name(conflict.nid, orphan_name); left_roster.get_name(conflict.parent_name.first, parent_name); - P(F("parent directory '%s' was deleted on the right") - % parent_name); + if (basic_io) + st.push_str_pair(syms::right_type, "deleted directory"); + else + P(F("parent directory '%s' was deleted on the right") + % parent_name); if (parent_lca_roster->has_node(conflict.nid)) { @@ -435,8 +481,14 @@ roster_merge_result::report_orphaned_nod P(F("file '%s' was renamed from '%s' on the left") % orphan_name % lca_name); else - P(F("directory '%s' was renamed from '%s' on the left") - % orphan_name % lca_name); + if (basic_io) + { + st.push_str_pair(syms::left_type, "renamed directory"); + st.push_str_pair(syms::left_name, orphan_name.as_external()); + } + else + P(F("directory '%s' was renamed from '%s' on the left") + % orphan_name % lca_name); } else { @@ -480,11 +532,20 @@ roster_merge_result::report_orphaned_nod } else I(false); + + if (basic_io) + { + basic_io::printer pr; + output << "\n"; + pr.print_stanza(st); + output.write(pr.buf.data(), pr.buf.size()); + } } } void -roster_merge_result::report_multiple_name_conflicts(roster_t const & left_roster, +roster_merge_result::report_multiple_name_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -738,7 +799,8 @@ void } void -roster_merge_result::report_attribute_conflicts(roster_t const & left_roster, +roster_merge_result::report_attribute_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, @@ -842,7 +904,8 @@ void } void -roster_merge_result::report_file_content_conflicts(roster_t const & left_roster, +roster_merge_result::report_file_content_conflicts(database & db, + roster_t const & left_roster, roster_t const & right_roster, content_merge_adaptor & adaptor, bool basic_io, ============================================================ --- roster_merge.hh 61dc2b4c870575223b62bca5b1edc56bf4589485 +++ roster_merge.hh 34abb44e0b7f62ef5fff648ced4d43984511ccd6 @@ -154,28 +154,33 @@ struct roster_merge_result bool has_non_content_conflicts() const; void log_conflicts() const; - void report_missing_root_conflicts(roster_t const & left, + void report_missing_root_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, std::ostream & output) const; - void report_invalid_name_conflicts(roster_t const & left, + void report_invalid_name_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, std::ostream & output) const; - void report_directory_loop_conflicts(roster_t const & left, + void report_directory_loop_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, std::ostream & output) const; - void report_orphaned_node_conflicts(roster_t const & left, + void report_orphaned_node_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, std::ostream & output) const; - void report_multiple_name_conflicts(roster_t const & left, + void report_multiple_name_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, @@ -187,12 +192,14 @@ struct roster_merge_result bool const basic_io, std::ostream & output) const; - void report_attribute_conflicts(roster_t const & left, + void report_attribute_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io, std::ostream & output) const; - void report_file_content_conflicts(roster_t const & left, + void report_file_content_conflicts(database & db, + roster_t const & left, roster_t const & right, content_merge_adaptor & adaptor, bool basic_io,