# # # patch "cmd_ws_commit.cc" # from [b06703abd2d59814b4636f50029dfb3d691e4ebe] # to [70986e6ed4d55eec1ed9894caea8d975ca5344b1] # # patch "tests/changelog_editor/__driver__.lua" # from [0aa84b9ada49d2ae820046f1fa511c0ae8975383] # to [8a0027f25dc5933548ff2bf9aa46564bef466193] # # patch "tests/changelog_editor/changelog.lua" # from [0f572fde86fd0535c7de4dd06c8dbd29721d3057] # to [77dc2058a61be80b4067c2e2c772085f3ee41a65] # ============================================================ --- cmd_ws_commit.cc b06703abd2d59814b4636f50029dfb3d691e4ebe +++ cmd_ws_commit.cc 70986e6ed4d55eec1ed9894caea8d975ca5344b1 @@ -69,8 +69,8 @@ public: class message_reader { public: - message_reader(string const & message) : - message(message), offset(0) {} + message_reader(string const & message, size_t offset) : + message(message), offset(offset) {} bool read(string const & text) { @@ -181,18 +181,24 @@ get_log_message_interactively(lua_hooks "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" - "\n")); + _("-- Enter a description of this change above --\n" + "-- Edit fields below to modify certificate values --\n")); + utf8 ignored( + _("\n-- Modifications below this line are ignored entirely --\n")); + utf8 cancel(_("*** REMOVE THIS LINE TO CANCEL THE COMMIT ***\n")); + utf8 const BRANCH(trim_right(_("Branch: ")).c_str()); + utf8 const AUTHOR(trim_right(_("Author: ")).c_str()); + utf8 const DATE(trim_right(_("Date: ")).c_str()); + + bool is_date_fmt_valid = date_fmt_valid(date_fmt); + utf8 changelog; work.read_user_log(changelog); - // ensure the changelog message is non-empty so that the Changelog: cert - // line is produced by revision_header and there is somewhere to enter a - // message + // ensure changelog ends in a newline string text = changelog(); @@ -202,52 +208,67 @@ get_log_message_interactively(lua_hooks changelog = utf8(text, origin::user); } - ostringstream oss; + // build editable fields + utf8 editable; + { + ostringstream oss; - oss << string(70, '-') << '\n'; - if (!old_branches.empty() && old_branches.find(branch) == old_branches.end()) - { - oss << _("*** THIS REVISION WILL CREATE A NEW BRANCH ***") << "\n\n"; - for (set::const_iterator i = old_branches.begin(); - i != old_branches.end(); ++i) - oss << _("Old Branch: ") << *i << '\n'; - oss << _("New Branch: ") << branch << "\n\n"; - } - set heads; - project.get_branch_heads(branch, heads, false); - if (!heads.empty()) - { - for (edge_map::const_iterator e = rev.edges.begin(); - e != rev.edges.end(); ++e) - { - if (heads.find(edge_old_revision(e)) == heads.end()) - { - oss << _("*** THIS REVISION WILL CREATE DIVERGENCE ***") << "\n\n"; - break; - } - } - } + oss << BRANCH << ' ' << branch << '\n'; + oss << AUTHOR << ' ' << author << '\n'; - utf8 notes(oss.str().c_str()); + if (!is_date_fmt_valid) + { + W(F("date format '%s' cannot be parsed; using default instead") % date_fmt); + } - utf8 header; - utf8 summary; + if (!is_date_fmt_valid || date_fmt.empty()) + { + oss << DATE << ' ' << date << '\n'; + } + else + { + oss << DATE << date.as_formatted_localtime(date_fmt) << '\n'; + } - bool is_date_fmt_valid = date_fmt_valid(date_fmt); - string null_date_fmt(""); + editable = utf8(oss.str().c_str()); + } - if (!is_date_fmt_valid) - { - W(F("date format '%s' cannot be parsed; using default instead") % date_fmt); - revision_header(rid, rev, author, date, branch, changelog, null_date_fmt, header); - } - else - { - revision_header(rid, rev, author, date, branch, changelog, date_fmt, header); - } + // Build notes + utf8 notes; + { + ostringstream oss; + + if (!old_branches.empty() && old_branches.find(branch) == old_branches.end()) + { + oss << _("*** THIS REVISION WILL CREATE A NEW BRANCH ***") << "\n\n"; + for (set::const_iterator i = old_branches.begin(); + i != old_branches.end(); ++i) + oss << _("Old Branch: ") << *i << '\n'; + oss << _("New Branch: ") << branch << "\n\n"; + } + set heads; + project.get_branch_heads(branch, heads, false); + if (!heads.empty()) + { + for (edge_map::const_iterator e = rev.edges.begin(); + e != rev.edges.end(); ++e) + { + if (heads.find(edge_old_revision(e)) == heads.end()) + { + oss << _("*** THIS REVISION WILL CREATE DIVERGENCE ***") << "\n\n"; + break; + } + } + } + + notes = utf8(oss.str().c_str()); + } + + utf8 summary; revision_summary(rev, summary); - utf8 full_message(instructions() + cancel() + header() + notes() + summary(), + utf8 full_message(changelog() + cancel() + instructions() + editable() + ignored() + + notes() + summary(), origin::internal); external input_message; @@ -261,51 +282,42 @@ get_log_message_interactively(lua_hooks system_to_utf8(output_message, full_message); - // look for the cancel notification anywhere in the text. - // this might be needed in case the user moves it down accidentially - // and the previous instructions are kept intact - if (full_message().find(cancel()) == string::npos) + // Everything up to the cancel message is the changelog; trailing blank + // lines trimmed. + size_t changelog_end = full_message().find(cancel()); + if (changelog_end == string::npos) { E(false, origin::user, F("Commit cancelled.")); } - // save the message in _MTN/commit so its not lost if something fails below + // save the message in _MTN/commit so it's not lost if something fails + // below work.save_commit_text(full_message); - message_reader message(full_message()); + string content = trim_right(full_message().substr(0, changelog_end)) + '\n'; + log_message = utf8(content, origin::user); - // Check the message carefully to make sure the user didn't edit somewhere - // outside of the author, date, branch or changelog values. The section - // between the "Changelog: " line from the header and the following line - // of dashes preceeding the summary is where they should be adding - // lines. Ideally, there is a blank line following "Changelog:" - // (preceeding the changelog message) and another blank line preceeding - // the next line of dashes (following the changelog message) but both of - // these are optional. + message_reader message(full_message(), changelog_end); + // Parse the editable fields. + + // this can't fail, since we start reading where we found it above. + message.read(cancel()); + E(message.read(instructions()), origin::user, F("Commit failed. Instructions not found.")); - E(message.read(cancel()), origin::user, - F("Commit failed. Cancel hint not found.")); + // Branch: - utf8 const AUTHOR(trim_right(_("Author: ")).c_str()); - utf8 const DATE(trim_right(_("Date: ")).c_str()); - utf8 const BRANCH(trim_right(_("Branch: ")).c_str()); - utf8 const CHANGELOG(trim_right(_("Changelog: ")).c_str()); + E(message.read(BRANCH()), origin::user, + F("Commit failed. Branch header not found.")); - // ---------------------------------------------------------------------- - // Revision: - // Parent: - // Parent: - // Author: + string b = message.readline(); - size_t pos = header().find(AUTHOR()); // look in unedited header - I(pos != string::npos); + E(!b.empty(), origin::user, + F("Commit failed. Branch value empty.")); - string prefix = header().substr(0, pos); - E(message.read(prefix), origin::user, - F("Commit failed. Revision/Parent header not found.")); + branch = branch_name(b, origin::user); // Author: @@ -332,44 +344,8 @@ get_log_message_interactively(lua_hooks else date = date_t::from_formatted_localtime(d, date_fmt); - // Branch: + // rest is ignored - E(message.read(BRANCH()), origin::user, - F("Commit failed. Branch header not found.")); - - string b = message.readline(); - - E(!b.empty(), origin::user, - F("Commit failed. Branch value empty.")); - - branch = branch_name(b, origin::user); - - string blank = message.readline(); - E(blank == "", origin::user, - F("Commit failed. Blank line before Changelog header not found.")); - - // Changelog: - - E(message.read(CHANGELOG()), origin::user, - F("Commit failed. Changelog header not found.")); - - // remove the summary before extracting the changelog content - - string footer = notes() + summary(); - - if (!footer.empty()) - { - E(message.contains(footer), origin::user, - F("Commit failed. Change summary not found.")); - - E(message.remove(footer), origin::user, - F("Commit failed. Text following Change summary.")); - } - - 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(); } ============================================================ --- tests/changelog_editor/__driver__.lua 0aa84b9ada49d2ae820046f1fa511c0ae8975383 +++ tests/changelog_editor/__driver__.lua 8a0027f25dc5933548ff2bf9aa46564bef466193 @@ -15,6 +15,13 @@ check(not exists("_MTN/commit")) check(qgrep("empty log message", "stderr")) check(not exists("_MTN/commit")) +-- commit can be cancelled + +writefile("_MTN/log", "cancel hint removed") +check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) +check(qgrep("Commit cancelled.", "stderr")) +check(not exists("_MTN/commit")) + -- commit fails with modified/missing instructions writefile("_MTN/log", "missing instructions") @@ -33,45 +40,6 @@ remove("_MTN/commit") check(id1 == id2) remove("_MTN/commit") --- commit can be cancelled - -writefile("_MTN/log", "cancel hint removed") -check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) -check(qgrep("Commit cancelled.", "stderr")) -check(not exists("_MTN/commit")) - --- and we notice if the cancel message is still intact and --- hasn't been moved - -writefile("_MTN/log", "cancel hint moved") -check(mtn("commit", "--rcfile=changelog.lua"), 1, false, true) -check(qgrep("Cancel hint not found.", "stderr")) -check(exists("_MTN/commit")) -remove("_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") @@ -126,52 +94,7 @@ remove("_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 -- commit succeeds with bad date format (uses default format instead) @@ -230,13 +153,6 @@ check(not exists("_MTN/commit")) 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") ============================================================ --- tests/changelog_editor/changelog.lua 0f572fde86fd0535c7de4dd06c8dbd29721d3057 +++ tests/changelog_editor/changelog.lua 77dc2058a61be80b4067c2e2c772085f3ee41a65 @@ -1,64 +1,46 @@ function edit_comment(user_log_file) function edit_comment(user_log_file) - + local result, date + -- commits that fail - if (string.find(user_log_file, "\nempty message\n")) then - return string.gsub(user_log_file, "\nempty message\n", "") - elseif (string.find(user_log_file, "\nmissing instructions\n")) then - return "foobar" .. user_log_file - elseif (string.find(user_log_file, "\ncancel hint removed\n")) then + if (string.find(user_log_file, "empty message\n")) then + return string.gsub(user_log_file, "empty message\n", "") + elseif (string.find(user_log_file, "cancel hint removed\n")) then return string.gsub(user_log_file, "... REMOVE THIS LINE TO CANCEL THE COMMIT ...\n", "") - elseif (string.find(user_log_file, "\ncancel hint moved\n")) then - return string.gsub(user_log_file, "(... REMOVE THIS LINE TO CANCEL THE COMMIT ...)", "\n\n%1") - elseif (string.find(user_log_file, "\nmissing separator\n")) then - return string.gsub(user_log_file, "---------------\n", "\n") - elseif (string.find(user_log_file, "\nmissing revision\n")) then - return string.gsub(user_log_file, "\nRevision:", "\nrevision:") - elseif (string.find(user_log_file, "\nmissing parent\n")) then - return string.gsub(user_log_file, "\nAuthor:", "foobar\nAuthor:") - elseif (string.find(user_log_file, "\nmissing author\n")) then + elseif (string.find(user_log_file, "missing instructions\n")) then + return string.gsub(user_log_file, "Enter a description", "\n") + elseif (string.find(user_log_file, "missing author\n")) then return string.gsub(user_log_file, "\nAuthor:", "\nAutor:") - elseif (string.find(user_log_file, "\nempty author\n")) then + elseif (string.find(user_log_file, "empty author\n")) then return string.gsub(user_log_file, "\nAuthor: [^\n]*\n", "\nAuthor: \n") - elseif (string.find(user_log_file, "\nmissing date\n")) then + elseif (string.find(user_log_file, "missing date\n")) then return string.gsub(user_log_file, "\nDate:", "\nDate") - elseif (string.find(user_log_file, "\nempty date\n")) then + elseif (string.find(user_log_file, "empty date\n")) then return string.gsub(user_log_file, "\nDate: [^\n]*\n", "\nDate: \n") - elseif (string.find(user_log_file, "\nmissing branch\n")) then + elseif (string.find(user_log_file, "missing branch\n")) then return string.gsub(user_log_file, "\nBranch:", "\nranch:") - elseif (string.find(user_log_file, "\nempty branch\n")) then + elseif (string.find(user_log_file, "empty branch\n")) then return string.gsub(user_log_file, "\nBranch: [^\n]*\n", "\nBranch: \n") - elseif (string.find(user_log_file, "\nmissing blank line\n")) then - return string.gsub(user_log_file, "\n\nChangelog:", "\nChangelog:") - elseif (string.find(user_log_file, "\nmissing changelog\n")) then - return string.gsub(user_log_file, "\nChangelog:", "\n") - elseif (string.find(user_log_file, "\nmissing summary\n")) then - return string.gsub(user_log_file, "\nChanges against parent ", "\nChange foobar") - elseif (string.find(user_log_file, "\nduplicated summary\n")) then - return string.gsub(user_log_file, "(Changes against parent .*)", "%1%1") - elseif (string.find(user_log_file, "\ntrailing text\n")) then - return user_log_file .. "foobar" end -- commits that succeed - if (string.find(user_log_file, "\nchange author/date/branch\n")) then + if (string.find(user_log_file, "change author/date/branch\n")) then result = user_log_file - result = string.gsub(result, "\nDate: [^\n]*\n", "\nDate: 2010-02-02T02:02:02\n") - result = string.gsub(result, "\nAuthor: bobo\n", "\nAuthor: baba\n") - result = string.gsub(result, "\nBranch: left\n", "\nBranch: right\n") + result = string.gsub(result, "\nBranch: left\n", "\nBranch: right\n") + result = string.gsub(result, "\nAuthor: bobo\n", "\nAuthor: baba\n") + result = string.gsub(result, "\nDate: [^\n]*\n", "\nDate: 2010-02-02T02:02:02\n") return result - elseif (string.find(user_log_file, "\nsleep\n")) then - date = string.match(user_log_file, "\nDate: ([^\n]*)") + elseif (string.find(user_log_file, "sleep\n")) then + date = string.match(user_log_file, "\nDate: ([^\n]*)") sleep(2) - return string.gsub(user_log_file, "\nChangelog: \n\nsleep", "\nChangelog: \n\nOld: " .. date) - elseif (string.find(user_log_file, "\nchange date\n")) then - return string.gsub(user_log_file, "\nDate: [^\n]*\n", "\nDate: 2010-01-01T01:01:01\n") - elseif (string.find(user_log_file, "\nchangelog line\n")) then - return string.gsub(user_log_file, "\nChangelog: \n\nchangelog line", "\nChangelog:message on changelog line") - elseif (string.find(user_log_file, "\nfull changelog\n")) then - return string.gsub(user_log_file, "\nChangelog: \n\nfull changelog\n\n", "\nChangelog:no\nspace\naround\nthis\nchangelog\n-----") + return string.gsub(user_log_file, "sleep", "Old: " .. date) + elseif (string.find(user_log_file, "change date\n")) then + return string.gsub(user_log_file, "\nDate: [^\n]*\n", "\nDate: 2010-01-01T01:01:01\n") + elseif (string.find(user_log_file, "full changelog\n")) then + return string.gsub(user_log_file, "full changelog\n\n", "full changelog\n-----") end + -- otherwise don't modify anything return user_log_file end