# # add_file "tests/README" # # add_file "tests/t_add_stomp_file.at" # # add_file "tests/t_add_vs_commit.at" # # add_file "tests/t_cvsimport2.at" # # add_file "tests/t_database_check_minor.at" # # add_file "tests/t_db_kill_rev_locally.at" # # add_file "tests/t_lf_crlf.at" # # add_file "tests/t_override_author_date.at" # # add_file "tests/t_rcs_import.at" # # add_file "tests/t_rename_dir_add_dir_with_old_name.at" # # add_file "tests/t_update_nonexistent.at" # # patch ".mt-attrs" # from [f9b65d9f157beb7932b20e4a85eb65487949ade9] # to [d916e2d789f2c0f4e92f983099b16ec904a2379b] # # patch "AUTHORS" # from [48363e3c336bf23f49dcd9cc07f1461ec464695e] # to [61efab75094672d58484e63167b5c48a21d0e5c4] # # patch "ChangeLog" # from [b3e7d29d85bd050938da01d54c0d1a5139f9b317] # to [a8c8177d569d38b1854e9b99413352ae7b1950d7] # # patch "app_state.cc" # from [63120a266ad19ebd72022cf172ccbd0c299aa413] # to [b821fb66100e7626444a2f1e4bc2af4d572880b1] # # patch "app_state.hh" # from [a239548498beda52f5c4c4488917ef2516cf2fd8] # to [cb2e94d501a33cfe4d861f8ece44f68a626b9b54] # # patch "cert.cc" # from [f5dc319c1553610d1246dd94f7397669ab09a77f] # to [f680bae9dd64dcfa40eef9a38038a160f5ddadcc] # # patch "cert.hh" # from [4b8178c3e7303d3465932f7d44039e8315f850a6] # to [e2b1d86c8f2610e854afed7ab661c4c2554e7004] # # patch "change_set.cc" # from [f45da9e6d6bc784324d6f606a12d21c34a6f306d] # to [514f646e5cc061ba8bf85e92eebd65357b4c85bf] # # patch "commands.cc" # from [958cd3bbc982aef81f63b90b24dcebaddf135166] # to [180e64e3feece8d9611ce2ab1eaaf85e44375cca] # # patch "commands.hh" # from [f2f45ab20f37d33f52054ae343a0b1a099494a35] # to [cce382e66cf7064f56e731f8e9a48fe8be62d71c] # # patch "contrib/README" # from [6d1db875fa3a286389a8c19eee3b19bf1772a3d6] # to [41748f8afac99a87841c3f94004bbfc6f4295611] # # patch "contrib/monotone-notify.pl" # from [7b80bc10043bcd080e0a98742cd49a3c8aa7b8df] # to [5fe63044551809be3609ccdd179c51b68109ffb1] # # patch "database.cc" # from [221715fff7bde7114e97c033876cfd103d9e84ce] # to [2f0d694a3ff58c86f98ae425dba01c9f0e467578] # # patch "database.hh" # from [90a982ae9ae9e6ef58d778958e64735ba93b5029] # to [7c532dedbdcb9c8dd9a19b7a80c3ce63a855f3c9] # # patch "database_check.cc" # from [4cd1803a15f7248d15530e9a1b9f1440655341cf] # to [3f9cec85909aa49e0da47732370e6ec427f2f4ba] # # patch "diff_patch.cc" # from [c080a1bd6428d24243c0fc224414be6eb0fb7949] # to [ef0c0571db4382835b2fa73475bd23cbe6a6ea33] # # patch "manifest.cc" # from [3d231ab4795853b737dc33a8dcb2dd4d50361fbc] # to [aefd8d086360ea7349a541e2dfc0f4f7c28d9913] # # patch "monotone.1" # from [7ce55a7b2b6ec659e94edd7947ee9ce6c62053e1] # to [33e5e256e415dd185535ac1331d44a9de3f243cc] # # patch "monotone.cc" # from [65759f227bc57b92883f785551586f30039721cc] # to [4c79e9536eda561c30ef51c3e03d08a154a2c40f] # # patch "monotone.texi" # from [82e309cffe8832da1e871c00c9795af49531f1df] # to [38e796ddafa66c78001fb53209d8c7e0dd8c0b75] # # patch "netsync.cc" # from [177a062ed520ffe6ba1b92a07fbc6f7d8bcb2c0f] # to [ec1c826c7bd7e01ce30946a1b9a7cce2d8be6b8e] # # patch "packet.cc" # from [d5c6132eb05a5c5f64c504e465d825bf8a7e17d0] # to [7f4cc56b5a4fd62495fcec98bcc2e67c6ddb96a8] # # patch "rcs_import.cc" # from [4e0b292289773a83cad888d88a347d0423f1ad69] # to [60f5ef2fcd089c3f6bde86d3dcca879dabe58d5b] # # patch "revision.cc" # from [c2d18cfa9db45131465b2d6a7e569c7c96532528] # to [bac73f865fd7cdfe7fd183a428fbffb27613e4bd] # # patch "sanity.cc" # from [2fd252fa148582b35ba95b8ae368e2c500d4b01f] # to [87f3bbf0785ac602b26fc8f36ff4efdf475db220] # # patch "sanity.hh" # from [e7e2010f734f9e54499e14c700017b4f0577e9ac] # to [952408e83f22dc43392c7afcffc6e556cfd80d6b] # # patch "tests/README" # from [] # to [fdbb3d9f63642092343e0ea58b7ee937f910103a] # # patch "tests/t_add_stomp_file.at" # from [] # to [ff7f16f34c202ec044ad232af78786bd119b5e98] # # patch "tests/t_add_vs_commit.at" # from [] # to [7fd4b57e97609427e699e0cf3cca5c984ffd1330] # # patch "tests/t_cvsimport2.at" # from [] # to [4641874ff2fbedf55b84c1169dd86e4d2354c36b] # # patch "tests/t_database_check.at" # from [a5bf8e5ed630bd268f71307fd95a23e7affbd5ff] # to [208a81e56219fc72a6db6330841255baab90824b] # # patch "tests/t_database_check_minor.at" # from [] # to [eaba52f05e60be3f4bfc420af0722fd5ed63df02] # # patch "tests/t_db_kill_rev_locally.at" # from [] # to [7833b3652bf45b72b9fb6490b86fe2b66d7105ee] # # patch "tests/t_diff_binary.at" # from [651f781bf58848169446baee444f3f1418b7b24f] # to [de45d9dca34e5765bd998968752d25368c8238e1] # # patch "tests/t_lf_crlf.at" # from [] # to [5c7f37f18e1ae4e195a4f7581145705d390e1747] # # patch "tests/t_netsync_defaults.at" # from [8f37b7de81c532b83851011586762f8c7fd91083] # to [09eba929e451e94f6075bd99fe75fc0469ba84e1] # # patch "tests/t_netsync_single.at" # from [1fa271a852629b392339705a3b6d5e143b7948c3] # to [faa5253849ae86d140b67fb93609d191456b0ec3] # # patch "tests/t_no_rename_overwrite.at" # from [345db00bfc31ac8c23f0735fe890c4d869e37448] # to [1fe86d8eeda1180521602e9b66628aadad9b75ac] # # patch "tests/t_override_author_date.at" # from [] # to [0f0499f19e887a4dbe83663f5e704110ca270bb3] # # patch "tests/t_rcs_import.at" # from [] # to [a77bf74fb559cc91d3167022f6595e2fb471bac4] # # patch "tests/t_rename_dir_add_dir_with_old_name.at" # from [] # to [b005c1839d019fa629d02fa1373f391715937594] # # patch "tests/t_update_nonexistent.at" # from [] # to [a7f5255b645f8be25c40d15a62f9f5279bf2aa59] # # patch "testsuite.at" # from [14b8600c48fdafc19eba6942e2270738b4bd8c71] # to [78e75421c02747bc50d1966394370014a1e16858] # # patch "transforms.cc" # from [012bff62d5ad1dc8bba4123b2b366b02de6fbb75] # to [3c627fa48277dc799dd2fc2eccb8d7b9ca93688b] # # patch "transforms.hh" # from [0886fc260e3989244f914da25c26e8b6ffc8342f] # to [10ff93cf2f434ffbd5476823a28d883269f9d417] # # patch "vocab.hh" # from [17d5f1d1a5d0cbd5c7edb41ba42e8023273b0e99] # to [8748cf40dd076dd813dbdab8d722b6da167218f0] # # patch "vocab_terms.hh" # from [45c113192d19c5615f5d9822d76c686c8a301849] # to [e1637d600ff20487c2c87a9f46a5291a49c09b50] # # patch "work.cc" # from [2edb3fc960fee8c34b1abb13dff648ec63eeb40f] # to [dd1b1800d78f63c9f5615622d7c638c01c2f7459] # --- .mt-attrs +++ .mt-attrs @@ -1,4 +1,4 @@ - file "contrib/Notify.pl" + file "contrib/monotone-notify.pl" execute "true" file "contrib/ciabot_monotone.py" --- AUTHORS +++ AUTHORS @@ -52,6 +52,8 @@ Jon Bright Corey Halpin Jeremy Cowgar + Martin Dvorak + Emile Snyder supporting files: --- ChangeLog +++ ChangeLog @@ -1,3 +1,219 @@ +2005-04-17 Matt Johnston + + * vocab_terms.hh: remove commented out lines. + +2005-04-17 Matt Johnston + + * Move base64 code as close to the database as possible, + to avoid unnecessary inflating and deflating. + +2005-04-17 Nathaniel Smith + + * monotone.texi (Branching and Merging): A few small edits. + +2005-04-17 Nathaniel Smith + + * change_set.cc (path_item, sanity_check_path_item): Mark things + inline. + +2005-04-17 Henrik Holmboe + + * contrib/monotone-notify.pl: Add signal handlers. Correct some + typos. + (my_exit): New function that does a cleanup and exit. + +2005-04-17 Olivier Andrieu + + * transforms.cc: fix glob_to_regexp assertions + +2005-04-17 Sebastian Spaeth + + * tests/t_db_kill_rev_locally.at: new test; + make sure that db kill_rev_locally works as intended + +2005-04-17 Sebastian Spaeth + + * commands.cc,database.cc: add 'db kill_rev_locally ' command + still missing: documentation and autotests. Otherwise seems ok. + +2005-04-17 Richard Levitte + + * transforms.cc: Remove tabs and make sure emacs doesn't add + them. + +2005-04-17 Nathaniel Smith + + * sanity.{hh,cc} (E, error_failure): New sort of invariant. + * netsync.cc (process_hello_cmd): Make initial pull message + more clear and friendly. + Also, if the key has changed, that is an error, not naughtiness. + * database_check.cc (check_db): Database problems are also errors, + not naughtiness. Revamp output in case of errors, to better + distinguish non-serious errors and serious errors. + * tests/t_database_check.at: Update accordingly. + * tests/t_database_check_minor.at: New test. + * testsuite.at: Add it. + +2005-04-17 Richard Levitte + + * transforms.cc (glob_to_regexp): New function that takes a glob + expression and transforms it into a regexp. This will be useful + for globbing branch expressions when collections are exchanged to + branch globs and regexps. + (glob_to_regexp_test): A unit test for glob_to_regexp(). + +2005-04-16 Emile Snyder + + * tests/t_add_stomp_file.at: New test for failing case. + If you have a file foo in your working dir (not monotone + controlled) and someone else adds a file foo and commits, + update should at least warn you before stomping your + non-recoverable foo file. + * testsuite.at: Add it. + +2005-04-17 Matt Johnston + + * commands.cc: warn that dropkey won't truly erase the privkey + from the database + * monotone.texi: same + +2005-04-17 Matt Johnston + + * database.cc: mention that it could be the filesystem that + is full in the SQLITE_FULL error message + +2005-04-16 Derek Scherger + + * work.cc (known_preimage_path): rename to... + (known_path): this, since it's image agnostic + (build_deletions): update for renamed function + (build_rename): ensure rename source exists in current revision + and rename target does not exist in current revision + + * tests/t_no_rename_overwrite.at: un-XFAIL + +2005-04-16 Nathaniel Smith + + * app_state.{cc,hh} (set_author, set_date): New methods. + * cert.cc (cert_revision_date): Rename to... + (cert_revision_date_time): ...an overloaded version of this. + (cert_revision_author_default): Check app.date. + * cert.hh: Expose cert_revision_date_time. + * commands.cc (commit): Handle --date. + * main.cc: Parse --date and --author options. + * monotone.1: Document --date, --author. + * monotone.texi (Working Copy, OPTIONS): Likewise. + + * tests/t_override_author_date.at: New test. + * testsuite.at: Add it. + + This commit heavily based on a patch by Markus Schiltknecht + . + +2005-04-16 Nathaniel Smith + + * ChangeLog: Fixup after merge. + +2005-04-17 Matthew Gregan + + * monotone.cc: Fix warnings: add missing initializers. + * netsync.cc: Fix warnings: inline static vs static inline. + +2005-04-16 Nathaniel Smith + + * tests/t_update_nonexistent.at: New test. + * testsuite.at: Add it. + + * commands.cc (update): Verify that user's requested revision + exists. + +2005-04-16 Nathaniel Smith + + * ChangeLog: Fixup after merge. + +2005-04-16 Emile Snyder + + * tests/t_add_vs_commit.at: New test for failing case. If you + add a file in you working dir, someone else adds the same file + and commits, then you do an update it messes up your working + directory. + * testsuite.at: Add it. + +2005-04-16 Nathaniel Smith + + * commands.cc (checkout): Move check for existence of revision + earlier. + + * tests/t_netsync_defaults.at, tests/t_netsync_single.at: + Don't hard-code netsync port. + +2005-04-16 Nathaniel Smith + + * testsuite.at: Use a random server port. + + * .mt-attrs, contrib/README: Update for Notify.pl -> + monotone-notify.pl rename. + + * monotone.1: Warn people off rcs_import. + * monotone.texi (Commands): Likewise. + +2005-04-16 Nathaniel Smith + + * AUTHORS: Add Emile Snyder . + +2005-04-16 Nathaniel Smith + + * tests/t_lf_crlf.at: New test from Emile Snyder + , with tweaks. + * testsuite.at: Add it. + +2005-04-16 Nathaniel Smith + + * ChangeLog: Small fixups. + +2005-04-16 Sebastian Spaeth + + * tests/t_cvsimport2.at: new test; CVS Attic files fail test + reported by: address@hidden 15.04.2005 02:45 + +2005-04-16 Sebastian Spaeth + + * tests/t_rcs_import.at: new test; problematic CVS import as + reported in the list. However it works just fine here, so it + really tests for a successful pass + +2005-04-16 Sebastian Spaeth + + * tests/README: new file, on how to create/run tests + +2005-04-16 Nathaniel Smith + + * tests/t_rename_dir_add_dir_with_old_name.at: XFAIL. + +2005-04-16 Nathaniel Smith + + * tests/t_diff_binary.at: Un-XFAIL. + +2005-04-16 Nathaniel Smith + + * monotone.texi (Network Service): Rewrite to include former + Exchanging Keys section. + (Branching and Merging): New tutorial section, inspired by a patch + from Martin Kihlgren . + (CVS Phrasebook): Add "Importing a New Project". + + * AUTHORS: Add Martin Dvorak. + +2005-04-15 Martin Dvorak + + * tests/t_rename_dir_add_dir_with_old_name.at: New test. + * testsuite.at: Add it. + +2005-04-16 Matt Johnston + + * change_set.cc (compose_rearrangement): remove logging statements + that were using noticable CPU time. + 2005-04-15 Olivier Andrieu * diff_patch.cc(guess_binary): do not use '\x00' as first @@ -2796,7 +3012,7 @@ * AUTHORS: Mention Wojciech and Neil. * revision.cc (calculate_ancestors_from_graph): Make non-recursive. -2005-01-17 Wojciech Miłkowski +2005-01-17 Wojciech Miłkowski * std_hooks.lua: Teach about meld. --- app_state.cc +++ app_state.cc @@ -273,6 +273,18 @@ } void +app_state::set_date(utf8 const & d) +{ + date = d; +} + +void +app_state::set_author(utf8 const & a) +{ + author = a; +} + +void app_state::set_depth(long d) { N(d > 0, --- app_state.hh +++ app_state.hh @@ -37,6 +37,8 @@ bool rcfiles; options_map options; utf8 message; + utf8 date; + utf8 author; utf8 search_root; std::vector revision_selectors; std::vector extra_rcfiles; @@ -78,6 +80,8 @@ void set_signing_key(utf8 const & key); void set_root(utf8 const & root); void set_message(utf8 const & message); + void set_date(utf8 const & date); + void set_author(utf8 const & author); void set_depth(long depth); void add_revision(utf8 const & selector); --- cert.cc +++ cert.cc @@ -566,11 +566,11 @@ string const testresult_cert_name = "testresult"; -static void -cert_revision_date(revision_id const & m, - boost::posix_time::ptime t, - app_state & app, - packet_consumer & pc) +void +cert_revision_date_time(revision_id const & m, + boost::posix_time::ptime t, + app_state & app, + packet_consumer & pc) { string val = boost::posix_time::to_iso_extended_string(t); put_simple_revision_cert(m, date_cert_name, val, app, pc); @@ -585,7 +585,7 @@ // make sure you do all your CVS conversions by 2038! boost::posix_time::ptime tmp(boost::gregorian::date(1970,1,1), boost::posix_time::seconds(static_cast(t))); - cert_revision_date(m, tmp, app, pc); + cert_revision_date_time(m, tmp, app, pc); } void @@ -593,7 +593,7 @@ app_state & app, packet_consumer & pc) { - cert_revision_date(m, boost::posix_time::second_clock::universal_time(), app, pc); + cert_revision_date_time(m, boost::posix_time::second_clock::universal_time(), app, pc); } void @@ -620,8 +620,7 @@ % app.branch_name); author = key(); } - put_simple_revision_cert(m, author_cert_name, - author, app, pc); + cert_revision_author(m, author, app, pc); } void --- cert.hh +++ cert.hh @@ -12,6 +12,7 @@ #include #include #include +#include // certs associate an opaque name/value pair with a particular identifier in // the system (eg. a manifest or file id) and are accompanied by an RSA @@ -119,6 +120,12 @@ void cert_revision_date_time(revision_id const & m, + boost::posix_time::ptime t, + app_state & app, + packet_consumer & pc); + +void +cert_revision_date_time(revision_id const & m, time_t time, app_state & app, packet_consumer & pc); --- change_set.cc +++ change_set.cc @@ -61,11 +61,11 @@ tid parent; ptype ty; path_component name; - path_item() {} - path_item(tid p, ptype t, path_component n); - path_item(path_item const & other); - path_item const & operator=(path_item const & other); - bool operator==(path_item const & other) const; + inline path_item() {} + inline path_item(tid p, ptype t, path_component n); + inline path_item(path_item const & other); + inline path_item const & operator=(path_item const & other); + inline bool operator==(path_item const & other) const; }; @@ -506,7 +506,7 @@ } -static void +inline static void sanity_check_path_item(path_item const & pi) { } @@ -761,15 +761,19 @@ if (old_path == new_path) { + /* L(F("skipping preserved %s %d : '%s'\n") % (path_item_type(old_item) == ptype_directory ? "directory" : "file") % curr % old_path); + */ continue; } + /* L(F("analyzing %s %d : '%s' -> '%s'\n") % (path_item_type(old_item) == ptype_directory ? "directory" : "file") % curr % old_path % new_path); + */ if (null_name(path_item_name(old_item))) { --- commands.cc +++ commands.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "commands.hh" #include "constants.hh" @@ -1183,7 +1184,10 @@ if (app.db.private_key_exists(ident)) { - P(F("dropping private key '%s' from database\n") % ident); + P(F("dropping private key '%s' from database\n\n") % ident); + W(F("the private key data may not have been erased from the")); + W(F("database. it is recommended that you use 'db dump' and")); + W(F("'db load' to be sure.")); app.db.delete_private_key(ident); key_deleted = true; } @@ -1495,8 +1499,7 @@ boost::shared_ptr cs(new change_set()); string log_message(""); - base64< gzip< data > > gz_dat; - base64< gzip< delta > > gz_del; + delta del; file_path pth(idx(args, 1)()); transaction_guard guard(app.db); @@ -1514,24 +1517,23 @@ // fetch the new file input string s = get_stdin(); - pack(data(s), gz_dat); - new_fdata = file_data(gz_dat); + new_fdata = file_data(s); calculate_ident(new_fdata, new_fid); // diff and store the file edge old_fid = manifest_entry_id(i); app.db.get_file_version(old_fid, old_fdata); - diff(old_fdata.inner(), new_fdata.inner(), gz_del); + diff(old_fdata.inner(), new_fdata.inner(), del); dbw.consume_file_delta(old_fid, new_fid, - file_delta(gz_del)); + file_delta(del)); // diff and store the manifest edge new_man = old_man; new_man[pth] = new_fid; calculate_ident(new_man, new_mid); - diff(old_man, new_man, gz_del); + diff(old_man, new_man, del); dbw.consume_manifest_delta(old_mid, new_mid, - manifest_delta(gz_del)); + manifest_delta(del)); // build and store a changeset and revision cs->apply_delta(pth, old_fid, new_fid); @@ -1566,12 +1568,9 @@ CMD(fload, "debug", "", "load file contents into db") { string s = get_stdin(); - base64< gzip< data > > gzd; - pack(data(s), gzd); - file_id f_id; - file_data f_data(gzd); + file_data f_data(s); calculate_ident (f_data, f_id); @@ -1586,7 +1585,6 @@ file_id anc_id(idx(args, 0)()), left_id(idx(args, 1)()), right_id(idx(args, 2)()); file_data anc, left, right; - data anc_unpacked, left_unpacked, right_unpacked; N(app.db.file_version_exists (anc_id), F("ancestor file id does not exist")); @@ -1601,15 +1599,11 @@ app.db.get_file_version(left_id, left); app.db.get_file_version(right_id, right); - unpack(left.inner(), left_unpacked); - unpack(anc.inner(), anc_unpacked); - unpack(right.inner(), right_unpacked); - vector anc_lines, left_lines, right_lines, merged_lines; - split_into_lines(anc_unpacked(), anc_lines); - split_into_lines(left_unpacked(), left_lines); - split_into_lines(right_unpacked(), right_lines); + split_into_lines(anc.inner()(), anc_lines); + split_into_lines(left.inner()(), left_lines); + split_into_lines(right.inner()(), right_lines); N(merge3(anc_lines, left_lines, right_lines, merged_lines), F("merge failed")); copy(merged_lines.begin(), merged_lines.end(), ostream_iterator(cout, "\n")); @@ -1692,9 +1686,7 @@ file_data dat; L(F("dumping file %s\n") % ident); app.db.get_file_version(ident, dat); - data unpacked; - unpack(dat.inner(), unpacked); - cout.write(unpacked().data(), unpacked().size()); + cout.write(dat.inner()().data(), dat.inner()().size()); } else if (idx(args, 0)() == "manifest") { @@ -1721,9 +1713,7 @@ } L(F("dumping manifest %s\n") % ident); - data unpacked; - unpack(dat.inner(), unpacked); - cout.write(unpacked().data(), unpacked().size()); + cout.write(dat.inner()().data(), dat.inner()().size()); } else if (idx(args, 0)() == "revision") @@ -1750,9 +1740,7 @@ } L(F("dumping revision %s\n") % ident); - data unpacked; - unpack(dat.inner(), unpacked); - cout.write(unpacked().data(), unpacked().size()); + cout.write(dat.inner()().data(), dat.inner()().size()); } else throw usage(name); @@ -1793,6 +1781,9 @@ dir = idx(args, 1)(); complete(app, idx(args, 0)(), ident); + N(app.db.revision_exists(ident), + F("no revision %s found in database") % ident); + { cert_value b; guess_branch(ident, app, b); @@ -1824,9 +1815,6 @@ manifest_id mid; manifest_map m; - N(app.db.revision_exists(ident), - F("no revision %s found in database") % ident); - app.db.get_revision_manifest(ident, mid); put_revision_id(ident); @@ -2140,7 +2128,7 @@ app.db.get_manifest(m_old_id, m_old); app.db.get_manifest(m_new_id, m_new); - base64< gzip > del; + delta del; diff(m_old, m_new, del); pw.consume_manifest_delta(m_old_id, m_new_id, manifest_delta(del)); @@ -2161,7 +2149,7 @@ app.db.get_file_version(f_old_id, f_old_data); app.db.get_file_version(f_new_id, f_new_data); - base64< gzip > del; + delta del; diff(f_old_data.inner(), f_new_data.inner(), del); pw.consume_file_delta(f_old_id, f_new_id, file_delta(del)); } @@ -2414,6 +2402,7 @@ "load\n" "migrate\n" "execute\n" + "kill_rev_locally \n" "check\n" "changesetify\n" "rebuild\n" @@ -2447,6 +2436,8 @@ { if (idx(args, 0)() == "execute") app.db.debug(idx(args, 1)(), cout); + else if (idx(args, 0)() == "kill_rev_locally") + kill_rev_locally(app,idx(args, 1)()); else if (idx(args, 0)() == "clear_epoch") app.db.clear_epoch(cert_value(idx(args, 1)())); else @@ -2464,6 +2455,24 @@ throw usage(name); } +/// Deletes a revision from the local database +/// This can be used to 'undo' a changed revision from a local database without +/// leaving a trace. +void kill_rev_locally(app_state& app, std::string const& id){ + revision_id ident; + complete(app, id, ident); + N(app.db.revision_exists(ident), + F("no revision %s found in database") % ident); + + //check that the revision does not have any children + set children; + app.db.get_revision_children(ident, children); + N(!children.size(), + F("revision %s already has children. We cannot kill it.") % ident); + + app.db.delete_existing_rev_and_certs(ident); +} + CMD(attr, "working copy", "set FILE ATTR VALUE\nget FILE [ATTR]", "get or set file attributes") { @@ -2541,6 +2550,33 @@ else throw usage(name); } + +static boost::posix_time::ptime +string_to_datetime(std::string const & s) +{ + try + { + // boost::posix_time is lame: it can parse "basic" ISO times, of the + // form 20000101T120000, but not "extended" ISO times, of the form + // 2000-01-01T12:00:00. So do something stupid to convert one to the + // other. + std::string tmp = s; + std::string::size_type pos = 0; + while ((pos = tmp.find_first_of("-:")) != string::npos) + tmp.erase(pos, 1); + return boost::posix_time::from_iso_string(tmp); + } + catch (std::out_of_range &e) + { + N(false, F("failed to parse date string '%s': %s") % s % e.what()); + } + catch (std::exception &) + { + N(false, F("failed to parse date string '%s'") % s); + } + I(false); +} + CMD(commit, "working copy", "[--message=STRING] [PATH]...", "commit working copy to database") { @@ -2611,7 +2647,7 @@ L(F("inserting manifest delta %s -> %s\n") % edge_old_manifest(edge) % rs.new_manifest); - base64< gzip > del; + delta del; diff(m_old, m_new, del); dbw.consume_manifest_delta(edge_old_manifest(edge), rs.new_manifest, @@ -2641,7 +2677,7 @@ L(F("inserting delta %s -> %s\n") % delta_entry_src(i) % delta_entry_dst(i)); file_data old_data; - base64< gzip > new_data; + data new_data; app.db.get_file_version(delta_entry_src(i), old_data); read_localized_data(delta_entry_path(i), new_data, app.lua); // sanity check @@ -2650,7 +2686,7 @@ N(tid == delta_entry_dst(i).inner(), F("file '%s' modified during commit, aborting") % delta_entry_path(i)); - base64< gzip > del; + delta del; diff(old_data.inner(), new_data, del); dbw.consume_file_delta(delta_entry_src(i), delta_entry_dst(i), @@ -2659,7 +2695,7 @@ else { L(F("inserting full version %s\n") % delta_entry_dst(i)); - base64< gzip > new_data; + data new_data; read_localized_data(delta_entry_path(i), new_data, app.lua); // sanity check hexenc tid; @@ -2677,8 +2713,14 @@ dbw.consume_revision_data(rid, rdat); cert_revision_in_branch(rid, branchname, app, dbw); - cert_revision_date_now(rid, app, dbw); - cert_revision_author_default(rid, app, dbw); + if (app.date().length() > 0) + cert_revision_date_time(rid, string_to_datetime(app.date()), app, dbw); + else + cert_revision_date_now(rid, app, dbw); + if (app.author().length() > 0) + cert_revision_author(rid, app.author(), app, dbw); + else + cert_revision_author_default(rid, app, dbw); cert_revision_changelog(rid, log_message, app, dbw); } @@ -2733,7 +2775,7 @@ { file_data dat; app.db.get_file_version(delta_entry_dst(i), dat); - unpack(dat.inner(), unpacked); + unpacked = dat.inner(); } else { @@ -2762,35 +2804,31 @@ else { file_data f_old; - gzip decoded_old; - data decompressed_old, decompressed_new; + data data_old, data_new; vector old_lines, new_lines; app.db.get_file_version(delta_entry_src(i), f_old); - decode_base64(f_old.inner(), decoded_old); - decode_gzip(decoded_old, decompressed_old); + data_old = f_old.inner(); if (new_is_archived) { file_data f_new; - gzip decoded_new; app.db.get_file_version(delta_entry_dst(i), f_new); - decode_base64(f_new.inner(), decoded_new); - decode_gzip(decoded_new, decompressed_new); + data_new = f_new.inner(); } else { read_localized_data(delta_entry_path(i), - decompressed_new, app.lua); + data_new, app.lua); } - if (guess_binary(decompressed_new()) || - guess_binary(decompressed_old())) + if (guess_binary(data_new()) || + guess_binary(data_old())) cout << "# " << delta_entry_path(i) << " is binary\n"; else { - split_into_lines(decompressed_old(), old_lines); - split_into_lines(decompressed_new(), new_lines); + split_into_lines(data_old(), old_lines); + split_into_lines(data_new(), new_lines); make_diff(delta_entry_path(i)(), delta_entry_path(i)(), old_lines, new_lines, @@ -3142,7 +3180,11 @@ r_chosen_id = *(candidates.begin()); } else - complete(app, idx(args, 0)(), r_chosen_id); + { + complete(app, idx(args, 0)(), r_chosen_id); + N(app.db.revision_exists(r_chosen_id), + F("no revision %s found in database") % r_chosen_id); + } if (r_old_id == r_chosen_id) { @@ -3311,7 +3353,7 @@ apply_change_set(anc_man, *anc_to_left, tmp); apply_change_set(tmp, *left_to_merged, merged_man); calculate_ident(merged_man, merged_rev.new_manifest); - base64< gzip > left_mdelta, right_mdelta; + delta left_mdelta, right_mdelta; diff(left_man, merged_man, left_mdelta); diff(right_man, merged_man, right_mdelta); if (left_mdelta().size() < right_mdelta().size()) --- commands.hh +++ commands.hh @@ -25,6 +25,7 @@ namespace commands { void explain_usage(std::string const & cmd, std::ostream & out); int process(app_state & app, std::string const & cmd, std::vector const & args); + void kill_rev_locally(app_state& app, std::string const & id); typedef enum { sel_author, --- contrib/README +++ contrib/README @@ -5,8 +5,8 @@ -- Log2Gxl.java: Java code to convert output from 'monotone log' into GXL, an XML-based graph description language. - -- Notify.pl: a Perl hack to create email logs with recent changes - in a repository database. It's designed to be run at regular + -- monotone-notify.pl: a Perl hack to create email logs with recent + changes in a repository database. It's designed to be run at regular intervals, for example from a cron script. It uses database variables (those handled with 'monotone set/unset') in the domain domain 'notify' to keep track of the last revisions that have --- contrib/monotone-notify.pl +++ contrib/monotone-notify.pl @@ -73,6 +73,11 @@ 'quiet' => \$quiet, 'debug' => \$debug) or pod2usage(2); +$SIG{HUP} = \&my_exit; +$SIG{KILL} = \&my_exit; +$SIG{TERM} = \&my_exit; +$SIG{INT} = \&my_exit; + ###################################################################### # Respond to user input # @@ -97,7 +102,7 @@ pod2usage(2); } if (!defined $difflogs_to && !defined $nodifflogs_to) { - my_errlog("You need to specify a To address with --to"); + my_errlog("You need to specify a To address with --diffslogs-to or --nodiffslogs-to"); pod2usage(2); } } @@ -140,12 +145,15 @@ my $remove_workdir = 0; if (!defined $workdir) { - $workdir = "/var/tmp/monotone_motify.work.$$"; + $workdir = "/var/tmp/monotone_notify.work.$$"; mkdir $workdir; $remove_workdir = 1; } elsif (! file_name_is_absolute($workdir)) { $workdir = rel2abs($workdir); } +if (! -d $workdir && ! -w $workdir && ! -r $workdir) { + my_error("work directory $workdir not accessible, exiting"); +} my_debug("using work directory $workdir"); my_debug("(to be removed after I'm done)") if $remove_workdir; @@ -448,13 +456,8 @@ ###################################################################### # Clean up. # -my_log("cleaning up."); -unlink @files_to_clean_up; -rmdir $workdir if $remove_workdir; +my_exit(); -my_log("all done."); -exit(0); - ###################################################################### # Subroutines # @@ -594,6 +597,17 @@ return $return; } +# my_exit removes temporary files and gracefully closes network +# connections. +sub my_exit +{ + my_log("cleaning up."); + unlink @files_to_clean_up; + rmdir $workdir if $remove_workdir; + my_log("all done."); + exit(0); +} + # my_backtick does the same thing as backtick commands, but will print a bit # of debugging output when $debug is true. It will also die if the subprocess # returned an error code. @@ -738,7 +752,7 @@ two files F and F will be left in the work directory. -The default working directory is F, +The default working directory is F, and will be removed automatically unless F or F are left in it. --- database.cc +++ database.cc @@ -589,7 +589,7 @@ break; case SQLITE_FULL: - throw oops("Insertion failed because database is full"); + throw oops("Insertion failed because database (or filesystem) is full"); break; case SQLITE_CANTOPEN: @@ -858,7 +858,7 @@ void database::get(hexenc const & ident, - base64< gzip > & dat, + data & dat, string const & table) { results res; @@ -868,17 +868,20 @@ // consistency check base64 > rdata(res[0][0]); + data rdata_unpacked; + unpack(rdata, rdata_unpacked); + hexenc tid; - calculate_ident(rdata, tid); + calculate_ident(rdata_unpacked, tid); I(tid == ident); - dat = rdata; + dat = rdata_unpacked; } void database::get_delta(hexenc const & ident, hexenc const & base, - base64< gzip > & del, + delta & del, string const & table) { I(ident() != ""); @@ -887,12 +890,14 @@ fetch(res, one_col, one_row, "SELECT delta FROM '%q' WHERE id = '%q' AND base = '%q'", table.c_str(), ident().c_str(), base().c_str()); - del = res[0][0]; + + base64 > del_packed = res[0][0]; + unpack(del_packed, del); } void database::put(hexenc const & ident, - base64< gzip > const & dat, + data const & dat, string const & table) { // consistency check @@ -900,22 +905,29 @@ hexenc tid; calculate_ident(dat, tid); I(tid == ident); + + base64 > dat_packed; + pack(dat, dat_packed); execute("INSERT INTO '%q' VALUES('%q', '%q')", - table.c_str(), ident().c_str(), dat().c_str()); + table.c_str(), ident().c_str(), dat_packed().c_str()); } void database::put_delta(hexenc const & ident, hexenc const & base, - base64 > const & del, + delta const & del, string const & table) { // nb: delta schema is (id, base, delta) I(ident() != ""); I(base() != ""); + + base64 > del_packed; + pack(del, del_packed); + execute("INSERT INTO '%q' VALUES('%q', '%q', '%q')", table.c_str(), - ident().c_str(), base().c_str(), del().c_str()); + ident().c_str(), base().c_str(), del_packed().c_str()); } // static ticker cache_hits("vcache hits", "h", 1); @@ -924,22 +936,21 @@ { size_t capacity; size_t use; - std::map, base64< gzip > > cache; + std::map, data> cache; version_cache() : capacity(constants::db_version_cache_sz), use(0) { srand(time(NULL)); } - void put(hexenc const & ident, - base64< gzip > const & dat) + void put(hexenc const & ident, data const & dat) { while (!cache.empty() && use + dat().size() > capacity) { std::string key = (F("%08.8x%08.8x%08.8x%08.8x%08.8x") % rand() % rand() % rand() % rand() % rand()).str(); - std::map, base64< gzip > >::const_iterator i; + std::map, data>::const_iterator i; i = cache.lower_bound(hexenc(key)); if (i == cache.end()) { @@ -959,15 +970,14 @@ bool exists(hexenc const & ident) { - std::map, base64< gzip > >::const_iterator i; + std::map, data>::const_iterator i; i = cache.find(ident); return i != cache.end(); } - bool get(hexenc const & ident, - base64< gzip > & dat) + bool get(hexenc const & ident, data & dat) { - std::map, base64< gzip > >::const_iterator i; + std::map, data>::const_iterator i; i = cache.find(ident); if (i == cache.end()) return false; @@ -982,7 +992,7 @@ void database::get_version(hexenc const & ident, - base64< gzip > & dat, + data & dat, string const & data_table, string const & delta_table) { @@ -1086,17 +1096,17 @@ I(found_root); I(root() != ""); - base64< gzip > begin_packed; data begin; if (vcache.exists(root)) { - I(vcache.get(root, begin_packed)); + I(vcache.get(root, begin)); } else - get(root, begin_packed, data_table); + { + get(root, begin, data_table); + } - unpack(begin_packed, begin); hexenc curr = root; boost::shared_ptr app = new_piecewise_applicator(); @@ -1112,18 +1122,14 @@ if (!vcache.exists(curr)) { string tmp; - base64< gzip > tmp_packed; app->finish(tmp); - pack(data(tmp), tmp_packed); - vcache.put(curr, tmp_packed); + vcache.put(curr, tmp); } L(F("following delta %s -> %s\n") % curr % nxt); - base64< gzip > del_packed; - get_delta(nxt, curr, del_packed, delta_table); delta del; - unpack(del_packed, del); + get_delta(nxt, curr, del, delta_table); apply_delta (app, del()); app->next(); @@ -1132,12 +1138,11 @@ string tmp; app->finish(tmp); - data end(tmp); + dat = data(tmp); hexenc final; - calculate_ident(end, final); + calculate_ident(dat, final); I(final == ident); - pack(end, dat); } vcache.put(ident, dat); } @@ -1155,13 +1160,13 @@ void database::put_version(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, string const & data_table, string const & delta_table) { - base64< gzip > old_data, new_data; - base64< gzip > reverse_delta; + data old_data, new_data; + delta reverse_delta; get_version(old_id, old_data, data_table, delta_table); patch(old_data, del, new_data); @@ -1182,11 +1187,11 @@ void database::put_reverse_version(hexenc const & new_id, hexenc const & old_id, - base64< gzip > const & reverse_del, + delta const & reverse_del, string const & data_table, string const & delta_table) { - base64< gzip > old_data, new_data; + data old_data, new_data; get_version(new_id, new_data, data_table, delta_table); patch(new_data, reverse_del, old_data); @@ -1260,7 +1265,7 @@ database::get_file_version(file_id const & id, file_data & dat) { - base64< gzip > tmp; + data tmp; get_version(id.inner(), tmp, "files", "file_deltas"); dat = tmp; } @@ -1269,7 +1274,7 @@ database::get_manifest_version(manifest_id const & id, manifest_data & dat) { - base64< gzip > tmp; + data tmp; get_version(id.inner(), tmp, "manifests", "manifest_deltas"); dat = tmp; } @@ -1404,14 +1409,19 @@ "SELECT data FROM revisions WHERE id = '%q'", id.inner()().c_str()); - dat = revision_data(res[0][0]); + base64 > rdat_packed; + rdat_packed = base64 >(res[0][0]); + data rdat; + unpack(rdat_packed, rdat); // verify that we got a revision with the right id { revision_id tmp; - calculate_ident(dat, tmp); + calculate_ident(rdat, tmp); I(id == tmp); } + + dat = rdat; } void @@ -1430,11 +1440,14 @@ calculate_ident(d, tmp); I(tmp == new_id); + base64 > d_packed; + pack(d.inner(), d_packed); + transaction_guard guard(*this); execute("INSERT INTO revisions VALUES('%q', '%q')", new_id.inner()().c_str(), - d.inner()().c_str()); + d_packed().c_str()); for (edge_map::const_iterator e = rev.edges.begin(); e != rev.edges.end(); ++e) @@ -1467,7 +1480,25 @@ execute("DELETE from revision_certs"); } +/// Deletes one revision from the local database. +/// @see kill_rev_locally +void +database::delete_existing_rev_and_certs(revision_id const & rid){ + //check that the revision exists and doesn't have any children + I(revision_exists(rid)); + set children; + get_revision_children(rid, children); + I(!children.size()); + + // perform the actual SQL transactions to kill rev rid here + L(F("Killing revision %s locally\n") % rid); + execute("DELETE from revision_certs WHERE id = '%s'",rid.inner()().c_str()); + execute("DELETE from revision_ancestry WHERE child = '%s'", + rid.inner()().c_str()); + execute("DELETE from revisions WHERE id = '%s'",rid.inner()().c_str()); +} + // crypto key management void --- database.hh +++ database.hh @@ -102,34 +102,34 @@ void get_ids(std::string const & table, std::set< hexenc > & ids); void get(hexenc const & new_id, - base64< gzip > & dat, + data & dat, std::string const & table); void get_delta(hexenc const & ident, hexenc const & base, - base64< gzip > & del, + delta & del, std::string const & table); void get_version(hexenc const & id, - base64< gzip > & dat, + data & dat, std::string const & data_table, std::string const & delta_table); void put(hexenc const & new_id, - base64< gzip > const & dat, + data const & dat, std::string const & table); void drop(hexenc const & base, std::string const & table); void put_delta(hexenc const & id, hexenc const & base, - base64< gzip > const & del, + delta const & del, std::string const & table); void put_version(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, std::string const & data_table, std::string const & delta_table); void put_reverse_version(hexenc const & new_id, hexenc const & old_id, - base64< gzip > const & reverse_del, + delta const & reverse_del, std::string const & data_table, std::string const & delta_table); @@ -174,11 +174,11 @@ friend class transaction_guard; friend void rcs_put_raw_file_edge(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, database & db); friend void rcs_put_raw_manifest_edge(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, database & db); public: @@ -288,6 +288,8 @@ void delete_existing_revs_and_certs(); + void delete_existing_rev_and_certs(revision_id const & rid); + // crypto key / cert operations void get_key_ids(std::string const & pattern, --- database_check.cc +++ database_check.cc @@ -669,22 +669,28 @@ missing_certs + mismatched_certs + unchecked_sigs + bad_sigs + missing_keys; + // unreferenced files and manifests and mismatched certs are not actually + // serious errors; odd, but nothing will break. + size_t serious = missing_files + + missing_manifests + incomplete_manifests + + missing_revisions + incomplete_revisions + + mismatched_parents + mismatched_children + + bad_history + + missing_certs + + unchecked_sigs + bad_sigs + + missing_keys; - if (total > 0) - N(total == 0, - F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs; %d problems detected\n") - % checked_files.size() - % checked_manifests.size() - % checked_revisions.size() - % checked_keys.size() - % total_certs - % total); + P(F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs\n") + % checked_files.size() + % checked_manifests.size() + % checked_revisions.size() + % checked_keys.size() + % total_certs); + P(F("total problems detected: %d (%d serious)\n") % total % serious); + if (serious) + E(false, F("serious problems detected")); + else if (total) + P(F("minor problems detected\n")); else - P(F("check complete: %d files; %d manifests; %d revisions; %d keys; %d certs; database is good\n") - % checked_files.size() - % checked_manifests.size() - % checked_revisions.size() - % checked_keys.size() - % total_certs - ); + P(F("database is good\n")); } --- diff_patch.cc +++ diff_patch.cc @@ -484,7 +484,7 @@ L(F("recording successful merge of %s <-> %s into %s\n") % left_ident % right_ident % merged_ident); - base64< gzip > merge_delta; + delta merge_delta; transaction_guard guard(app.db); diff(left_data.inner(), merged_data.inner(), merge_delta); @@ -548,9 +548,9 @@ anc_encoding = this->get_file_encoding(anc_path, anc_man); right_encoding = this->get_file_encoding(right_path, right_man); - unpack(left_data.inner(), left_unpacked); - unpack(ancestor_data.inner(), ancestor_unpacked); - unpack(right_data.inner(), right_unpacked); + left_unpacked = left_data.inner(); + ancestor_unpacked = ancestor_data.inner(); + right_unpacked = right_data.inner(); split_into_lines(left_unpacked(), left_encoding, left_lines); split_into_lines(ancestor_unpacked(), anc_encoding, ancestor_lines); @@ -562,18 +562,18 @@ merged_lines)) { hexenc tmp_id; - base64< gzip > packed_merge; + file_data merge_data; string tmp; L(F("internal 3-way merged ok\n")); join_lines(merged_lines, tmp); calculate_ident(data(tmp), tmp_id); file_id merged_fid(tmp_id); - pack(data(tmp), packed_merge); + merge_data = file_data(tmp); merged_id = merged_fid; record_merge(left_id, right_id, merged_fid, - left_data, packed_merge); + left_data, merge_data); return true; } @@ -589,16 +589,16 @@ right_unpacked, merged_unpacked)) { hexenc tmp_id; - base64< gzip > packed_merge; + file_data merge_data; L(F("lua merge3 hook merged ok\n")); calculate_ident(merged_unpacked, tmp_id); file_id merged_fid(tmp_id); - pack(merged_unpacked, packed_merge); + merge_data = file_data(merged_unpacked); merged_id = merged_fid; record_merge(left_id, right_id, merged_fid, - left_data, packed_merge); + left_data, merge_data); return true; } @@ -631,8 +631,8 @@ this->get_version(left_path, left_id, left_data); this->get_version(right_path, right_id, right_data); - unpack(left_data.inner(), left_unpacked); - unpack(right_data.inner(), right_unpacked); + left_unpacked = left_data.inner(); + right_unpacked = right_data.inner(); P(F("help required for 2-way merge\n")); P(F("[ left] %s\n") % left_path); @@ -643,16 +643,16 @@ left_unpacked, right_unpacked, merged_unpacked)) { hexenc tmp_id; - base64< gzip > packed_merge; + file_data merge_data; L(F("lua merge2 hook merged ok\n")); calculate_ident(merged_unpacked, tmp_id); file_id merged_fid(tmp_id); - pack(merged_unpacked, packed_merge); + merge_data = file_data(merged_unpacked); merged_id = merged_fid; record_merge(left_id, right_id, merged_fid, - left_data, packed_merge); + left_data, merge_data); return true; } @@ -690,7 +690,7 @@ app.db.get_file_version(ident, dat); else { - base64< gzip > tmp; + data tmp; file_id fid; N(file_exists (path), F("file %s does not exist in working copy") % path); --- manifest.cc +++ manifest.cc @@ -140,11 +140,7 @@ read_manifest_map(manifest_data const & dat, manifest_map & man) { - gzip decoded; - data decompressed; - decode_base64(dat.inner(), decoded); - decode_gzip(decoded, decompressed); - read_manifest_map(decompressed, man); + read_manifest_map(dat.inner(), man); } @@ -167,14 +163,7 @@ man.end(), ostream_iterator(sstr)); - data raw; - gzip compressed; - base64< gzip > encoded; - - raw = sstr.str(); - encode_gzip(raw, compressed); - encode_base64(compressed, encoded); - dat = manifest_data(encoded); + dat = manifest_data(sstr.str()); } void --- monotone.1 +++ monotone.1 @@ -114,7 +114,8 @@ .TP \fBrcs_import\fP \fI ...\fP Import all file versions in RCS files. Does not reconstruct revisions -across the entire tree. +across the entire tree. You do not want this command, it is for +debugging; use cvs_import. .TP \fBcheckout\fP \fI[manifest-id]\fP \fI\fP An alias for \fB--message=\fI\fP .TP +\fB--author=\fI\fP +Use the given author as the value of the "author" cert when committing +a new revision, rather than the default author. Useful when +committing a patch on behalf of someone else, or when importing +history from another version control system. +.TP +\fB--date=\fI\fP +Use the given given date and time as value of the "date" cert when +committing a new revision, rather than the current time. Useful when +importing history from another version control system. +.TP \fB--root=\fI\fP Stop the search for a working copy (containing the @file{MT} directory) at the specified root directory rather than at the physical root of the --- monotone.cc +++ monotone.cc @@ -47,6 +47,8 @@ #define OPT_ROOT 16 #define OPT_DEPTH 17 #define OPT_ARGFILE 18 +#define OPT_DATE 19 +#define OPT_AUTHOR 20 // main option processing and exception handling code @@ -72,10 +74,12 @@ {"ticker", 0, POPT_ARG_STRING, &argstr, OPT_TICKER, "set ticker style (count|dot|none) [count]", NULL}, {"revision", 'r', POPT_ARG_STRING, &argstr, OPT_REVISION, "select revision id for operation", NULL}, {"message", 'm', POPT_ARG_STRING, &argstr, OPT_MESSAGE, "set commit changelog message", NULL}, + {"date", 0, POPT_ARG_STRING, &argstr, OPT_DATE, "override date/time for commit", NULL}, + {"author", 0, POPT_ARG_STRING, &argstr, OPT_AUTHOR, "override author for commit", NULL}, {"root", 0, POPT_ARG_STRING, &argstr, OPT_ROOT, "limit search for working copy to specified root", NULL}, {"depth", 0, POPT_ARG_LONG, &arglong, OPT_DEPTH, "limit the log output to the given number of entries", NULL}, {"xargs", '@', POPT_ARG_STRING, &argstr, OPT_ARGFILE, "insert command line arguments taken from the given file", NULL}, - { NULL, 0, 0, NULL, 0 } + { NULL, 0, 0, NULL, 0, NULL, NULL } }; // there are 3 variables which serve as roots for our system. @@ -189,8 +193,8 @@ // the argv array be null-terminated. I(argv[argc] == NULL); N((rc = poptStuffArgs(con, argv)) >= 0, - F("weird error when stuffing arguments read from %s: %s\n") - % filename % poptStrerror(rc)); + F("weird error when stuffing arguments read from %s: %s\n") + % filename % poptStrerror(rc)); } else { @@ -331,6 +335,14 @@ app.set_message(string(argstr)); break; + case OPT_DATE: + app.set_date(string(argstr)); + break; + + case OPT_AUTHOR: + app.set_author(string(argstr)); + break; + case OPT_ROOT: app.set_root(string(argstr)); break; @@ -341,7 +353,7 @@ case OPT_ARGFILE: sub_argvs.push_back(my_poptStuffArgFile(ctx(), - utf8(string(argstr)))); + utf8(string(argstr)))); break; case OPT_HELP: --- monotone.texi +++ monotone.texi @@ -212,11 +212,11 @@ @end ifnotinfo Version control systems, such as monotone, are principally concerned -with the storage and management of @i{multiple} versions of some -files. One way to store multiple versions of a file is, literally, to -save a separate @i{complete} copy of the file, every time you make a +with the storage and management of @i{multiple} versions of some files. +One way to store multiple versions of a file is, literally, to save a +separate @i{complete} copy of the file, every time you make a change. When necessary, monotone will save complete copies of your -files in their, compressed with the @command{zlib} compression format. +files, compressed with the @command{zlib} compression format. @ifinfo @smallexample @@ -1071,13 +1071,13 @@ @menu * Creating a Database:: * Generating Keys:: -* Exchanging Keys:: * Starting a New Project:: * Adding Files:: * Committing Work:: * Network Service:: * Making Changes:: * Dealing with a Fork:: +* Branching and Merging:: @end menu @@ -1221,82 +1221,10 @@ Abe and Beth do the same, with their secret passphrases. @page address@hidden Exchanging Keys address@hidden Exchanging Keys - -Jim, Abe and Beth all wish to work with one another, and trust one -another. For monotone to accept this situation, the team members will -need to exchange the public parts of their @sc{rsa} key with each -other. - -First, Jim exports his public key: - address@hidden address@hidden -$ monotone --db=~/jim.db pubkey jim@@juicebot.co.jp >~/jim.pubkey address@hidden group address@hidden smallexample - -His public key is just a plain block of ASCII text: - address@hidden address@hidden -$ cat ~/jim.pubkey -[pubkey jim@@juicebot.co.jp] -MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCbaVff9SF78FiB/1nUdmjbU/TtPyQqe/fW -CDg7hSg1yY/hWgClXE9FI0bHtjPMIx1kBOig09AkCT7tBXM9z6iGWxTBhSR7D/qsJQGPorOD -DO7xovIHthMbZZ9FnvyB/BCyiibdWgGT0Gtq94OKdvCRNuT59e5v9L4pBkvajb+IzQIBEQ== -[end] address@hidden group address@hidden smallexample - -Abe also exports his public key: - address@hidden address@hidden -$ monotone --db=~/abe.db pubkey abe@@juicebot.co.jp >~/abe.pubkey address@hidden group address@hidden smallexample - -As does Beth: - address@hidden address@hidden -$ monotone --db=~/beth.db pubkey beth@@juicebot.co.jp >~/beth.pubkey address@hidden group address@hidden smallexample - -Then all three team members exchange keys. The keys are not secret, -but the team members must be relatively certain that they are -communicating with the person they intend to trust, when exchanging -keys, and not some malicious person pretending to be a team -member. Key exchange may involve sending keys over an encrypted -medium, or meeting in person to exchange physical copies, or any -number of techniques. All that matters, ultimately, is for each team -member to receive the keys of the others. - -So eventually, after key exchange, Jim has Beth's and Abe's public key -files in his home directory, along with his own. He tells monotone to -read the associated key packets into his database: - address@hidden address@hidden -$ monotone --db=~/jim.db read <~/abe.pubkey -monotone: read 1 packet -$ monotone --db=~/jim.db read <~/beth.pubkey -monotone: read 1 packet address@hidden group address@hidden smallexample - -Beth and Abe similarly tell monotone to read read the two new public -keys they received into their respective databases. - - address@hidden @node Starting a New Project @section Starting a New Project -Before they can begin work on the project, Jim needs to create a +Before he can begin work on the project, Jim needs to create a @i{working copy} --- a directory whose contents monotone will keep track of. Often, one works on projects that someone else has started, and creates working copies with the @code{checkout} command, which you'll @@ -1529,10 +1457,7 @@ @smallexample @group $ monotone --db=jim.db --branch=jp.co.juicebot.jb7 commit --message='initial checkin of project' -monotone: beginning commit -monotone: manifest 2098eddbe833046174de28172a813150a6cbda7b -monotone: revision 2e24d49a48adf9acf3a1b6391a080008cbef9c21 -monotone: branch jp.co.juicebot.jb7 +monotone: beginning commit on branch 'jp.co.juicebot.jb7' monotone: committed revision 2e24d49a48adf9acf3a1b6391a080008cbef9c21 @end group @end smallexample @@ -1643,12 +1568,68 @@ @section Network Service Jim now decides he will make his base revision available to his -employees. To do this first adds a small amount of extra information -to his @file{.monotonerc} file, permitting Abe and Beth to access his -database: +employees. To do this he gives Abe and Beth permission to access his +database. There are two parts to this: first, he has to get a copy of +each of their public keys; then, he has to tell monotone that the +holders of those keys are permitted to access his database. +First, Abe exports his public key: + @smallexample @group +$ monotone --db=~/abe.db pubkey abe@@juicebot.co.jp >~/abe.pubkey address@hidden group address@hidden smallexample + +His public key is just a plain block of ASCII text: + address@hidden address@hidden +$ cat ~/abe.pubkey +[pubkey abe@@juicebot.co.jp] +MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCbaVff9SF78FiB/1nUdmjbU/TtPyQqe/fW +CDg7hSg1yY/hWgClXE9FI0bHtjPMIx1kBOig09AkCT7tBXM9z6iGWxTBhSR7D/qsJQGPorOD +DO7xovIHthMbZZ9FnvyB/BCyiibdWgGT0Gtq94OKdvCRNuT59e5v9L4pBkvajb+IzQIBEQ== +[end] address@hidden group address@hidden smallexample + +Beth also exports her public key: + address@hidden address@hidden +$ monotone --db=~/beth.db pubkey beth@@juicebot.co.jp >~/beth.pubkey address@hidden group address@hidden smallexample + +Then Abe and Beth both send their keys to Jim. The keys are not secret, +but the team members must be relatively certain that they are +communicating with the person they intend to trust, when exchanging +keys, and not some malicious person pretending to be a team +member. Key exchange may involve sending keys over an encrypted +medium, or meeting in person to exchange physical copies, or any +number of techniques. All that matters, ultimately, is that Jim receive +both Abe and Beth's key. + +So eventually, after key exchange, Jim has the public key files in his +home directory. He tells monotone to read the associated key packets +into his database: + address@hidden address@hidden +$ monotone --db=~/jim.db read <~/abe.pubkey +monotone: read 1 packet +$ monotone --db=~/jim.db read <~/beth.pubkey +monotone: read 1 packet address@hidden group address@hidden smallexample + +Now Jim's monotone is able to identify Beth and Abe, and he is ready to +give them permission to access his database. He does this by adding a +small amount of extra information to his @file{.monotonerc} file: + address@hidden address@hidden $ cat >>~/.monotonerc function get_netsync_read_permitted (collection, identity) if (identity == "abe@@juicebot.co.jp") then return true end @@ -1719,8 +1700,9 @@ always running; this way, everyone always knows where to go to get the latest changes, and people can push their changes out without first calling their friends and making sure that they have their servers -running. To support this style of working, monotone remembers the first -server you use, and makes that the default for future operations. +running. To make this style of working more convenient, monotone +remembers the first server you use, and makes that the default for +future operations. @page @node Making Changes @@ -1785,10 +1767,8 @@ @smallexample @group $ monotone commit -monotone: beginning commit -monotone: manifest b33cb337dccf21d6673f462d677a6010b60699d1 -monotone: revision 70decb4b31a8227a629c0e364495286c5c75f979 -monotone: branch jp.co.juicebot.jb7 +monotone: beginning commit on branch 'jp.co.juicebot.jb7' +monotone: commited revision 70decb4b31a8227a629c0e364495286c5c75f979 @end group @end smallexample @@ -1894,10 +1874,7 @@ @smallexample @group $ monotone commit -monotone: beginning commit -monotone: manifest eaebc3c558d9e30db6616ef543595a5a64cc6d5f -monotone: revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb -monotone: branch jp.co.juicebot.jb7 +monotone: beginning commit on branch 'jp.co.juicebot.jb7' monotone: committed revision 80ef9c9d251d39074d37e72abf4897e0bbae1cfb @end group @end smallexample @@ -2012,10 +1989,7 @@ @smallexample @group $ monotone commit --message='interrupt implementation of src/banana.c' -monotone: beginning commit -monotone: manifest de81e46acb24b2950effb18572d5166f83af3881 -monotone: revision 8b41b5399a564494993063287a737d26ede3dee4 -monotone: branch jp.co.juicebot.jb7 +monotone: beginning commit on branch 'jp.co.juicebot.jb7' monotone: committed revision 8b41b5399a564494993063287a737d26ede3dee4 @end group @end smallexample @@ -2126,6 +2100,95 @@ step) and your committed state is still safe. It is therefore recommended that you commit your work @emph{first}, before merging. address@hidden address@hidden Branching and Merging address@hidden Branching and Merging + +So by now you're familiar with making changes, sharing them with other +people, and integrating your changes with their changes. Sometimes, +though, you may want to make some changes, and @emph{not} integrate them +with other people's --- or at least not right away. One way to do this +would be to simply never run @command{monotone merge}; but it would +quickly become confusing to try and keep track of which changes were in +which revisions. This is where @emph{branches} are useful. + +Continuing our example, suppose that Jim is so impressed by Beth's work +on banana juice support that he assigns her to work on the JuiceBot 7's +surprise new feature: muffins. In the mean time, Abe will continue +working on the JuiceBot's basic juice-related functions. + +The changes required to support muffins are somewhat complicated, and +Beth is worried that her work might destabilize the program, and +interfere with Abe's work. In fact, she isn't even sure her first +attempt will turn out to be the right approach; she might work on it for +a while and then decide it was a bad idea, and should be discarded. For +all these reasons, she decides that she will work on a branch, and then +once she is satisfied with the new code, she will merge back onto the +mainline. + +She decides that since main development is in branch address@hidden, she will use branch address@hidden So, she makes the first few edits to +the new muffins code, and commits it on a new branch by simply passing address@hidden to commit: + address@hidden address@hidden +$ monotone commit --branch=jp.co.juicebot.jb7.muffins --message='autobake framework' +monotone: beginning commit on branch 'jp.co.juicebot.jb7.muffins' +monotone: committed revision d33caefd61823ecbb605c39ffb84705dec449857 address@hidden group address@hidden smallexample + +That's all there is to it --- there is now a address@hidden branch, with her initial checkin on +it. She can make further checkins from the same working copy, and they +will automatically go to the muffins branch; if anyone else wants to +help her work on muffins, they can check out that branch as usual. + +Of course, while Beth is working on the new muffins code, Abe is still +making fixes to the main line. Occasionally, Beth wants to integrate +his latest work into the muffins branch, so that her version doesn't +fall too far behind. She does this by using the @command{propagate} +command: + address@hidden address@hidden +$ monotone propagate jp.co.juicebot.jb7 jp.co.juicebot.jb7.muffins +monotone: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins +monotone: [source] da003f115752ac6e4750b89aaca9dbba178ac80c +monotone: [target] d0e5c93bb61e5fd25a0dadf41426f209b73f40af +monotone: common ancestor 853b8c7ac5689181d4b958504adfb5d07fd959ab found address@hidden group address@hidden smallexample + +The @command{propagate} merges all of the new changes on one branch onto +another. + +When the muffins code is eventually stable and ready to be integrated +into the main line of development, she simple propagates the other way: + address@hidden address@hidden +$ monotone propagate jp.co.juicebot.jb7.muffins jp.co.juicebot.jb7 +monotone: propagating jp.co.juicebot.jb7 -> jp.co.juicebot.jb7.muffins +monotone: [source] 4e48e2c9a3d2ca8a708cb0cc545700544efb5021 +monotone: [target] bd29b2bfd07644ab370f50e0d68f26dcfd3bb4af +monotone: common ancestor 652b1035343281a0d2a5de79919f9a31a30c9028 found address@hidden group address@hidden smallexample + +Monotone always records the full history of all merges, and is designed +to handle an arbitrarily complicated graph of changes. You can make a +branch, then branch off from that branch, propagate changes between +arbitrary branches, and so on; monotone will track all of it, and do +something sensible for each merge. Of course, it is still probably a +good idea to come up with some organization of branches and a plan for +which should be merged to which other ones. Monotone may keep track of +graphs of arbitrary complexity; but, you will have more trouble. +Whatever arrangement of branches you come up with, though, monotone +should be able to handle it. + @node Advanced Uses @chapter Advanced Uses @@ -2942,7 +3005,7 @@ @tab @smallexample @group -$ monotone diff 3e7db 278df +$ monotone diff -r 3e7db -r 278df @end group @end smallexample @end multitable @@ -3024,6 +3087,29 @@ manifest of the current revision. address@hidden Importing a New Project + address@hidden @columnfractions .4 .4 address@hidden address@hidden address@hidden +$ cvs import wobbler vendor start address@hidden group address@hidden smallexample address@hidden address@hidden address@hidden +$ monotone --db=/path/to/database.db --branch=com.foo.wobbler setup . +$ monotone add . +$ monotone commit address@hidden group address@hidden smallexample address@hidden multitable + +The @command{setup} command turns an ordinary directory into a +monotone working copy. After that, you can add your files and commit +them as usual. + @heading Initializing a Repository @multitable @columnfractions .4 .4 @@ -3288,12 +3374,19 @@ @itemize @item An @code{author} cert, indicating the person responsible for the changes -leading to the new revision. +leading to the new revision. Normally this defaults to your signing key +or the return value of the @code{get_author} hook; you may override this +by passing the @option{--author} option to commit. This is useful when +committing a patch on behalf of someone else, or when importing ``by +hand'' from another version control system. @item A @code{branch} cert, indicating the branch the committed revision belongs to. @item A @code{date} cert, indicating when the new revision was created. +Normally this defaults to the current time; you may override this by +passing the @option{--date} option to commit. This is useful when +importing ``by hand'' from another version control system. @item A @code{changelog} cert, containing the ``log message'' for these changes. If you provided @var{logmsg} on the command line, this text @@ -3720,7 +3813,10 @@ This command drops the public and/or private key. If both exist, both are dropped, if only one exists, it is dropped. This command should be used with caution as changes are irreversible without a backup of -the key(s) that were dropped. +the key(s) that were dropped. Note also that the private key is not +guaranteed to actually be erased from your database file - if you are +going to make the database file public, you should use 'db dump' +and 'db load' to import into a fresh database. @item monotone chkeypass @var{id} @@ -5763,7 +5859,8 @@ @item @b{rcs_import} @i{ ...} Import all file versions in RCS files. Does not reconstruct revisions -across the entire tree. +across the entire tree. You do not want this command, it is for +debugging; use cvs_import. @comment TROFF INPUT: .TP @item @b{checkout} @i{[manifest-id]} @i{} @@ -5935,6 +6032,17 @@ applies to the commit command but it may also apply to the comment command in the future. address@hidden @address@hidden} +Use the given author as the value of the "author" cert when committing +a new revision, rather than the default author. Useful when +committing a patch on behalf of someone else, or when importing +history from another version control system. + address@hidden @address@hidden} +Use the given given date and time as value of the "date" cert when +committing a new revision, rather than the current time. Useful when +importing history from another version control system. + @item @address@hidden} Stop the search for a working copy (containing the @file{MT} directory) at the specified root directory rather than at the physical root of the --- netsync.cc +++ netsync.cc @@ -712,7 +712,7 @@ attached[i] = curr_attached; } -static inline id +inline static id plain_id(manifest_id const & i) { id tmp; @@ -721,7 +721,7 @@ return tmp; } -static inline id +inline static id plain_id(file_id const & i) { id tmp; @@ -1432,13 +1432,14 @@ P(F("I expected %s\n") % expected_key_hash); P(F("'monotone unset %s %s' overrides this check\n") % their_key_key.first % their_key_key.second); - N(false, F("server key changed")); + E(false, F("server key changed")); } } else { - W(F("first time connecting to server %s; authenticity can't be established\n") % peer_id); - W(F("their key is %s\n") % their_key_hash); + P(F("first time connecting to server %s\n") % peer_id); + P(F("I'll assume it's really them, but you might want to\n")); + P(F("double-check their key's fingerprint: %s\n") % their_key_hash); app.db.set_var(their_key_key, var_value(their_key_hash())); } if (!app.db.public_key_exists(their_key_hash)) @@ -1834,8 +1835,7 @@ revision_data mdat; data dat; app.db.get_revision(revision_id(hitem), mdat); - unpack(mdat.inner(), dat); - out = dat(); + out = mdat.inner()(); } else { @@ -1849,8 +1849,7 @@ manifest_data mdat; data dat; app.db.get_manifest_version(manifest_id(hitem), mdat); - unpack(mdat.inner(), dat); - out = dat(); + out = mdat.inner()(); } else { @@ -1864,8 +1863,7 @@ file_data fdat; data dat; app.db.get_file_version(file_id(hitem), fdat); - unpack(fdat.inner(), dat); - out = dat(); + out = fdat.inner()(); } else { @@ -2330,8 +2328,8 @@ this->app.db.get_file_version(fbase, base_fdat); this->app.db.get_file_version(fident, ident_fdat); string tmp; - unpack(base_fdat.inner(), base_dat); - unpack(ident_fdat.inner(), ident_dat); + base_dat = base_fdat.inner(); + ident_dat = ident_fdat.inner(); compute_delta(base_dat(), ident_dat(), tmp); del = delta(tmp); } @@ -2354,8 +2352,8 @@ this->app.db.get_manifest_version(mbase, base_mdat); this->app.db.get_manifest_version(mident, ident_mdat); string tmp; - unpack(base_mdat.inner(), base_dat); - unpack(ident_mdat.inner(), ident_dat); + base_dat = base_mdat.inner(); + ident_dat = ident_mdat.inner(); compute_delta(base_dat(), ident_dat(), tmp); del = delta(tmp); } @@ -2480,9 +2478,7 @@ boost::shared_ptr< pair > rp(new pair()); - base64< gzip > packed; - pack(data(dat), packed); - rp->first = revision_data(packed); + rp->first = revision_data(dat); read_revision_set(dat, rp->second); ancestry.insert(std::make_pair(rid, rp)); if (cert_refinement_done()) @@ -2500,9 +2496,7 @@ L(F("manifest version '%s' already exists in our database\n") % hitem); else { - base64< gzip > packed_dat; - pack(data(dat), packed_dat); - this->dbw.consume_manifest_data(mid, manifest_data(packed_dat)); + this->dbw.consume_manifest_data(mid, manifest_data(dat)); manifest_map man; read_manifest_map(data(dat), man); analyze_manifest(man); @@ -2517,9 +2511,7 @@ L(F("file version '%s' already exists in our database\n") % hitem); else { - base64< gzip > packed_dat; - pack(data(dat), packed_dat); - this->dbw.consume_file_data(fid, file_data(packed_dat)); + this->dbw.consume_file_data(fid, file_data(dat)); } } break; @@ -2553,20 +2545,18 @@ case manifest_item: { manifest_id src_manifest(hbase), dst_manifest(hident); - base64< gzip > packed_del; - pack(del, packed_del); if (reverse_delta_requests.find(id_pair) != reverse_delta_requests.end()) { reverse_delta_requests.erase(id_pair); this->dbw.consume_manifest_reverse_delta(src_manifest, dst_manifest, - manifest_delta(packed_del)); + manifest_delta(del)); } else this->dbw.consume_manifest_delta(src_manifest, dst_manifest, - manifest_delta(packed_del)); + manifest_delta(del)); } break; @@ -2574,20 +2564,18 @@ case file_item: { file_id src_file(hbase), dst_file(hident); - base64< gzip > packed_del; - pack(del, packed_del); if (reverse_delta_requests.find(id_pair) != reverse_delta_requests.end()) { reverse_delta_requests.erase(id_pair); this->dbw.consume_file_reverse_delta(src_file, dst_file, - file_delta(packed_del)); + file_delta(del)); } else this->dbw.consume_file_delta(src_file, dst_file, - file_delta(packed_del)); + file_delta(del)); } break; --- packet.cc +++ packet.cc @@ -601,7 +601,7 @@ { file_id confirm; file_data old_dat; - base64< gzip > new_dat; + data new_dat; pimpl->app.db.get_file_version(old_id, old_dat); patch(old_dat.inner(), del.inner(), new_dat); calculate_ident(file_data(new_dat), confirm); @@ -645,7 +645,7 @@ { file_id confirm; file_data new_dat; - base64< gzip > old_dat; + data old_dat; pimpl->app.db.get_file_version(new_id, new_dat); patch(new_dat.inner(), del.inner(), old_dat); calculate_ident(file_data(old_dat), confirm); @@ -706,7 +706,7 @@ { manifest_id confirm; manifest_data old_dat; - base64< gzip > new_dat; + data new_dat; pimpl->app.db.get_manifest_version(old_id, old_dat); patch(old_dat.inner(), del.inner(), new_dat); calculate_ident(manifest_data(new_dat), confirm); @@ -750,7 +750,7 @@ { manifest_id confirm; manifest_data new_dat; - base64< gzip > old_dat; + data old_dat; pimpl->app.db.get_manifest_version(new_id, new_dat); patch(new_dat.inner(), del.inner(), old_dat); calculate_ident(manifest_data(old_dat), confirm); @@ -1073,8 +1073,10 @@ packet_writer::consume_file_data(file_id const & ident, file_data const & dat) { + base64 > packed; + pack(dat.inner(), packed); ost << "[fdata " << ident.inner()() << "]" << endl - << trim_ws(dat.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1083,9 +1085,11 @@ file_id const & new_id, file_delta const & del) { + base64 > packed; + pack(del.inner(), packed); ost << "[fdelta " << old_id.inner()() << endl << " " << new_id.inner()() << "]" << endl - << trim_ws(del.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1094,9 +1098,11 @@ file_id const & old_id, file_delta const & del) { + base64 > packed; + pack(del.inner(), packed); ost << "[frdelta " << new_id.inner()() << endl << " " << old_id.inner()() << "]" << endl - << trim_ws(del.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1104,8 +1110,10 @@ packet_writer::consume_manifest_data(manifest_id const & ident, manifest_data const & dat) { + base64 > packed; + pack(dat.inner(), packed); ost << "[mdata " << ident.inner()() << "]" << endl - << trim_ws(dat.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1113,8 +1121,10 @@ packet_writer::consume_revision_data(revision_id const & ident, revision_data const & dat) { + base64 > packed; + pack(dat.inner(), packed); ost << "[rdata " << ident.inner()() << "]" << endl - << trim_ws(dat.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1123,9 +1133,11 @@ manifest_id const & new_id, manifest_delta const & del) { + base64 > packed; + pack(del.inner(), packed); ost << "[mdelta " << old_id.inner()() << endl << " " << new_id.inner()() << "]" << endl - << trim_ws(del.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1134,9 +1146,11 @@ manifest_id const & old_id, manifest_delta const & del) { + base64 > packed; + pack(del.inner(), packed); ost << "[mrdelta " << new_id.inner()() << endl << " " << old_id.inner()() << "]" << endl - << trim_ws(del.inner()()) << endl + << trim_ws(packed()) << endl << "[end]" << endl; } @@ -1193,7 +1207,9 @@ I(res[3].matched); string head(res[1].first, res[1].second); string ident(res[2].first, res[2].second); - string body(trim_ws(string(res[3].first, res[3].second))); + base64 > body_packed(trim_ws(string(res[3].first, res[3].second))); + data body; + unpack(body_packed, body); if (head == "rdata") cons.consume_revision_data(revision_id(hexenc(ident)), revision_data(body)); @@ -1215,7 +1231,9 @@ string head(res[4].first, res[4].second); string src_id(res[5].first, res[5].second); string dst_id(res[6].first, res[6].second); - string body(trim_ws(string(res[7].first, res[7].second))); + base64 > body_packed(trim_ws(string(res[7].first, res[7].second))); + delta body; + unpack(body_packed, body); if (head == "mdelta") cons.consume_manifest_delta(manifest_id(hexenc(src_id)), manifest_id(hexenc(dst_id)), @@ -1248,7 +1266,7 @@ string certname(res[10].first, res[10].second); string key(res[11].first, res[11].second); string val(res[12].first, res[12].second); - string body(res[13].first, res[13].second); + string body(trim_ws(string(res[13].first, res[13].second))); // canonicalize the base64 encodings to permit searches cert t = cert(hexenc(ident), @@ -1359,20 +1377,16 @@ packet_writer pw(oss); // an fdata packet - base64< gzip > gzdata; - pack(data("this is some file data"), gzdata); - file_data fdata(gzdata); + file_data fdata("this is some file data"); file_id fid; calculate_ident(fdata, fid); pw.consume_file_data(fid, fdata); // an fdelta packet - base64< gzip > gzdata2; - pack(data("this is some file data which is not the same as the first one"), gzdata2); - file_data fdata2(gzdata2); + file_data fdata2("this is some file data which is not the same as the first one"); file_id fid2; calculate_ident(fdata2, fid); - base64< gzip > del; + delta del; diff(fdata.inner(), fdata2.inner(), del); pw.consume_file_delta(fid, fid2, file_delta(del)); @@ -1408,7 +1422,7 @@ file_id(hexenc("54f373ed07b4c5a88eaa93370e1bbac02dc432a8")))); write_manifest_map(mm2, mdata2); calculate_ident(mdata2, mid2); - base64< gzip > del2; + delta del2; diff(mdata.inner(), mdata2.inner(), del2); pw.consume_manifest_delta(mid, mid2, manifest_delta(del)); --- rcs_import.cc +++ rcs_import.cc @@ -393,7 +393,7 @@ void rcs_put_raw_file_edge(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, database & db) { if (old_id == new_id) @@ -419,7 +419,7 @@ void rcs_put_raw_manifest_edge(hexenc const & old_id, hexenc const & new_id, - base64< gzip > const & del, + delta const & del, database & db) { if (old_id == new_id) @@ -458,7 +458,7 @@ global_pieces.build_string(next_lines, tmp); next_data = tmp; } - base64< gzip > del; + delta del; diff(curr_data, next_data, del); calculate_ident(next_data, next_id); rcs_put_raw_file_edge(next_id, curr_id, del, db); @@ -571,7 +571,6 @@ I(r.deltas.find(r.admin.head) != r.deltas.end()); hexenc id; - base64< gzip > packed; data dat(r.deltatexts.find(r.admin.head)->second->text); calculate_ident(dat, id); file_id fid = id; @@ -580,9 +579,7 @@ if (! db.file_version_exists (fid)) { - pack(dat, packed); - file_data fdat = packed; - db.put_file(fid, fdat); + db.put_file(fid, dat); } { @@ -1083,7 +1080,7 @@ // tree version, which can be constructed from the 'newer' child. so // the delta should run from child (new) -> parent (old). - base64< gzip > del; + delta del; diff(child, parent, del); rcs_put_raw_manifest_edge(parent_mid.inner(), child_mid.inner(), @@ -1102,7 +1099,7 @@ // from temporally new -> temporally old. so the delta should go from // parent (new) -> child (old) - base64< gzip > del; + delta del; diff(parent, child, del); rcs_put_raw_manifest_edge(child_mid.inner(), parent_mid.inner(), --- revision.cc +++ revision.cc @@ -1698,9 +1698,7 @@ read_revision_set(revision_data const & dat, revision_set & rev) { - data unpacked; - unpack(dat.inner(), unpacked); - read_revision_set(unpacked, rev); + read_revision_set(dat.inner(), rev); rev.check_sane(); } @@ -1722,9 +1720,7 @@ rev.check_sane(); data d; write_revision_set(rev, d); - base64< gzip > packed; - pack(d, packed); - dat = revision_data(packed); + dat = revision_data(d); } #ifdef BUILD_UNIT_TESTS --- sanity.cc +++ sanity.cc @@ -185,6 +185,17 @@ } void +sanity::error_failure(string const & expr, format const & explain, + string const & file, int line) +{ + string message; + log(format("%s:%d: detected error '%s' violated\n") % file % line % expr, + file.c_str(), line); + prefix_lines_with("error: ", explain.str(), message); + throw informative_failure(message); +} + +void sanity::invariant_failure(string const & expr, string const & file, int line) { --- sanity.hh +++ sanity.hh @@ -44,20 +44,22 @@ std::string filename; void log(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void progress(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void warning(boost::format const & fmt, - char const * file, int line); + char const * file, int line); void naughty_failure(std::string const & expr, boost::format const & explain, - std::string const & file, int line); + std::string const & file, int line); + void error_failure(std::string const & expr, boost::format const & explain, + std::string const & file, int line); void invariant_failure(std::string const & expr, - std::string const & file, int line); + std::string const & file, int line); void index_failure(std::string const & vec_expr, - std::string const & idx_expr, - unsigned long sz, - unsigned long idx, - std::string const & file, int line); + std::string const & idx_expr, + unsigned long sz, + unsigned long idx, + std::string const & file, int line); }; typedef std::runtime_error oops; @@ -107,7 +109,16 @@ } \ } while(0) +// E is for errors; they are normal (i.e., not a bug), but not necessarily +// attributable to user naughtiness +#define E(e, explain)\ +do { \ + if(UNLIKELY(!(e))) { \ + global_sanity.error_failure("E("#e")", (explain), __FILE__, __LINE__); \ + } \ +} while(0) + // we're interested in trapping index overflows early and precisely, // because they usually represent *very significant* logic errors. we use // an inline template function because the idx(...) needs to be used as an @@ -115,11 +126,11 @@ template inline T & checked_index(std::vector & v, - typename std::vector::size_type i, - char const * vec, - char const * index, - char const * file, - int line) + typename std::vector::size_type i, + char const * vec, + char const * index, + char const * file, + int line) { if (UNLIKELY(i >= v.size())) global_sanity.index_failure(vec, index, v.size(), i, file, line); @@ -128,11 +139,11 @@ template inline T const & checked_index(std::vector const & v, - typename std::vector::size_type i, - char const * vec, - char const * index, - char const * file, - int line) + typename std::vector::size_type i, + char const * vec, + char const * index, + char const * file, + int line) { if (UNLIKELY(i >= v.size())) global_sanity.index_failure(vec, index, v.size(), i, file, line); --- tests/README +++ tests/README @@ -0,0 +1,48 @@ +HOWTO make tests +================ + +Quick and dirty howto, to get you up to create and run tests. +Very early draft. Feel free to improve. + +Running tests: +-------------- +- Starting in the monotone main dir. After having './configure'd monotone you + can do 'make testsuite' to create 'testsuite'. Running './testsuite' will + run all tests. +- 'testsuite -l' lists the names and numbers of all available tests +- 'testsuite ' runs only test number n +- option -v will show the command outputs +- option -d will keep the testsuite.dir files for post-test debugging +- option -h is your friend :) + +Creating tests: +--------------- +- Copy and paste is your friend :) +- TODO: need more here... + + +Template for a test (name t_.at +------------------- + +AT_SETUP([brief test description goes here]) +MONOTONE_SETUP + +#the next lines (including AT_XFAIL_IF) state that the test is +#expected to fail as this is still a bug. Remove if it should pass! +# This test is a bug report. It is expected to fail +AT_XFAIL_IF(true) + +#AT_CHECK executes a shell command, second argument is the error +#return code we expect, third argument is the output we expect from +#stdout. use ignore if you don't care about the output. If oyu use +#stdout all following commands can use file 'stdout' for further +#processing. Same goes with 'stderr' as the last argument +#see autoconf tutorials +#e.g. http://www.gnu.org/software/autoconf/manual/autoconf-2.57/ +# html_node/autoconf_167.html for further information. +AT_CHECK(MONOTONE import foo, [], ignore, ignore) +... +... +... +AT_CLEANUP +(END) --- tests/t_add_stomp_file.at +++ tests/t_add_stomp_file.at @@ -0,0 +1,44 @@ +AT_SETUP([make sure update does not stomp non-monotone file]) +MONOTONE_SETUP + +# This test is a bug report +AT_XFAIL_IF(true) + +# 1. Alice checks out project, creates file foo +# 2. Bob checks out project, creates foo, adds foo, and commits +# 3. Now Alice does an update +# +# monotone should warn her before stomping her non-revision controlled 'foo' file +# + +AT_DATA(initial, [some initial data +]) + +AT_DATA(foo.alice, [foo +not revision controlled +]) + +AT_DATA(foo.bob, [foo +checked into project +]) + +# Alice make project, writes foo, but doesn't check it in +AT_CHECK(mkdir alicewd) +AT_CHECK(cp initial alicewd/initial) +AT_CHECK(MONOTONE --branch=testbranch setup alicewd, [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. add initial), [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. commit -m 'initial commit'), [], [ignore], [ignore]) +AT_CHECK(cp foo.alice alicewd/foo) + +# Bob does add of file foo, and commits +AT_CHECK(MONOTONE --branch=testbranch checkout bobwd, [], [ignore], [ignore]) +AT_CHECK(cp foo.bob bobwd/foo) +AT_CHECK( (cd bobwd; MONOTONE --branch=testbranch --root=. add foo), [], [ignore], [ignore]) +AT_CHECK( (cd bobwd; MONOTONE --branch=testbranch --root=. commit -m 'bob commit'), [], [ignore], [ignore]) +REV=`BASE_REVISION` + +# Alice does her update, discovers foo has been stomped! +AT_CHECK( (cd alicewd; MONOTONE --branch=testbranch --root=. update $REV), [], [ignore], [ignore]) +AT_CHECK(cmp foo.alice alicewd/foo) + +AT_CLEANUP --- tests/t_add_vs_commit.at +++ tests/t_add_vs_commit.at @@ -0,0 +1,47 @@ +AT_SETUP([add working copy commit in another]) +MONOTONE_SETUP + +# This test is a bug report +AT_XFAIL_IF(true) + +# 1. Alice writes a file, does an add, *doesn't* do a commit, and sends patch +# 2. Bob applies (modified) patch to tree, does the add, then a commit. +# 3. Now Alice does an update (resolves the merge conflict, choosing Bob's changes). +# +# Alice's working dir now give I()'s when she tries to do stuff (diff, update, etc.). + +AT_DATA(initial, [some initial data +]) + +AT_DATA(foo.alice, [foo +change me +bar +]) + +AT_DATA(foo.bob, [foo +me change +bar +]) + +# Alice does her add +AT_CHECK(mkdir alicewd) +AT_CHECK(cp initial alicewd/initial) +AT_CHECK(MONOTONE --branch=testbranch setup alicewd, [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --root=. add initial), [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --root=. commit -m 'initial commit'), [], [ignore], [ignore]) +AT_CHECK(cp foo.alice alicewd/foo) +AT_CHECK( (cd alicewd; MONOTONE add --root=. foo), [], [ignore], [ignore]) +# Note, alice does not commit this add... + +# Bob does add of same file, with edits, and commits +AT_CHECK(MONOTONE --branch=testbranch checkout bobwd, [], [ignore], [ignore]) +AT_CHECK(cp foo.bob bobwd/foo) +AT_CHECK( (cd bobwd; MONOTONE --root=. add foo), [], [ignore], [ignore]) +AT_CHECK( (cd bobwd; MONOTONE --root=. commit -m 'bob commit'), [], [ignore], [ignore]) +REV=`BASE_REVISION` + +# Alice does her update, then attempts, eg., a diff +AT_CHECK( (cd alicewd; MONOTONE --root=. update $REV), [], [ignore], [ignore]) +AT_CHECK( (cd alicewd; MONOTONE --root=. diff), [], [ignore], [ignore]) + +AT_CLEANUP --- tests/t_cvsimport2.at +++ tests/t_cvsimport2.at @@ -0,0 +1,41 @@ +AT_SETUP([test failing cvs import (Attic)]) +MONOTONE_SETUP + +# This test imports a cvs archive with files in the Attic which fails +#monotone: fatal: std::logic_error: change_set.cc:2546: invariant 'I(b.rearrangement.deleted_files.empty())' violated + +# reported by: address@hidden 15.04.2005 02:45 +# This test is a bug report. +AT_XFAIL_IF(true) + +AT_DATA(prepare.sh,[#!/bin/sh +export CVSROOT=$PWD/cvsroot + +cvs init +mkdir src +echo FOO > src/foo +( + cd src + cvs import -m import mod vtag rtag +) +rm -rf src + +mkdir src +( + cd src + cvs co mod + cd mod + cvs tag -b branch + cvs up -r branch + echo BAR > bar + cvs add bar + cvs ci -m 'add bar' +) +]) + +AT_CHECK(sh prepare.sh, [], ignore, ignore) + +AT_CHECK(MONOTONE --branch=test cvs_import ., [], ignore, ignore) + +AT_CLEANUP +(END) --- tests/t_database_check.at +++ tests/t_database_check.at @@ -66,7 +66,8 @@ AT_CHECK(grep '1 missing file' stderr, [0], [ignore], [ignore]) AT_CHECK(grep '2 incomplete manifest' stderr, [0], [ignore], [ignore]) AT_CHECK(grep '2 incomplete revision' stderr, [0], [ignore], [ignore]) -AT_CHECK(grep '5 problems' stderr, [0], [ignore], [ignore]) +AT_CHECK(grep 'total problems detected: 5' stderr, [0], [ignore], [ignore]) +AT_CHECK(grep '5 serious' stderr, [0], [ignore], [ignore]) # add an unreferenced file, and an unreferenced manifest that # references several files, none of which exist in the db --- tests/t_database_check_minor.at +++ tests/t_database_check_minor.at @@ -0,0 +1,40 @@ +AT_SETUP([db check and non-serious errors]) +MONOTONE_SETUP + +# Make sure that db check detects minor problems, but doesn't complain +# about them too loudly (and doesn't exit with error status). + +AT_DATA(fileX, [blah blah +]) +AT_DATA(fileY, [stuff stuff +]) +# manifestX is: +# +# e3f2b0427b57241225ba1ffc2b67fecd64d07613 fileX +# +# where e3f2 etc. is as above + +AT_DATA(manifestX, [@<:@mdata 8e5d7e5ffca2393cfca5625ac9c1a19a46489f92@:>@ +H4sIAAAAAAAAAEs1TjNKMjAxMk8yNTcyMTQyMk1KNExLSzZKMjNPS01OMTNJMTA3MzRWUEjL +zEmN4AIA90gN9zAAAAA= +@<:@end@:>@ +]) + +ADD_FILE(testfile, [more stuff +]) +COMMIT(testbranch) +REV=`BASE_REVISION` + +AT_CHECK(MONOTONE cert $REV author extra_author, [], [ignore], [ignore]) + +AT_CHECK(MONOTONE fload < fileX, [], [ignore], [ignore]) +AT_CHECK(MONOTONE fload < fileY, [], [ignore], [ignore]) +AT_CHECK(MONOTONE read < manifestX, [], [ignore], [ignore]) + +AT_CHECK(MONOTONE db check, [], [ignore], [stderr]) + +AT_CHECK(grep -q 'problems detected: 3' stderr) +AT_CHECK(grep -q '0 serious' stderr) +AT_CHECK(grep -q 'minor problems detected' stderr) + +AT_CLEANUP --- tests/t_db_kill_rev_locally.at +++ tests/t_db_kill_rev_locally.at @@ -0,0 +1,24 @@ +AT_SETUP([db kill_rev_locally command]) +MONOTONE_SETUP + +# This tests the db kill_rev_locally command + +# Prepare a db with two revisions +AT_CHECK(echo "test">tmp, [], ignore, ignore) +AT_CHECK(MONOTONE add tmp, [], ignore, ignore) +AT_CHECK(MONOTONE --branch="test" --message="logtext" commit, [], ignore, stderr) +ANCESTOR=`cat stderr|awk '/committed revision/ {print $4}'` +AT_CHECK(echo "test2">>tmp, [], ignore, ignore) +AT_CHECK(MONOTONE --message="logtext" commit, [], ignore, stderr) +CHILD=`cat stderr|awk '/committed revision/ {print $4}'` + +# trying to kill the ancestor. This *is supposed to fail* +AT_CHECK(MONOTONE db kill_rev_locally $ANCESTOR, [1], ignore, ignore) +AT_CHECK(MONOTONE db check, [], ignore, ignore) + +# killing children is ok, though :) +AT_CHECK(MONOTONE db kill_rev_locally $CHILD, [], ignore, ignore) +AT_CHECK(MONOTONE db check, [], ignore, ignore) + +AT_CLEANUP +(END) --- tests/t_diff_binary.at +++ tests/t_diff_binary.at @@ -1,10 +1,7 @@ AT_SETUP([diff a binary file]) MONOTONE_SETUP NEED_UNGZB64 -# This test is a bug report. -AT_XFAIL_IF(true) - # diff should probably not display the contents of binary files AT_DATA(binary.gz.b64, --- tests/t_lf_crlf.at +++ tests/t_lf_crlf.at @@ -0,0 +1,44 @@ +AT_SETUP([use get_linesep_conv hook]) +MONOTONE_SETUP +NEED_UNB64 + +# This test is a bug report. +AT_XFAIL_IF(true) + +# This test excercises the common case of wanting to do newline +# character conversion so that win32 users can have native line endings +# in their working copies. + + +# wrote files with 'foo\r\n' and 'foo\r\nfoo\r\n' and used mimencode +# to generate the following b64 encoded contents. +# +# Note the newline before the closing ']', it's necessary. +# +AT_DATA(foo.lfcr.b64, [Zm9vDQo= +]) +AT_DATA(foofoo.lfcr.b64, [Zm9vDQpmb28NCg== +]) + +UNB64(foo.lfcr.b64, foo.lfcr) +UNB64(foofoo.lfcr.b64, foofoo.lfcr) + +AT_DATA(linesep.lua, [ +function get_linesep_conv(name) + return {"LF", "CRLF"} +end +]) + +AT_CHECK(cp foo.lfcr foo, [], [ignore], [ignore]) +AT_CHECK(MONOTONE --rcfile=linesep.lua add foo, [], [ignore], [ignore]) +AT_CHECK(MONOTONE --rcfile=linesep.lua --branch=foo commit -m foo, [], [ignore], [ignore]) +FOO_REV=`BASE_REVISION` + +AT_CHECK(cp foofoo.lfcr foo, [], [ignore], [ignore]) +AT_CHECK(MONOTONE --rcfile=linesep.lua commit -m foofoo, [], [ignore], [ignore]) +FOO_FOO_REV=`BASE_REVISION` + +AT_CHECK(MONOTONE --rcfile=linesep.lua update $FOO_REV, [], [ignore], [ignore]) +AT_CHECK(cmp foo foo.lfcr, [], [ignore], [ignore]) + +AT_CLEANUP --- tests/t_netsync_defaults.at +++ tests/t_netsync_defaults.at @@ -35,7 +35,7 @@ AT_CHECK(test -f testdir2/testfile) # And finally, -AT_CHECK(MONOTONE2 set database default-server 127.0.0.1:5555, [], [ignore], [ignore]) +AT_CHECK(MONOTONE2 set database default-server 127.0.0.1:$_PORT, [], [ignore], [ignore]) AT_CHECK(MONOTONE2 set database default-collection thirdbranch, [], [ignore], [ignore]) AT_CHECK(MONOTONE2 sync, [], [ignore], [ignore]) AT_CHECK(MONOTONE2 checkout --branch=thirdbranch $THIRDBRANCH_R testdir3, [], [ignore], [ignore]) --- tests/t_netsync_single.at +++ tests/t_netsync_single.at @@ -24,9 +24,9 @@ VER0=`BASE_REVISION` NETSYNC_KILLHARD - MONOTONE --rcfile=netsync.lua serve 127.0.0.1:5555 testbranch & + MONOTONE --rcfile=netsync.lua serve 127.0.0.1:$_PORT testbranch & sleep 5 -AT_CHECK(MONOTONE --rcfile=netsync.lua --db=test2.db pull 127.0.0.1:5555 testbranch, [], [ignore], [ignore]) +AT_CHECK(MONOTONE --rcfile=netsync.lua --db=test2.db pull 127.0.0.1:$_PORT testbranch, [], [ignore], [ignore]) NETSYNC_KILLHARD AT_CHECK(MONOTONE --db=test2.db ls certs $VER0, [], [stdout]) --- tests/t_no_rename_overwrite.at +++ tests/t_no_rename_overwrite.at @@ -1,9 +1,6 @@ AT_SETUP([rename cannot overwrite files]) MONOTONE_SETUP -# This test is a bug report. -AT_XFAIL_IF(true) - # "rename" needs to check that it isn't overwriting existing # files/directories. @@ -19,19 +16,18 @@ ADD_FILE("rename_dir/file", [bar bar ]) -AT_CHECK(MONOTONE rename rename_file target_file, [3], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_file target_dir, [3], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_dir target_file, [3], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_dir target_dir, [3], [ignore], [ignore]) +AT_CHECK(MONOTONE rename unknown_file other_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_file target_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_file target_dir, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_dir target_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_dir target_dir, [1], [ignore], [ignore]) COMMIT(testbranch) -AT_CHECK(MONOTONE rename rename_file target_file, [0], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_file target_dir, [0], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_dir target_file, [0], [ignore], [ignore]) -AT_CHECK(MONOTONE rename rename_dir target_dir, [0], [ignore], [ignore]) +AT_CHECK(MONOTONE rename unknown_file other_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_file target_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_file target_dir, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_dir target_file, [1], [ignore], [ignore]) +AT_CHECK(MONOTONE rename rename_dir target_dir, [1], [ignore], [ignore]) -# this should fail -COMMIT(testbranch) - AT_CLEANUP --- tests/t_override_author_date.at +++ tests/t_override_author_date.at @@ -0,0 +1,17 @@ +AT_SETUP([--author, --date]) +MONOTONE_SETUP + +ADD_FILE(testfile, [floooooo +]) +AT_CHECK(MONOTONE commit --author=the_author --date=1999-12-31T12:00:00 --branch=foo --message=foo, [], [ignore], [ignore]) +REV=`BASE_REVISION` +AT_CHECK(MONOTONE log $REV, [], [stdout], [ignore]) + +AT_CHECK(grep -q '^Author: the_author' stdout) +AT_CHECK(grep -q '^Date: 1999-12-31T12:00:00' stdout) + +SET_FILE(testfile, [oovel +]) +AT_CHECK(MONOTONE commit --date=1999-12-31T12:00foo --branch=foo --message=foo, [1], [ignore], [ignore]) + +AT_CLEANUP --- tests/t_rcs_import.at +++ tests/t_rcs_import.at @@ -0,0 +1,190 @@ +AT_SETUP([test problematic cvs import]) +MONOTONE_SETUP +NEED_UNGZB64 + +# This test imports a rcs file which fails according to the mailing list +# reporter: Carl Christian Kanne "Bug? in CVS import monotone 0.18" +# Date: Fri, 15 Apr 2005 12:53:13 +0200 +# However when I test it, it succeeds---> examine +# This test is a bug report. +#AT_XFAIL_IF(true) + +# This rcs file fails to be imported correctly by monotone + +AT_DATA(rcsfile.gz.b64, [ +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 +NksYX3riaBmsp4m/Pv0ZR559DrmcAAA= +]) + +AT_CHECK(mkdir tmp, [], stdout, ignore) +UNGZB64(rcsfile.gz.b64, tmp/rcsfile,v) + +AT_CHECK(MONOTONE --branch=test cvs_import tmp, [], ignore, ignore) + +AT_CLEANUP +(END) --- tests/t_rename_dir_add_dir_with_old_name.at +++ tests/t_rename_dir_add_dir_with_old_name.at @@ -0,0 +1,28 @@ +# -*- Autoconf -*- + +AT_SETUP([renaming a directory and then adding a new with the old name]) + +MONOTONE_SETUP + +# This test is a bug report. +AT_XFAIL_IF(true) + +# add 'foo/test' file +AT_CHECK(mkdir foo) +AT_DATA(foo/test, [test file in foo dir +]) +AT_CHECK(MONOTONE add foo, [], [ignore], [ignore]) +COMMIT(testbranch) + +# rename 'foo' dir to 'bar' +AT_CHECK(MONOTONE rename foo bar, [], [ignore], [ignore]) +AT_CHECK(mv foo bar) + +# add new 'foo' dir +AT_CHECK(mkdir foo) +AT_DATA(foo/test, [test file in new foo dir +]) +AT_CHECK(MONOTONE add foo, [], [ignore], [ignore]) +COMMIT(testbranch) + +AT_CLEANUP --- tests/t_update_nonexistent.at +++ tests/t_update_nonexistent.at @@ -0,0 +1,10 @@ +AT_SETUP([update to non-existent rev]) +MONOTONE_SETUP + +ADD_FILE(testfile, [blah blah +]) +COMMIT(testbranch) + +AT_CHECK(MONOTONE update 73070030f7b0d0f3d4ee02545d45ca4bbe5e189f, [1], [ignore], [ignore]) + +AT_CLEANUP --- testsuite.at +++ testsuite.at @@ -37,6 +37,10 @@ # Save the PWD so MONOTONE macro can refer back to it _ROOT_DIR=`pwd -P` +# Pick a random port, so running multiple testsuites concurrently on +# the same machine has a hope of working. +_PORT=`awk 'BEGIN {srand()} END {printf "%.0f\n", (rand() * 450 + 20050)}'< /dev/null` + AT_DATA(test_keys, [@<:@pubkey address@hidden@:>@ MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCfN/cAMabgb6T7m8ksGnpQ7LO6hOdnc/7V yivrRGtmpwSItljht1bmgLQF37KiSPoMEDUb1stfKxaMsYiy8iTyoQ+M2EVFP37n2rtnNZ0H @@ -336,13 +340,13 @@ # note that NETSYNC_SERVE_START is _not_ a special case of this macro. m4_define([NETSYNC_SERVE_N_START], [ NETSYNC_KILLHARD -MONOTONE --db=test$1.db --rcfile=netsync.lua serve localhost:5555 $2 & +MONOTONE --db=test$1.db --rcfile=netsync.lua serve localhost:$_PORT $2 & sleep 4 ]) # run as NETSYNC_SERVE_START(collection name) m4_define([NETSYNC_SERVE_START], [ NETSYNC_KILLHARD -MONOTONE --rcfile=netsync.lua serve localhost:5555 $1 & +MONOTONE --rcfile=netsync.lua serve localhost:$_PORT $1 & sleep 4 ]) # run as NETSYNC_SERVE_STOP @@ -354,7 +358,7 @@ # run as NETSYNC_CLIENT_N_RUN(2|3, push|pull|sync, collection name[, expected result]) # first argument chooses whether to use the 2nd or 3rd database m4_define([NETSYNC_CLIENT_N_RUN], [ -AT_CHECK(MONOTONE --db=test$1.db --rcfile=netsync.lua $2 localhost:5555 $3, [$4], [ignore], [ignore]) +AT_CHECK(MONOTONE --db=test$1.db --rcfile=netsync.lua $2 localhost:$_PORT $3, [$4], [ignore], [ignore]) ]) # run as NETSYNC_CLIENT_RUN(push|pull|sync, collection name[, expected result]) m4_define([NETSYNC_CLIENT_RUN], [ @@ -551,3 +555,13 @@ m4_include(tests/t_netsync_largish_file.at) m4_include(tests/t_update_off_branch.at) m4_include(tests/t_setup_checkout_modify_new_dir.at) +m4_include(tests/t_rename_dir_add_dir_with_old_name.at) +m4_include(tests/t_rcs_import.at) +m4_include(tests/t_cvsimport2.at) +m4_include(tests/t_lf_crlf.at) +m4_include(tests/t_add_vs_commit.at) +m4_include(tests/t_update_nonexistent.at) +m4_include(tests/t_override_author_date.at) +m4_include(tests/t_add_stomp_file.at) +m4_include(tests/t_database_check_minor.at) +m4_include(tests/t_db_kill_rev_locally.at) --- transforms.cc +++ transforms.cc @@ -1,3 +1,4 @@ +// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*- // copyright (C) 2002, 2003 graydon hoare // all rights reserved. // licensed to the public under the terms of the GNU GPL (>= 2) @@ -119,69 +120,33 @@ void diff(data const & olddata, data const & newdata, - base64< gzip > & del) + delta & del) { string unpacked; compute_delta(olddata(), newdata(), unpacked); - pack(delta(unpacked), del); + del = delta(unpacked); } void patch(data const & olddata, - base64< gzip > const & del, + delta const & del, data & newdata) { - delta unpacked; - unpack(del, unpacked); string result; - apply_delta(olddata(), unpacked(), result); + apply_delta(olddata(), del(), result); newdata = result; } void diff(manifest_map const & oldman, manifest_map const & newman, - base64< gzip > & del) + delta & del) { string xd; compute_delta(oldman, newman, xd); - pack(delta(xd), del); + del = delta(xd); } -void -diff(base64< gzip > const & olddata, - base64< gzip > const & newdata, - base64< gzip > & del) -{ - gzip olddata_decoded; - gzip newdata_decoded; - - decode_base64(olddata, olddata_decoded); - decode_base64(newdata, newdata_decoded); - - data olddata_decompressed; - data newdata_decompressed; - - decode_gzip(olddata_decoded, olddata_decompressed); - decode_gzip(newdata_decoded, newdata_decompressed); - - diff(olddata_decompressed, - newdata_decompressed, - del); -} - -void -patch(base64< gzip > const & olddata, - base64< gzip > const & del, - base64< gzip > & newdata) -{ - data olddata_unpacked, newdata_unpacked; - unpack(olddata, olddata_unpacked); - patch(olddata_unpacked, del, newdata_unpacked); - pack(newdata_unpacked, newdata); -} - - // identifier (a.k.a. sha1 signature) calculation void @@ -273,9 +238,7 @@ revision_id & ident) { hexenc tmp; - data unpacked; - unpack(dat.inner(), unpacked); - calculate_ident(unpacked, tmp); + calculate_ident(dat.inner(), tmp); ident = tmp; } @@ -703,7 +666,142 @@ dst += linesep_str; } +// glob_to_regexp converts a sh file glob to a regexp. The regexp should +// be usable by the Boost regexp library. +// +// Pattern tranformation: +// +// - Any character except those described below are copied as they are. +// - The backslash (\) escapes the following character. The escaping +// backslash is copied to the regexp along with the following character. +// - * is transformed to .* in the regexp. +// - ? is transformed to . in the regexp. +// - { is transformed to ( in the regexp, unless within [ and ]. +// - } is transformed to ) in the regexp, unless within [ and ]. +// - , is transformed to | in the regexp, if within { and } and not +// within [ and ]. +// - ^ is escaped unless it comes directly after an unescaped [. +// - ! is transformed to ^ in the regexp if it comes directly after an +// unescaped [. +// - ] directly following an unescaped [ is escaped. +string glob_to_regexp(const string & glob) +{ + int in_braces = 0; // counter for levels if {} + bool in_brackets = false; // flags if we're inside a [], which + // has higher precedence than {}. + // Also, [ is accepted inside [] unescaped. + bool this_was_opening_bracket = false; + string tmp; + + tmp.reserve(glob.size() * 2); + #ifdef BUILD_UNIT_TESTS + cerr << "DEBUG[glob_to_regexp]: input = \"" << glob << "\"" << endl; +#endif + + for (string::const_iterator i = glob.begin(); i != glob.end(); ++i) + { + char c = *i; + bool last_was_opening_bracket = this_was_opening_bracket; + this_was_opening_bracket = false; + + // Special case ^ and ! at the beginning of a [] expression. + if (in_brackets && last_was_opening_bracket + && (c == '!' || c == '^')) + { + tmp += '^'; + if (++i == glob.end()) + break; + c = *i; + } + + if (c == '\\') + { + tmp += c; + if (++i == glob.end()) + break; + tmp += *i; + } + else if (in_brackets) + { + switch(c) + { + case ']': + if (!last_was_opening_bracket) + { + in_brackets = false; + tmp += c; + break; + } + // Trickling through to the standard character conversion, + // because ] as the first character of a set is regarded as + // a normal character. + default: + if (!(isalnum(c) || c == '_')) + { + tmp += '\\'; + } + tmp += c; + break; + } + } + else + { + switch(c) + { + case '*': + tmp += ".*"; + break; + case '?': + tmp += '.'; + break; + case '{': + in_braces++; + tmp += '('; + break; + case '}': + N(in_braces != 0, + F("trying to end a brace expression in a glob when none is started")); + tmp += ')'; + in_braces--; + break; + case '[': + in_brackets = true; + this_was_opening_bracket = true; + tmp += c; + break; + case ',': + if (in_braces > 0) + { + tmp += '|'; + break; + } + // Trickling through to default: here, since a comma outside of + // brace notation is just a normal character. + default: + if (!(isalnum(c) || c == '_')) + { + tmp += '\\'; + } + tmp += c; + break; + } + } + } + + N(!in_brackets, + F("run-away bracket expression in glob")); + N(in_braces == 0, + F("run-away brace expression in glob")); + +#ifdef BUILD_UNIT_TESTS + cerr << "DEBUG[glob_to_regexp]: output = \"" << tmp << "\"" << endl; +#endif + + return tmp; +} + +#ifdef BUILD_UNIT_TESTS #include "unit_tests.hh" static void @@ -989,6 +1087,15 @@ check_idna_encoding(); } +static void glob_to_regexp_test() +{ + BOOST_CHECK(glob_to_regexp("abc,v") == "abc\\,v"); + BOOST_CHECK(glob_to_regexp("foo[12m,]") == "foo[12m\\,]"); + // A full fledged, use all damn features test... + BOOST_CHECK(glob_to_regexp("foo.{bar*,cookie?{haha,hehe[^\\123!,]}}[!]a^b]") + == "foo\\.(bar.*|cookie.(haha|hehe[^\\123\\!\\,]))[^\\]a\\^b]"); +} + void add_transform_tests(test_suite * suite) { @@ -1000,6 +1107,7 @@ suite->add(BOOST_TEST_CASE(&join_lines_test)); suite->add(BOOST_TEST_CASE(&strip_ws_test)); suite->add(BOOST_TEST_CASE(&encode_test)); + suite->add(BOOST_TEST_CASE(&glob_to_regexp_test)); } #endif // BUILD_UNIT_TESTS --- transforms.hh +++ transforms.hh @@ -94,23 +94,16 @@ void diff(data const & olddata, data const & newdata, - base64< gzip > & del); + delta & del); void diff(manifest_map const & oldman, manifest_map const & newman, - base64< gzip > & del); + delta & del); -void diff(base64< gzip > const & old_data, - base64< gzip > const & new_data, - base64< gzip > & delta); - void patch(data const & olddata, - base64< gzip > const & del, + delta const & del, data & newdata); -void patch(base64< gzip > const & old_data, - base64< gzip > const & delta, - base64< gzip > & new_data); // version (a.k.a. sha1 fingerprint) calculation --- vocab.hh +++ vocab.hh @@ -115,12 +115,12 @@ typedef epoch< hexenc > epoch_id; typedef epoch< hexenc > epoch_data; -typedef revision< base64< gzip > > revision_data; -typedef manifest< base64< gzip > > manifest_data; -typedef file< base64< gzip > > file_data; +typedef revision< data > revision_data; +typedef manifest< data > manifest_data; +typedef file< data > file_data; -typedef manifest< base64< gzip > > manifest_delta; -typedef file< base64< gzip > > file_delta; +typedef manifest< delta > manifest_delta; +typedef file< delta > file_delta; typedef std::pair var_key; --- vocab_terms.hh +++ vocab_terms.hh @@ -63,15 +63,17 @@ EXTERN template class gzip; EXTERN template class base64< gzip >; -EXTERN template class revision< base64< gzip > >; -EXTERN template class manifest< base64< gzip > >; -EXTERN template class file< base64< gzip > >; +EXTERN template class revision< data >; +EXTERN template class manifest< data >; +EXTERN template class file< data >; + EXTERN template class gzip; EXTERN template class base64< gzip >; -EXTERN template class manifest< base64< gzip > >; -EXTERN template class file< base64< gzip > >; +EXTERN template class manifest< delta >; +EXTERN template class file< delta >; + EXTERN template class arc4; EXTERN template class base64< arc4 >; EXTERN template class base64< rsa_pub_key >; --- work.cc +++ work.cc @@ -88,9 +88,9 @@ } static bool -known_preimage_path(file_path const & p, - path_set const & ps, - bool & path_is_directory) +known_path(file_path const & p, + path_set const & ps, + bool & path_is_directory) { std::string path_as_dir = p() + "/"; for (path_set::const_iterator i = ps.begin(); i != ps.end(); ++i) @@ -126,7 +126,7 @@ N((*i)() != "", F("invalid path ''")); - if (! known_preimage_path(*i, ps, dir_p)) + if (! known_path(*i, ps, dir_p)) { P(F("skipping %s, not currently tracked\n") % *i); continue; @@ -163,16 +163,17 @@ extract_path_set(man, ps); apply_path_rearrangement(pr, ps); - bool dir_p = false; + bool src_dir_p = false; + bool dst_dir_p = false; - if (! known_preimage_path(src, ps, dir_p)) - { - P(F("skipping %s, not currently tracked\n") % src); - return; - } + N(known_path(src, ps, src_dir_p), + F("%s does not exist in current revision\n") % src); + N(!known_path(dst, ps, dst_dir_p), + F("%s already exists in current revision\n") % dst); + P(F("adding %s -> %s to working copy rename set\n") % src % dst); - if (dir_p) + if (src_dir_p) pr_new.renamed_dirs.insert(std::make_pair(src, dst)); else pr_new.renamed_files.insert(std::make_pair(src, dst));