All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paul Tan <pyokagan@gmail.com>
To: Git List <git@vger.kernel.org>
Cc: Junio C Hamano <gitster@pobox.com>,
	Johannes Schindelin <johannes.schindelin@gmx.de>,
	Duy Nguyen <pclouds@gmail.com>,
	Stefan Beller <sbeller@google.com>,
	sam.halliday@gmail.com, Paul Tan <pyokagan@gmail.com>
Subject: [PATCH/RFC/GSoC 04/17] builtin-rebase: parse rebase arguments into a common rebase_options struct
Date: Sat, 12 Mar 2016 18:46:24 +0800	[thread overview]
Message-ID: <1457779597-6918-5-git-send-email-pyokagan@gmail.com> (raw)
In-Reply-To: <1457779597-6918-1-git-send-email-pyokagan@gmail.com>

A non-root rebase takes 3 arguments:

* branch_name -- the branch or commit that will be rebased. If it is not
  specified, the current branch is used.

* upstream -- The upstream commit to compare against. If it is not
  specified, the configured upstream for the current branch is used.

* onto (or newbase) -- The commit to be used as the starting point to
  re-apply the commits. If it is not specified, `upstream` is used.

Since these parameters are used by all 3 rebase backends, introduce a
common rebase_options struct to hold all these options. Teach
builtin/rebase.c to handle the above arguments and store them in a
rebase_options struct. In later patches we will pass the rebase_options
struct to the appropriate backend to perform the rebase.

Signed-off-by: Paul Tan <pyokagan@gmail.com>
---
 Makefile         |   1 +
 builtin/rebase.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 rebase-common.c  |  28 +++++++++
 rebase-common.h  |  23 +++++++
 4 files changed, 235 insertions(+), 1 deletion(-)
 create mode 100644 rebase-common.c
 create mode 100644 rebase-common.h

diff --git a/Makefile b/Makefile
index ad98714..b29c672 100644
--- a/Makefile
+++ b/Makefile
@@ -779,6 +779,7 @@ LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
+LIB_OBJS += rebase-common.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 04cc1bd..40176ca 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -4,6 +4,112 @@
 #include "cache.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "rebase-common.h"
