[PATCH 1/7] builtin rebase: support --continue

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <predatoramigo@xxxxxxxxx>
---
 builtin/rebase.c | 115 +++++++++++++++++++++++++++++++++++++++++++++--
 strbuf.c         |   9 ++++
 strbuf.h         |   3 ++
 3 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index b2ddfa8dbf..10da4c978b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -91,6 +91,7 @@ struct rebase_options {
 		REBASE_INTERACTIVE_EXPLICIT = 1<<4,
 	} flags;
 	struct strbuf git_am_opt;
+	const char *action;
 };
 
 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
 	return path.buf;
 }
 
+/* Read one file, then strip line endings */
+static int read_one(const char *path, struct strbuf *buf)
+{
+	if (strbuf_read_file(buf, path, 0) < 0)
+		return error_errno(_("could not read '%s'"), path);
+	strbuf_trim_trailing_newline(buf);
+	return 0;
+}
+
+/* Initialize the rebase options from the state directory. */
+static int read_basic_state(struct rebase_options *opts)
+{
+	struct strbuf head_name = STRBUF_INIT;
+	struct strbuf buf = STRBUF_INIT;
+	struct object_id oid;
+
+	if (read_one(state_dir_path("head-name", opts), &head_name) ||
+	    read_one(state_dir_path("onto", opts), &buf))
+		return -1;
+	opts->head_name = starts_with(head_name.buf, "refs/") ?
+		xstrdup(head_name.buf) : NULL;
+	strbuf_release(&head_name);
+	if (get_oid(buf.buf, &oid))
+		return error(_("could not get 'onto': '%s'"), buf.buf);
+	opts->onto = lookup_commit_or_die(&oid, buf.buf);
+
+	/*
+	 * We always write to orig-head, but interactive rebase used to write to
+	 * head. Fall back to reading from head to cover for the case that the
+	 * user upgraded git with an ongoing interactive rebase.
+	 */
+	strbuf_reset(&buf);
+	if (file_exists(state_dir_path("orig-head", opts))) {
+		if (read_one(state_dir_path("orig-head", opts), &buf))
+			return -1;
+	} else if (read_one(state_dir_path("head", opts), &buf))
+		return -1;
+	if (get_oid(buf.buf, &opts->orig_head))
+		return error(_("invalid orig-head: '%s'"), buf.buf);
+
+	strbuf_reset(&buf);
+	if (read_one(state_dir_path("quiet", opts), &buf))
+		return -1;
+	if (buf.len)
+		opts->flags &= ~REBASE_NO_QUIET;
+	else
+		opts->flags |= REBASE_NO_QUIET;
+
+	if (file_exists(state_dir_path("verbose", opts)))
+		opts->flags |= REBASE_VERBOSE;
+
+	strbuf_release(&buf);
+
+	return 0;
+}
+
 static int finish_rebase(struct rebase_options *opts)
 {
 	struct strbuf dir = STRBUF_INIT;
@@ -168,12 +225,13 @@ static int run_specific_rebase(struct rebase_options *opts)
 	add_var(&script_snippet, "state_dir", opts->state_dir);
 
 	add_var(&script_snippet, "upstream_name", opts->upstream_name);
-	add_var(&script_snippet, "upstream",
-				 oid_to_hex(&opts->upstream->object.oid));
+	add_var(&script_snippet, "upstream", opts->upstream ?
+		oid_to_hex(&opts->upstream->object.oid) : NULL);
 	add_var(&script_snippet, "head_name",
 		opts->head_name ? opts->head_name : "detached HEAD");
 	add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
-	add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid));
+	add_var(&script_snippet, "onto", opts->onto ?
+		oid_to_hex(&opts->onto->object.oid) : NULL);
 	add_var(&script_snippet, "onto_name", opts->onto_name);
 	add_var(&script_snippet, "revisions", opts->revisions);
 	add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
@@ -189,6 +247,7 @@ static int run_specific_rebase(struct rebase_options *opts)
 		opts->flags & REBASE_FORCE ? "t" : "");
 	if (opts->switch_to)
 		add_var(&script_snippet, "switch_to", opts->switch_to);
