Skip to content

Commit f957362

Browse files
prertikgitster
authored andcommitted
builtin rebase: support --continue
This commit adds the option `--continue` which is used to resume rebase after merge conflicts. The code tries to stay as close to the equivalent shell scripts found in `git-legacy-rebase.sh` as possible. When continuing a rebase, the state variables are read from state_dir. Some of the state variables are not actually stored there, such as `upstream`. The shell script version simply does not set them, but for consistency, we unset them in the builtin version. Signed-off-by: Pratik Karki <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e65123a commit f957362

File tree

3 files changed

+123
-4
lines changed

3 files changed

+123
-4
lines changed

builtin/rebase.c

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ struct rebase_options {
9191
REBASE_INTERACTIVE_EXPLICIT = 1<<4,
9292
} flags;
9393
struct strbuf git_am_opt;
94+
const char *action;
9495
};
9596

9697
static int is_interactive(struct rebase_options *opts)
@@ -115,6 +116,62 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
115116
return path.buf;
116117
}
117118

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

170227
add_var(&script_snippet, "upstream_name", opts->upstream_name);
171-
add_var(&script_snippet, "upstream",
172-
oid_to_hex(&opts->upstream->object.oid));
228+
add_var(&script_snippet, "upstream", opts->upstream ?
229+
oid_to_hex(&opts->upstream->object.oid) : NULL);
173230
add_var(&script_snippet, "head_name",
174231
opts->head_name ? opts->head_name : "detached HEAD");
175232
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));
233+
add_var(&script_snippet, "onto", opts->onto ?
234+
oid_to_hex(&opts->onto->object.oid) : NULL);
177235
add_var(&script_snippet, "onto_name", opts->onto_name);
178236
add_var(&script_snippet, "revisions", opts->revisions);
179237
add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
@@ -189,6 +247,7 @@ static int run_specific_rebase(struct rebase_options *opts)
189247
opts->flags & REBASE_FORCE ? "t" : "");
190248
if (opts->switch_to)
191249
add_var(&script_snippet, "switch_to", opts->switch_to);
250+
add_var(&script_snippet, "action", opts->action ? opts->action : "");
192251

193252
switch (opts->type) {
194253
case REBASE_AM:
@@ -400,12 +459,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
400459
.git_am_opt = STRBUF_INIT,
401460
};
402461
const char *branch_name;
403-
int ret, flags, in_progress = 0;
462+
int ret, flags, total_argc, in_progress = 0;
404463
int ok_to_skip_pre_rebase = 0;
405464
struct strbuf msg = STRBUF_INIT;
406465
struct strbuf revisions = STRBUF_INIT;
407466
struct strbuf buf = STRBUF_INIT;
408467
struct object_id merge_base;
468+
enum {
469+
NO_ACTION,
470+
ACTION_CONTINUE,
471+
} action = NO_ACTION;
409472
struct option builtin_rebase_options[] = {
410473
OPT_STRING(0, "onto", &options.onto_name,
411474
N_("revision"),
@@ -427,6 +490,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
427490
OPT_BIT(0, "no-ff", &options.flags,
428491
N_("cherry-pick all commits, even if unchanged"),
429492
REBASE_FORCE),
493+
OPT_CMDMODE(0, "continue", &action, N_("continue"),
494+
ACTION_CONTINUE),
430495
OPT_END(),
431496
};
432497

@@ -480,14 +545,55 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
480545
if (options.type != REBASE_UNSPECIFIED)
481546
in_progress = 1;
482547

548+
total_argc = argc;
483549
argc = parse_options(argc, argv, prefix,
484550
builtin_rebase_options,
485551
builtin_rebase_usage, 0);
486552

553+
if (action != NO_ACTION && total_argc != 2) {
554+
usage_with_options(builtin_rebase_usage,
555+
builtin_rebase_options);
556+
}
557+
487558
if (argc > 2)
488559
usage_with_options(builtin_rebase_usage,
489560
builtin_rebase_options);
490561

562+
switch (action) {
563+
case ACTION_CONTINUE: {
564+
struct object_id head;
565+
struct lock_file lock_file = LOCK_INIT;
566+
int fd;
567+
568+
options.action = "continue";
569+
570+
/* Sanity check */
571+
if (get_oid("HEAD", &head))
572+
die(_("Cannot read HEAD"));
573+
574+
fd = hold_locked_index(&lock_file, 0);
575+
if (read_index(the_repository->index) < 0)
576+
die(_("could not read index"));
577+
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
578+
NULL);
579+
if (0 <= fd)
580+
update_index_if_able(the_repository->index,
581+
&lock_file);
582+
rollback_lock_file(&lock_file);
583+
584+
if (has_unstaged_changes(1)) {
585+
puts(_("You must edit all merge conflicts and then\n"
586+
"mark them as resolved using git add"));
587+
exit(1);
588+
}
589+
if (read_basic_state(&options))
590+
exit(1);
591+
goto run_rebase;
592+
}
593+
default:
594+
die("TODO");
595+
}
596+
491597
/* Make sure no rebase is in progress */
492598
if (in_progress) {
493599
const char *last_slash = strrchr(options.state_dir, '/');
@@ -719,6 +825,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
719825

720826
options.revisions = revisions.buf;
721827

828+
run_rebase:
722829
ret = !!run_specific_rebase(&options);
723830

724831
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)