+#include "remote.h"
+#include "branch.h"
+#include "refs.h"
+
+/**
+ * Used by get_curr_branch_upstream_name() as a for_each_remote() callback to
+ * retrieve the name of the remote if the repository only has one remote.
+ */
+static int get_only_remote(struct remote *remote, void *cb_data)
+{
+	const char **remote_name = cb_data;
+
+	if (*remote_name)
+		return -1;
+
+	*remote_name = remote->name;
+	return 0;
+}
+
+const char *get_curr_branch_upstream_name(void)
+{
+	const char *upstream_name;
+	struct branch *curr_branch;
+
+	curr_branch = branch_get("HEAD");
+	if (!curr_branch) {
+		fprintf_ln(stderr, _("You are not currently on a branch."));
+		fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
+		fprintf_ln(stderr, _("See git-rebase(1) for details."));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, "    git rebase <branch>");
+		fprintf(stderr, "\n");
+		exit(1);
+	}
+
+	upstream_name = branch_get_upstream(curr_branch, NULL);
+	if (!upstream_name) {
+		const char *remote_name = NULL;
+
+		if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
+			remote_name = "<remote>";
+
+		fprintf_ln(stderr, _("There is no tracking information for the current branch."));
+		fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
+		fprintf_ln(stderr, _("See git-rebase(1) for details."));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, "    git rebase <branch>");
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:"));
+		fprintf(stderr, "\n");
+		fprintf_ln(stderr, _("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"),
+		remote_name, curr_branch->name);
+		exit(1);
+	}
+
+	return upstream_name;
+}
+
+/**
+ * Given the --onto <name>, return the onto hash
+ */
+static void get_onto_oid(const char *_onto_name, struct object_id *onto)
+{
+	char *onto_name = xstrdup(_onto_name);
+	struct commit *onto_commit;
+	char *dotdot;
+
+	dotdot = strstr(onto_name, "...");
+	if (dotdot) {
+		const char *left = onto_name;
+		const char *right = dotdot + 3;
+		struct commit *left_commit, *right_commit;
+		struct commit_list *merge_bases;
+
+		*dotdot = 0;
+		if (!*left)
+			left = "HEAD";
+		if (!*right)
+			right = "HEAD";
+
+		/* git merge-base --all $left $right */
+		left_commit = lookup_commit_reference_by_name(left);
+		right_commit = lookup_commit_reference_by_name(right);
+		if (!left_commit || !right_commit)
+			die(_("%s: there is no merge base"), _onto_name);
+
+		merge_bases = get_merge_bases(left_commit, right_commit);
+		if (merge_bases && merge_bases->next)
+			die(_("%s: there are more than one merge bases"), _onto_name);
+		else if (!merge_bases)
+			die(_("%s: there is no merge base"), _onto_name);
+
+		onto_commit = merge_bases->item;
+		free_commit_list(merge_bases);
+	} else {
+		onto_commit = lookup_commit_reference_by_name(onto_name);
+		if (!onto_commit)
+			die(_("invalid upstream %s"), onto_name);
+	}
+
+	free(onto_name);
+	oidcpy(onto, &onto_commit->object.oid);
+}
 
 static int git_rebase_config(const char *k, const char *v, void *cb)
 {
@@ -12,20 +118,96 @@ static int git_rebase_config(const char *k, const char *v, void *cb)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
+	struct rebase_options rebase_opts;
+	const char *onto_name = NULL;
+	const char *branch_name;
+
 	const char * const usage[] = {
-		N_("git rebase [options]"),
+		N_("git rebase [options] [--onto <newbase>] [<upstream>] [<branch>]"),
 		NULL
 	};
 	struct option options[] = {
+		OPT_GROUP(N_("Available options are")),
+		OPT_STRING(0, "onto", &onto_name, NULL,
+			N_("rebase onto given branch instead of upstream")),
 		OPT_END()
 	};
 
 	git_config(git_rebase_config, NULL);
+	rebase_options_init(&rebase_opts);
+	rebase_opts.resolvemsg = _("\nWhen you have resolved this problem, run \"git rebase --continue\".\n"
+			"If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
+			"To check out the original branch and stop rebasing, run \"git rebase --abort\".");
 
 	argc = parse_options(argc, argv, prefix, options, usage, 0);
 
 	if (read_cache_preload(NULL) < 0)
 		die(_("failed to read the index"));
 
+	/*
+	 * Parse command-line arguments:
+	 *    rebase [<options>] [<upstream_name>] [<branch_name>]
+	 */
+
+	/* Parse <upstream_name> into rebase_opts.upstream */
+	{
+		const char *upstream_name;
+		if (argc > 2)
+			usage_with_options(usage, options);
+		if (!argc) {
+			upstream_name = get_curr_branch_upstream_name();
+		} else {
+			upstream_name = argv[0];
+			argv++, argc--;
+			if (!strcmp(upstream_name, "-"))
+				upstream_name = "@{-1}";
+		}
+		if (get_oid_commit(upstream_name, &rebase_opts.upstream))
+			die(_("invalid upstream %s"), upstream_name);
+		if (!onto_name)
+			onto_name = upstream_name;
+	}
+
+	/*
+	 * Parse --onto <onto_name> into rebase_opts.onto and
+	 * rebase_opts.onto_name
+	 */
+	get_onto_oid(onto_name, &rebase_opts.onto);
+	rebase_opts.onto_name = xstrdup(onto_name);
+
+	/*
+	 * Parse <branch_name> into rebase_opts.orig_head and
+	 * rebase_opts.orig_refname
+	 */
+	branch_name = argv[0];
+	if (branch_name) {
+		/* Is branch_name a branch or commit? */
+		char *ref_name = xstrfmt("refs/heads/%s", branch_name);
+		struct object_id orig_head_id;
+
+		if (!read_ref(ref_name, orig_head_id.hash)) {
+			rebase_opts.orig_refname = ref_name;
+			if (get_oid_commit(ref_name, &rebase_opts.orig_head))
+				die("get_sha1_commit failed");
+		} else if (!get_oid_commit(branch_name, &rebase_opts.orig_head)) {
+			rebase_opts.orig_refname = NULL;
+			free(ref_name);
+		} else {
+			die(_("no such branch: %s"), branch_name);
+		}
+	} else {
+		/* Do not need to switch branches, we are already on it */
+		struct branch *curr_branch = branch_get("HEAD");
+
+		if (curr_branch)
+			rebase_opts.orig_refname = xstrdup(curr_branch->refname);
+		else
+			rebase_opts.orig_refname = NULL;
+
+		if (get_oid_commit("HEAD", &rebase_opts.orig_head))
+			die(_("Failed to resolve '%s' as a valid revision."), "HEAD");
+	}
+
+	rebase_options_release(&rebase_opts);
 	return 0;
 }
