Skip to content

Commit e12a7ef

Browse files
dschogitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d5bc6f2 commit e12a7ef

File tree

2 files changed

+49
-35
lines changed

2 files changed

+49
-35
lines changed

sequencer.c

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,6 @@ static GIT_PATH_FUNC(rebase_path_message, "rebase-merge/message")
7474
* previous commit and from the first squash/fixup commit are written
7575
* to it. The commit message for each subsequent squash/fixup commit
7676
* is appended to the file as it is processed.
77-
*
78-
* The first line of the file is of the form
79-
* # This is a combination of $count commits.
80-
* where $count is the number of commits whose messages have been
81-
* written to the file so far (including the initial "pick" commit).
82-
* Each time that a commit message is processed, this line is read and
83-
* updated. It is deleted just before the combined commit is made.
8477
*/
8578
static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
8679
/*
@@ -91,6 +84,11 @@ static GIT_PATH_FUNC(rebase_path_squash_msg, "rebase-merge/message-squash")
9184
* commit without opening the editor.)
9285
*/
9386
static GIT_PATH_FUNC(rebase_path_fixup_msg, "rebase-merge/message-fixup")
87+
/*
88+
* This file contains the list fixup/squash commands that have been
89+
* accumulated into message-fixup or message-squash so far.
90+
*/
91+
static GIT_PATH_FUNC(rebase_path_current_fixups, "rebase-merge/current-fixups")
9492
/*
9593
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
9694
* 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)
252250
for (i = 0; i < opts->xopts_nr; i++)
253251
free(opts->xopts[i]);
254252
free(opts->xopts);
253+
strbuf_release(&opts->current_fixups);
255254

256255
strbuf_addstr(&dir, get_dir(opts));
257256
remove_dir_recursively(&dir, 0);
@@ -1328,34 +1327,23 @@ static int update_squash_messages(enum todo_command command,
13281327
struct commit *commit, struct replay_opts *opts)
13291328
{
13301329
struct strbuf buf = STRBUF_INIT;
1331-
int count, res;
1330+
int res;
13321331
const char *message, *body;
13331332

1334-
if (file_exists(rebase_path_squash_msg())) {
1333+
if (opts->current_fixup_count > 0) {
13351334
struct strbuf header = STRBUF_INIT;
1336-
char *eol, *p;
1335+
char *eol;
13371336

1338-
if (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)
1337+
if (strbuf_read_file(&buf, rebase_path_squash_msg(), 9) <= 0)
13391338
return error(_("could not read '%s'"),
13401339
rebase_path_squash_msg());
13411340

1342-
p = buf.buf + 1;
1343-
eol = strchrnul(buf.buf, '\n');
1344-
if (buf.buf[0] != comment_line_char ||
1345-
(p += strcspn(p, "0123456789\n")) == eol)
1346-
return error(_("unexpected 1st line of squash message:"
1347-
"\n\n\t%.*s"),
1348-
(int)(eol - buf.buf), buf.buf);
1349-
count = strtol(p, NULL, 10);
1350-
1351-
if (count < 1)
1352-
return error(_("invalid 1st line of squash message:\n"
1353-
"\n\t%.*s"),
1354-
(int)(eol - buf.buf), buf.buf);
1341+
eol = buf.buf[0] != comment_line_char ?
1342+
buf.buf : strchrnul(buf.buf, '\n');
13551343

13561344
strbuf_addf(&header, "%c ", comment_line_char);
1357-
strbuf_addf(&header,
1358-
_("This is a combination of %d commits."), ++count);
1345+
strbuf_addf(&header, _("This is a combination of %d commits."),
1346+
opts->current_fixup_count + 2);
13591347
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
13601348
strbuf_release(&header);
13611349
} else {
@@ -1378,10 +1366,8 @@ static int update_squash_messages(enum todo_command command,
13781366
rebase_path_fixup_msg());
13791367
}
13801368

1381-
count = 2;
13821369
strbuf_addf(&buf, "%c ", comment_line_char);
1383-
strbuf_addf(&buf, _("This is a combination of %d commits."),
1384-
count);
1370+
strbuf_addf(&buf, _("This is a combination of %d commits."), 2);
13851371
strbuf_addf(&buf, "\n%c ", comment_line_char);
13861372
strbuf_addstr(&buf, _("This is the 1st commit message:"));
13871373
strbuf_addstr(&buf, "\n\n");
@@ -1398,13 +1384,14 @@ static int update_squash_messages(enum todo_command command,
13981384
if (command == TODO_SQUASH) {
13991385
unlink(rebase_path_fixup_msg());
14001386
strbuf_addf(&buf, "\n%c ", comment_line_char);
1401-
strbuf_addf(&buf, _("This is the commit message #%d:"), count);
1387+
strbuf_addf(&buf, _("This is the commit message #%d:"),
1388+
++opts->current_fixup_count);
14021389
strbuf_addstr(&buf, "\n\n");
14031390
strbuf_addstr(&buf, body);
14041391
} else if (command == TODO_FIXUP) {
14051392
strbuf_addf(&buf, "\n%c ", comment_line_char);
14061393
strbuf_addf(&buf, _("The commit message #%d will be skipped:"),
1407-
count);
1394+
++opts->current_fixup_count);
14081395
strbuf_addstr(&buf, "\n\n");
14091396
strbuf_add_commented_lines(&buf, body, strlen(body));
14101397
} else
@@ -1413,6 +1400,17 @@ static int update_squash_messages(enum todo_command command,
14131400

14141401
res = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);
14151402
strbuf_release(&buf);
1403+
1404+
if (!res) {
1405+
strbuf_addf(&opts->current_fixups, "%s%s %s",
1406+
opts->current_fixups.len ? "\n" : "",
1407+
command_to_string(command),
1408+
oid_to_hex(&commit->object.oid));
1409+
res = write_message(opts->current_fixups.buf,
1410+
opts->current_fixups.len,
1411+
rebase_path_current_fixups(), 0);
1412+
}
1413+
14161414
return res;
14171415
}
14181416

@@ -1675,6 +1673,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
16751673
if (!res && final_fixup) {
16761674
unlink(rebase_path_fixup_msg());
16771675
unlink(rebase_path_squash_msg());
1676+
unlink(rebase_path_current_fixups());
1677+
strbuf_reset(&opts->current_fixups);
1678+
opts->current_fixup_count = 0;
16781679
}
16791680

16801681
leave:
@@ -2046,6 +2047,16 @@ static int read_populate_opts(struct replay_opts *opts)
20462047
read_strategy_opts(opts, &buf);
20472048
strbuf_release(&buf);
20482049

2050+
if (read_oneliner(&opts->current_fixups,
2051+
rebase_path_current_fixups(), 1)) {
2052+
const char *p = opts->current_fixups.buf;
2053+
opts->current_fixup_count = 1;
2054+
while ((p = strchr(p, '\n'))) {
2055+
opts->current_fixup_count++;
2056+
p++;
2057+
}
2058+
}
2059+
20492060
return 0;
20502061
}
20512062

@@ -2392,10 +2403,9 @@ static int error_with_patch(struct commit *commit,
23922403
static int error_failed_squash(struct commit *commit,
23932404
struct replay_opts *opts, int subject_len, const char *subject)
23942405
{
2395-
if (rename(rebase_path_squash_msg(), rebase_path_message()))
2396-
return error(_("could not rename '%s' to '%s'"),
2406+
if (copy_file(rebase_path_message(), rebase_path_squash_msg(), 0666))
2407+
return error(_("could not copy '%s' to '%s'"),
23972408
rebase_path_squash_msg(), rebase_path_message());
2398-
unlink(rebase_path_fixup_msg());
23992409
unlink(git_path_merge_msg());
24002410
if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
24012411
return error(_("could not copy '%s' to '%s'"),

sequencer.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@ struct replay_opts {
4444
char **xopts;
4545
size_t xopts_nr, xopts_alloc;
4646

47+
/* Used by fixup/squash */
48+
struct strbuf current_fixups;
49+
int current_fixup_count;
50+
4751
/* Only used by REPLAY_NONE */
4852
struct rev_info *revs;
4953
};
50-
#define REPLAY_OPTS_INIT { -1 }
54+
#define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
5155

5256
/* Call this to setup defaults before parsing command line options */
5357
void sequencer_init_config(struct replay_opts *opts);

0 commit comments

Comments
 (0)