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) &&
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")
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^^) &&
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)
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^^) &&
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")
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^^) &&
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)
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^^) &&
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)
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" &&
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)
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" &&
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")
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" &&
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)
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 */
+
+ switch (cmd) {
+ case TODO_COMMENT:
+ return false;
- 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);
+ 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)
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);