Skip to content

Commit f6e76c0

Browse files
agrngitster
authored andcommitted
rebase -i: rewrite complete_action() in C
This rewrites complete_action() from shell to C. A new mode is added to rebase--helper (`--complete-action`), as well as a new flag (`--autosquash`). Finally, complete_action() is stripped from git-rebase--interactive.sh. The original complete_action() would return the code 2 when the todo list contained no actions. This was a special case for rebase -i and -p; git-rebase.sh would then apply the autostash, delete the state directory, and die with the message "Nothing to do". This cleanup is rewritten in C instead of returning 2. As rebase -i no longer returns 2, the comment describing this behaviour in git-rebase.sh is updated to reflect this change. The first check might seem useless as we write "noop" to the todo list if it is empty. Actually, the todo list might contain commented commands (ie. empty commits). In this case, complete_action() won’t write "noop", and will abort without starting the editor. Signed-off-by: Alban Gruin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 646d39c commit f6e76c0

File tree

5 files changed

+118
-52
lines changed

5 files changed

+118
-52
lines changed

builtin/rebase--helper.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ static const char * const builtin_rebase_helper_usage[] = {
1313
int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
1414
{
1515
struct replay_opts opts = REPLAY_OPTS_INIT;
16-
unsigned flags = 0, keep_empty = 0, rebase_merges = 0;
16+
unsigned flags = 0, keep_empty = 0, rebase_merges = 0, autosquash = 0;
1717
int abbreviate_commands = 0, rebase_cousins = -1;
1818
enum {
1919
CONTINUE = 1, ABORT, MAKE_SCRIPT, SHORTEN_OIDS, EXPAND_OIDS,
2020
CHECK_TODO_LIST, SKIP_UNNECESSARY_PICKS, REARRANGE_SQUASH,
2121
ADD_EXEC, APPEND_TODO_HELP, EDIT_TODO, PREPARE_BRANCH,
22-
CHECKOUT_ONTO
22+
CHECKOUT_ONTO, COMPLETE_ACTION
2323
} command = 0;
2424
struct option options[] = {
2525
OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
@@ -29,6 +29,8 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
2929
OPT_BOOL(0, "rebase-merges", &rebase_merges, N_("rebase merge commits")),
3030
OPT_BOOL(0, "rebase-cousins", &rebase_cousins,
3131
N_("keep original branch points of cousins")),
32+
OPT_BOOL(0, "autosquash", &autosquash,
33+
N_("move commits thas begin with squash!/fixup!")),
3234
OPT__VERBOSE(&opts.verbose, N_("be verbose")),
3335
OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
3436
CONTINUE),
@@ -57,6 +59,8 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
5759
N_("prepare the branch to be rebased"), PREPARE_BRANCH),
5860
OPT_CMDMODE(0, "checkout-onto", &command,
5961
N_("checkout a commit"), CHECKOUT_ONTO),
62+
OPT_CMDMODE(0, "complete-action", &command,
63+
N_("complete the action"), COMPLETE_ACTION),
6064
OPT_END()
6165
};
6266

@@ -110,5 +114,9 @@ int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
110114
return !!prepare_branch_to_be_rebased(&opts, argv[1]);
111115
if (command == CHECKOUT_ONTO && argc == 4)
112116
return !!checkout_onto(&opts, argv[1], argv[2], argv[3]);
117+
if (command == COMPLETE_ACTION && argc == 6)
118+
return !!complete_action(&opts, flags, argv[1], argv[2], argv[3],
119+
argv[4], argv[5], autosquash);
120+
113121
usage_with_options(builtin_rebase_helper_usage, options);
114122
}

git-rebase--interactive.sh

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -127,54 +127,6 @@ init_revisions_and_shortrevisions () {
127127
fi
128128
}
129129

