mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-13 08:57:56 -05:00
Merge branch 'pw/status-rebase-todo' into seen
The display of the rebase todo list in "git status" has been improved to correctly abbreviate object IDs for more commands and avoid misinterpreting refs as object IDs. * pw/status-rebase-todo: status: improve rebase todo list parsing sequencer: factor out parsing of todo commands
This commit is contained in:
39
sequencer.c
39
sequencer.c
@@ -2627,6 +2627,27 @@ static int is_command(enum todo_command command, const char **bol)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd)
|
||||
{
|
||||
const char *s = *p;
|
||||
|
||||
for (int i = 0; i < TODO_COMMENT; i++)
|
||||
if (is_command(i, p)) {
|
||||
*cmd = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (starts_with(s, comment_line_str)) {
|
||||
*cmd = TODO_COMMENT;
|
||||
return true;
|
||||
} else if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n') || !s[0]) {
|
||||
*cmd = TODO_COMMENT;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int check_label_or_ref_arg(enum todo_command command, const char *arg)
|
||||
{
|
||||
switch (command) {
|
||||
@@ -2716,30 +2737,24 @@ static int parse_insn_line(struct repository *r, struct replay_opts *opts,
|
||||
{
|
||||
struct object_id commit_oid;
|
||||
char *end_of_object_name;
|
||||
int i, saved, status, padding;
|
||||
int saved, status, padding;
|
||||
|
||||
item->flags = 0;
|
||||
|
||||
/* left-trim */
|
||||
bol += strspn(bol, " \t");
|
||||
|
||||
if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
|
||||
item->command = TODO_COMMENT;
|
||||
if (!sequencer_parse_todo_command(&bol, &item->command))
|
||||
return error(_("invalid command '%.*s'"),
|
||||
(int)strcspn(bol, " \t\r\n"), bol);
|
||||
|
||||
if (item->command == TODO_COMMENT) {
|
||||
item->commit = NULL;
|
||||
item->arg_offset = bol - buf;
|
||||
item->arg_len = eol - bol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < TODO_COMMENT; i++)
|
||||
if (is_command(i, &bol)) {
|
||||
item->command = i;
|
||||
break;
|
||||
}
|
||||
if (i >= TODO_COMMENT)
|
||||
return error(_("invalid command '%.*s'"),
|
||||
(int)strcspn(bol, " \t\r\n"), bol);
|
||||
|
||||
/* Eat up extra spaces/ tabs before object name */
|
||||
padding = strspn(bol, " \t");
|
||||
bol += padding;
|
||||
|
||||
@@ -265,6 +265,14 @@ int read_author_script(const char *path, char **name, char **email, char **date,
|
||||
int write_basic_state(struct replay_opts *opts, const char *head_name,
|
||||
struct commit *onto, const struct object_id *orig_head);
|
||||
void sequencer_post_commit_cleanup(struct repository *r, int verbose);
|
||||
|
||||
/*
|
||||
* Try to parse the todo command pointed to by *p. On success sets cmd,
|
||||
* advances p and returns true. On failure returns false, leaves p and
|
||||
* cmd unchanged.
|
||||
*/
|
||||
bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd);
|
||||
|
||||
int sequencer_get_last_command(struct repository* r,
|
||||
enum replay_action *action);
|
||||
int sequencer_determine_whence(struct repository *r, enum commit_whence *whence);
|
||||
|
||||
@@ -224,7 +224,7 @@ test_expect_success 'status when splitting a commit' '
|
||||
COMMIT3=$(git rev-parse --short split_commit) &&
|
||||
test_commit four_split main.txt four &&
|
||||
COMMIT4=$(git rev-parse --short split_commit) &&
|
||||
FAKE_LINES="1 edit 2 3" &&
|
||||
FAKE_LINES="reword 1 edit 2 fixup_-C 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
ONTO=$(git rev-parse --short HEAD~3) &&
|
||||
@@ -233,10 +233,10 @@ test_expect_success 'status when splitting a commit' '
|
||||
cat >expected <<EOF &&
|
||||
interactive rebase in progress; onto $ONTO
|
||||
Last commands done (2 commands done):
|
||||
pick $COMMIT2 # two_split
|
||||
reword $COMMIT2 # two_split
|
||||
edit $COMMIT3 # three_split
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_split
|
||||
fixup -C $COMMIT4 # four_split
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
|
||||
(Once your working directory is clean, run "git rebase --continue")
|
||||
@@ -297,7 +297,7 @@ test_expect_success 'prepare for several edits' '
|
||||
|
||||
|
||||
test_expect_success 'status: (continue first edit) second edit' '
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
FAKE_LINES="edit 1 edit 2 drop 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
@@ -312,7 +312,7 @@ Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
drop $COMMIT4 # four_edits
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(use "git commit --amend" to amend the current commit)
|
||||
@@ -327,7 +327,7 @@ EOF
|
||||
|
||||
test_expect_success 'status: (continue first edit) second edit and split' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
FAKE_LINES="edit 1 edit 2 squash 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
@@ -343,7 +343,7 @@ Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
squash $COMMIT4 # four_edits
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(Once your working directory is clean, run "git rebase --continue")
|
||||
@@ -362,7 +362,7 @@ EOF
|
||||
|
||||
test_expect_success 'status: (continue first edit) second edit and amend' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
FAKE_LINES="edit 1 edit 2 fixup 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
@@ -378,7 +378,7 @@ Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
fixup $COMMIT4 # four_edits
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(use "git commit --amend" to amend the current commit)
|
||||
@@ -393,7 +393,7 @@ EOF
|
||||
|
||||
test_expect_success 'status: (amend first edit) second edit' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
FAKE_LINES="edit 1 edit 2 fixup_-c 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
@@ -409,7 +409,7 @@ Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
fixup -c $COMMIT4 # four_edits
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(use "git commit --amend" to amend the current commit)
|
||||
@@ -460,14 +460,20 @@ EOF
|
||||
|
||||
test_expect_success 'status: (amend first edit) second edit and amend' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
COMMIT3=$(git rev-parse --short several_edits^) &&
|
||||
COMMIT4=$(git rev-parse --short several_edits) &&
|
||||
ONTO=$(git rev-parse --short HEAD~3) &&
|
||||
git rebase -i HEAD~3 &&
|
||||
cat >todo <<-EOF &&
|
||||
edit several_edits^^ # two_edits
|
||||
edit several_edits^ # three_edits
|
||||
merge $(git rev-parse main) $(git rev-parse several_edits)
|
||||
EOF
|
||||
(
|
||||
set_replace_editor todo &&
|
||||
git rebase -i HEAD~3
|
||||
) &&
|
||||
git commit --amend -m "c" &&
|
||||
git rebase --continue &&
|
||||
git commit --amend -m "d" &&
|
||||
@@ -477,7 +483,7 @@ Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
merge $(git rev-parse --short main) $COMMIT4
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(use "git commit --amend" to amend the current commit)
|
||||
@@ -525,14 +531,21 @@ EOF
|
||||
|
||||
test_expect_success 'status: (split first edit) second edit and split' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
COMMIT3=$(git rev-parse --short several_edits^) &&
|
||||
COMMIT4=$(git rev-parse --short several_edits) &&
|
||||
cat >todo <<-EOF &&
|
||||
edit several_edits^^ # two_edits
|
||||
edit several_edits^ # three_edits
|
||||
reset $(git rev-parse main)
|
||||
merge -C several_edits topic # title
|
||||
EOF
|
||||
ONTO=$(git rev-parse --short HEAD~3) &&
|
||||
git rebase -i HEAD~3 &&
|
||||
(
|
||||
set_replace_editor todo &&
|
||||
git rebase -i HEAD~3
|
||||
) &&
|
||||
git reset HEAD^ &&
|
||||
git add main.txt &&
|
||||
git commit --amend -m "f" &&
|
||||
@@ -543,8 +556,9 @@ interactive rebase in progress; onto $ONTO
|
||||
Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
Next commands to do (2 remaining commands):
|
||||
reset $(git rev-parse --short main)
|
||||
merge -C $COMMIT4 topic # title
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(Once your working directory is clean, run "git rebase --continue")
|
||||
@@ -563,14 +577,21 @@ EOF
|
||||
|
||||
test_expect_success 'status: (split first edit) second edit and amend' '
|
||||
git reset --hard several_edits &&
|
||||
FAKE_LINES="edit 1 edit 2 3" &&
|
||||
export FAKE_LINES &&
|
||||
test_when_finished "git rebase --abort" &&
|
||||
git branch cafe main &&
|
||||
COMMIT2=$(git rev-parse --short several_edits^^) &&
|
||||
COMMIT3=$(git rev-parse --short several_edits^) &&
|
||||
COMMIT4=$(git rev-parse --short several_edits) &&
|
||||
cat >todo <<-EOF &&
|
||||
edit several_edits^^ # two_edits
|
||||
edit several_edits^ # three_edits
|
||||
update-ref refs/heads/main
|
||||
reset cafe
|
||||
EOF
|
||||
ONTO=$(git rev-parse --short HEAD~3) &&
|
||||
git rebase -i HEAD~3 &&
|
||||
(
|
||||
set_replace_editor todo &&
|
||||
git rebase -i HEAD~3
|
||||
) &&
|
||||
git reset HEAD^ &&
|
||||
git add main.txt &&
|
||||
git commit --amend -m "g" &&
|
||||
@@ -581,8 +602,9 @@ interactive rebase in progress; onto $ONTO
|
||||
Last commands done (2 commands done):
|
||||
edit $COMMIT2 # two_edits
|
||||
edit $COMMIT3 # three_edits
|
||||
Next command to do (1 remaining command):
|
||||
pick $COMMIT4 # four_edits
|
||||
Next commands to do (2 remaining commands):
|
||||
update-ref refs/heads/main
|
||||
reset cafe
|
||||
(use "git rebase --edit-todo" to view and edit)
|
||||
You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
|
||||
(use "git commit --amend" to amend the current commit)
|
||||
|
||||
127
wt-status.c
127
wt-status.c
@@ -1365,35 +1365,118 @@ static int split_commit_in_progress(struct wt_status *s)
|
||||
return split_in_progress;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the whitespace-delimited token starting at or just after *pp *
|
||||
* is a hex object id that is longer than its default abbreviation, *
|
||||
* abbreviate it in-place, shrinking `line` accordingly. On return
|
||||
* *pp points one past the (possibly abbreviated) token. Leaves both
|
||||
* `line` and *pp-advanced-past-the-token unchanged in all other cases
|
||||
* (non-hex token, unresolvable, or a refname that happens to consist
|
||||
* only of hex digits).
|
||||
*/
|
||||
static void abbrev_oid_in_line(struct repository *r,
|
||||
struct strbuf *line, char **pp)
|
||||
{
|
||||
char *p = *pp;
|
||||
char *end_of_object_name, saved;
|
||||
const char *abbrev;
|
||||
struct object_id oid;
|
||||
bool have_oid;
|
||||
|
||||
p += strspn(p, " \t");
|
||||
end_of_object_name = p + strcspn(p, " \t");
|
||||
/*
|
||||
* For "merge" and "reset" the object name may be a label or
|
||||
* ref rather than a hex object id. Only abbreviate the object
|
||||
* name if it is a hex object id.
|
||||
*/
|
||||
for (const char *q = p; q < end_of_object_name; q++) {
|
||||
if (!isxdigit(*q))
|
||||
goto out;
|
||||
}
|
||||
saved = *end_of_object_name;
|
||||
*end_of_object_name = '\0';
|
||||
have_oid = !repo_get_oid(r, p, &oid);
|
||||
*end_of_object_name = saved;
|
||||
if (!have_oid)
|
||||
goto out; /* object name was a label */
|
||||
abbrev = repo_find_unique_abbrev(r, &oid, DEFAULT_ABBREV);
|
||||
if (!starts_with(p, abbrev))
|
||||
goto out; /* object name was a refname containing only xdigits */
|
||||
p += strlen(abbrev);
|
||||
strbuf_remove(line, p - line->buf, end_of_object_name - p);
|
||||
end_of_object_name = p;
|
||||
out:
|
||||
*pp = end_of_object_name;
|
||||
}
|
||||
|
||||
static void skip_dash_c(char **pp)
|
||||
{
|
||||
char *p = *pp;
|
||||
|
||||
p += strspn(p, " \t");
|
||||
/* The (void) cast is required to silence -Wunused-value */
|
||||
(void)(skip_prefix(p, "-C", &p) || skip_prefix(p, "-c", &p));
|
||||
*pp = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn
|
||||
* "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message"
|
||||
* into
|
||||
* "pick d6a2f03 some message"
|
||||
*
|
||||
* The function assumes that the line does not contain useless spaces
|
||||
* before or after the command.
|
||||
* Returns false on comment lines, true otherwise
|
||||
*/
|
||||
static void abbrev_oid_in_line(struct repository *r, struct strbuf *line)
|
||||
static bool format_todo_line(struct repository *r, struct strbuf *line)
|
||||
{
|
||||
struct string_list split = STRING_LIST_INIT_DUP;
|
||||
struct object_id oid;
|
||||
enum todo_command cmd;
|
||||
char *p = line->buf;
|
||||
|
||||
if (starts_with(line->buf, "exec ") ||
|
||||
starts_with(line->buf, "x ") ||
|
||||
starts_with(line->buf, "label ") ||
|
||||
starts_with(line->buf, "l "))
|
||||
return;
|
||||
if (!sequencer_parse_todo_command((const char**)&p, &cmd))
|
||||
return true; /* keep invalid lines */
|
||||
|
||||
if ((2 <= string_list_split(&split, line->buf, " ", 2)) &&
|
||||
!repo_get_oid(r, split.items[1].string, &oid)) {
|
||||
strbuf_reset(line);
|
||||
strbuf_addf(line, "%s ", split.items[0].string);
|
||||
strbuf_add_unique_abbrev(line, &oid, DEFAULT_ABBREV);
|
||||
for (size_t i = 2; i < split.nr; i++)
|
||||
strbuf_addf(line, " %s", split.items[i].string);
|
||||
switch (cmd) {
|
||||
case TODO_COMMENT:
|
||||
return false;
|
||||
|
||||
case TODO_MERGE:
|
||||
skip_dash_c(&p);
|
||||
while (true) {
|
||||
p += strspn(p, " \t");
|
||||
if (!p[0] || (p[0] == '#' && (!p[1] || isspace(p[1]))))
|
||||
break;
|
||||
abbrev_oid_in_line(r, line, &p);
|
||||
}
|
||||
break;
|
||||
|
||||
case TODO_FIXUP:
|
||||
skip_dash_c(&p);
|
||||
/* fallthrough */
|
||||
case TODO_DROP:
|
||||
case TODO_EDIT:
|
||||
case TODO_PICK:
|
||||
case TODO_RESET:
|
||||
case TODO_REVERT:
|
||||
case TODO_REWORD:
|
||||
case TODO_SQUASH:
|
||||
abbrev_oid_in_line(r, line, &p);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Avoid "default" and instead list all the other commands so
|
||||
* that -Wswitch (which is included in -Wall) warns if a new
|
||||
* command is added without handling it in this function.
|
||||
*/
|
||||
case TODO_BREAK:
|
||||
case TODO_EXEC:
|
||||
case TODO_LABEL:
|
||||
case TODO_NOOP:
|
||||
case TODO_UPDATE_REF:
|
||||
break;
|
||||
}
|
||||
string_list_clear(&split, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int read_rebase_todolist(struct repository *r, const char *fname, struct string_list *lines)
|
||||
@@ -1411,13 +1494,9 @@ static int read_rebase_todolist(struct repository *r, const char *fname, struct
|
||||
repo_git_path_replace(r, &buf, "%s", fname));
|
||||
}
|
||||
while (!strbuf_getline_lf(&buf, f)) {
|
||||
if (starts_with(buf.buf, comment_line_str))
|
||||
continue;
|
||||
strbuf_trim(&buf);
|
||||
if (!buf.len)
|
||||
continue;
|
||||
abbrev_oid_in_line(r, &buf);
|
||||
string_list_append(lines, buf.buf);
|
||||
if (format_todo_line(r, &buf))
|
||||
string_list_append(lines, buf.buf);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user