diff --git a/rebase-common.c b/rebase-common.c
new file mode 100644
index 0000000..5a49ac4
--- /dev/null
+++ b/rebase-common.c
@@ -0,0 +1,28 @@
+#include "cache.h"
+#include "rebase-common.h"
+
+void rebase_options_init(struct rebase_options *opts)
+{
+	oidclr(&opts->onto);
+	opts->onto_name = NULL;
+
+	oidclr(&opts->upstream);
+
+	oidclr(&opts->orig_head);
+	opts->orig_refname = NULL;
+
+	opts->resolvemsg = NULL;
+}
+
+void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->onto_name);
+	free(opts->orig_refname);
+}
+
+void rebase_options_swap(struct rebase_options *dst, struct rebase_options *src)
+{
+	struct rebase_options tmp = *dst;
+	*dst = *src;
+	*src = tmp;
+}
diff --git a/rebase-common.h b/rebase-common.h
new file mode 100644
index 0000000..db5146a
--- /dev/null
+++ b/rebase-common.h
@@ -0,0 +1,23 @@
+#ifndef REBASE_COMMON_H
+#define REBASE_COMMON_H
+
+/* common rebase backend options */
+struct rebase_options {
+	struct object_id onto;
+	char *onto_name;
+
+	struct object_id upstream;
+
+	struct object_id orig_head;
+	char *orig_refname;
+
+	const char *resolvemsg;
+};
+
+void rebase_options_init(struct rebase_options *);
+
+void rebase_options_release(struct rebase_options *);
+
+void rebase_options_swap(struct rebase_options *dst, struct rebase_options *src);
+
+#endif /* REBASE_COMMON_H */
-- 
2.7.0

  parent reply	other threads:[~2016-03-12 10:47 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-12 10:46 [PATCH/RFC/GSoC 00/17] A barebones git-rebase in C Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 01/17] perf: introduce performance tests for git-rebase Paul Tan
2016-03-16  7:58   ` Johannes Schindelin
2016-03-16 11:51     ` Paul Tan
2016-03-16 15:59       ` Johannes Schindelin
2016-03-18 11:01         ` Thomas Gummerer
2016-03-18 16:00           ` Johannes Schindelin
2016-03-20 14:00             ` Thomas Gummerer
2016-03-21  7:54               ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 02/17] sha1_name: implement get_oid() and friends Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 03/17] builtin-rebase: implement skeletal builtin rebase Paul Tan
2016-03-14 18:31   ` Stefan Beller
2016-03-15  8:01     ` Johannes Schindelin
2016-03-12 10:46 ` Paul Tan [this message]
2016-03-14 20:05   ` [PATCH/RFC/GSoC 04/17] builtin-rebase: parse rebase arguments into a common rebase_options struct Stefan Beller
2016-03-15 10:54   ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 05/17] rebase-options: implement rebase_options_load() and rebase_options_save() Paul Tan
2016-03-14 20:30   ` Stefan Beller
2016-03-16  8:04     ` Johannes Schindelin
2016-03-16 12:28       ` Paul Tan
2016-03-16 17:11         ` Johannes Schindelin
2016-03-21 14:55           ` Paul Tan
2016-03-16 12:04     ` Paul Tan
2016-03-16 17:10       ` Stefan Beller
2016-03-12 10:46 ` [PATCH/RFC/GSoC 06/17] rebase-am: introduce am backend for builtin rebase Paul Tan
2016-03-16 13:21   ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 07/17] rebase-common: implement refresh_and_write_cache() Paul Tan
2016-03-14 21:10   ` Junio C Hamano
2016-03-16 12:56     ` Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 08/17] rebase-common: let refresh_and_write_cache() take a flags argument Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 09/17] rebase-common: implement cache_has_unstaged_changes() Paul Tan
2016-03-14 20:54   ` Johannes Schindelin
2016-03-14 21:52     ` Junio C Hamano
2016-03-15 11:51       ` Johannes Schindelin
2016-03-15 11:07     ` Duy Nguyen
2016-03-15 14:15       ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 10/17] rebase-common: implement cache_has_uncommitted_changes() Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 11/17] rebase-merge: introduce merge backend for builtin rebase Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 12/17] rebase-todo: introduce rebase_todo_item Paul Tan
2016-03-14 13:43   ` Christian Couder
2016-03-14 20:33     ` Johannes Schindelin
2016-03-16 12:54     ` Paul Tan
2016-03-16 15:55       ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 13/17] rebase-todo: introduce rebase_todo_list Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 14/17] status: use rebase_todo_list Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 15/17] wrapper: implement append_file() Paul Tan
2016-03-12 10:46 ` [PATCH/RFC/GSoC 16/17] editor: implement git_sequence_editor() and launch_sequence_editor() Paul Tan
2016-03-15  7:00   ` Johannes Schindelin
2016-03-16 13:06     ` Paul Tan
2016-03-16 18:21       ` Johannes Schindelin
2016-03-12 10:46 ` [PATCH/RFC/GSoC 17/17] rebase-interactive: introduce interactive backend for builtin rebase Paul Tan
2016-03-15  7:57   ` Johannes Schindelin
2016-03-15 16:48     ` Paul Tan
2016-03-15 19:45       ` Johannes Schindelin
2016-03-14 12:15 ` [PATCH/RFC/GSoC 00/17] A barebones git-rebase in C Duy Nguyen
2016-03-14 17:32   ` Stefan Beller
2016-03-14 18:43   ` Junio C Hamano
2016-03-16 12:46     ` Paul Tan
2016-03-14 20:44   ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1457779597-6918-5-git-send-email-pyokagan@gmail.com \
    --to=pyokagan@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=johannes.schindelin@gmx.de \
    --cc=pclouds@gmail.com \
    --cc=sam.halliday@gmail.com \
    --cc=sbeller@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.