130-
complete_action() {
131-
test -s "$todo" || echo noop >> "$todo"
132-
test -z "$autosquash" || git rebase--helper --rearrange-squash || exit
133-
test -n "$cmd" && git rebase--helper --add-exec-commands "$cmd"
134-
135-
todocount=$(git stripspace --strip-comments <"$todo" | wc -l)
136-
todocount=${todocount##* }
137-
138-
cat >>"$todo" <<EOF
139-
140-
$comment_char $(eval_ngettext \
141-
"Rebase \$shortrevisions onto \$shortonto (\$todocount command)" \
142-
"Rebase \$shortrevisions onto \$shortonto (\$todocount commands)" \
143-
"$todocount")
144-
EOF
145-
git rebase--helper --append-todo-help ${keep_empty:+--keep-empty}
146-
147-
has_action "$todo" ||
148-
return 2
149-
150-
cp "$todo" "$todo".backup
151-
collapse_todo_ids
152-
git_sequence_editor "$todo" ||
153-
die_abort "$(gettext "Could not execute editor")"
154-
155-
has_action "$todo" ||
156-
return 2
157-
158-
git rebase--helper --check-todo-list || {
159-
ret=$?
160-
git rebase--helper --checkout-onto "$onto_name" "$onto" \
161-
"$orig_head" ${verbose:+--verbose}
162-
exit $ret
163-
}
164-
165-
expand_todo_ids
166-
167-
test -n "$force_rebase" ||
168-
onto="$(git rebase--helper --skip-unnecessary-picks)" ||
169-
die "Could not skip unnecessary pick commands"
170-
171-
git rebase--helper --checkout-onto "$onto_name" "$onto" "$orig_head" \
172-
${verbose:+--verbose}
173-
require_clean_work_tree "rebase"
174-
exec git rebase--helper ${force_rebase:+--no-ff} $allow_empty_message \
175-
--continue
176-
}
177-
178130
git_rebase__interactive () {
179131
initiate_action "$action"
180132
ret=$?
@@ -193,5 +145,8 @@ git_rebase__interactive () {
193145
$revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
194146
die "$(gettext "Could not generate todo list")"
195147

196-
complete_action
148+
exec git rebase--helper --complete-action "$shortrevisions" "$onto_name" \
149+
"$shortonto" "$orig_head" "$cmd" $allow_empty_message \
150+
${autosquash:+--autosquash} ${keep_empty:+--keep-empty} \
151+
${verbose:+--verbose} ${force_rebase:+--no-ff}
197152
}

git-rebase.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ run_specific_rebase () {
219219
if test $ret -eq 0
220220
then
221221
finish_rebase
222-
elif test $ret -eq 2 # special exit status for rebase -i
222+
elif test $ret -eq 2 # special exit status for rebase -p
223223
then
224224
apply_autostash &&
225225
rm -rf "$state_dir" &&

sequencer.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "oidset.h"
3131
#include "commit-slab.h"
3232
#include "alias.h"
33+
#include "rebase-interactive.h"
3334

3435
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
3536

@@ -53,6 +54,9 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
5354
* file and written to the tail of 'done'.
5455
*/
5556
GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
57+
static GIT_PATH_FUNC(rebase_path_todo_backup,
58+
"rebase-merge/git-rebase-todo.backup")
59+
5660
/*
5761
* The rebase command lines that have already been processed. A line
5862
* is moved here when it is first handled, before any associated user
@@ -4495,6 +4499,101 @@ int skip_unnecessary_picks(struct object_id *output_oid)
44954499
return 0;
44964500
}
44974501

4502+
int complete_action(struct replay_opts *opts, unsigned flags,
4503+
const char *shortrevisions, const char *onto_name,
4504+
const char *onto, const char *orig_head, const char *cmd,
4505+
unsigned autosquash)
4506+
{
4507+
const char *shortonto, *todo_file = rebase_path_todo();
4508+
struct todo_list todo_list = TODO_LIST_INIT;
4509+
struct strbuf *buf = &(todo_list.buf);
4510+
struct object_id oid;
4511+
struct stat st;
4512+
4513+
get_oid(onto, &oid);
4514+
shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
4515+
4516+
if (!lstat(todo_file, &st) && st.st_size == 0 &&
4517+
write_message("noop\n", 5, todo_file, 0))
4518+
return error_errno(_("could not write '%s'"), todo_file);
4519+
4520+
if (autosquash && rearrange_squash())
4521+
return 1;
4522+
4523+
if (cmd && *cmd)
4524+
sequencer_add_exec_commands(cmd);
4525+
4526+
if (strbuf_read_file(buf, todo_file, 0) < 0)
4527+
return error_errno(_("could not read '%s'."), todo_file);
4528+
4529+
if (parse_insn_buffer(buf->buf, &todo_list)) {
4530+
todo_list_release(&todo_list);
4531+
return error(_("unusable todo list: '%s'"), todo_file);
4532+
}
4533+
4534+
if (count_commands(&todo_list) == 0) {
4535+
apply_autostash(opts);
4536+
sequencer_remove_state(opts);
4537+
todo_list_release(&todo_list);
4538+
4539+
fputs("Nothing to do\n", stderr);
4540+
return 1;
4541+
}
4542+
4543+
strbuf_addch(buf, '\n');
4544+
strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
4545+
"Rebase %s onto %s (%d commands)",
4546+
count_commands(&todo_list)),
4547+
shortrevisions, shortonto, count_commands(&todo_list));
4548+
append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
4549+
4550+
if (write_message(buf->buf, buf->len, todo_file, 0)) {
4551+
todo_list_release(&todo_list);
4552+
return error_errno(_("could not write '%s'"), todo_file);
4553+
}
4554+
4555+
copy_file(rebase_path_todo_backup(), todo_file, 0666);
4556+
transform_todos(flags | TODO_LIST_SHORTEN_IDS);
4557+
4558+
strbuf_reset(buf);
4559+
4560+
if (launch_sequence_editor(todo_file, buf, NULL)) {
4561+
apply_autostash(opts);
4562+
sequencer_remove_state(opts);
4563+
todo_list_release(&todo_list);
4564+
4565+
return error(_("could not execute editor"));
4566+
}
4567+
4568+
strbuf_stripspace(buf, 1);
4569+
if (buf->len == 0) {
4570+
apply_autostash(opts);
4571+
sequencer_remove_state(opts);
4572+
todo_list_release(&todo_list);
4573+
4574+
fputs("Nothing to do\n", stderr);
4575+
return 1;
4576+
}
4577+
4578+
todo_list_release(&todo_list);
4579+
4580+
if (check_todo_list()) {
4581+
checkout_onto(opts, onto_name, onto, orig_head);
4582+
return 1;
4583+
}
4584+
4585+
transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));
4586+
4587+
if (opts->allow_ff && skip_unnecessary_picks(&oid))
4588+
return error(_("could not skip unnecessary pick commands"));
4589+
4590+
checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head);
4591+
if (require_clean_work_tree("rebase", "", 1, 1))
4592+
return 1;
4593+
4594+
return sequencer_continue(opts);
4595+
}
4596+
44984597
struct subject2item_entry {
44994598
struct hashmap_entry entry;
45004599
int i;

sequencer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ int transform_todos(unsigned flags);
8989
enum missing_commit_check_level get_missing_commit_check_level(void);
9090
int check_todo_list(void);
9191
int skip_unnecessary_picks(struct object_id *output_oid);
92+
int complete_action(struct replay_opts *opts, unsigned flags,
93+
const char *shortrevisions, const char *onto_name,
94+
const char *onto, const char *orig_head, const char *cmd,
95+
unsigned autosquash);
9296
int rearrange_squash(void);
9397

9498
extern const char sign_off_header[];

0 commit comments

Comments
 (0)