Skip to content

Commit 2b88fe0

Browse files
phillipwoodgitster
authored andcommitted
rebase: fix todo-list rereading
54fd324 ("rebase -i: reread the todo list if `exec` touched it", 2017-04-26) sought to reread the todo list after running an exec command only if it had been changed. To accomplish this it checks the stat data of the todo list after running an exec command to see if it has changed. Unfortunately there are two problems, firstly the implementation is buggy we actually reread the list after each exec which is quadratic in the number of commit lookups and secondly the design is predicated on using nanosecond time stamps which are not the default. The implementation bug stems from the fact that we write a new todo list to disk before running each command but do not update the stat data to reflect this[1]. The design problem is that it is possible for the user to edit the todo list without changing its size or inode which means we have to rely on the mtime to tell us if it has changed. Unfortunately unless git is built with USE_NSEC it is possible for the original and edited list to share the same mtime. Ideally "git rebase --edit-todo" would set a flag that we would then check in sequencer.c. Unfortunately this is approach will not work as there are scripts in the wild that write to the todo list directly without running "git rebase --edit-todo". Instead of relying on stat data this patch simply reads the possibly edited todo list and compares it to the original with memcmp(). This is much faster than reparsing the todo list each time. This patch reduces the time to run git rebase -r -xtrue v2.32.0~100 v2.32.0 which runs 419 exec commands by 6.6%. For comparison fixing the implementation bug in stat based approach reduces the time by a further 1.4% and is indistinguishable from never rereading the todo list. [1] https://lore.kernel.org/git/[email protected]/ Reported-by: SZEDER Gábor <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent dfa8bae commit 2b88fe0

File tree

2 files changed

+8
-12
lines changed

2 files changed

+8
-12
lines changed

sequencer.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2671,19 +2671,13 @@ static int read_populate_todo(struct repository *r,
26712671
struct todo_list *todo_list,
26722672
struct replay_opts *opts)
26732673
{
2674-
struct stat st;
26752674
const char *todo_file = get_todo_path(opts);
26762675
int res;
26772676

26782677
strbuf_reset(&todo_list->buf);
26792678
if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0)
26802679
return -1;
26812680

2682-
res = stat(todo_file, &st);
2683-
if (res)
2684-
return error(_("could not stat '%s'"), todo_file);
2685-
fill_stat_data(&todo_list->stat, &st);
2686-
26872681
res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
26882682
if (res) {
26892683
if (is_rebase_i(opts))
@@ -4258,19 +4252,22 @@ static int reread_todo_if_changed(struct repository *r,
42584252
struct todo_list *todo_list,
42594253
struct replay_opts *opts)
42604254
{
4261-
struct stat st;
4255+
int offset;
4256+
struct strbuf buf = STRBUF_INIT;
42624257

4263-
if (stat(get_todo_path(opts), &st)) {
4264-
return error_errno(_("could not stat '%s'"),
4265-
get_todo_path(opts));
4266-
} else if (match_stat_data(&todo_list->stat, &st)) {
4258+
if (strbuf_read_file_or_whine(&buf, get_todo_path(opts)) < 0)
4259+
return -1;
4260+
offset = get_item_line_offset(todo_list, todo_list->current + 1);
4261+
if (buf.len != todo_list->buf.len - offset ||
4262+
memcmp(buf.buf, todo_list->buf.buf + offset, buf.len)) {
42674263
/* Reread the todo file if it has changed. */
42684264
todo_list_release(todo_list);
42694265
if (read_populate_todo(r, todo_list, opts))
42704266
return -1; /* message was printed */
42714267
/* `current` will be incremented on return */
42724268
todo_list->current = -1;
42734269
}
4270+
strbuf_release(&buf);
42744271

42754272
return 0;
42764273
}

sequencer.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ struct todo_list {
116116
struct todo_item *items;
117117
int nr, alloc, current;
118118
int done_nr, total_nr;
119-
struct stat_data stat;
120119
};
121120

122121
#define TODO_LIST_INIT { STRBUF_INIT }

0 commit comments

Comments
 (0)