# # add_file "tests/t_add_vs_commit.at" # # add_file "tests/t_override_author_date.at" # # add_file "tests/t_update_nonexistent.at" # # patch "ChangeLog" # from [cca8fa572e36410501c7b8be9e59811146b45165] # to [ac4b2df4b7132e8a010350e135f645ab6bb7ae7d] # # patch "app_state.cc" # from [63120a266ad19ebd72022cf172ccbd0c299aa413] # to [b821fb66100e7626444a2f1e4bc2af4d572880b1] # # patch "app_state.hh" # from [9cfdb7a5976dc31edda11e99ee74046fdc318469] # to [27b58d6683bc026b41c07308738952067bf82926] # # patch "cert.cc" # from [451241f5ac6f41b70211dc92aa500e36688579af] # to [07be077e4c0d53abd9cac0bbdf201e808cf221a5] # # patch "cert.hh" # from [4b8178c3e7303d3465932f7d44039e8315f850a6] # to [e2b1d86c8f2610e854afed7ab661c4c2554e7004] # # patch "commands.cc" # from [958cd3bbc982aef81f63b90b24dcebaddf135166] # to [e24b9d3f4cf1db10ea68ce3733c1a8bdafb5371d] # # patch "monotone.1" # from [9a00ecadd333fe803fe4788d579afb75f3a8aa51] # to [33e5e256e415dd185535ac1331d44a9de3f243cc] # # patch "monotone.cc" # from [2419f27fdb3cba77c658c0f4eac4d0b5026e1884] # to [daff288cbe075dc65c3dfd45e89ea3e71d438ed8] # # patch "monotone.texi" # from [31fabd3d2cdf017dfc5af29af363dbd9e6f74c21] # to [412d8c60e2a7e06ae07a6b8350393421d7f87b2b] # # patch "netsync.cc" # from [98bb94f9c22a40bc6f826cad62737a7f9bfb9a04] # to [b68476ffc5852e56195629f4998af91221e48dcd] # # patch "tests/t_add_vs_commit.at" # from [] # to [7fd4b57e97609427e699e0cf3cca5c984ffd1330] # # 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_update_nonexistent.at" # from [] # to [a7f5255b645f8be25c40d15a62f9f5279bf2aa59] # # patch "testsuite.at" # from [d2d93700980437bc4d42f041d1d088c06b4e5200] # to [63f222bf1549f1b5ea773c38860809fd131a88e4] # # patch "work.cc" # from [2edb3fc960fee8c34b1abb13dff648ec63eeb40f] # to [dd1b1800d78f63c9f5615622d7c638c01c2f7459] # --- ChangeLog +++ ChangeLog @@ -1,5 +1,70 @@ +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 -> @@ -2828,7 +2893,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 @@ -33,6 +33,8 @@ bool rcfiles; options_map options; utf8 message; + utf8 date; + utf8 author; utf8 search_root; std::vector revision_selectors; std::vector extra_rcfiles; @@ -62,6 +64,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); --- commands.cc +++ commands.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "commands.hh" #include "constants.hh" @@ -1793,6 +1794,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 +1828,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); @@ -2541,6 +2542,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") { @@ -2677,8 +2705,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); } @@ -3142,7 +3176,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) { --- monotone.1 +++ monotone.1 @@ -260,6 +260,17 @@ \fB-m \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 @@ -45,6 +45,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 @@ -70,10 +72,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. @@ -185,8 +189,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 { @@ -324,6 +328,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; @@ -334,7 +346,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 @@ -3374,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 @@ -6022,6 +6029,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 @@ -725,7 +725,7 @@ attached[i] = curr_attached; } -static inline id +inline static id plain_id(manifest_id const & i) { id tmp; @@ -734,7 +734,7 @@ return tmp; } -static inline id +inline static id plain_id(file_id const & i) { id tmp; --- 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_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_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 @@ -559,3 +559,6 @@ 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) --- 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));