# # # add_dir "tests/automate_put_revision_for_merge" # # add_file "tests/automate_put_revision_for_merge/__driver__.lua" # content [aa5ec996f615b4bbc6c293310e1c51f08acc9fd1] # # patch "automate.cc" # from [2d83f22200a3999fe8416b301f0bbbca0d90723c] # to [6519694114e5381dce9f2c2d3fe1454f5b5b6f64] # # patch "database.cc" # from [3e65a0b3f6e71edd464d6dc18e8ac46652a44cb1] # to [bf62bfdda4c5f56914474aaf4992b6806e0de30c] # # patch "monotone.texi" # from [7938c0b79baba85d7579efbceb33795ab6ba0be8] # to [672d0287cfc7c1e6cd99e90cf7219013c620a86e] # # patch "revision.cc" # from [f22703d784fb1f7fab53662f2337e5b85d76c95b] # to [51b9684b70e04f68d017220e801b43f0e1a613fd] # # patch "tests/automate_put_file/__driver__.lua" # from [577c79983115ee48055e7cb5ee4cfe84868bdb1b] # to [a7aef8144d70bf049d0b8aa3d47a2252c01e7279] # # patch "tests/automate_put_revision/__driver__.lua" # from [dfe8e3ab661ea525a3d7dd5f1cbf85ac3a3cb987] # to [deeeb9cb4ff8625284a4f69846ef9fa561a42ccf] # # patch "tests/automate_put_revision_unknown_file/__driver__.lua" # from [d5b1f6c033de58c6e27f13ac6296d055e79eb5f8] # to [dc7ec109a51b1e8ccc29e876be4d40bad5363dbf] # ============================================================ --- tests/automate_put_revision_for_merge/__driver__.lua aa5ec996f615b4bbc6c293310e1c51f08acc9fd1 +++ tests/automate_put_revision_for_merge/__driver__.lua aa5ec996f615b4bbc6c293310e1c51f08acc9fd1 @@ -0,0 +1,43 @@ +-- test that 'automate put_revision' works for merge revisions +mtn_setup() + +addfile("foo", "blah blah parent") +commit() +f_parent = sha1("foo") +r_parent = base_revision() + +writefile("foo", "blah blah left") +commit() +f_left = sha1("foo") +r_left = base_revision() + +revert_to(r_parent) + +writefile("foo", "blah blah right") +commit() +f_right = sha1("foo") +r_right = base_revision() + +writefile("foo-merge", "blah blah merge") +f_merge = sha1("foo-merge") + +-- intentionally somewhat idiosyncratic whitespace... +revision_text = ("format_version \"1\"\n" + .. "new_manifest [aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]\n" + .. "old_revision [" .. r_left .. "]\n" + .. "patch \"foo\" from [" .. f_left .. "] \n" + .. "to [" .. f_merge .. "]\n" + .. "\n" + .. "old_revision [" .. r_right .. "]\n" + .. "patch \"foo\" from [" .. f_right .. "] \n" + .. "to [" .. f_merge .. "]\n" + .. "\n" + .. "\n\n\n" -- just for fun + ) +check(mtn("automate", "put_file", readfile("foo-merge")), 0, false, false) +check(mtn("automate", "put_revision", revision_text), 0, true, false) +r_merge = trim(readfile("stdout")) + +check(mtn("update", "-r", r_merge, "-b", "asdf"), 0, false, false) +check(base_revision() == r_merge) +check(samefile("foo", "foo-merge")) ============================================================ --- automate.cc 2d83f22200a3999fe8416b301f0bbbca0d90723c +++ automate.cc 6519694114e5381dce9f2c2d3fe1454f5b5b6f64 @@ -1620,7 +1620,7 @@ AUTOMATE(put_file, N_("[FILEID] CONTENTS // a runtime exception is thrown if base revision is not available AUTOMATE(put_file, N_("[FILEID] CONTENTS"), options::opts::none) { - N(args.size() <= 2, + N(args.size() == 1 || args.size() == 2, F("wrong argument count")); file_id sha1sum; @@ -1630,37 +1630,30 @@ AUTOMATE(put_file, N_("[FILEID] CONTENTS file_data dat(idx(args, 0)()); calculate_ident(dat, sha1sum); - E( - !app.db.file_version_exists(sha1sum), - F("file version %s already known") % sha1sum - ); - - app.db.put_file(sha1sum, dat); + if (!app.db.file_version_exists(sha1sum)) + app.db.put_file(sha1sum, dat); } else if (args.size() == 2) { file_data dat(idx(args, 1)()); calculate_ident(dat, sha1sum); - - E( - !app.db.file_version_exists(sha1sum), - F("file version %s already known") % sha1sum - ); - file_id base_id(idx(args, 0)()); N(app.db.file_version_exists(base_id), F("no file version %s found in database") % base_id); - - file_data olddat; - app.db.get_file_version(base_id, olddat); - delta del; - diff(olddat.inner(), dat.inner(), del); - L(FL("data size %d, delta size %d") % dat.inner()().size() % del().size()); - if (dat.inner()().size() <= del().size()) - // the data is smaller or of equal size to the patch - app.db.put_file(sha1sum, dat); - else - app.db.put_file_version(base_id, sha1sum, file_delta(del)); + + if (!app.db.file_version_exists(sha1sum)) + { + file_data olddat; + app.db.get_file_version(base_id, olddat); + delta del; + diff(olddat.inner(), dat.inner(), del); + L(FL("data size %d, delta size %d") % dat.inner()().size() % del().size()); + if (dat.inner()().size() <= del().size()) + // the data is smaller or of equal size to the patch + app.db.put_file(sha1sum, dat); + else + app.db.put_file_version(base_id, sha1sum, file_delta(del)); + } } else I(false); @@ -1678,7 +1671,7 @@ AUTOMATE(put_file, N_("[FILEID] CONTENTS // The ID of the new revision // Error conditions: // none -AUTOMATE(put_revision, N_("SINGLE-EDGE-DATA"), options::opts::none) +AUTOMATE(put_revision, N_("REVISION-DATA"), options::opts::none) { N(args.size() == 1, F("wrong argument count")); @@ -1712,16 +1705,16 @@ AUTOMATE(put_revision, N_("SINGLE-EDGE-D revision_id id; calculate_ident(rev, id); - E( - !app.db.revision_exists(id), - F("revision %s already present in the database") % id - ); + if (app.db.revision_exists(id)) + P(F("revision %s already present in the database, skipping") % id); + else + { + transaction_guard tr(app.db); + rev.made_for = made_for_database; + app.db.put_revision(id, rev); + tr.commit(); + } - transaction_guard tr(app.db); - rev.made_for = made_for_database; - app.db.put_revision(id, rev); - tr.commit(); - output << id << '\n'; } ============================================================ --- database.cc 3e65a0b3f6e71edd464d6dc18e8ac46652a44cb1 +++ database.cc bf62bfdda4c5f56914474aaf4992b6806e0de30c @@ -1956,12 +1956,23 @@ database::put_revision(revision_id const MM(d.inner()); write_revision(rev, d); - // Phase 1: confirm the revision makes sense + // Phase 1: confirm the revision makes sense, and we the required files + // actually exist { revision_id tmp; MM(tmp); calculate_ident(d, tmp); I(tmp == new_id); + for (edge_map::const_iterator e = rev.edges.begin(); e != rev.edges.end(); ++e) + { + cset const & cs = edge_changes(e); + for (map::const_iterator + i = cs.files_added.begin(); i != cs.files_added.end(); ++i) + I(file_version_exists(i->second)); + for (map >::const_iterator + i = cs.deltas_applied.begin(); i != cs.deltas_applied.end(); ++i) + I(file_version_exists(i->second.second)); + } } transaction_guard guard(*this); ============================================================ --- monotone.texi 7938c0b79baba85d7579efbceb33795ab6ba0be8 +++ monotone.texi 672d0287cfc7c1e6cd99e90cf7219013c620a86e @@ -7513,8 +7513,15 @@ @section Automation @table @strong @item Arguments: address@hidden is the new revision. See example -below. address@hidden is the new revision. See example below. Note that +the new_manifest entry is ignored -- @command{put_revision} will +ignore whatever you put here and calculate the correct manifest id +itself. (However, for now, you must put 40 hex digits here -- it's +just that which particular digits you put are entirely irrelevant. +All zeros is a good choice.) Monotone will also canonicalize your +whitespace automatically. You do not need to worry about getting just +the right amount of indentation in front of each line. However, +everything else about your revision must be valid. @item Added in: @@ -7554,7 +7561,9 @@ @section Automation @item Error conditions: If the changeset is invalid prints an error message to stderr and -exits with status 1. May abort on invalid formats. +exits with status 1. May abort on invalid formats. If the revision is +already present in the database, prints a message to stderr noting +this fact, but otherwise works as normal. @end table ============================================================ --- revision.cc f22703d784fb1f7fab53662f2337e5b85d76c95b +++ revision.cc 51b9684b70e04f68d017220e801b43f0e1a613fd @@ -67,6 +67,10 @@ void revision_t::check_sane() const { // null id in current manifest only permitted if previous // state was null and no changes + // FIXME: above comment makes no sense. This should just be + // I(!null_id(new_manifest)), and the only reason I am not making it so + // right now is that I don't have time to immediately track down all the + // fallout. if (null_id(new_manifest)) { for (edge_map::const_iterator i = edges.begin(); ============================================================ --- tests/automate_put_file/__driver__.lua 577c79983115ee48055e7cb5ee4cfe84868bdb1b +++ tests/automate_put_file/__driver__.lua a7aef8144d70bf049d0b8aa3d47a2252c01e7279 @@ -6,6 +6,11 @@ writefile("expected2", contents2) writefile("expected", contents) writefile("expected2", contents2) +-- check we error on wrong number of args +check(mtn("automate", "put_file"), 1, false, false) +check(mtn("automate", "put_file", "a", "b", "c"), 1, false, false) + +-- check actually putting a file in check(mtn("automate", "put_file", contents), 0, true, false) canonicalize("stdout") file = "4cbd040533a2f43fc6691d773d510cda70f4126a" @@ -27,3 +32,21 @@ check(samefile("expected2", "stdout")) check(mtn("automate", "get_file", file2), 0, true, false) canonicalize("stdout") check(samefile("expected2", "stdout")) + +-- check that nothing blows up if we feed it a file for a second time (with or +-- without a delta base) +check(mtn("automate", "put_file", contents2), 0, true, false) +canonicalize("stdout") +file2 = "ea2e27149f06a6519aa46084da815265c10b0a2a" +result = readfile("stdout") +check(result == file2.."\n") + +check(mtn("automate", "put_file", file, contents2), 0, true, false) +canonicalize("stdout") +file2 = "ea2e27149f06a6519aa46084da815265c10b0a2a" +result = readfile("stdout") +check(result == file2.."\n") + +-- check that things _do_ blow up if we feed it a file with a non-existent +-- base (even if we already have the file we are trying to write!) +check(mtn("automate", "put_file", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", contents2), 1, true, false) ============================================================ --- tests/automate_put_revision/__driver__.lua dfe8e3ab661ea525a3d7dd5f1cbf85ac3a3cb987 +++ tests/automate_put_revision/__driver__.lua deeeb9cb4ff8625284a4f69846ef9fa561a42ccf @@ -1,6 +1,6 @@ mtn_setup() mtn_setup() -edge = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000004]\n\nold_revision []\n\nadd_dir \"\"\n\nadd_file \"foo\"\n content [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145]\n" +rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000004]\n\nold_revision []\n\nadd_dir \"\"\n\nadd_file \"foo\"\n content [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145]\n" check(mtn("automate", "put_file", "blah"), 0, true, false) canonicalize("stdout") @@ -8,7 +8,7 @@ check(result == file.."\n") result = readfile("stdout") check(result == file.."\n") -check(mtn("automate", "put_revision", edge), 0, true, false) +check(mtn("automate", "put_revision", rev), 0, true, false) canonicalize("stdout") rev = "4c2c1d846fa561601254200918fba1fd71e6795d" result = readfile("stdout") @@ -24,9 +24,14 @@ check(rev.."\n" == readfile("stdout")) check(rev.."\n" == readfile("stdout")) -- --- this should exit cleanly without an invariant being violated --- I'm trying to add a node which already exists +-- this should trigger an invariant +-- I'm trying to re-add a file which already exists -- -edge = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [4c2c1d846fa561601254200918fba1fd71e6795d]\n\nadd_file \"foo\"\n content [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145]\n" -check(mtn("automate", "put_revision", edge), 1, false, false) +rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [4c2c1d846fa561601254200918fba1fd71e6795d]\n\nadd_file \"foo\"\n content [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145]\n" +check(mtn("automate", "put_revision", rev), 3, false, false) +-- but this should work (tests that we can use put_revision to commit a +-- single-parent revision) +check(mtn("automate", "put_file", ""), 0, false, false) +rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [4c2c1d846fa561601254200918fba1fd71e6795d]\n\patch \"foo\"\n from [5bf1fd927dfb8679496a2e6cf00cbe50c1c87145] to [da39a3ee5e6b4b0d3255bfef95601890afd80709]\n" +check(mtn("automate", "put_revision", rev), 0, false, false) ============================================================ --- tests/automate_put_revision_unknown_file/__driver__.lua d5b1f6c033de58c6e27f13ac6296d055e79eb5f8 +++ tests/automate_put_revision_unknown_file/__driver__.lua dc7ec109a51b1e8ccc29e876be4d40bad5363dbf @@ -1,8 +1,18 @@ mtn_setup() -- -- subsequent test for automate put_revision -- this should error out since the file version for foo.txt is unknown -- mtn_setup() -edge = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision []\n\nadd_dir \"\"\n\nadd_file \"foo.txt\"\ncontent [1234567890123456789012345678901234567890]" -check(mtn("automate", "put_revision", edge), 1, false, false) +-- added files are checked +rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision []\n\nadd_dir \"\"\n\nadd_file \"foo.txt\"\ncontent [1234567890123456789012345678901234567890]" +check(mtn("automate", "put_revision", rev), 3, false, false) + +addfile("foo", "asdf") +commit() +fhash = sha1("foo") +rhash = base_revision() + +-- modified files are also checked +rev = "format_version \"1\"\n\nnew_manifest [0000000000000000000000000000000000000000]\n\nold_revision [" .. rhash .. "]]\n\npatch \"foo\" from [" .. fhash .. "] to [0000000000000000000000000000000000000000]" +check(mtn("automate", "put_revision", rev), 3, false, false)