+	add_var(&script_snippet, "action", opts->action ? opts->action : "");
 
 	switch (opts->type) {
 	case REBASE_AM:
@@ -400,12 +459,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		.git_am_opt = STRBUF_INIT,
 	};
 	const char *branch_name;
-	int ret, flags, in_progress = 0;
+	int ret, flags, total_argc, in_progress = 0;
 	int ok_to_skip_pre_rebase = 0;
 	struct strbuf msg = STRBUF_INIT;
 	struct strbuf revisions = STRBUF_INIT;
 	struct strbuf buf = STRBUF_INIT;
 	struct object_id merge_base;
+	enum {
+		NO_ACTION,
+		ACTION_CONTINUE,
+	} action = NO_ACTION;
 	struct option builtin_rebase_options[] = {
 		OPT_STRING(0, "onto", &options.onto_name,
 			   N_("revision"),
@@ -427,6 +490,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		OPT_BIT(0, "no-ff", &options.flags,
 			N_("cherry-pick all commits, even if unchanged"),
 			REBASE_FORCE),
+		OPT_CMDMODE(0, "continue", &action, N_("continue"),
+			    ACTION_CONTINUE),
 		OPT_END(),
 	};
 
@@ -480,14 +545,55 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.type != REBASE_UNSPECIFIED)
 		in_progress = 1;
 
+	total_argc = argc;
 	argc = parse_options(argc, argv, prefix,
 			     builtin_rebase_options,
 			     builtin_rebase_usage, 0);
 
+	if (action != NO_ACTION && total_argc != 2) {
+		usage_with_options(builtin_rebase_usage,
+				   builtin_rebase_options);
+	}
+
 	if (argc > 2)
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
 
+	switch (action) {
+	case ACTION_CONTINUE: {
+		struct object_id head;
+		struct lock_file lock_file = LOCK_INIT;
+		int fd;
+
+		options.action = "continue";
+
+		/* Sanity check */
+		if (get_oid("HEAD", &head))
+			die(_("Cannot read HEAD"));
+
+		fd = hold_locked_index(&lock_file, 0);
+		if (read_index(the_repository->index) < 0)
+			die(_("could not read index"));
+		refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL,
+			      NULL);
+		if (0 <= fd)
+			update_index_if_able(the_repository->index,
+					     &lock_file);
+		rollback_lock_file(&lock_file);
+
+		if (has_unstaged_changes(1)) {
+			puts(_("You must edit all merge conflicts and then\n"
+			       "mark them as resolved using git add"));
+			exit(1);
+		}
+		if (read_basic_state(&options))
+			exit(1);
+		goto run_rebase;
+	}
+	default:
+		die("TODO");
+	}
+
 	/* Make sure no rebase is in progress */
 	if (in_progress) {
 		const char *last_slash = strrchr(options.state_dir, '/');
@@ -719,6 +825,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	options.revisions = revisions.buf;
 
+run_rebase:
 	ret = !!run_specific_rebase(&options);
 
 cleanup:
diff --git a/strbuf.c b/strbuf.c
index 030556111d..fdc0ffbafb 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -120,6 +120,15 @@ void strbuf_trim_trailing_dir_sep(struct strbuf *sb)
 	sb->buf[sb->len] = '\0';
 }
 
+void strbuf_trim_trailing_newline(struct strbuf *sb)
+{
+	if (sb->len > 0 && sb->buf[sb->len - 1] == '\n') {
+		if (--sb->len > 0 && sb->buf[sb->len - 1] == '\r')
+			--sb->len;
+		sb->buf[sb->len] = '\0';
+	}
+}
+
 void strbuf_ltrim(struct strbuf *sb)
 {
 	char *b = sb->buf;
diff --git a/strbuf.h b/strbuf.h
index 60a35aef16..b7aea8a966 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -190,6 +190,9 @@ extern void strbuf_ltrim(struct strbuf *);
 /* Strip trailing directory separators */
 extern void strbuf_trim_trailing_dir_sep(struct strbuf *);
 
+/* Strip trailing LF or CR/LF */
+extern void strbuf_trim_trailing_newline(struct strbuf *sb);
+
 /**
  * Replace the contents of the strbuf with a reencoded form.  Returns -1
  * on error, 0 on success.
-- 
2.18.0




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux