rebase -i: Handle "combination of <n> commits" with GETTEXT_POISON
We previously relied on the localized versions of # This is a combination of <N> commits (which we write into the commit messages during fixup/squash chains) to contain <N> encoded in ASCII. This is not true in general, and certainly not true when compiled with GETTEXT_POISON=TryToKillMe, as demonstrated by the regression test we just introduced in t3418. So let's decouple keeping track of the count from the (localized) commit messages by introducing a new file called 'current-fixups' that keeps track of the current fixup/squash chain. This file contains a bit more than just the count (it contains a list of "fixup <commit>"/"squash <commit>" lines). This is done on purpose, as it will come in handy for a fix for the bug where `git rebase --skip` on a final fixup/squash will leave the commit message in limbo. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
d5bc6f292a
commit
e12a7ef597
78
sequencer.c
78
sequencer.c
@ -74,13 +74,6 @@ static GIT_PATH_FUNC(rebase_path_message, "rebase-merge/message")
|
|||||||
* previous commit and from the first squash/fixup commit are written
|
* previous commit and from the first squash/fixup commit are written
|
||||||
* to it. The commit message for each subsequent squash/fixup commit
|
* to it. The commit message for each subsequent squash/fixup commit
|
||||||
* is appended to the file as it is processed.
|
* is appended to the file as it is processed.
|
||||||
*
|
|
||||||
* The first line of the file is of the form
|
|
||||||
* # This is a combination of $count commits.
|
|
||||||
* where $count is the number of commits whose messages have been
|
|
||||||
* written to the file so far (including the initial "pick" commit).
|
|
||||||
* Each time that a commit message is processed, this line is read and
|
|
||||||
* updated. It is deleted just before the combined commit is made.
|
|
||||||
*/
|
*/
|
||||||
static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
|
static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
|
||||||
/*
|
/*
|
||||||
@ -91,6 +84,11 @@ static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
|
|||||||
* commit without opening the editor.)
|
* commit without opening the editor.)
|
||||||
*/
|
*/
|
||||||
static GIT_PATH_FUNC(rebase_path_fixup_msg, "rebase-merge/message-fixup")
|
static GIT_PATH_FUNC(rebase_path_fixup_msg, "rebase-merge/message-fixup")
|
||||||
|
/*
|
||||||
|
* This file contains the list fixup/squash commands that have been
|
||||||
|
* accumulated into message-fixup or message-squash so far.
|
||||||
|
*/
|
||||||
|
static GIT_PATH_FUNC(rebase_path_current_fixups, "rebase-merge/current-fixups")
|
||||||
/*
|
/*
|
||||||
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
|
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
|
||||||
* GIT_AUTHOR_DATE that will be used for the commit that is currently
|
* GIT_AUTHOR_DATE that will be used for the commit that is currently
|
||||||
@ -252,6 +250,7 @@ int sequencer_remove_state(struct replay_opts *opts)
|
|||||||
for (i = 0; i < opts->xopts_nr; i++)
|
for (i = 0; i < opts->xopts_nr; i++)
|
||||||
free(opts->xopts[i]);
|
free(opts->xopts[i]);
|
||||||
free(opts->xopts);
|
free(opts->xopts);
|
||||||
|
strbuf_release(&opts->current_fixups);
|
||||||
|
|
||||||
strbuf_addstr(&dir, get_dir(opts));
|
strbuf_addstr(&dir, get_dir(opts));
|
||||||
remove_dir_recursively(&dir, 0);
|
remove_dir_recursively(&dir, 0);
|
||||||
@ -1328,34 +1327,23 @@ static int update_squash_messages(enum todo_command command,
|
|||||||
struct commit *commit, struct replay_opts *opts)
|
struct commit *commit, struct replay_opts *opts)
|
||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
int count, res;
|
int res;
|
||||||
const char *message, *body;
|
const char *message, *body;
|
||||||
|
|
||||||
if (file_exists(rebase_path_squash_msg())) {
|
if (opts->current_fixup_count > 0) {
|
||||||
struct strbuf header = STRBUF_INIT;
|
struct strbuf header = STRBUF_INIT;
|
||||||
char *eol, *p;
|
char *eol;
|
||||||
|
|
||||||
if (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)
|
if (strbuf_read_file(&buf, rebase_path_squash_msg(), 9) <= 0)
|
||||||
return error(_("could not read '%s'"),
|
return error(_("could not read '%s'"),
|
||||||
rebase_path_squash_msg());
|
rebase_path_squash_msg());
|
||||||
|
|
||||||
p = buf.buf + 1;
|
eol = buf.buf[0] != comment_line_char ?
|
||||||
eol = strchrnul(buf.buf, '\n');
|
buf.buf : strchrnul(buf.buf, '\n');
|
||||||
if (buf.buf[0] != comment_line_char ||
|
|
||||||
(p += strcspn(p, "0123456789\n")) == eol)
|
|
||||||
return error(_("unexpected 1st line of squash message:"
|
|
||||||
"\n\n\t%.*s"),
|
|
||||||
(int)(eol - buf.buf), buf.buf);
|
|
||||||
count = strtol(p, NULL, 10);
|
|
||||||
|
|
||||||
if (count < 1)
|
|
||||||
return error(_("invalid 1st line of squash message:\n"
|
|
||||||
"\n\t%.*s"),
|
|
||||||
(int)(eol - buf.buf), buf.buf);
|
|
||||||
|
|
||||||
strbuf_addf(&header, "%c ", comment_line_char);
|
strbuf_addf(&header, "%c ", comment_line_char);
|
||||||
strbuf_addf(&header,
|
strbuf_addf(&header, _("This is a combination of %d commits."),
|
||||||
_("This is a combination of %d commits."), ++count);
|
opts->current_fixup_count + 2);
|
||||||
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
|
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
|
||||||
strbuf_release(&header);
|
strbuf_release(&header);
|
||||||
} else {
|
} else {
|
||||||
@ -1378,10 +1366,8 @@ static int update_squash_messages(enum todo_command command,
|
|||||||
rebase_path_fixup_msg());
|
rebase_path_fixup_msg());
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 2;
|
|
||||||
strbuf_addf(&buf, "%c ", comment_line_char);
|
strbuf_addf(&buf, "%c ", comment_line_char);
|
||||||
strbuf_addf(&buf, _("This is a combination of %d commits."),
|
strbuf_addf(&buf, _("This is a combination of %d commits."), 2);
|
||||||
count);
|
|
||||||
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addstr(&buf, _("This is the 1st commit message:"));
|
strbuf_addstr(&buf, _("This is the 1st commit message:"));
|
||||||
strbuf_addstr(&buf, "\n\n");
|
strbuf_addstr(&buf, "\n\n");
|
||||||
@ -1398,13 +1384,14 @@ static int update_squash_messages(enum todo_command command,
|
|||||||
if (command == TODO_SQUASH) {
|
if (command == TODO_SQUASH) {
|
||||||
unlink(rebase_path_fixup_msg());
|
unlink(rebase_path_fixup_msg());
|
||||||
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addf(&buf, _("This is the commit message #%d:"), count);
|
strbuf_addf(&buf, _("This is the commit message #%d:"),
|
||||||
|
++opts->current_fixup_count);
|
||||||
strbuf_addstr(&buf, "\n\n");
|
strbuf_addstr(&buf, "\n\n");
|
||||||
strbuf_addstr(&buf, body);
|
strbuf_addstr(&buf, body);
|
||||||
} else if (command == TODO_FIXUP) {
|
} else if (command == TODO_FIXUP) {
|
||||||
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
strbuf_addf(&buf, "\n%c ", comment_line_char);
|
||||||
strbuf_addf(&buf, _("The commit message #%d will be skipped:"),
|
strbuf_addf(&buf, _("The commit message #%d will be skipped:"),
|
||||||
count);
|
++opts->current_fixup_count);
|
||||||
strbuf_addstr(&buf, "\n\n");
|
strbuf_addstr(&buf, "\n\n");
|
||||||
strbuf_add_commented_lines(&buf, body, strlen(body));
|
strbuf_add_commented_lines(&buf, body, strlen(body));
|
||||||
} else
|
} else
|
||||||
@ -1413,6 +1400,17 @@ static int update_squash_messages(enum todo_command command,
|
|||||||
|
|
||||||
res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
|
res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
strbuf_addf(&opts->current_fixups, "%s%s %s",
|
||||||
|
opts->current_fixups.len ? "\n" : "",
|
||||||
|
command_to_string(command),
|
||||||
|
oid_to_hex(&commit->object.oid));
|
||||||
|
res = write_message(opts->current_fixups.buf,
|
||||||
|
opts->current_fixups.len,
|
||||||
|
rebase_path_current_fixups(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1675,6 +1673,9 @@ fast_forward_edit:
|
|||||||
if (!res && final_fixup) {
|
if (!res && final_fixup) {
|
||||||
unlink(rebase_path_fixup_msg());
|
unlink(rebase_path_fixup_msg());
|
||||||
unlink(rebase_path_squash_msg());
|
unlink(rebase_path_squash_msg());
|
||||||
|
unlink(rebase_path_current_fixups());
|
||||||
|
strbuf_reset(&opts->current_fixups);
|
||||||
|
opts->current_fixup_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
@ -2046,6 +2047,16 @@ static int read_populate_opts(struct replay_opts *opts)
|
|||||||
read_strategy_opts(opts, &buf);
|
read_strategy_opts(opts, &buf);
|
||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
|
|
||||||
|
if (read_oneliner(&opts->current_fixups,
|
||||||
|
rebase_path_current_fixups(), 1)) {
|
||||||
|
const char *p = opts->current_fixups.buf;
|
||||||
|
opts->current_fixup_count = 1;
|
||||||
|
while ((p = strchr(p, '\n'))) {
|
||||||
|
opts->current_fixup_count++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2392,10 +2403,9 @@ static int error_with_patch(struct commit *commit,
|
|||||||
static int error_failed_squash(struct commit *commit,
|
static int error_failed_squash(struct commit *commit,
|
||||||
struct replay_opts *opts, int subject_len, const char *subject)
|
struct replay_opts *opts, int subject_len, const char *subject)
|
||||||
{
|
{
|
||||||
if (rename(rebase_path_squash_msg(), rebase_path_message()))
|
if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
|
||||||
return error(_("could not rename '%s' to '%s'"),
|
return error(_("could not copy '%s' to '%s'"),
|
||||||
rebase_path_squash_msg(), rebase_path_message());
|
rebase_path_squash_msg(), rebase_path_message());
|
||||||
unlink(rebase_path_fixup_msg());
|
|
||||||
unlink(git_path_merge_msg());
|
unlink(git_path_merge_msg());
|
||||||
if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
|
if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
|
||||||
return error(_("could not copy '%s' to '%s'"),
|
return error(_("could not copy '%s' to '%s'"),
|
||||||
|
@ -44,10 +44,14 @@ struct replay_opts {
|
|||||||
char **xopts;
|
char **xopts;
|
||||||
size_t xopts_nr, xopts_alloc;
|
size_t xopts_nr, xopts_alloc;
|
||||||
|
|
||||||
|
/* Used by fixup/squash */
|
||||||
|
struct strbuf current_fixups;
|
||||||
|
int current_fixup_count;
|
||||||
|
|
||||||
/* Only used by REPLAY_NONE */
|
/* Only used by REPLAY_NONE */
|
||||||
struct rev_info *revs;
|
struct rev_info *revs;
|
||||||
};
|
};
|
||||||
#define REPLAY_OPTS_INIT { -1 }
|
#define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
|
||||||
|
|
||||||
/* Call this to setup defaults before parsing command line options */
|
/* Call this to setup defaults before parsing command line options */
|
||||||
void sequencer_init_config(struct replay_opts *opts);
|
void sequencer_init_config(struct replay_opts *opts);
|
||||||
|
Loading…
Reference in New Issue
Block a user