# # # rename "tests/resolve_conflict_all_resolutions" # to "tests/resolve_conflicts_all_resolutions" # # add_dir "tests/resolve_conflicts_read_all" # # add_file "tests/resolve_conflicts_all_resolutions/show_remaining-checkout_left" # content [cd1882e61df967d6ee9f21f466be8d0a03c88fb5] # # add_file "tests/resolve_conflicts_all_resolutions/show_remaining-thermostat" # content [2bf5ad8ca1cd6a549e4bbe01c0e0440b99dabb61] # # add_file "tests/resolve_conflicts_read_all/__driver__.lua" # content [69803956634d281a23535588d085287b58753a4b] # # add_file "tests/resolve_conflicts_read_all/invalid-add_left" # content [7e560447bbcfe9bd416582dcc143a0ea45bc828d] # # add_file "tests/resolve_conflicts_read_all/invalid-add_right" # content [9d8195f9f9cd124855b9e82ff639b5c140d663bf] # # add_file "tests/resolve_conflicts_read_all/invalid-rename_left" # content [3be81555ea98530c4d530b1922fcd4537915560e] # # add_file "tests/resolve_conflicts_read_all/invalid-rename_right" # content [1259ba3642e09d01b143bb561ea1e038d6e9fe93] # # add_file "tests/resolve_conflicts_read_all/missing-root_left" # content [6fd2fb1fb92b77814c448d18b1f5ed64b3229ede] # # add_file "tests/resolve_conflicts_read_all/missing-root_right" # content [e09bcaac9f0d6f89f1b3bd1c043bb4688bf4e9f9] # # patch "cmd_conflicts.cc" # from [c4100c05922de53abdd3312733827efd0c45e6ec] # to [df7b2a3dc45d5ed89b083ea582641ff87da8d9c0] # # patch "cmd_merging.cc" # from [d4113f1b227d26e48ca23fce88ee1b9fd929bedf] # to [8e40c6a97219656dfdd2b59d68c3ad24091a61e6] # # patch "merge.cc" # from [46e286382febebbed4f099acb5e83b17146cffb8] # to [068d8567d6562b4be8cbe18635b62b77f02c6ae6] # # patch "monotone.texi" # from [2ea319de3fbb847eabd1d47a9644e130eae0fe12] # to [e5741f52ff77e9e60745aa611b7803d6a58a2c90] # # patch "paths.cc" # from [4ac782ac2313374cd8944cd2296181753ba71f58] # to [92e0e0dbe7421fc5acc93c16a5f37dd925bd92c7] # # patch "roster_merge.cc" # from [4419b097ddc55b68e39810cb05be31794315ca74] # to [65e827163cf31ec89f77c73353baa5da43eacd1e] # # patch "roster_merge.hh" # from [3bbdacaa0de545b88f71e178ca7931616ea0eb83] # to [8eb079ece2445e93680f9a7f5821da6066e1c52a] # # patch "tests/resolve_conflicts_all_resolutions/__driver__.lua" # from [eae4fa4b06ac70013d1cabc187778aa0a8f6685b] # to [8342870d88378e1df4e10f20f10aae3554727620] # ============================================================ --- tests/resolve_conflicts_all_resolutions/show_remaining-checkout_left cd1882e61df967d6ee9f21f466be8d0a03c88fb5 +++ tests/resolve_conflicts_all_resolutions/show_remaining-checkout_left cd1882e61df967d6ee9f21f466be8d0a03c88fb5 @@ -0,0 +1,4 @@ +mtn: duplicate_name checkout_left.sh +mtn: duplicate_name checkout_right.sh +mtn: duplicate_name thermostat.c +mtn: content user_file ============================================================ --- tests/resolve_conflicts_all_resolutions/show_remaining-thermostat 2bf5ad8ca1cd6a549e4bbe01c0e0440b99dabb61 +++ tests/resolve_conflicts_all_resolutions/show_remaining-thermostat 2bf5ad8ca1cd6a549e4bbe01c0e0440b99dabb61 @@ -0,0 +1,2 @@ +mtn: duplicate_name thermostat.c +mtn: content user_file ============================================================ --- tests/resolve_conflicts_read_all/__driver__.lua 69803956634d281a23535588d085287b58753a4b +++ tests/resolve_conflicts_read_all/__driver__.lua 69803956634d281a23535588d085287b58753a4b @@ -0,0 +1,861 @@ +mtn_setup() + +-- this test creates the various non-content conflict cases and tests +-- that "conflicts show_remaining" can properly process the output of +-- "conflicts store". +-- +-- test case generation borrowed from conflict_messages test. + + +function setup(branch) + remove("_MTN") + remove("foo") + remove("bar") + remove("baz") + remove(branch) + check(mtn("setup", ".", "--branch", branch), 0, false, false) +end + +function check_conflicts_left(branch) + check(mtn("conflicts", "store", "h:" .. branch, "h:" .. branch .. "-propagate"), 0, nil, true) + check(mtn("conflicts", "show_remaining"), 0, nil, true) + canonicalize("stderr") + check(samefilestd(branch .. "_left", "stderr")) +end + +function check_conflicts_right(branch) + check(mtn("conflicts", "store", "h:" .. branch .. "-propagate", "h:" .. branch), 0, nil, true) + check(mtn("conflicts", "show_remaining"), 0, nil, true) + canonicalize("stderr") + check(samefilestd(branch .. "_right", "stderr")) +end + +function check_conflicts(branch) + commit(branch .. "-propagate") + check_conflicts_left(branch) + check_conflicts_right(branch) +end + +-- missing root conflict + +branch = "missing-root" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +commit(branch) + +check(mtn("co", "--branch", branch, branch), 0, nil, nil) +check(indir(branch, mtn("pivot_root", "foo", "bar")), 0, nil, false) +check(indir(branch, mtn("commit", "--message", "commit")), 0, nil, false) + +check(mtn("drop", "--recursive", "foo"), 0, nil, false) + +check_conflicts(branch) + +-- invalid name add + +branch = "invalid-add" +setup(branch) + +mkdir("foo") +addfile("foo/foo", branch .. "-foofoo") +commit(branch) + +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) + +mkdir("foo/_MTN") +addfile("foo/_MTN/foo", branch .. "-foo") +addfile("foo/_MTN/bar", branch .. "-bar") + +check_conflicts(branch) + +-- invalid name rename + +branch = "invalid-rename" +setup(branch) + +mkdir("foo") +mkdir("bad") +addfile("foo/foo", branch .. "-foofoo") +addfile("bad/_MTN", branch .. "--bar") +commit(branch) + +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) + +check(mtn("mv", "bad/_MTN", "foo/_MTN"), 0, false, false) + +check_conflicts(branch) + +-- 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) + +revert_to(base) + +check(mtn("mv", "bar", "foo"), 0, false, false) + +check_conflicts(branch) + +commit(branch .. "-propagate") + + +-- 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) + +revert_to(base) + +remove("foo") +check(mtn("drop", "--recursive", "foo"), 0, false, false) + +check_conflicts(branch) + + +-- 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) + +revert_to(base) + +remove("foo") +check(mtn("drop", "--recursive", "foo"), 0, false, false) + +check_conflicts(branch) + + +-- 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) + +revert_to(base) + +check(mtn("mv", "foo", "baz"), 0, false, false) + +check_conflicts(branch) + + +-- 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) +--addfile("bar", "duplicate add bar1") +commit(branch) +first = base_revision() + +revert_to(base) + +addfile("bar", branch .. "-bar2") + +message = "conflict: duplicate name" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + +-- 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) + +message = "conflict: duplicate name" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + +-- 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") + +message = "conflict: duplicate name" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + + +-- 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) + +message = "conflict: multiple values for attribute" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + + +-- 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) + +message = "conflict: multiple values for attribute" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + + +-- 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") + +message = "conflict: content conflict on file 'foo'" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) +check(not qgrep("conflict: content conflict on file 'bar'", "stderr")) +check(not qgrep("conflict: content conflict on file 'baz'", "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + +-- 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) + +message = "conflict: content conflict on file" + +check(mtn("update"), 1, false, true) +check(qgrep(message, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message, "stderr")) + + + +-- complex_structural_conflicts cases from roster_merge.cc unit tests + + + +-- 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") + +message1 = "conflict: multiple names" +message2 = "conflict: duplicate name" + +-- this doesn't result in a duplicate name conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +check(mtn("update"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + + + +-- 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) + +message1 = "conflict: multiple names" +message2 = "conflict: orphaned" + +-- this doesn't result in a directory loop conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +check(mtn("update"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + + + +-- 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) + +message1 = "conflict: multiple names" +message2 = "conflict: directory loop" + +-- this doesn't result in a directory loop conflict because the multiple name +-- conflict prevents foo from being attached in the result roster + +check(mtn("update"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +commit(branch .. "-propagate") +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + +check(mtn("merge_into_workspace", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(not qgrep(message2, "stderr")) + + + + +-- 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) + +message1 = "conflict: missing root directory" +message2 = "conflict: duplicate name" +message3 = "conflict: multiple names" + +check(indir(dir2, mtn("update")), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) +check(indir(dir2, mtn("commit", "--message", "commit", "--branch", branch .. "-propagate")), 0, false, false) + +second = indir(dir2, {base_revision})[1]() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) + +check(indir(dir2, mtn("pluck", "--revision", base, "--revision", first)), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) + +check(indir(dir2, mtn("merge_into_workspace", first)), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(qgrep(message3, "stderr")) + + + +-- 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") + +message1 = "conflict: missing root directory" +message2 = "conflict: duplicate name" + +-- check(mtn("update"), 1, false, true) +-- check(qgrep(message1, "stderr")) +-- check(qgrep(message2, "stderr")) + +check(mtn("commit", "--message", "commit", "--branch", branch .. "-propagate"), 0, false, false) + +second = base_revision() + +check(mtn("propagate", branch , branch .. "-propagate"), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) +check(mtn("cert", second, "branch", branch)) + +check(mtn("show_conflicts", first, second), 0, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) + +check(mtn("explicit_merge", first, second, branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) + +check(mtn("merge", "--branch", branch), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) + +check(mtn("pluck", "--revision", base, "--revision", first), 1, false, true) +check(qgrep(message1, "stderr")) +check(qgrep(message2, "stderr")) + +-- check(mtn("merge_into_workspace", first), 1, false, true) +-- check(qgrep(message1, "stderr")) +-- check(qgrep(message2, "stderr")) ============================================================ --- tests/resolve_conflicts_read_all/invalid-add_left 7e560447bbcfe9bd416582dcc143a0ea45bc828d +++ tests/resolve_conflicts_read_all/invalid-add_left 7e560447bbcfe9bd416582dcc143a0ea45bc828d @@ -0,0 +1,4 @@ +mtn: warning: 1 conflict with no supported resolutions. +mtn: conflict: invalid name _MTN in root directory +mtn: 'foo' pivoted to root on the left +mtn: 'foo/_MTN' added in revision cdf9a4171d1d386f9ecb83218a11df6c5fa75c6c on the right ============================================================ --- tests/resolve_conflicts_read_all/invalid-add_right 9d8195f9f9cd124855b9e82ff639b5c140d663bf +++ tests/resolve_conflicts_read_all/invalid-add_right 9d8195f9f9cd124855b9e82ff639b5c140d663bf @@ -0,0 +1,4 @@ +mtn: warning: 1 conflict with no supported resolutions. +mtn: conflict: invalid name _MTN in root directory +mtn: 'foo' pivoted to root on the right +mtn: 'foo/_MTN' added in revision cdf9a4171d1d386f9ecb83218a11df6c5fa75c6c on the left ============================================================ --- tests/resolve_conflicts_read_all/invalid-rename_left 3be81555ea98530c4d530b1922fcd4537915560e +++ tests/resolve_conflicts_read_all/invalid-rename_left 3be81555ea98530c4d530b1922fcd4537915560e @@ -0,0 +1,4 @@ +mtn: warning: 1 conflict with no supported resolutions. +mtn: conflict: invalid name _MTN in root directory +mtn: 'foo' pivoted to root on the left +mtn: 'bad/_MTN' renamed to 'foo/_MTN' on the right ============================================================ --- tests/resolve_conflicts_read_all/invalid-rename_right 1259ba3642e09d01b143bb561ea1e038d6e9fe93 +++ tests/resolve_conflicts_read_all/invalid-rename_right 1259ba3642e09d01b143bb561ea1e038d6e9fe93 @@ -0,0 +1,4 @@ +mtn: warning: 1 conflict with no supported resolutions. +mtn: conflict: invalid name _MTN in root directory +mtn: 'foo' pivoted to root on the right +mtn: 'bad/_MTN' renamed to 'foo/_MTN' on the left ============================================================ --- tests/resolve_conflicts_read_all/missing-root_left 6fd2fb1fb92b77814c448d18b1f5ed64b3229ede +++ tests/resolve_conflicts_read_all/missing-root_left 6fd2fb1fb92b77814c448d18b1f5ed64b3229ede @@ -0,0 +1,7 @@ +mtn: warning: 2 conflicts with no supported resolutions. +mtn: conflict: missing root directory +mtn: directory 'foo' pivoted to root on the left +mtn: directory 'foo' deleted on the right +mtn: conflict: orphaned directory '' from revision ead03530f5fefe50c9010157c42c0ebe18086559 +mtn: parent directory '' was deleted on the right +mtn: directory 'bar' was renamed from '' on the left ============================================================ --- tests/resolve_conflicts_read_all/missing-root_right e09bcaac9f0d6f89f1b3bd1c043bb4688bf4e9f9 +++ tests/resolve_conflicts_read_all/missing-root_right e09bcaac9f0d6f89f1b3bd1c043bb4688bf4e9f9 @@ -0,0 +1,7 @@ +mtn: warning: 2 conflicts with no supported resolutions. +mtn: conflict: missing root directory +mtn: directory 'foo' deleted on the left +mtn: directory 'foo' pivoted to root on the right +mtn: conflict: orphaned directory '' from revision ead03530f5fefe50c9010157c42c0ebe18086559 +mtn: parent directory '' was deleted on the left +mtn: directory 'bar' was renamed from '' on the right ============================================================ --- cmd_conflicts.cc c4100c05922de53abdd3312733827efd0c45e6ec +++ cmd_conflicts.cc df7b2a3dc45d5ed89b083ea582641ff87da8d9c0 @@ -8,6 +8,7 @@ // PURPOSE. #include "base.hh" +#include #include "app_state.hh" #include "cmd.hh" @@ -45,11 +46,13 @@ struct conflicts_t }; }; +typedef enum {first, remaining} show_conflicts_case_t; + static void -show_first_conflict(conflicts_t conflicts) +show_conflicts(database & db, conflicts_t conflicts, show_conflicts_case_t show_case) { - // To find the first conflict, go thru the conflicts we know how to - // resolve in the same order we output them. + // Go thru the conflicts we know how to resolve in the same order + // merge.cc resolve_merge_conflicts outputs them. for (std::vector::iterator i = conflicts.result.duplicate_name_conflicts.begin(); i != conflicts.result.duplicate_name_conflicts.end(); ++i) @@ -62,22 +65,30 @@ show_first_conflict(conflicts_t conflict 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) + switch (show_case) { - P(F("resolve_first_left drop")); - P(F("resolve_first_left rename \"name\"")); - P(F("resolve_first_left user \"name\"")); - } + case first: + P(F("possible resolutions:")); - 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\"")); + 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; + + case remaining: + break; } - return; } } @@ -92,16 +103,65 @@ show_first_conflict(conflicts_t conflict 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; + + switch (show_case) + { + case first: + P(F("possible resolutions:")); + P(F("resolve user \"file_name\"")); + return; + + case remaining: + break; + } } } - N(false, F("no resolvable yet unresolved conflicts")); - -} // show_first_conflict + switch (show_case) + { + case first: + { + int const count = conflicts.result.count_unsupported_resolution(); + if (count > 0) + P(FP("warning: %s conflict with no supported resolutions.", + "warning: %s conflicts with no supported resolutions.", + count) % count); + else + P(F("all conflicts resolved")); + } + break; + case remaining: + { + int const count = conflicts.result.count_unsupported_resolution(); + if (count > 0) + { + P(FP("warning: %s conflict with no supported resolutions.", + "warning: %s conflicts with no supported resolutions.", + count) % count); + + content_merge_database_adaptor adaptor + (db, conflicts.left_rid, conflicts.right_rid, conflicts.left_marking, conflicts.right_marking); + + conflicts.result.report_missing_root_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + conflicts.result.report_invalid_name_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + conflicts.result.report_directory_loop_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + conflicts.result.report_orphaned_node_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + conflicts.result.report_multiple_name_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + conflicts.result.report_attribute_conflicts + (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout); + } + } + break; + } + +} // show_conflicts + enum side_t {left, right, neither}; static char const * const conflict_resolution_not_supported_msg = "%s is not a supported conflict resolution for %s"; @@ -128,7 +188,7 @@ set_duplicate_name_conflict(resolve_conf } else N(false, F(conflict_resolution_not_supported_msg) % idx(args,0) % "duplicate_name"); - + } //set_duplicate_name_conflict static void @@ -173,13 +233,13 @@ set_first_conflict(side_t side, conflict ++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)()); } @@ -219,19 +279,31 @@ CMD(show_first, "show_first", "", CMD_RE CMD(show_first, "show_first", "", CMD_REF(conflicts), "", - N_("Show the first conflict in the conflicts file, and possible resolutions."), + N_("Show the first unresolved 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); + show_conflicts(db, conflicts, first); } +CMD(show_remaining, "show_remaining", "", CMD_REF(conflicts), + "", + N_("Show the remaining unresolved conflicts in the conflicts file."), + "", + options::opts::conflicts_opts) +{ + database db(app); + conflicts_t conflicts (db, app.opts.conflicts_file); + + show_conflicts(db, conflicts, remaining); +} + CMD(resolve_first, "resolve_first", "", CMD_REF(conflicts), N_("RESOLUTION"), - N_("Set the resolution for the first single-file conflict."), + N_("Set the resolution for the first unresolved single-file conflict."), "", options::opts::conflicts_opts) { @@ -245,7 +317,7 @@ CMD(resolve_first_left, "resolve_first_l CMD(resolve_first_left, "resolve_first_left", "", CMD_REF(conflicts), N_("RESOLUTION"), - N_("Set the left resolution for the first two-file conflict."), + N_("Set the left resolution for the first unresolved two-file conflict."), "", options::opts::conflicts_opts) { @@ -259,7 +331,7 @@ CMD(resolve_first_right, "resolve_first_ CMD(resolve_first_right, "resolve_first_right", "", CMD_REF(conflicts), N_("RESOLUTION"), - N_("Set the right resolution for the first two-file conflict."), + N_("Set the right resolution for the first unresolved two-file conflict."), "", options::opts::conflicts_opts) { @@ -279,7 +351,7 @@ CMD(clean, "clean", "", CMD_REF(conflict { bookkeeping_path conflicts_file("conflicts"); bookkeeping_path resolutions_dir("resolutions"); - + if (path_exists(conflicts_file)) delete_file(conflicts_file); ============================================================ --- cmd_merging.cc d4113f1b227d26e48ca23fce88ee1b9fd929bedf +++ cmd_merging.cc 8e40c6a97219656dfdd2b59d68c3ad24091a61e6 @@ -850,6 +850,7 @@ show_conflicts_core (database & db, revision_id const & l_id, revision_id const & r_id, bool const basic_io, + bool warn_unsupported, std::ostream & output) { N(!is_ancestor(db, l_id, r_id), @@ -926,6 +927,15 @@ show_conflicts_core (database & db, result.report_attribute_conflicts(*l_roster, *r_roster, adaptor, basic_io, output); result.report_file_content_conflicts(lua, *l_roster, *r_roster, adaptor, basic_io, output); + + if (warn_unsupported) + { + int const count = result.count_unsupported_resolution(); + if (count > 0) + P(FP("warning: %s conflict with no supported resolutions.", + "warning: %s conflicts with no supported resolutions.", + count) % count); + } } } @@ -944,7 +954,7 @@ CMD(show_conflicts, "show_conflicts", "" 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, app.lua, l_id, r_id, false, std::cout); + show_conflicts_core(db, app.lua, l_id, r_id, false, false, std::cout); } static void get_conflicts_rids(args_vector const & args, @@ -1008,14 +1018,15 @@ CMD_AUTOMATE(show_conflicts, N_("[LEFT_R revision_id l_id, r_id; get_conflicts_rids(args, db, project, app, l_id, r_id); - show_conflicts_core(db, app.lua, l_id, r_id, true, output); + show_conflicts_core(db, app.lua, l_id, r_id, true, false, output); } 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."), + "first two heads that would be chosen by the 'merge' command. If " + "--conflicts-file is not given, '_MTN/conflicts' is used."), options::opts::branch | options::opts::conflicts_opts) { database db(app); @@ -1025,7 +1036,7 @@ CMD(store, "store", "", CMD_REF(conflict 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); + show_conflicts_core(db, app.lua, left_id, right_id, true, true, output); data dat(output.str()); write_data(system_path(app.opts.conflicts_file), dat, system_path("_MTN")); ============================================================ --- merge.cc 46e286382febebbed4f099acb5e83b17146cffb8 +++ merge.cc 068d8567d6562b4be8cbe18635b62b77f02c6ae6 @@ -133,7 +133,7 @@ resolve_merge_conflicts(lua_hooks & lua, // resolutions, give a nice error message. char const * const msg = "conflict resolution for %s not yet supported"; - N(!result.missing_root_dir, F(msg) % "missing_root_dir"); + N(!result.missing_root_conflict, F(msg) % "missing_root_dir"); N(result.invalid_name_conflicts.size() == 0, F(msg) % "invalid_name_conflicts"); N(result.directory_loop_conflicts.size() == 0, F(msg) % "directory_loop_conflicts"); N(result.orphaned_node_conflicts.size() == 0, F(msg) % "orphaned_node_conflicts"); ============================================================ --- monotone.texi 2ea319de3fbb847eabd1d47a9644e130eae0fe12 +++ monotone.texi e5741f52ff77e9e60745aa611b7803d6a58a2c90 @@ -4687,6 +4687,9 @@ @subsection Conflicts Show the first unresolved conflict in the conflicts file, and list the possible resolutions. address@hidden mtn conflicts address@hidden show_remaining +Show remaining unresolved conflicts in the conflicts file. + @item mtn conflicts address@hidden resolve_first @var{resolution} Specify a resolution for the first conflict in the conflicts file; it must be a single file conflict. The conflicts file is updated. @@ -8768,8 +8771,8 @@ @section Automation @itemize @item -8.1: -- added default resolution for file content conflicts, user -resolutions for others. +8.1: -- Added default resolution for file content conflicts, user +resolutions for others. @code{directory_loop_created} changed to @code{directory_loop} @item 7.1 -- initial @end itemize @@ -8889,7 +8892,7 @@ @section Automation Directory loop created: @verbatim - conflict directory_loop_created + conflict directory_loop left_type "renamed directory" ancestor_name "foo" left_name "bar/foo" ============================================================ --- paths.cc 4ac782ac2313374cd8944cd2296181753ba71f58 +++ paths.cc 92e0e0dbe7421fc5acc93c16a5f37dd925bd92c7 @@ -366,7 +366,6 @@ normalize_external_path(string const & p } else { - N(!path.empty(), F("empty path '%s' is invalid") % path); N(!is_absolute_here(path), F("absolute path '%s' is invalid") % path); string base; try ============================================================ --- roster_merge.cc 4419b097ddc55b68e39810cb05be31794315ca74 +++ roster_merge.cc 65e827163cf31ec89f77c73353baa5da43eacd1e @@ -180,7 +180,7 @@ roster_merge_result::has_non_content_con bool roster_merge_result::has_non_content_conflicts() const { - return missing_root_dir + return missing_root_conflict || !invalid_name_conflicts.empty() || !directory_loop_conflicts.empty() || !orphaned_node_conflicts.empty() @@ -188,10 +188,22 @@ roster_merge_result::has_non_content_con || !duplicate_name_conflicts.empty() || !attribute_conflicts.empty(); } + +int +roster_merge_result::count_unsupported_resolution() const +{ + return (missing_root_conflict ? 1 : 0) + + invalid_name_conflicts.size() + + directory_loop_conflicts.size() + + orphaned_node_conflicts.size() + + multiple_name_conflicts.size() + + attribute_conflicts.size(); +} + static void dump_conflicts(roster_merge_result const & result, string & out) { - if (result.missing_root_dir) + if (result.missing_root_conflict) out += (FL("missing_root_conflict: root directory has been removed\n")).str(); dump(result.invalid_name_conflicts, out); @@ -250,7 +262,7 @@ namespace symbol const attribute("attribute"); symbol const conflict("conflict"); symbol const content("content"); - symbol const directory_loop_created("directory_loop_created"); + symbol const directory_loop("directory_loop"); symbol const duplicate_name("duplicate_name"); symbol const invalid_name("invalid_name"); symbol const left("left"); @@ -282,9 +294,9 @@ static void } static void -put_added_conflict_left (basic_io::stanza & st, - content_merge_adaptor & adaptor, - node_id const nid) +put_added_conflict_left(basic_io::stanza & st, + content_merge_adaptor & adaptor, + node_id const nid) { // We access the roster via the adaptor, to be sure we use the left // roster; avoids typos in long parameter lists. @@ -314,9 +326,9 @@ static void } static void -put_added_conflict_right (basic_io::stanza & st, - content_merge_adaptor & adaptor, - node_id const nid) +put_added_conflict_right(basic_io::stanza & st, + content_merge_adaptor & adaptor, + node_id const nid) { content_merge_database_adaptor & db_adaptor (dynamic_cast(adaptor)); boost::shared_ptr roster(db_adaptor.rosters[db_adaptor.right_rid]); @@ -343,9 +355,9 @@ static void } static void -put_rename_conflict_left (basic_io::stanza & st, - content_merge_adaptor & adaptor, - node_id const nid) +put_rename_conflict_left(basic_io::stanza & st, + content_merge_adaptor & adaptor, + node_id const nid) { content_merge_database_adaptor & db_adaptor (dynamic_cast(adaptor)); boost::shared_ptr ancestor_roster(db_adaptor.rosters[db_adaptor.lca]); @@ -379,6 +391,97 @@ static void } static void +get_nid_name_pair(roster_t const & roster, + string const & path, + node_id & nid, + std::pair & name) +{ + node_t const node = roster.get_node(file_path_external(utf8(path))); + nid = node->self; + name = make_pair (node->parent, node->name); +} + +static void +read_added_rename_conflict_left(basic_io::parser & pars, + roster_t const & roster, + node_id & left_nid, + std::pair & left_name) +{ + string tmp; + + pars.esym(syms::left_type); + + pars.str(tmp); + + if (tmp == "renamed file") + { + pars.esym(syms::ancestor_name); pars.str(); + pars.esym(syms::ancestor_file_id); pars.hex(); + + pars.esym(syms::left_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, left_nid, left_name); + pars.esym(syms::left_file_id); pars.hex(); + } + else if (tmp == "renamed directory") + { + pars.esym(syms::ancestor_name); pars.str(); + pars.esym(syms::left_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, left_nid, left_name); + } + else if (tmp == "added file") + { + pars.esym(syms::left_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, left_nid, left_name); + pars.esym(syms::left_file_id); pars.hex(); + } + else if (tmp == "added directory") + { + pars.esym(syms::left_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, left_nid, left_name); + } +} // read_added_rename_conflict_left + +static void +read_added_rename_conflict_right(basic_io::parser & pars, + roster_t const & roster, + node_id & right_nid, + std::pair & right_name) +{ + string tmp; + + pars.esym(syms::right_type); + + pars.str(tmp); + + if (tmp == "renamed file") + { + pars.esym(syms::ancestor_name); pars.str(); + pars.esym(syms::ancestor_file_id); pars.hex(); + + pars.esym(syms::right_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, right_nid, right_name); + pars.esym(syms::right_file_id); pars.hex(); + } + else if (tmp == "renamed directory") + { + pars.esym(syms::ancestor_name); pars.str(tmp); + pars.esym(syms::right_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, right_nid, right_name); + } + else if (tmp == "added file") + { + pars.esym(syms::right_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, right_nid, right_name); + pars.esym(syms::right_file_id); pars.hex(); + } + else if (tmp == "added directory") + { + pars.esym(syms::right_name); pars.str(tmp); + get_nid_name_pair(roster, tmp, right_nid, right_name); + } +} + +static void put_rename_conflict_right (basic_io::stanza & st, content_merge_adaptor & adaptor, node_id const nid) @@ -645,7 +748,7 @@ roster_merge_result::report_missing_root MM(left_roster); MM(right_roster); - if (missing_root_dir) + if (missing_root_conflict) { node_id left_root, right_root; left_root = left_roster.root()->self; @@ -905,7 +1008,7 @@ roster_merge_result::report_directory_lo lca_roster->get_name(conflict.parent_name.first, lca_parent_name); if (basic_io) - st.push_str_pair(syms::conflict, syms::directory_loop_created); + st.push_str_pair(syms::conflict, syms::directory_loop); else P(F("conflict: directory loop created")); @@ -1579,6 +1682,154 @@ static void static char const * const conflict_extra = "extra chars at end of conflict"; static void +read_missing_root_conflicts(basic_io::parser & pars, + bool & missing_root_conflict, + roster_t const & left_roster, + roster_t const & right_roster) +{ + // There can be only one of these + if (pars.tok.in.lookahead != EOF && pars.symp(syms::missing_root)) + { + pars.sym(); + + pars.esym(syms::left_type); pars.str(); + pars.esym(syms::ancestor_name); pars.str(); + pars.esym(syms::right_type); pars.str(); + pars.esym(syms::ancestor_name); pars.str(); + + missing_root_conflict = true; + + if (pars.tok.in.lookahead != EOF) + pars.esym (syms::conflict); + } + else + { + missing_root_conflict = false; + } +} // read_missing_root_conflicts + +static void +read_invalid_name_conflict(basic_io::parser & pars, + invalid_name_conflict & conflict, + roster_t const & left_roster, + roster_t const & right_roster) +{ + if (pars.symp(syms::left_type)) + { + pars.sym(); + pars.str(); // "pivoted root" + pars.esym(syms::ancestor_name); pars.str(); // lca_parent_name + read_added_rename_conflict_right (pars, right_roster, conflict.nid, conflict.parent_name); + } + else + { + pars.esym(syms::right_type); + pars.str(); // "pivoted root" + pars.esym(syms::ancestor_name); pars.str(); // lca_parent_name + read_added_rename_conflict_left (pars, left_roster, conflict.nid, conflict.parent_name); + } +} // read_invalid_name_conflict + +static void +read_invalid_name_conflicts(basic_io::parser & pars, + std::vector & conflicts, + roster_t const & left_roster, + roster_t const & right_roster) +{ + while (pars.tok.in.lookahead != EOF && pars.symp(syms::invalid_name)) + { + invalid_name_conflict conflict; + + pars.sym(); + + read_invalid_name_conflict(pars, conflict, left_roster, right_roster); + + conflicts.push_back(conflict); + + if (pars.tok.in.lookahead != EOF) + pars.esym (syms::conflict); + } +} // read_invalid_name_conflicts + +static void +read_directory_loop_conflict(basic_io::parser & pars, + directory_loop_conflict & conflict, + roster_t const & left_roster, + roster_t const & right_roster) +{ + string tmp; + + // syms::directory_loop has been read + + I(false); + +} // read_directory_loop_conflict + +static void +read_directory_loop_conflicts(basic_io::parser & pars, + std::vector & conflicts, + roster_t const & left_roster, + roster_t const & right_roster) +{ + while (pars.tok.in.lookahead != EOF && pars.symp(syms::directory_loop)) + { + directory_loop_conflict conflict; + + pars.sym(); + + read_directory_loop_conflict(pars, conflict, left_roster, right_roster); + + conflicts.push_back(conflict); + + if (pars.tok.in.lookahead != EOF) + pars.esym (syms::conflict); + } +} // read_directory_loop_conflicts + + +static void +read_orphaned_node_conflict(basic_io::parser & pars, + orphaned_node_conflict & conflict, + roster_t const & left_roster, + roster_t const & right_roster) +{ + if (pars.symp(syms::left_type)) + { + pars.sym(); pars.str(); // "deleted directory | file" + pars.esym(syms::ancestor_name); pars.str(); + read_added_rename_conflict_right (pars, right_roster, conflict.nid, conflict.parent_name); + } + else + { + pars.esym(syms::right_type); + pars.str(); // "deleted directory | file" + pars.esym(syms::ancestor_name); pars.str(); + read_added_rename_conflict_left (pars, left_roster, conflict.nid, conflict.parent_name); + } +} // read_orphaned_node_conflict + +static void +read_orphaned_node_conflicts(basic_io::parser & pars, + std::vector & conflicts, + roster_t const & left_roster, + roster_t const & right_roster) +{ + while (pars.tok.in.lookahead != EOF && (pars.symp(syms::orphaned_directory) || pars.symp(syms::orphaned_file))) + { + orphaned_node_conflict conflict; + + pars.sym(); + + read_orphaned_node_conflict(pars, conflict, left_roster, right_roster); + + conflicts.push_back(conflict); + + if (pars.tok.in.lookahead != EOF) + pars.esym (syms::conflict); + } +} // read_orphaned_node_conflicts + +static void read_duplicate_name_conflict(basic_io::parser & pars, duplicate_name_conflict & conflict, roster_t const & left_roster, @@ -1814,26 +2065,34 @@ read_conflict_file_core(basic_io::parser // regenerated. So we go thru the conflicts in the same order they are // generated; see merge.cc resolve_merge_conflicts. - // resolve_merge_conflicts should not call us if there are any conflicts - // for which we don't currently support resolutions; assert that. + if (validate) + { + // resolve_merge_conflicts should not call us if there are any conflicts + // for which we don't currently support resolutions; assert that. - I(!result.missing_root_dir); - I(result.invalid_name_conflicts.size() == 0); - I(result.directory_loop_conflicts.size() == 0); - I(result.orphaned_node_conflicts.size() == 0); - I(result.multiple_name_conflicts.size() == 0); - I(result.attribute_conflicts.size() == 0); + I(!result.missing_root_conflict); + I(result.invalid_name_conflicts.size() == 0); + I(result.directory_loop_conflicts.size() == 0); + I(result.orphaned_node_conflicts.size() == 0); + I(result.multiple_name_conflicts.size() == 0); + I(result.attribute_conflicts.size() == 0); - // These are the ones we know how to resolve. + // These are the ones we know how to resolve. - if (validate) - { validate_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster); validate_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster); } else { + // Read in the ones we know how to resolve. Also read in the ones we + // don't know how to resolve, so we can report them. + read_missing_root_conflicts(pars, result.missing_root_conflict, left_roster, right_roster); + read_invalid_name_conflicts(pars, result.invalid_name_conflicts, left_roster, right_roster); +// read_directory_loop_conflicts(pars, result.directory_loop_conflicts, left_roster, right_roster); + read_orphaned_node_conflicts(pars, result.orphaned_node_conflicts, left_roster, right_roster); +// read_multiple_name_conflicts(pars, result.multiple_name_conflicts, left_roster, right_roster); read_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster); +// read_attribute_conflicts(pars, result.attribute_conflicts, left_roster, right_roster); read_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster); } @@ -2224,7 +2483,7 @@ roster_merge_result::clear() void roster_merge_result::clear() { - missing_root_dir = false; + missing_root_conflict = false; invalid_name_conflicts.clear(); directory_loop_conflicts.clear(); @@ -2716,7 +2975,7 @@ roster_merge(roster_t const & left_paren // now check for the possible global problems if (!result.roster.has_root()) - result.missing_root_dir = true; + result.missing_root_conflict = true; else { // we can't have an illegal _MTN dir unless we have a root node in the @@ -3696,9 +3955,9 @@ struct simple_missing_root_dir : public virtual void check() { I(!result.is_clean()); - I(result.missing_root_dir); + I(result.missing_root_conflict); result.roster.attach_node(result.roster.create_dir_node(nis), file_path()); - result.missing_root_dir = false; + result.missing_root_conflict = false; I(result.is_clean()); result.roster.check_sane(); } @@ -3888,13 +4147,13 @@ struct multiple_name_plus_missing_root : check_helper(idx(result.multiple_name_conflicts, 1), idx(result.multiple_name_conflicts, 0)); - I(result.missing_root_dir); + I(result.missing_root_conflict); result.roster.attach_node(left_root_nid, file_path()); result.roster.attach_node(right_root_nid, file_path_internal("totally_other_name")); result.multiple_name_conflicts.pop_back(); result.multiple_name_conflicts.pop_back(); - result.missing_root_dir = false; + result.missing_root_conflict = false; I(result.is_clean()); result.roster.check_sane(); } @@ -3924,7 +4183,7 @@ struct duplicate_name_plus_missing_root I(c.left_nid == left_root_nid && c.right_nid == right_root_nid); I(c.parent_name == make_pair(the_null_node, path_component())); - I(result.missing_root_dir); + I(result.missing_root_conflict); // we can't just attach one of these as the root -- see the massive // comment on the old_locations member of roster_t, in roster.hh. @@ -3932,7 +4191,7 @@ struct duplicate_name_plus_missing_root result.roster.attach_node(left_root_nid, file_path_internal("totally_left_name")); result.roster.attach_node(right_root_nid, file_path_internal("totally_right_name")); result.duplicate_name_conflicts.pop_back(); - result.missing_root_dir = false; + result.missing_root_conflict = false; I(result.is_clean()); result.roster.check_sane(); } ============================================================ --- roster_merge.hh 3bbdacaa0de545b88f71e178ca7931616ea0eb83 +++ roster_merge.hh 8eb079ece2445e93680f9a7f5821da6066e1c52a @@ -50,13 +50,13 @@ struct invalid_name_conflict struct invalid_name_conflict { node_id nid; - std::pair parent_name; + std::pair parent_name; // renamed from (if any) }; struct directory_loop_conflict { node_id nid; - std::pair parent_name; + std::pair parent_name; // renamed from (if any) }; // orphaned nodes always merged their name cleanly, so we simply put that name @@ -64,7 +64,13 @@ struct orphaned_node_conflict struct orphaned_node_conflict { node_id nid; + // nid is the orphaned node; it exists in one parent, but the directory + // containing it does not exist in the other. + std::pair parent_name; + // parent_name is the name of the orphaned node, in the parent revision + // where it exists. parent_name.first is the directory containing + // parent_name.second }; // our general strategy is to return a (possibly insane) roster, and a list of @@ -164,7 +170,7 @@ struct roster_merge_result // - attribute conflicts // - file content conflicts - bool missing_root_dir; + bool missing_root_conflict; std::vector invalid_name_conflicts; std::vector directory_loop_conflicts; @@ -181,6 +187,7 @@ struct roster_merge_result bool is_clean() const; bool has_content_conflicts() const; bool has_non_content_conflicts() const; + int count_unsupported_resolution() const; void log_conflicts() const; void report_missing_root_conflicts(roster_t const & left, ============================================================ --- tests/resolve_conflicts_all_resolutions/__driver__.lua eae4fa4b06ac70013d1cabc187778aa0a8f6685b +++ tests/resolve_conflicts_all_resolutions/__driver__.lua 8342870d88378e1df4e10f20f10aae3554727620 @@ -1,5 +1,5 @@ -- Test showing and setting all possible conflict resolutions in a --- conflict file. +-- conflict file. Also test 'conflict show_remaining'. mtn_setup() @@ -48,6 +48,10 @@ check(samefilestd("conflicts-1", "resolu check (mtn("conflicts", "--conflicts-file=resolutions/conflicts", "store", abe_1, beth_1), 0, nil, nil) check(samefilestd("conflicts-1", "resolutions/conflicts")) +check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "show_remaining"), 0, nil, true) +canonicalize("stderr") +check(samefilestd("show_remaining-checkout_left", "stderr")) + check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "show_first"), 0, nil, true) canonicalize("stderr") check(samefilestd("show_first-checkout_left", "stderr")) @@ -64,6 +68,10 @@ check(mtn("conflicts", "--conflicts-file check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "resolve_first_right", "drop"), 0, nil, nil) check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "resolve_first_left", "user", "resolutions/checkout_right.sh"), 0, nil, nil) +check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "show_remaining"), 0, nil, true) +canonicalize("stderr") +check(samefilestd("show_remaining-thermostat", "stderr")) + check(mtn("conflicts", "--conflicts-file=resolutions/conflicts", "show_first"), 0, nil, true) canonicalize("stderr") check(samefilestd("show_first-thermostat", "stderr"))