# # # rename "tests/commit_using__MTN_log/commit_log.lua" # to "tests/commit_using__MTN_log/commit_cancelled.lua" # # rename "tests/commit_using__MTN_log/commit_log_modified_return.lua" # to "tests/commit_using__MTN_log/commit_confirmed.lua" # # patch "cmd_ws_commit.cc" # from [3c83413e2d5067cf6ac9e79d8849191a5975f948] # to [e3eed43a97da68628c4fabc80ced9ea3327871f8] # # patch "tests/changelog_editor/__driver__.lua" # from [56b86ffc51a0bf5a60f2b10e578bdd631df76796] # to [96e979a9afb3cdab37abc18f09b32044d8f5a4ab] # # patch "tests/commit_using__MTN_log/__driver__.lua" # from [0766c36af8bc95807f4a2dd2b752e0e97aadb38a] # to [64507df85499593c9de2e854a3add3b4d6683c47] # # patch "tests/commit_using__MTN_log/commit_cancelled.lua" # from [e1b07045d69de57d75e5b73df41124c2c94b315d] # to [1b64ffe9a275897fe60c629350ac44b5bb3afc62] # # patch "work.cc" # from [28266a7fbb698d94c998047e52842b220b37756d] # to [7e3829c75634ee5bbf555209dfa152d0bd88138c] # # patch "work.hh" # from [021031510460f3cdc3762460937d94b575f04154] # to [3f94623eb296347caca9ed341f0bee2ef63481be] # ============================================================ --- cmd_ws_commit.cc 3c83413e2d5067cf6ac9e79d8849191a5975f948 +++ cmd_ws_commit.cc e3eed43a97da68628c4fabc80ced9ea3327871f8 @@ -133,6 +133,14 @@ get_log_message_interactively(lua_hooks set const & old_branches, string const & date_fmt, utf8 & log_message) { + utf8 backup; + work.load_commit_text(backup); + + E(backup().empty(), origin::user, + F("A backup from a previously failed commit exists in _MTN/commit.\n" + "This file must be removed before commit will proceed.\n" + "You may recover the previous message from this file if necessary.")); + utf8 instructions( _("Enter a description of this change following the Changelog line below.\n" "The values of Author, Date and Branch may be modified as required.\n" @@ -189,7 +197,8 @@ get_log_message_interactively(lua_hooks system_to_utf8(output_message, full_message); - // FIXME: save the full message in _MTN/changelog so its not lost + // save the message in _MTN/commit so its not lost if something fails below + work.save_commit_text(full_message); message_reader message(full_message()); @@ -205,8 +214,13 @@ get_log_message_interactively(lua_hooks E(message.read(instructions()), origin::user, F("Commit failed. Instructions not found.")); - E(message.read(cancel()), origin::user, - F("Commit cancelled.")); + if (!message.read(cancel())) + { + // clear the backup file if the commit was explicitly cancelled + work.clear_commit_text(); + E(message.read(cancel()), origin::user, + F("Commit cancelled.")); + } utf8 const AUTHOR(trim_right(_("Author: ")).c_str()); utf8 const DATE(trim_right(_("Date: ")).c_str()); @@ -288,6 +302,9 @@ get_log_message_interactively(lua_hooks string content = trim(message.content()) + '\n'; log_message = utf8(content, origin::user); + + // remove the backup file now that all values have been extracted + work.clear_commit_text(); } CMD(revert, "revert", "", CMD_REF(workspace), N_("[PATH]..."), ============================================================ --- tests/changelog_editor/__driver__.lua 56b86ffc51a0bf5a60f2b10e578bdd631df76796 +++ tests/changelog_editor/__driver__.lua 96e979a9afb3cdab37abc18f09b32044d8f5a4ab @@ -19,104 +19,161 @@ check(qgrep("date format", "stderr")) check(mtn("commit", "--date-format", "%F"), 1, false, true) check(qgrep("date format", "stderr")) +check(not exists("_MTN/commit")) -- commit fails with empty message writefile("_MTN/log", "empty message") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("empty log message", "stderr")) +check(not exists("_MTN/commit")) -- commit fails with modified/missing instructions writefile("_MTN/log", "missing instructions") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Instructions not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +id1=sha1("_MTN/commit") +-- commit fails if _MTN/commit exists from previously failed commit +check(mtn("commit"), 1, false, true) +check(qgrep("previously failed commit", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +id2=sha1("_MTN/commit") +check(id1 == id2) +remove("_MTN/commit") + -- commit can be cancelled writefile("_MTN/log", "cancel") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Commit cancelled.", "stderr")) +check(not exists("_MTN/commit")) -- commit fails with modified/missing separator, Revision: or Parent: lines writefile("_MTN/log", "missing separator") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Revision/Parent header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") writefile("_MTN/log", "missing revision") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Revision/Parent header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") writefile("_MTN/log", "missing parent") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Revision/Parent header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with modified/missing Author: line writefile("_MTN/log", "missing author") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Author header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with empty Author: line writefile("_MTN/log", "empty author") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Author value empty", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with modified/missing Date: line writefile("_MTN/log", "missing date") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Date header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with empty Date: line writefile("_MTN/log", "empty date") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Date value empty", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with modified/missing Branch: line writefile("_MTN/log", "missing branch") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Branch header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with empty Branch: line writefile("_MTN/log", "empty branch") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Branch value empty", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with modified/missing blank line before Changelog section writefile("_MTN/log", "missing blank line") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Changelog header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with modified/missing Changelog section writefile("_MTN/log", "missing changelog") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Changelog header not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with missing Change summary section writefile("_MTN/log", "missing summary") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Change summary not found", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with duplicated Change summary section writefile("_MTN/log", "duplicated summary") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Text following Change summary", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commit fails with new text after Change summary section writefile("_MTN/log", "trailing text") check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) check(qgrep("Text following Change summary", "stderr")) +check(exists("_MTN/commit")) +check(fsize("_MTN/commit") > 0) +remove("_MTN/commit") -- commits that succeed @@ -131,6 +188,7 @@ check(qgrep("Branch: left", "stdout")) check(qgrep("Date: 2010-01-01T01:01:01", "stdout")) check(qgrep("Author: bobo", "stdout")) check(qgrep("Branch: left", "stdout")) +check(not exists("_MTN/commit")) -- test changed --date, --author and --branch options @@ -141,6 +199,7 @@ check(not qgrep("Branch: left", "stdou check(not qgrep("Date: 2010-01-01T01:01:01", "stdout")) check(not qgrep("Author: bobo", "stdout")) check(not qgrep("Branch: left", "stdout")) +check(not exists("_MTN/commit")) -- test unchanged date gets updated to reflect current time @@ -152,6 +211,7 @@ check(old ~= new) old = string.match(log, "Old: ([^\n]*)") new = string.match(log, "Date: ([^\n]*)") check(old ~= new) +check(not exists("_MTN/commit")) -- test changed date does not get updated @@ -160,15 +220,18 @@ check(qgrep("Date: 2010-01-01T01:01: check(mtn("commit", "--rcfile=changelog.lua"), 0, false, false) check(mtn("log", "--last", "1", "--no-graph"), 0, true, false) check(qgrep("Date: 2010-01-01T01:01:01", "stdout")) +check(not exists("_MTN/commit")) -- message on same line as Changelog: header writefile("a", "a4") writefile("_MTN/log", "changelog line") check(mtn("commit", "--rcfile=changelog.lua"), 0, false, false) +check(not exists("_MTN/commit")) -- message filling entire Changelog section (no leading/trailing blank lines) writefile("a", "a5") writefile("_MTN/log", "full changelog") check(mtn("commit", "--rcfile=changelog.lua"), 0, false, false) +check(not exists("_MTN/commit")) ============================================================ --- tests/commit_using__MTN_log/__driver__.lua 0766c36af8bc95807f4a2dd2b752e0e97aadb38a +++ tests/commit_using__MTN_log/__driver__.lua 64507df85499593c9de2e854a3add3b4d6683c47 @@ -1,25 +1,25 @@ mtn_setup() mtn_setup() -check(get("commit_log.lua")) -check(get("commit_log_modified_return.lua")) +check(get("commit_cancelled.lua")) +check(get("commit_confirmed.lua")) writefile("_MTN/log", "Log Entry") writefile("input.txt", "version 0 of the file") check(mtn("add", "input.txt"), 0, false, false) ---this should now fail, given that the log file has content and we don't ---remove the 'magic' line ---check(mtn("--branch=testbranch", "--rcfile=commit_log.lua", "commit"), 1, false, true) ---check(qgrep('magic line; commit cancelled', "stderr")) +-- this should now fail, given that the log file has content and the cancel line +-- has been removed +check(mtn("--branch=testbranch", "--rcfile=commit_cancelled.lua", "commit"), 1, false, true) +check(qgrep('Commit cancelled.', "stderr")) ---check(exists("_MTN/log")) ---check(fsize("_MTN/log") > 0) +check(exists("_MTN/log")) +check(fsize("_MTN/log") > 0) ---this should pass, as the lua hook now returns a string that doesn't contain ---the 'magic' line -check(mtn("--branch=testbranch", "--rcfile=commit_log_modified_return.lua", "commit"), 0, false, false) +-- this should pass, as the lua hook now returns a string that still includes the +-- cancel line +check(mtn("--branch=testbranch", "--rcfile=commit_confirmed.lua", "commit"), 0, false, false) tsha = base_revision() check(exists("_MTN/log")) ============================================================ --- tests/commit_using__MTN_log/commit_log.lua e1b07045d69de57d75e5b73df41124c2c94b315d +++ tests/commit_using__MTN_log/commit_cancelled.lua 1b64ffe9a275897fe60c629350ac44b5bb3afc62 @@ -1,3 +1,3 @@ function edit_comment(user_log_file) function edit_comment(user_log_file) - return user_log_file + return string.gsub(user_log_file, "... REMOVE THIS LINE TO CANCEL THE COMMIT ...\n", "") end ============================================================ --- work.cc 28266a7fbb698d94c998047e52842b220b37756d +++ work.cc 7e3829c75634ee5bbf555209dfa152d0bd88138c @@ -53,6 +53,7 @@ static char const user_log_file_name[] = static char const local_dump_file_name[] = "debug"; static char const options_file_name[] = "options"; static char const user_log_file_name[] = "log"; +static char const commit_file_name[] = "commit"; static char const revision_file_name[] = "revision"; static char const update_file_name[] = "update"; static char const bisect_file_name[] = "bisect"; @@ -93,6 +94,13 @@ static void } static void +get_commit_path(bookkeeping_path & commit_path) +{ + commit_path = bookkeeping_root / commit_file_name; + L(FL("commit path is %s") % commit_path); +} + +static void get_update_path(bookkeeping_path & update_path) { update_path = bookkeeping_root / update_file_name; @@ -414,6 +422,41 @@ workspace::has_contents_user_log() return user_log_message().length() > 0; } +// commit buffer backup file + +void +workspace::load_commit_text(utf8 & dat) +{ + bookkeeping_path commit_path; + get_commit_path(commit_path); + + if (file_exists(commit_path)) + { + data tmp; + read_data(commit_path, tmp); + system_to_utf8(typecast_vocab(tmp), dat); + } +} + +void +workspace::save_commit_text(utf8 const & dat) +{ + bookkeeping_path commit_path; + get_commit_path(commit_path); + + external tmp; + utf8_to_system_best_effort(dat, tmp); + write_data(commit_path, typecast_vocab(tmp)); +} + +void +workspace::clear_commit_text() +{ + bookkeeping_path commit_path; + get_commit_path(commit_path); + delete_file(commit_path); +} + // _MTN/options handling. static void ============================================================ --- work.hh 021031510460f3cdc3762460937d94b575f04154 +++ work.hh 3f94623eb296347caca9ed341f0bee2ef63481be @@ -226,6 +226,16 @@ public: void blank_user_log(); bool has_contents_user_log(); + // The full commit text from the edit_comment lua hook is saved before + // attempting to extract the various Author: Date: Branch: and Changelog: + // values from it in case these values don't appear where they are + // expected. Once all the values have been extracted the backup file is + // removed. + + void load_commit_text(utf8 & dat); + void save_commit_text(utf8 const & dat); + void clear_commit_text(); + // the "options map" is another administrative file, stored in // _MTN/options. it keeps a list of name/value pairs which are considered // "persistent options", associated with a particular workspace and