Skip to content

Commit 730a0b1

Browse files
committed
Merge 'pk/rebase-in-c-3-acts'
This set of patches implements the actions (such as --continue, --skip, etc) in the builtin rebase. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 2b4c09e + 582d406 commit 730a0b1

File tree

3 files changed

+201
-4
lines changed

3 files changed

+201
-4
lines changed

builtin/rebase.c

Lines changed: 189 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "diff.h"
2222
#include "wt-status.h"
2323
#include "revision.h"
24+
#include "rerere.h"
2425

2526
static char const * const builtin_rebase_usage[] = {
2627
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -91,6 +92,7 @@ struct rebase_options {
9192
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
9293
} flags;
9394
struct strbuf git_am_opt;
95+
const char *action;
9496
};
9597

9698
static int is_interactive(struct rebase_options *opts)
@@ -115,6 +117,62 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
115117
return path.buf;
116118
}
117119

120+
/* Read one file, then strip line endings */
121+
static int read_one(const char *path, struct strbuf *buf)
122+
{
123+
if (strbuf_read_file(buf, path, 0) < 0)
124+
return error_errno(_("could not read '%s'"), path);
125+
strbuf_trim_trailing_newline(buf);
126+
return 0;
127+
}
128+
129+
/* Initialize the rebase options from the state directory. */
130+
static int read_basic_state(struct rebase_options *opts)
131+
{
132+
struct strbuf head_name = STRBUF_INIT;
133+
struct strbuf buf = STRBUF_INIT;
134+
struct object_id oid;
135+
136+
if (read_one(state_dir_path("head-name", opts), &head_name) ||
137+
read_one(state_dir_path("onto", opts), &buf))
138+
return -1;
139+
opts->head_name = starts_with(head_name.buf, "refs/") ?
140+
xstrdup(head_name.buf) : NULL;
141+
strbuf_release(&head_name);
142+
if (get_oid(buf.buf, &oid))
143+
return error(_("could not get 'onto': '%s'"), buf.buf);
144+
opts->onto = lookup_commit_or_die(&oid, buf.buf);
145+
146+
/*
147+
* We always write to orig-head, but interactive rebase used to write to
148+
* head. Fall back to reading from head to cover for the case that the
149+
* user upgraded git with an ongoing interactive rebase.
150+
*/
151+
strbuf_reset(&buf);
152+
if (file_exists(state_dir_path("orig-head", opts))) {
153+
if (read_one(state_dir_path("orig-head", opts), &buf))
154+
return -1;
155+
} else if (read_one(state_dir_path("head", opts), &buf))
156+
return -1;
157+
if (get_oid(buf.buf, &opts->orig_head))
158+
return error(_("invalid orig-head: '%s'"), buf.buf);
159+
160+
strbuf_reset(&buf);
161+
if (read_one(state_dir_path("quiet", opts), &buf))
162+
return -1;
163+
if (buf.len)
164+
opts->flags &= ~REBASE_NO_QUIET;
165+
else
166+
opts->flags |= REBASE_NO_QUIET;
167+
168+
if (file_exists(state_dir_path("verbose", opts)))
169+
opts->flags |= REBASE_VERBOSE;
170+
171+
strbuf_release(&buf);
172+
173+
return 0;
174+
}
175+
118176
static int finish_rebase(struct rebase_options *opts)
119177
{
120178
struct strbuf dir = STRBUF_INIT;
@@ -168,12 +226,13 @@ static int run_specific_rebase(struct rebase_options *opts)
168226
add_var(&script_snippet, "state_dir", opts->state_dir);
169227

170228
add_var(&script_snippet, "upstream_name", opts->upstream_name);
171-
add_var(&script_snippet, "upstream",
172-
oid_to_hex(&opts->upstream->object.oid));
229+
add_var(&script_snippet, "upstream", opts->upstream ?
230+
oid_to_hex(&opts->upstream->object.oid) : NULL);
173231
add_var(&script_snippet, "head_name",
174232
opts->head_name ? opts->head_name : "detached HEAD");
175233
add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
176-
add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid));
234+
add_var(&script_snippet, "onto", opts->onto ?
235+
oid_to_hex(&opts->onto->object.oid) : NULL);
177236
add_var(&script_snippet, "onto_name", opts->onto_name);
178237
add_var(&script_snippet, "revisions", opts->revisions);
179238
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
@@ -189,6 +248,7 @@ static int run_specific_rebase(struct rebase_options *opts)
189248
opts->flags & REBASE_FORCE ? "t" : "");
190249
if (opts->switch_to)
191250
add_var(&script_snippet, "switch_to", opts->switch_to);
251+
add_var(&script_snippet, "action", opts->action ? opts->action : "");
192252

