[GSoC] [PATCH 0/6] builtin rebase rest

7 messages, 1 author, 2018-08-08 · open the first message on its own page

[GSoC] [PATCH 0/6] builtin rebase rest

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:19

This patch pretty much converts all what is remaining from the shell-script
version of `git rebase` to the builtin version. After the conversion of
actions and different options which are important for builtin rebase and have
been organized to make a patch series of their own. There were still some
leftover shell scripts on the original rebase and hence, this patch series
addresses those by converting them and this completes the builtin rebase.

It adds the functionality to the previous patch series,
(https://public-inbox.org/git/20180808152140.14585-1-predatoramigo@gmail.com/).

This is the fifth patch series that brings us more closer to a builtin
"git rebase".

If you like to view the development branch, you can view
(https://github.com/git/git/pull/505), where I have kept my commits up to date
and leveraged Travis(there is sporadic failures in t5520 for macos gcc and
isn't due to my patches) for extra testing other than my system.

The next in line patch series which I'll be sending out today are:

default to builtin rebase: This will turn on the feature-complete builtin
rebase to on.

These patch series are built on top of each other, i.e. they depend on this
order.

Pratik Karki (6):
  builtin rebase: optionally auto-detect the upstream
  builtin rebase: optionally pass custom reflogs to reset_head()
  builtin rebase: fast-forward to onto if it is a proper descendant
  builtin rebase: show progress when connected to a terminal
  builtin rebase: use no-op editor when interactive is "implied"
  builtin rebase: error out on incompatible option/mode combinations

 builtin/rebase.c | 150 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 135 insertions(+), 15 deletions(-)

-- 
2.18.0

[PATCH 1/6] builtin rebase: optionally auto-detect the upstream

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:23

The `git rebase` command, when called without the `<upstream>`
command-line argument, automatically looks for the upstream
branch configured for the current branch.

With this commit, the builtin rebase learned that trick, too.

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 44 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 94abaaa890..c5b2534717 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -622,6 +622,36 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
 	return 0;
 }
 
+static void NORETURN error_on_missing_default_upstream(void)
+{
+	struct branch *current_branch = branch_get(NULL);
+
+	printf(_("%s\n"
+		 "Please specify which branch you want to rebase against.\n"
+		 "See git-rebase(1) for details.\n"
+		 "\n"
+		 "    git rebase '<branch>'\n"
+		 "\n"),
+		current_branch ? _("There is no tracking information for "
+			"the current branch.") :
+			_("You are not currently on a branch."));
+
+	if (current_branch) {
+		const char *remote = current_branch->remote_name;
+
+		if (!remote)
+			remote = _("<remote>");
+
+		printf(_("If you wish to set tracking information for this "
+			 "branch you can do so with:\n"
+			 "\n"
+			 "    git branch --set-upstream-to=%s/<branch> %s\n"
+			 "\n"),
+		       remote, current_branch->name);
+	}
+	exit(1);
+}
+
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = {
@@ -1056,9 +1086,17 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (!options.root) {
-		if (argc < 1)
-			die("TODO: handle @{upstream}");
-		else {
+		if (argc < 1) {
+			struct branch *branch;
+
+			branch = branch_get(NULL);
+			options.upstream_name = branch_get_upstream(branch,
+								    NULL);
+			if (!options.upstream_name)
+				error_on_missing_default_upstream();
+			if (fork_point < 0)
+				fork_point = 1;
+		} else {
 			options.upstream_name = argv[0];
 			argc--;
 			argv++;
-- 
2.18.0

[PATCH 2/6] builtin rebase: optionally pass custom reflogs to reset_head()

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:27

In the next patch, we will make use of that in the code that
fast-forwards to `onto` whenever possible.

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c5b2534717..c51b9d288a 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -431,7 +431,8 @@ static int run_specific_rebase(struct rebase_options *opts)
 #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
 
 static int reset_head(struct object_id *oid, const char *action,
-		      const char *switch_to_branch, int detach_head)
+		      const char *switch_to_branch, int detach_head,
+		      const char *reflog_orig_head, const char *reflog_head)
 {
 	struct object_id head_oid;
 	struct tree_desc desc;
@@ -506,20 +507,26 @@ static int reset_head(struct object_id *oid, const char *action,
 		old_orig = &oid_old_orig;
 	if (!get_oid("HEAD", &oid_orig)) {
 		orig = &oid_orig;
-		strbuf_addstr(&msg, "updating ORIG_HEAD");
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+		if (!reflog_orig_head) {
+			strbuf_addstr(&msg, "updating ORIG_HEAD");
+			reflog_orig_head = msg.buf;
+		}
+		update_ref(reflog_orig_head, "ORIG_HEAD", orig, old_orig, 0,
 			   UPDATE_REFS_MSG_ON_ERR);
 	} else if (old_orig)
 		delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-	strbuf_setlen(&msg, prefix_len);
-	strbuf_addstr(&msg, "updating HEAD");
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
 	if (!switch_to_branch)
-		ret = update_ref(msg.buf, "HEAD", oid, orig, REF_NO_DEREF,
+		ret = update_ref(reflog_head, "HEAD", oid, orig, REF_NO_DEREF,
 				 UPDATE_REFS_MSG_ON_ERR);
 	else {
 		ret = create_symref("HEAD", switch_to_branch, msg.buf);
 		if (!ret)
-			ret = update_ref(msg.buf, "HEAD", oid, NULL, 0,
+			ret = update_ref(reflog_head, "HEAD", oid, NULL, 0,
 					 UPDATE_REFS_MSG_ON_ERR);
 	}
 
@@ -899,7 +906,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(&merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, 0) < 0)
+		if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
 			die(_("could not discard worktree changes"));
 		if (read_basic_state(&options))
 			exit(1);
@@ -915,7 +922,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		if (read_basic_state(&options))
 			exit(1);
 		if (reset_head(&options.orig_head, "reset",
-			       options.head_name, 0) < 0)
+			       options.head_name, 0, NULL, NULL) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
 		ret = finish_rebase(&options);
@@ -1235,7 +1242,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			write_file(autostash, "%s", buf.buf);
 			printf(_("Created autostash: %s\n"), buf.buf);
 			if (reset_head(&head->object.oid, "reset --hard",
-				       NULL, 0) < 0)
+				       NULL, 0, NULL, NULL) < 0)
 				die(_("could not reset --hard"));
 			printf(_("HEAD is now at %s"),
 			       find_unique_abbrev(&head->object.oid,
@@ -1289,7 +1296,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				strbuf_addf(&buf, "rebase: checkout %s",
 					    options.switch_to);
 				if (reset_head(&oid, "checkout",
-					       options.head_name, 0) < 0) {
+					       options.head_name, 0,
+					       NULL, NULL) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
 						      options.switch_to);
@@ -1354,7 +1362,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			 "it...\n"));
 
 	strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
-	if (reset_head(&options.onto->object.oid, "checkout", NULL, 1))
+	if (reset_head(&options.onto->object.oid, "checkout", NULL, 1,
+	    NULL, msg.buf))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
-- 
2.18.0

[PATCH 3/6] builtin rebase: fast-forward to onto if it is a proper descendant

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:30

When trying to rebase onto a direct descendant of HEAD, we can
take a shortcut and fast-forward instead. This commit makes it so.

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index c51b9d288a..1bb64e7cd7 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1367,6 +1367,24 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
+	/*
+	 * If the onto is a proper descendant of the tip of the branch, then
+	 * we just fast-forwarded.
+	 */
+	strbuf_reset(&msg);
+	if (!oidcmp(&merge_base, &options.orig_head)) {
+		printf(_("Fast-forwarded %s to %s. \n"),
+			branch_name, options.onto_name);
+		strbuf_addf(&msg, "rebase finished: %s onto %s",
+			options.head_name ? options.head_name : "detached HEAD",
+			oid_to_hex(&options.onto->object.oid));
+		reset_head(NULL, "Fast-forwarded", options.head_name, 0,
+			   "HEAD", msg.buf);
+		strbuf_release(&msg);
+		ret = !!finish_rebase(&options);
+		goto cleanup;
+	}
+
 	strbuf_addf(&revisions, "%s..%s",
 		    options.root ? oid_to_hex(&options.onto->object.oid) :
 		    (options.restrict_revision ?
-- 
2.18.0

[PATCH 4/6] builtin rebase: show progress when connected to a terminal

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:34

In this commit, we pass `--progress` to the `format-patch` command if
stderr is connected to an interactive terminal, unless we're in quiet
mode.

This `--progress` option will be used in `format-patch` to show progress
reports on stderr as patches are generated.

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 1bb64e7cd7..04974dff6e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -98,6 +98,7 @@ struct rebase_options {
 	int allow_empty_message;
 	int rebase_merges, rebase_cousins;
 	char *strategy, *strategy_opts;
+	struct strbuf git_format_patch_opt;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -379,6 +380,8 @@ static int run_specific_rebase(struct rebase_options *opts)
 	add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
 	add_var(&script_snippet, "squash_onto",
 		opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
+	add_var(&script_snippet, "git_format_patch_opt",
+		opts->git_format_patch_opt.buf);
 
 	switch (opts->type) {
 	case REBASE_AM:
@@ -667,6 +670,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		.git_am_opt = STRBUF_INIT,
 		.allow_rerere_autoupdate  = -1,
 		.allow_empty_message = 1,
+		.git_format_patch_opt = STRBUF_INIT,
 	};
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
@@ -1068,6 +1072,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	if (options.root && !options.onto_name)
 		imply_interactive(&options, "--root without --onto");
 
+	if (isatty(2) && options.flags & REBASE_NO_QUIET)
+		strbuf_addstr(&options.git_format_patch_opt, " --progress");
+
 	switch (options.type) {
 	case REBASE_MERGE:
 	case REBASE_INTERACTIVE:
-- 
2.18.0

[PATCH 5/6] builtin rebase: use no-op editor when interactive is "implied"

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:37

Some options are only handled by the git-rebase--interactive backend,
even if run non-interactively. For this awkward situation (run
non-interactively, but use the interactive backend), the shell scripted
version of `git rebase` introduced the concept of an "implied
interactive rebase". All it does is to replace the editor by a dummy one
(`:` is the Unix command that takes arbitrary command-line parameters,
ignores them and simply exits with success).

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 04974dff6e..fb8ab5a177 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -383,6 +383,13 @@ static int run_specific_rebase(struct rebase_options *opts)
 	add_var(&script_snippet, "git_format_patch_opt",
 		opts->git_format_patch_opt.buf);
 
+	if (is_interactive(opts) &&
+	    !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
+		strbuf_addstr(&script_snippet,
+			      "GIT_EDITOR=:; export GIT_EDITOR; ");
+		opts->autosquash = 0;
+	}
+
 	switch (opts->type) {
 	case REBASE_AM:
 		backend = "git-rebase--am";
-- 
2.18.0

[PATCH 6/6] builtin rebase: error out on incompatible option/mode combinations

From: Pratik Karki <hidden>
Date: 2018-08-08 15:38:41

While working on the GSoC project to convert the rebase command to a
builtin, the rebase command learned to error out on certain command-line
option combinations that cannot work, such as --whitespace=fix with
--interactive.

This commit converts that code.

Signed-off-by: Pratik Karki <redacted>
---
 builtin/rebase.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index fb8ab5a177..4e69458161 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1098,6 +1098,28 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		break;
 	}
 
+	if (options.git_am_opt.len) {
+		const char *p;
+
+		/* all am options except -q are compatible only with --am */
+		strbuf_reset(&buf);
+		strbuf_addbuf(&buf, &options.git_am_opt);
+		strbuf_addch(&buf, ' ');
+		while ((p = strstr(buf.buf, " -q ")))
+			strbuf_splice(&buf, p - buf.buf, 4, " ", 1);
+		strbuf_trim(&buf);
+
+		if (is_interactive(&options) && buf.len)
+			die(_("error: cannot combine interactive options "
+			      "(--interactive, --exec, --rebase-merges, "
+			      "--preserve-merges, --keep-empty, --root + "
+			      "--onto) with am options (%s)"), buf.buf);
+		if (options.type == REBASE_MERGE && buf.len)
+			die(_("error: cannot combine merge options (--merge, "
+			      "--strategy, --strategy-option) with am options "
+			      "(%s)"), buf.buf);
+	}
+
 	if (options.signoff) {
 		if (options.type == REBASE_PRESERVE_MERGES)
 			die("cannot combine '--signoff' with "
@@ -1106,6 +1128,25 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		options.flags |= REBASE_FORCE;
 	}
 
+	if (options.type == REBASE_PRESERVE_MERGES)
+		/*
+		 * Note: incompatibility with --signoff handled in signoff block above
+		 * Note: incompatibility with --interactive is just a strong warning;
+		 *       git-rebase.txt caveats with "unless you know what you are doing"
+		 */
+		if (options.rebase_merges)
+			die(_("error: cannot combine '--preserve_merges' with "
+			      "'--rebase-merges'"));
+
+	if (options.rebase_merges) {
+		if (strategy_options.nr)
+			die(_("error: cannot combine '--rebase_merges' with "
+			      "'--strategy-option'"));
+		if (options.strategy)
+			die(_("error: cannot combine '--rebase_merges' with "
+			      "'--strategy'"));
+	}
+
 	if (!options.root) {
 		if (argc < 1) {
 			struct branch *branch;
-- 
2.18.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help