193253
switch (opts->type) {
194254
case REBASE_AM:
@@ -400,12 +460,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
400460
.git_am_opt = STRBUF_INIT,
401461
};
402462
const char *branch_name;
403-
int ret, flags, in_progress = 0;
463+
int ret, flags, total_argc, in_progress = 0;
404464
int ok_to_skip_pre_rebase = 0;
405465
struct strbuf msg = STRBUF_INIT;
406466
struct strbuf revisions = STRBUF_INIT;
407467
struct strbuf buf = STRBUF_INIT;
408468
struct object_id merge_base;
469+
enum {
470+
NO_ACTION,
471+
ACTION_CONTINUE,
472+
ACTION_SKIP,
473+
ACTION_ABORT,
474+
ACTION_QUIT,
475+
ACTION_EDIT_TODO,
476+
ACTION_SHOW_CURRENT_PATCH,
477+
} action = NO_ACTION;
409478
struct option builtin_rebase_options[] = {
410479
OPT_STRING(0, "onto", &options.onto_name,
411480
N_("revision"),
@@ -427,6 +496,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
427496
OPT_BIT(0, "no-ff", &options.flags,
428497
N_("cherry-pick all commits, even if unchanged"),
429498
REBASE_FORCE),
499+
OPT_CMDMODE(0, "continue", &action, N_("continue"),
500+
ACTION_CONTINUE),
501+
OPT_CMDMODE(0, "skip", &action,
502+
N_("skip current patch and continue"), ACTION_SKIP),
503+
OPT_CMDMODE(0, "abort", &action,
504+
N_("abort and check out the original branch"),
505+
ACTION_ABORT),
506+
OPT_CMDMODE(0, "quit", &action,
507+
N_("abort but keep HEAD where it is"), ACTION_QUIT),
508+
OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list "
509+
"during an interactive rebase"), ACTION_EDIT_TODO),
510+
OPT_CMDMODE(0, "show-current-patch", &action,
511+
N_("show the patch file being applied or merged"),
512+
ACTION_SHOW_CURRENT_PATCH),
430513
OPT_END(),
431514
};
432515

@@ -456,6 +539,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
456539

457540
git_config(rebase_config, &options);
458541

542+
strbuf_reset(&buf);
543+
strbuf_addf(&buf, "%s/applying", apply_dir());
544+
if(file_exists(buf.buf))
545+
die(_("It looks like 'git am' is in progress. Cannot rebase."));
546+
459547
if (is_directory(apply_dir())) {
460548
options.type = REBASE_AM;
461549
options.state_dir = apply_dir();
@@ -480,14 +568,110 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
480568
if (options.type != REBASE_UNSPECIFIED)
481569
in_progress = 1;
482570

571+
total_argc = argc;
483572
argc = parse_options(argc, argv, prefix,
484573
builtin_rebase_options,
485574
builtin_rebase_usage, 0);
486575

576+
if (action != NO_ACTION && total_argc != 2) {
577+
usage_with_options(builtin_rebase_usage,
578+
builtin_rebase_options);
579+
}
580+
487581
if (argc > 2)
488582
usage_with_options(builtin_rebase_usage,
489583
builtin_rebase_options);
490584

585+
if (action != NO_ACTION && !in_progress)
586+
die(_("No rebase in progress?"));
587+
588+
if (action == ACTION_EDIT_TODO && !is_interactive(&options))
589+
die(_("The --edit-todo action can only be used during "
590+
"interactive rebase."));
591+
592+
switch (action) {
593+
case ACTION_CONTINUE: {
594+
struct object_id head;
595+
struct lock_file lock_file = LOCK_INIT;
596+
int fd;
597+
598+
options.action = "continue";
599+
600+
/* Sanity check */
601+
if (get_oid("HEAD", &head))
602+
die(_("Cannot read HEAD"));
603+
604+
fd = hold_locked_index(&lock_file, 0);
605+
if (read_index(the_repository->index) < 0)
606+
die(_("could not read index"));
607+
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
608+
NULL);
609+
if (0 <= fd)
610+
update_index_if_able(the_repository->index,
611+
&lock_file);
612+
rollback_lock_file(&lock_file);
613+
614+
if (has_unstaged_changes(1)) {
615+
puts(_("You must edit all merge conflicts and then\n"
616+
"mark them as resolved using git add"));
617+
exit(1);
618+
}
619+
if (read_basic_state(&options))
620+
exit(1);
621+
goto run_rebase;
622+
}
623+
case ACTION_SKIP: {
624+
struct string_list merge_rr = STRING_LIST_INIT_DUP;
625+
626+
options.action = "skip";
627+
628+
rerere_clear(&merge_rr);
629+
string_list_clear(&merge_rr, 1);
630+
631+
if (reset_head(NULL, "reset", NULL, 0) < 0)
632+
die(_("could not discard worktree changes"));
633+
if (read_basic_state(&options))
634+
exit(1);
635+
goto run_rebase;
636+
}
637+
case ACTION_ABORT: {
638+
struct string_list merge_rr = STRING_LIST_INIT_DUP;
639+
options.action = "abort";
640+
641+
rerere_clear(&merge_rr);
642+
string_list_clear(&merge_rr, 1);
643+
644+
if (read_basic_state(&options))
645+
exit(1);
646+
if (reset_head(&options.orig_head, "reset",
647+
options.head_name, 0) < 0)
648+
die(_("could not move back to %s"),
649+
oid_to_hex(&options.orig_head));
650+
ret = finish_rebase(&options);
651+
goto cleanup;
652+
}
653+
case ACTION_QUIT: {
654+
strbuf_reset(&buf);
655+
strbuf_addstr(&buf, options.state_dir);
656+
ret = !!remove_dir_recursively(&buf, 0);
657+
if (ret)
658+
die(_("could not remove '%s'"), options.state_dir);
659+
goto cleanup;
660+
}
661+
case ACTION_EDIT_TODO:
662+
options.action = "edit-todo";
663+
options.dont_finish_rebase = 1;
664+
goto run_rebase;
665+
case ACTION_SHOW_CURRENT_PATCH:
666+
options.action = "show-current-patch";
667+
options.dont_finish_rebase = 1;
668+
goto run_rebase;
669+
case NO_ACTION:
670+
break;
671+
default:
672+
BUG("action: %d", action);
673+
}
674+
491675
/* Make sure no rebase is in progress */
492676
if (in_progress) {
493677
const char *last_slash = strrchr(options.state_dir, '/');
@@ -719,6 +903,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
719903

720904
options.revisions = revisions.buf;
721905

906+
run_rebase:
722907
ret = !!run_specific_rebase(&options);
723908

724909
cleanup:

strbuf.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ void strbuf_trim_trailing_dir_sep(struct strbuf *sb)
120120
sb->buf[sb->len] = '\0';
121121
}
122122

123+
void strbuf_trim_trailing_newline(struct strbuf *sb)
124+
{
125+
if (sb->len > 0 && sb->buf[sb->len - 1] == '\n') {
126+
if (--sb->len > 0 && sb->buf[sb->len - 1] == '\r')
127+
--sb->len;
128+
sb->buf[sb->len] = '\0';
129+
}
130+
}
131+
123132
void strbuf_ltrim(struct strbuf *sb)
124133
{
125134
char *b = sb->buf;

strbuf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ extern void strbuf_ltrim(struct strbuf *);
190190
/* Strip trailing directory separators */
191191
extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
192192

193+
/* Strip trailing LF or CR/LF */
194+
extern void strbuf_trim_trailing_newline(struct strbuf *sb);
195+
193196
/**
194197
* Replace the contents of the strbuf with a reencoded form. Returns -1
195198
* on error, 0 on success.

0 commit comments

Comments
 (0)