All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org, Junio C Hamano <gitster@pobox.com>
Cc: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v0 3/3] Build in git-rebase.sh
Date: Thu, 21 May 2009 19:47:09 +1000	[thread overview]
Message-ID: <1242899229-27603-3-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1242899229-27603-2-git-send-email-pclouds@gmail.com>


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Regression: "-M" is gone. Don't really want to mess up struct option for "-M"

 Makefile                       |    2 +-
 builtin-rebase.c               |  992 ++++++++++++++++++++++++++++++++++++++++
 builtin.h                      |    1 +
 contrib/examples/git-rebase.sh |  530 +++++++++++++++++++++
 git-rebase.sh                  |  530 ---------------------
 git.c                          |    1 +
 6 files changed, 1525 insertions(+), 531 deletions(-)
 create mode 100644 builtin-rebase.c
 create mode 100755 contrib/examples/git-rebase.sh
 delete mode 100755 git-rebase.sh

diff --git a/Makefile b/Makefile
index fdb39fa..fec6c40 100644
--- a/Makefile
+++ b/Makefile
@@ -302,7 +302,6 @@ SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase--interactive.sh
-SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-sh-setup.sh
@@ -597,6 +596,7 @@ BUILTIN_OBJS += builtin-prune-packed.o
 BUILTIN_OBJS += builtin-prune.o
 BUILTIN_OBJS += builtin-push.o
 BUILTIN_OBJS += builtin-read-tree.o
+BUILTIN_OBJS += builtin-rebase.o
 BUILTIN_OBJS += builtin-receive-pack.o
 BUILTIN_OBJS += builtin-reflog.o
 BUILTIN_OBJS += builtin-remote.o
diff --git a/builtin-rebase.c b/builtin-rebase.c
new file mode 100644
index 0000000..51c3121
--- /dev/null
+++ b/builtin-rebase.c
@@ -0,0 +1,992 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
+#include "commit.h"
+#include "run-command.h"
+#include "dir.h"
+#include "refs.h"
+#include "quote.h"
+#include "log-tree.h"
+
+static char const * const rebase_usage[] = {
+	"git rebase [-i | --interactive] [options] [--onto <newbase>]\n"
+	"	<upstream> [<branch>]",
+	"git rebase [-i | --interactive] [options] --onto <newbase>\n"
+	"	--root [<branch>]",
+	"git rebase --continue | --skip | --abort",
+	NULL
+};
+
+static const char *resolve_msg =
+	"When you have resolved this problem, run \"git rebase --continue\".\n"
+	"If you would prefer to skip this patch, instead run \"git rebase --skip\".\n"
+	"To restore the original branch and stop rebasing run \"git rebase --abort\".\n";
+
+#define REBASE_ABORT		0x0001
+#define REBASE_CONTINUE		0x0002
+#define REBASE_FORCE		0x0004
+#define REBASE_IGNORE_DATE	0x0008
+#define REBASE_INTERACTIVE	0x0010
+#define REBASE_MERGE		0x0020
+#define REBASE_STAT		0x0040
+#define REBASE_NO_VERIFY	0x0080
+#define REBASE_PRESERVE_MERGES	0x0100
+#define REBASE_ROOT		0x0200
+#define REBASE_SKIP		0x0400
+#define REBASE_VERBOSE		0x0800
+
+struct rebase_opt {
+	int flags;
+	int context;
+	const char *onto;
+	const char *upstream;
+	const char *branch;
+	const char *strategy;
+	const char *whitespace;
+};
+
+static int rebase_config(const char *var, const char *value, void *data)
+{
+	if (!strcmp(var, "rebase.stat")) {
+		if (git_config_bool(var, value))
+			((struct rebase_opt*)data)->flags |= REBASE_STAT;
+		return 0;
+	}
+	return 0;
+}
+
+/* utility functions */
+
+static char *load_string(char *git_pathname)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *path = git_path(git_pathname);
+
+	if (strbuf_read_file(&sb, path,  41) == -1)
+		die("Could not read %s", path);
+
+	while (sb.len && sb.buf[sb.len-1] == '\n') {
+		sb.len--;
+		sb.buf[sb.len] = '\0';
+	}
+
+	return sb.buf;
+}
+
+static void save_string(char *git_pathname, const char *str)
+{
+	const char *path = git_path(git_pathname);
+	int fd = open(path, O_CREAT | O_WRONLY, 0600);
+	if (fd < 0)
+		die("Could not open %s for writing", path);
+	write_in_full(fd, str, strlen(str));
+	write_in_full(fd, "\n", 1);
+	close(fd);
+}
+
+static void load_sha1(char *git_pathname, unsigned char *sha1)
+{
+	char *buf = load_string(git_pathname);
+	if (get_sha1_hex(buf, sha1) == -1)
+		die("Invalid SHA-1 in %s", git_path(git_pathname));
+	free(buf);
+}
+
+static void save_sha1(char *git_pathname, const unsigned char *sha1)
+{
+	save_string(git_pathname, sha1_to_hex(sha1));
+}
+
+static int load_int(char *git_pathname)
+{
+	char *buf = load_string(git_pathname);
+	int ret;
+
+	if (sscanf(buf, "%d", &ret) != 1)
+		die("Failed to read number from %s", git_path(git_pathname));
+	free(buf);
+	return ret;
+}
+
+static void save_int(char *git_pathname, int number)
+{
+	const char *path = git_path(git_pathname);
+	FILE *fp = fopen(path, "w");
+	if (!fp)
+		die("Could not open %s for writing", path);
+	fprintf(fp, "%d\n", number);
+	fclose(fp);
+}
+
+static void get_commit(const char *name, unsigned char *sha1, struct commit **cmt)
+{
+	if (get_sha1(name, sha1))
+		die("Failed to resolve SHA-1 from %s", name);
+	*cmt = lookup_commit(sha1);
+	if (!*cmt)
+		die("%s is not a commitish", name);
+}
+
+static int continue_merge(int msgnum, unsigned char *prev_head)
+{
+	const char *argv_diff[] = { "--quiet", "--ignore-submodules", "HEAD", "--", NULL };
+	struct strbuf result_line = STRBUF_INIT;
+	unsigned char cmt[20];
+	struct rev_info rev;
+	struct stat st;
+	int i, result;
+
+	if (stat(git_path("rebase-merge"), &st) || !S_ISDIR(st.st_mode))
+		die("%s directory does not exist", git_path("rebase-merge"));
+
+	/* git-am or git-merge-* may have been run, can no longer trust the old cache */
+	discard_cache();
+	if (read_cache() < 0)
+		die("Could not read the index");
+
+	for (i = 0;i < active_nr;i++)
+		if (ce_stage(active_cache[i]))
+			die("You still have unmerged paths in your index\n"
+			    "did you forget to use git add?\n"
+			    "%s",
+			    resolve_msg);
+
+	init_revisions(&rev, NULL);
+	rev.abbrev = 0;
+	setup_revisions(4, argv_diff, &rev, NULL);
+	result = run_diff_index(&rev, 0);
+	load_sha1("rebase-merge/current", cmt);
+
+	if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+		struct child_process cp;
+		const char *argv[5] = {"commit", "--no-verify", "-C", sha1_to_hex(cmt), NULL };
+
+		memset(&cp, 0, sizeof(cp));
+		cp.git_cmd = 1;
+		cp.argv = argv;
+		if (run_command(&cp))
+			die("Commit failed, please do not call \"git commit\"\n"
+			    "directly, but instead do one of the following:\n"
+			    "%s", resolve_msg);
+		strbuf_addf(&result_line, "Committed: %04d ", msgnum);
+	}
+	else
+		strbuf_addf(&result_line, "Already applied: %04d ", msgnum);
+
+	parse_commit(lookup_commit(cmt));
+	format_commit_message(lookup_commit(cmt), "%f", &result_line, 0);
+	puts(result_line.buf);
+	strbuf_release(&result_line);
+
+	if (get_sha1("HEAD^0", prev_head))
+		die("Could not resolve HEAD commit");
+	save_sha1("rebase-merge/prev_head", prev_head);
+	msgnum++;
+	save_int("rebase-merge/msgnum", msgnum);
+	return msgnum;
+}
+
+static void call_merge(const char *strategy, int msgnum)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf sb2 = STRBUF_INIT;
+	struct strbuf sb_env1 = STRBUF_INIT;
+	struct strbuf sb_env2 = STRBUF_INIT;
+	unsigned char cmt[20];
+	unsigned char head[20];
+	struct child_process cp;
+	const char *full_ref;
+	int end, result, type;
+	const char *argv[5];
+	char *onto_name, *head_hex, *cmt_hex;
+
+	strbuf_addf(&sb, "rebase-merge/cmt.%d", msgnum);
+	load_sha1(sb.buf, cmt);
+	save_sha1("rebase-merge/current", cmt);
+
+	full_ref = resolve_ref("HEAD", head, 1, &type);
+	if (!lookup_commit(head))
+		die("HEAD does not point to a commit");
+	msgnum = load_int("rebase-merge/msgnum");
+	end = load_int("rebase-merge/end");
+
+	strbuf_setlen(&sb, 0);
+	strbuf_addf(&sb, "%s~%d",
+		    !strncmp(full_ref, "refs/heads/", 11) ? full_ref + 11 : full_ref,
+		    end - msgnum);
+	strbuf_addf(&sb_env1, "GITHEAD_%s", sha1_to_hex(cmt));
+	setenv(sb_env1.buf, sb.buf, 1);
+
+	onto_name = load_string("rebase-merge/onto_name");
+	strbuf_setlen(&sb, 0);
+	strbuf_addf(&sb_env2, "GITHEAD_%s", sha1_to_hex(head));
+	setenv(sb_env2.buf, onto_name, 1);
+	free(onto_name);
+
+	strbuf_setlen(&sb, 0);
+	strbuf_addf(&sb, "merge-%s", strategy);
+	strbuf_addf(&sb2, "%s^", sha1_to_hex(cmt));
+	head_hex = xstrdup(sha1_to_hex(head));
+	cmt_hex = xstrdup(sha1_to_hex(cmt));
+	argv[0] = sb.buf;
+	argv[1] = sb2.buf;
+	argv[2] = "--";
+	argv[3] = head_hex;
+	argv[4] = cmt_hex;
+	argv[5] = NULL;
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv;
+	result = run_command(&cp);
+	free(head_hex);
+	free(cmt_hex);
+	strbuf_release(&sb);
+	strbuf_release(&sb2);
+	switch (result) {
+	case 0:
+		unsetenv(sb_env1.buf);
+		strbuf_release(&sb_env1);
+		unsetenv(sb_env2.buf);
+		strbuf_release(&sb_env2);
+		break;
+	case -1:
+		argv[0] = "rerere";
+		argv[1] = NULL;
+		run_command(&cp);
+		die(resolve_msg);
+
+	case -2:
+		fprintf(stderr, "Strategy: %d %s failed, try another", result, strategy);
+		die(resolve_msg);
+
+	default:
+		cmt_hex = xstrdup(sha1_to_hex(cmt));
+		die("Unknown exit code (%d) from command: git-merge-%s %s^ -- HEAD %s",
+		    result, strategy, cmt_hex, cmt_hex);
+	}
+}
+
+static void move_to_original_branch(const unsigned char *onto,
+				    const unsigned char *orig_head,
+				    const char *head_name)
+{
+	struct strbuf sb = STRBUF_INIT;
+	unsigned char head[20];
+
+	if (strncmp(head_name, "refs/", 5))
+		return;
+
+	strbuf_addf(&sb, "rebase finished: %s onto %s", head_name, sha1_to_hex(onto));
+	if (get_sha1("HEAD", head))
+		die("Could not resolve HEAD");
+	update_ref(sb.buf, head_name, head, orig_head, 0, DIE_ON_ERR);
+	create_symref("HEAD", head_name, NULL);
+	strbuf_release(&sb);
+}
+
+static int finish_rebase_merge(const unsigned char *onto,
+			       const unsigned char *orig_head,
+			       const char *head_name)
+{
+	struct strbuf sb = STRBUF_INIT;
+	move_to_original_branch(onto, orig_head, head_name);
+	strbuf_addstr(&sb, git_path("rebase-merge"));
+	remove_dir_recursively(&sb, 0);
+	strbuf_release(&sb);
+	puts("All done.");
+	return 0;
+}
+
+static void check_rebase_in_progress()
+{
+	struct stat st;
+	if ((!stat(git_path("rebase-merge"), &st) && S_ISDIR(st.st_mode)) ||
+	    (!stat(git_path("rebase-apply"), &st) && S_ISDIR(st.st_mode)))
+		return;
+	die("No rebase in progress?");
+}
+
+static int cmd_continue(const struct rebase_opt *opt)
+{
+	struct stat st;
+	struct rev_info rev;
+	struct child_process cp;
+	const char *argv_am[] = { "am", "--resolved", "--3way", "--resolvemsg", resolve_msg, NULL };
+	char *head_name;
+	unsigned char onto[20], orig_head[20];
+	int ret;
+
+	check_rebase_in_progress();
+
+	if (read_cache() < 0)
+		die("Could not read the index");
+
+	init_revisions(&rev, NULL);
+	rev.abbrev = 0;
+	DIFF_OPT_SET(&rev.diffopt, QUIET);
+	DIFF_OPT_SET(&rev.diffopt, IGNORE_SUBMODULES);
+	run_diff_files(&rev, DIFF_SILENT_ON_REMOVED);
+	if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+		printf("You must edit all merge conflicts and then\n"
+		       "mark them as resolved using git add\n");
+		return 1;
+	}
+
+	if (!stat(git_path("rebase-merge"), &st) && S_ISDIR(st.st_mode)) {
+		unsigned char prev_head[20], onto[20], orig_head[20];
+		char *head_name;
+		int msgnum, end;
+
+		load_sha1("rebase-merge/prev_head", prev_head);
+		load_sha1("rebase-merge/onto", onto);
+		end = load_int("rebase-merge/end");
+		msgnum = load_int("rebase-merge/msgnum");
+
+		msgnum = continue_merge(msgnum, prev_head);
+		while (msgnum <= end) {
+			call_merge(opt->strategy, msgnum);
+			msgnum = continue_merge(msgnum, prev_head);
+		}
+
+		load_sha1("rebase-merge/orig-head", orig_head);
+		head_name = load_string("rebase-merge/head-name");
+
+		ret = finish_rebase_merge(onto, orig_head, head_name);
+		free(head_name);
+		return ret;
+	}
+
+	head_name = load_string("rebase-apply/head-name");
+	load_sha1("rebase-apply/onto", onto);
+	load_sha1("rebase-apply/orig-head", orig_head);
+
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv_am;
+	ret = run_command(&cp);
+	if (IS_RUN_COMMAND_ERR(ret))
+		die("Could not run git am");
+	if (!ret)
+		move_to_original_branch(onto, orig_head, head_name);
+	free(head_name);
+	return ret < 0 ? -ret : 0;
+}
+
+static int cmd_abort()
+{
+	const char *argv_rerere[] = { "rerere", "clear", NULL };
+	const char *argv_reset[] = { "reset", "--hard", NULL /* placeholder */, NULL };
+	struct child_process cp;
+	struct stat st;
+	unsigned char onto[20], orig_head[20];
+	char *head_name;
+	struct strbuf sb = STRBUF_INIT;
+
+	check_rebase_in_progress();
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv_rerere;
+	run_command(&cp);
+
+	if (!stat(git_path("rebase-merge"), &st) && S_ISDIR(st.st_mode)) {
+		strbuf_addstr(&sb, git_path("rebase-merge"));
+		head_name = load_string("rebase-merge/head-name");
+		load_sha1("rebase-merge/onto", onto);
+		load_sha1("rebase-merge/orig-head", orig_head);
+		move_to_original_branch(onto, orig_head, head_name);
+	}
+	else {
+		strbuf_addstr(&sb, git_path("rebase-apply"));
+		head_name = load_string("rebase-apply/head-name");
+		load_sha1("rebase-apply/onto", onto);
+		load_sha1("rebase-apply/orig-head", orig_head);
+		move_to_original_branch(onto, orig_head, head_name);
+	}
+	free(head_name);
+
+	cp.argv = argv_reset;
+	argv_reset[2] = sha1_to_hex(orig_head);
+	if (run_command(&cp))
+		die("Failed running git reset");
+	remove_dir_recursively(&sb, 0);
+	strbuf_release(&sb);
+	return 0;
+}
+
+static int cmd_skip(const struct rebase_opt *opt)
+{
+	const char *argv_reset[] = { "reset", "--hard", "HEAD", NULL };
+	const char *argv_am[] = { "am", "-3", "--skip", "--resolvemsg", resolve_msg, NULL };
+	struct child_process cp;
+	char *head_name;
+	unsigned char orig_head[20], onto[20];
+	struct stat st;
+	int ret;
+
+	check_rebase_in_progress();
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv_reset;
+	ret = run_command(&cp);
+	if (IS_RUN_COMMAND_ERR(ret))
+		die("Failed to run git reset");
+	if (ret)
+		return -ret;
+	if (!stat(git_path("rebase-merge"), &st) && S_ISDIR(st.st_mode)) {
+		const char *argv_rerere[] = { "rerere", "clear", NULL };
+		unsigned char prev_head[20], onto[20], orig_head[20];
+		int msgnum, end;
+		char *head_name;
+
+		cp.argv = argv_rerere;
+		run_command(&cp);
+		load_sha1("rebase-merge/prev_head", prev_head);
+		end = load_int("rebase-merge/end");
+		msgnum = load_int("rebase-merge/msgnum") + 1;
+		load_sha1("rebase-merge/onto", onto);
+		while (msgnum <= end) {
+			call_merge(opt->strategy, msgnum);
+			msgnum = continue_merge(msgnum, prev_head);
+		}
+
+		load_sha1("rebase-merge/orig-head", orig_head);
+		head_name = load_string("rebase-merge/head-name");
+
+		ret = finish_rebase_merge(onto, orig_head, head_name);
+		free(head_name);
+		return ret;
+	}
+
+	head_name = load_string("rebase-apply/head-name");
+	load_sha1("rebase-apply/onto", onto);
+	load_sha1("rebase-apply/orig-head", orig_head);
+
+	cp.argv = argv_am;
+	ret = run_command(&cp);
+	if (IS_RUN_COMMAND_ERR(ret))
+		die("Failed to run git am");
+	if (ret)
+		return -ret;
+
+	move_to_original_branch(onto, orig_head, head_name);
+	free(head_name);
+	return ret < 0 ? -ret : 0;
+}
+
+static void run_interactive(int argc, const char **argv)
+{
+	int ret, i;
+	struct child_process cp;
+	for (i = 1;i < argc;i++) {
+		if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interactive"))
+			break;
+		if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--preserve-merges")) {
+			setenv("GIT_EDITOR", ":", 1);
+			break;
+		}
+	}
+
+	if (i == argc) {
+		struct stat st;
+		if (stat(git_path("rebase-merge/interactive"), &st) ||
+		    !S_ISREG(st.st_mode))
+			return;
+	}
+
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+	cp.argv = argv;
+	argv[0] = "rebase--interactive";
+	ret = run_command(&cp);
+	if (IS_RUN_COMMAND_ERR(ret))
+		die("Failed to run git rebase--interactive");
+	exit(ret);
+}
+
+/* from diff.c */
+static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
+{
+	/* Strip the prefix but do not molest /dev/null and absolute paths */
+	if (*namep && **namep != '/')
+		*namep += prefix_length;
+	if (*otherp && **otherp != '/')
+		*otherp += prefix_length;
+}
+
+static void die_dirty_cache(struct diff_queue_struct *q,
+			    struct diff_options *opt,
+			    void *data)
+{
+	int i;
+
+	if (!q->nr)
+		return;
+
+	fprintf(stderr,"cannot rebase: your index contains uncommitted changes\n");
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+
+		fprintf(stderr, "%c ",p->status);
+
+		if (p->status == DIFF_STATUS_COPIED ||
+		    p->status == DIFF_STATUS_RENAMED) {
+			const char *name_a, *name_b;
+			name_a = p->one->path;
+			name_b = p->two->path;
+			strip_prefix(opt->prefix_length, &name_a, &name_b);
+			write_name_quoted(name_a, stderr, '\t');
+			write_name_quoted(name_b, stderr, '\n');
+		} else {
+			const char *name_a, *name_b;
+			name_a = p->one->mode ? p->one->path : p->two->path;
+			name_b = NULL;
+			strip_prefix(opt->prefix_length, &name_a, &name_b);
+			write_name_quoted(name_a, stderr, '\n');
+		}
+	}
+	exit(1);
+}
+
+static void abort_continue_skip(const struct rebase_opt *opt)
+{
+	int count = 0;
+	if (opt->flags & REBASE_CONTINUE) count++;
+	if (opt->flags & REBASE_SKIP) count++;
+	if (opt->flags & REBASE_ABORT) count++;
+	if (count > 1)
+		die("--continue, --skip and --abort are mutually exclusive");
+	if (!count)
+		return;
+
+	/* REBASE_STAT may be set by git config */
+	if (opt->flags & ~(REBASE_CONTINUE | REBASE_SKIP | REBASE_ABORT | REBASE_STAT))
+		die("--continue, --skip and --abort do not take any other argument");
+
+	if (opt->flags & REBASE_CONTINUE) exit(cmd_continue(opt));
+	if (opt->flags & REBASE_ABORT) exit(cmd_abort());
+	if (opt->flags & REBASE_SKIP) exit(cmd_skip(opt));
+
+	die("Unexpected operation");
+}
+
+static int cmd_apply_rebase(const struct rebase_opt *opt,
+			    struct object *onto,
+			    struct object *upstream,
+			    struct object *orig_head,
+			    const char *onto_name,
+			    const char *head_name)
+{
+	struct strbuf sb = STRBUF_INIT;
+	const char *argv_fp[] = { "format-patch", "-k", "--stdout", "--full-index",
+				  "--ignore-if-in-upstream", NULL /* placeholder */, NULL };
+	const char *argv_am[] = { "am", "--rebasing", "--resolvemsg", resolve_msg,
+				  NULL, NULL, /* --whitespace */
+				  NULL,	      /* -Cn */
+				  NULL,	      /* --ignore-date */
+				  NULL };
+	const char **argp;
+	struct child_process cp_fp, cp_am;
+	int fd[2], ret;
+	char context_buf[10];
+
+	strbuf_addstr(&sb, sha1_to_hex(upstream->sha1));
+	strbuf_addstr(&sb, "..");
+	strbuf_addstr(&sb, sha1_to_hex(orig_head->sha1));
+
+	memset(&cp_fp, 0, sizeof(cp_fp));
+	memset(&cp_am, 0, sizeof(cp_am));
+
+	if (pipe(fd))
+		die("Failed to make pipe");
+
+	cp_fp.git_cmd = 1;
+	cp_fp.argv = argv_fp;
+	cp_fp.out = fd[1];
+	argv_fp[5] = sb.buf;
+
+	argp = argv_am+4;
+	if (opt->whitespace) {
+		*argp++ = "--whitespace";
+		*argp++ = opt->whitespace;
+	}
+	if (opt->flags & REBASE_IGNORE_DATE)
+		*argp++ = "--ignore-date";
+	if (opt->context >= 0) {
+		snprintf(context_buf, 10, "-C%d", opt->context);
+		*argp++ = context_buf;
+	}
+	cp_am.git_cmd = 1;
+	cp_am.argv = argv_am;
+	cp_am.in = fd[0];
+
+	if (start_command(&cp_fp))
+		die("Could not run git format-patch");
+	if (start_command(&cp_am))
+		die("Could not run git am");
+
+	ret = finish_command(&cp_am);
+	strbuf_release(&sb);
+	close(fd[0]);
+	close(fd[1]);
+
+	if (IS_RUN_COMMAND_ERR(ret))
+		die("Failed to run 'git format-patch|git am'");
+	if (!ret)
+		move_to_original_branch(onto->sha1, orig_head->sha1, head_name);
+	else {
+		struct stat st;
+
+		if (!stat(git_path("rebase-apply"), &st) && S_ISDIR(st.st_mode)) {
+			save_string("rebase-apply/head-name", head_name);
+			save_sha1("rebase-apply/onto", onto->sha1);
+			save_sha1("rebase-apply/orig-head", orig_head->sha1);
+		}
+	}
+	return ret;
+}
+
+static int cmd_merge_rebase(const struct rebase_opt *opt,
+			    struct object *onto,
+			    struct object *upstream,
+			    struct object *orig_head,
+			    const char *onto_name,
+			    const char *head_name)
+{
+	int msgnum, end = 0;
+	struct rev_info revs;
+	struct commit *cmt;
+	struct strbuf sb = STRBUF_INIT;
+	unsigned char prev_head[20];
+
+	/* git rev-list --reverse --no-merges $onto..$orig_head */
+	init_revisions(&revs, NULL);
+	revs.abbrev = 0;
+	revs.reverse = 1;
+	revs.no_merges = 1;
+	upstream->flags |= UNINTERESTING;
+	add_pending_object(&revs, upstream, NULL);
+	orig_head->flags &= ~UNINTERESTING;
+	add_pending_object(&revs, orig_head, NULL);
+
+	if (prepare_revision_walk(&revs))
+		die("revision walk setup failed");
+
+	memcpy(prev_head, orig_head->sha1, 20);
+
+	mkdir(git_path("rebase-merge"), 0700);
+	save_sha1  ("rebase-merge/onto",      onto->sha1);
+	save_string("rebase-merge/onto_name", onto_name);
+	save_sha1  ("rebase-merge/prev_head", prev_head);
+	save_sha1  ("rebase-merge/orig-head", orig_head->sha1);
+	save_string("rebase-merge/head-name", head_name);
+
+	strbuf_addstr(&sb, "rebase-merge/cmt.");
+	while ((cmt = get_revision(&revs)) != NULL) {
+		end++;
+		strbuf_addf(&sb, "%d", end);
+		save_sha1(sb.buf, cmt->object.sha1);
+		strbuf_setlen(&sb, 17);
+	}
+	strbuf_release(&sb);
+
+	msgnum = 1;
+
+	save_int("rebase-merge/msgnum", msgnum);
+	save_int("rebase-merge/end", end);
+
+	while (msgnum <= end) {
+		call_merge(opt->strategy, msgnum);
+		msgnum = continue_merge(msgnum, prev_head);
+	}
+
+	return finish_rebase_merge(onto->sha1, orig_head->sha1, head_name);
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+	struct rebase_opt opt;
+	struct option options[] = {
+		OPT_STRING(0,"onto",           &opt.onto, "newbase", "starting point at which to create new commits"),
+		OPT_STRING('s',"strategy",     &opt.strategy, "strategy", "merge strategy to be used"),
+		OPT_STRING(0,"whitespace",     &opt.whitespace, "action", "to be passed to \"git apply\""),
+		OPT_INTEGER('C', NULL,         &opt.context, "surrounding context lines to be matched"),
+		OPT_BIT   ('i', "interactive", &opt.flags, "interactive mode", REBASE_INTERACTIVE),
+		OPT_BIT   ('v', "verbose",     &opt.flags, "be verbose", REBASE_VERBOSE | REBASE_STAT),
+		OPT_BIT   ('f', "force-rebase",&opt.flags, "force rebase", REBASE_FORCE),
+		OPT_BIT   ('p', "preserve-merges",&opt.flags, "preserve merges during rebase", REBASE_PRESERVE_MERGES),
+		OPT_BIT   ('m', "merge",       &opt.flags, "use merge strategies to rebase", REBASE_MERGE),
+		OPT_BIT   (0,   "no-verify",   &opt.flags, "bypass pre-rebase hook", REBASE_NO_VERIFY),
+		OPT_NEGBIT('n', "no-stat",     &opt.flags, "do not show diffstat as part of rebase process", REBASE_STAT),
+		OPT_BIT   (0,   "committer-date-is-author-date", &opt.flags, "to be passed to \"git am\"", REBASE_IGNORE_DATE),
+		OPT_BIT   (0,   "ignore-date", &opt.flags, "to be passed to \"git am\"", REBASE_IGNORE_DATE),
+		OPT_BIT   (0,   "stat",        &opt.flags, "show diffstat as part of rebase process", REBASE_STAT),
+		OPT_BIT   (0,   "root",        &opt.flags, "rebase all reachable commits from <branch>", REBASE_ROOT),
+		OPT_BIT   (0,   "continue",    &opt.flags, "restart rebase after having resolved a merge conflict", REBASE_CONTINUE),
+		OPT_BIT   (0,   "skip",        &opt.flags, "restart rebase by skipping the current patch", REBASE_SKIP),
+		OPT_BIT   (0,   "abort",       &opt.flags, "restore original branch and abort rebase", REBASE_ABORT),
+		OPT_END()
+	};
+
+	unsigned char upstream_sha1[20], branch_sha1[20], onto_sha1[20], head_sha1[20];
+	struct commit *upstream_cmt, *branch_cmt, *onto_cmt, *head_cmt;
+	const char *argv_checkout_onto[] = { "checkout", "-q", NULL /* placeholder */, NULL };
+	int (*rebase_func)(const struct rebase_opt *, struct object *,
+			   struct object *, struct object *,
+			   const char *, const char *);
+	const char *switch_to = NULL;
+	struct commit_list *mb_cmts;
+	struct child_process cp;
+	struct rev_info revs;
+	struct stat st;
+	char *head_name;
+	char *buf;
+	int do_merge;
+
+	if (!stat(git_path("rebase-apply/applying"), &st) && S_ISREG(st.st_mode))
+		die("It looks like git-am is in progress. Cannot rebase.");
+
+	setenv("GIT_REFLOG_ACTION", "rebase", 1);
+	memset(&opt, 0, sizeof(opt));
+	opt.context = -1;
+	git_config(rebase_config, &opt);
+
+	run_interactive(argc, argv);
+
+	if (argc == 1) {
+		int rebase_merge_exists = !stat(git_path("rebase-merge"), &st) && S_ISDIR(st.st_mode);
+		int rebase_apply_exists = !stat(git_path("rebase-apply"), &st) && S_ISDIR(st.st_mode);
+		if (rebase_merge_exists || rebase_apply_exists) {
+			if (rebase_merge_exists ||
+			    (!stat(git_path("rebase-apply/rebasing"), &st) && S_ISREG(st.st_mode)))
+				die("A rebase is in progress, try --continue, --skip or --abort.");
+			die("No arguments given and %s already exists.", git_path("rebase-apply"));
+		}
+		else
+			usage_with_options(rebase_usage, options);
+	}
+
+	argc = parse_options(argc, argv, options, rebase_usage, 0);
+
+	do_merge = (opt.flags & REBASE_MERGE) || opt.strategy;
+	if (!opt.strategy)
+		opt.strategy = "recursive";
+
+	abort_continue_skip(&opt);
+
+	if (opt.whitespace &&
+	    (!strcmp(opt.whitespace, "fix") ||
+	     !strcmp(opt.whitespace, "strip")))
+		opt.flags |= REBASE_FORCE;
+
+	if (opt.flags & REBASE_ROOT) {
+		if (argc > 1)
+			die("Invalid argument");
+		if (argc)
+			opt.branch = *argv;
+		if (!opt.onto)
+			die("--root must be used with --onto");
+	}
+	else {
+		/* without --root only takes either 1 or 2 arguments */
+		if (argc == 0 || argc > 2 ||
+		    (argc == 2 && (opt.flags & REBASE_ROOT)))
+			die("Invalid argument");
+		if (argc) {
+			opt.upstream = *argv++;
+			if (argc)
+				opt.branch = *argv;
+		}
+	}
+
+	/* No rebase ongoing */
+	if (do_merge) {
+		if (!stat(git_path("rebase-merge"), &st) && S_ISREG(st.st_mode))
+			die("previous rebase directory rebase-merge still exists."
+			    "Try git rebase (--continue | --abort | --skip)");
+	}
+	else {
+		if (!mkdir(git_path("rebase-apply"), 0700))
+			rmdir(git_path("rebase-apply"));
+		else
+			die("It seems that I cannot create a rebase-apply directory, and\n"
+			    "I wonder if you are in the middle of the patch application or another\n"
+			    "rebase. If that is not the case, please\n"
+			    "\trm -fr \"%s\"/rebase_apply\n"
+			    "and run me again. I am stopping in case you still have something\n"
+			    "valuable there.",
+			    get_git_dir());
+	}
+
+	/* worktree clean */
+	if (read_cache() < 0)
+		die("Could not read the index");
+	if (refresh_cache(REFRESH_IGNORE_SUBMODULES))
+		die("cannot rebase: you have unstaged changes");
+
+	/* cache clean */
+	init_revisions(&revs, NULL);
+	revs.abbrev = 0;
+	revs.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	revs.diffopt.format_callback = die_dirty_cache;
+	DIFF_OPT_SET(&revs.diffopt, IGNORE_SUBMODULES);
+	DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+	if (get_sha1("HEAD", head_sha1))
+		die("Could not resolve HEAD");
+	head_cmt = lookup_commit(head_sha1);
+	add_pending_object(&revs, &head_cmt->object, NULL);
+	run_diff_index(&revs, 1);
+
+	if (!(opt.flags & REBASE_NO_VERIFY) &&
+	    run_hook(NULL, "pre-rebase", opt.flags & REBASE_ROOT ? "--root" : opt.upstream, opt.branch, NULL)) {
+		die("The pre-rebase hook refused to rebase.");
+	}
+
+	/* commit validation */
+	if (!(opt.flags & REBASE_ROOT))
+		get_commit(opt.upstream, upstream_sha1, &upstream_cmt);
+	get_commit("HEAD", head_sha1, &head_cmt);
+	if (opt.onto)
+		get_commit(opt.onto, onto_sha1, &onto_cmt);
+	else {
+		opt.onto = opt.upstream;
+		memcpy(onto_sha1, upstream_sha1, 20);
+		onto_cmt = upstream_cmt;
+	}
+
+	if (opt.branch) {
+		struct strbuf sb = STRBUF_INIT;
+		const char *full_ref;
+		int type;
+
+		switch_to = opt.branch;
+		strbuf_addf(&sb, "refs/heads/%s", opt.branch);
+		full_ref = resolve_ref(sb.buf, branch_sha1, 1, &type);
+		if (full_ref)
+			head_name = xstrdup(full_ref);
+		else {
+			if (!get_sha1(opt.branch, branch_sha1))
+				head_name = xstrdup("detached HEAD");
+			else
+				die("Failed to parse %s", opt.branch);
+		}
+		strbuf_release(&sb);
+	}
+	else {
+		const char *full_ref;
+		int type;
+
+		opt.branch = "HEAD";
+		full_ref = resolve_ref(opt.branch, branch_sha1, 1, &type);
+		if (full_ref) {
+			head_name = xstrdup(full_ref);
+			if (!strncmp(full_ref, "refs/heads/", 11))
+				opt.branch = head_name+11;
+		}
+		else {
+			head_name = xstrdup("detached HEAD");
+			if (get_sha1(opt.branch, branch_sha1))
+				die("Could not resolve HEAD");
+		}
+	}
+
+	branch_cmt = lookup_commit(branch_sha1);
+	if (!branch_cmt)
+		die("Failed to lookup commit for %s", branch_sha1);
+
+	memset(&cp, 0, sizeof(cp));
+	cp.git_cmd = 1;
+
+	/*
+	 * Now we are rebasing commits upstream_sha1..branch_sha1
+	 * (or with --root, everything leading up to branch_sha1)
+	 * on top of onto_sha1
+	 */
+
+	/*
+	 * Check if we are already based on onto_sha1 with linear history,
+	 * but this should be done only when upstream and onto are the same.
+	 */
+	if (!(opt.flags & REBASE_ROOT) && !memcmp(upstream_sha1, onto_sha1, 20)) {
+		struct commit *cmt = branch_cmt;
+		while (1) {
+			if (parse_commit(cmt))
+				die("Could not parse commit");
+
+			if (!cmt->parents || cmt->parents->next)
+				break;
+
+			if (cmt->parents->item != onto_cmt) {
+				cmt = cmt->parents->item;
+				continue;
+			}
+
+			if (opt.flags & REBASE_FORCE) {
+				printf("Current branch %s is up to date, rebase forced.\n", opt.branch);
+				break;
+			}
+			if (switch_to) {
+				const char *argv_checkout[] = { "checkout", switch_to, NULL };
+				cp.argv = argv_checkout;
+				if (run_command(&cp))
+					die("Failed to switch to branch %s", switch_to);
+			}
+			fprintf(stderr, "Current branch %s is up to date.\n", opt.branch);
+			return 0;
+		}
+	}
+
+	/* Detach HEAD and reset the tree */
+	printf("First, rewinding head to replay your work on top of it...\n");
+	buf = xmalloc(43);
+	memcpy(buf, sha1_to_hex(onto_sha1), 40);
+	buf[40] = '^';
+	buf[41] = '0';
+	buf[42] = '\0';
+	argv_checkout_onto[2] = buf;
+	cp.argv = argv_checkout_onto;
+	if (run_command(&cp))
+		die("could not detach HEAD");
+	update_ref(NULL, "ORIG_HEAD", branch_sha1, NULL, REF_NODEREF, DIE_ON_ERR);
+
+	/* diffstat */
+	mb_cmts = get_merge_bases(onto_cmt, branch_cmt, 0);
+	if (mb_cmts && (opt.flags & REBASE_STAT)) {
+		struct rev_info diffrev;
+		struct commit *mb_cmt = mb_cmts->item;
+
+		if (opt.flags & REBASE_VERBOSE)
+			printf("Changes from %s to %s:\n", sha1_to_hex(mb_cmt->object.sha1), opt.onto);
+		init_revisions(&diffrev, NULL);
+		diffrev.abbrev = 0;
+		DIFF_OPT_SET(&diffrev.diffopt, RECURSIVE);
+		diffrev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY;
+		if (parse_commit(mb_cmt) || parse_commit(onto_cmt))
+			die("Could not parse commits");
+		diff_tree_sha1(mb_cmt->tree->object.sha1, onto_cmt->tree->object.sha1, "", &diffrev.diffopt);
+		log_tree_diff_flush(&diffrev);
+	}
+
+	/*
+	 * If the opt.onto is a proper descendant of the tip of the branch,
+	 * then we just fast forward it.
+	 */
+	if (mb_cmts && mb_cmts->item == branch_cmt) {
+		fprintf(stderr, "Fast-forwarded %s to %s", opt.branch, opt.onto);
+		move_to_original_branch(onto_sha1, branch_sha1, head_name);
+		return 0;
+	}
+
+	/*
+	 * if --root, revision range will be onto..orig_head
+	 * otherwise upstream..orig_head
+	 */
+
+	rebase_func = do_merge ? cmd_merge_rebase : cmd_apply_rebase;
+	return rebase_func(&opt, &onto_cmt->object,
+			   opt.flags & REBASE_ROOT ? &onto_cmt->object : &upstream_cmt->object,
+			   &branch_cmt->object, opt.onto, head_name);
+}
diff --git a/builtin.h b/builtin.h
index 425ff8e..53bf2b0 100644
--- a/builtin.h
+++ b/builtin.h
@@ -80,6 +80,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --git a/contrib/examples/git-rebase.sh b/contrib/examples/git-rebase.sh
new file mode 100755
index 0000000..b83fd3f
--- /dev/null
+++ b/contrib/examples/git-rebase.sh
@@ -0,0 +1,530 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano.
+#
+
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]'
+LONG_USAGE='git-rebase replaces <branch> with a new branch of the
+same name.  When the --onto option is provided the new branch starts
+out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
+It then attempts to create a new commit for each commit from the original
+<branch> that does not exist in the <upstream> branch.
+
+It is possible that a merge failure will prevent this process from being
+completely automatic.  You will have to resolve any such merge failure
+and run git rebase --continue.  Another option is to bypass the commit
+that caused the merge failure with git rebase --skip.  To restore the
+original <branch> and remove the .git/rebase-apply working files, use the
+command git rebase --abort instead.
+
+Note that if <branch> is not specified on the command line, the
+currently checked out branch is used.
+
+Example:       git-rebase master~1 topic
+
+        A---B---C topic                   A'\''--B'\''--C'\'' topic
+       /                   -->           /
+  D---E---F---G master          D---E---F---G master
+'
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
+. git-sh-setup
+set_reflog_action rebase
+require_work_tree
+cd_to_toplevel
+
+OK_TO_SKIP_PRE_REBASE=
+RESOLVEMSG="
+When you have resolved this problem run \"git rebase --continue\".
+If you would prefer to skip this patch, instead run \"git rebase --skip\".
+To restore the original branch and stop rebasing run \"git rebase --abort\".
+"
+unset newbase
+strategy=recursive
+do_merge=
+dotest="$GIT_DIR"/rebase-merge
+prec=4
+verbose=
+diffstat=$(git config --bool rebase.stat)
+git_am_opt=
+rebase_root=
+force_rebase=
+
+continue_merge () {
+	test -n "$prev_head" || die "prev_head must be defined"
+	test -d "$dotest" || die "$dotest directory does not exist"
+
+	unmerged=$(git ls-files -u)
+	if test -n "$unmerged"
+	then
+		echo "You still have unmerged paths in your index"
+		echo "did you forget to use git add?"
+		die "$RESOLVEMSG"
+	fi
+
+	cmt=`cat "$dotest/current"`
+	if ! git diff-index --quiet --ignore-submodules HEAD --
+	then
+		if ! git commit --no-verify -C "$cmt"
+		then
+			echo "Commit failed, please do not call \"git commit\""
+			echo "directly, but instead do one of the following: "
+			die "$RESOLVEMSG"
+		fi
+		printf "Committed: %0${prec}d " $msgnum
+	else
+		printf "Already applied: %0${prec}d " $msgnum
+	fi
+	git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
+
+	prev_head=`git rev-parse HEAD^0`
+	# save the resulting commit so we can read-tree on it later
+	echo "$prev_head" > "$dotest/prev_head"
+
+	# onto the next patch:
+	msgnum=$(($msgnum + 1))
+	echo "$msgnum" >"$dotest/msgnum"
+}
+
+call_merge () {
+	cmt="$(cat "$dotest/cmt.$1")"
+	echo "$cmt" > "$dotest/current"
+	hd=$(git rev-parse --verify HEAD)
+	cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
+	msgnum=$(cat "$dotest/msgnum")
+	end=$(cat "$dotest/end")
+	eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
+	eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
+	export GITHEAD_$cmt GITHEAD_$hd
+	git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
+	rv=$?
+	case "$rv" in
+	0)
+		unset GITHEAD_$cmt GITHEAD_$hd
+		return
+		;;
+	1)
+		git rerere
+		die "$RESOLVEMSG"
+		;;
+	2)
+		echo "Strategy: $rv $strategy failed, try another" 1>&2
+		die "$RESOLVEMSG"
+		;;
+	*)
+		die "Unknown exit code ($rv) from command:" \
+			"git-merge-$strategy $cmt^ -- HEAD $cmt"
+		;;
+	esac
+}
+
+move_to_original_branch () {
+	test -z "$head_name" &&
+		head_name="$(cat "$dotest"/head-name)" &&
+		onto="$(cat "$dotest"/onto)" &&
+		orig_head="$(cat "$dotest"/orig-head)"
+	case "$head_name" in
+	refs/*)
+		message="rebase finished: $head_name onto $onto"
+		git update-ref -m "$message" \
+			$head_name $(git rev-parse HEAD) $orig_head &&
+		git symbolic-ref HEAD $head_name ||
+		die "Could not move back to $head_name"
+		;;
+	esac
+}
+
+finish_rb_merge () {
+	move_to_original_branch
+	rm -r "$dotest"
+	echo "All done."
+}
+
+is_interactive () {
+	while test $# != 0
+	do
+		case "$1" in
+			-i|--interactive)
+				interactive_rebase=explicit
+				break
+			;;
+			-p|--preserve-merges)
+				interactive_rebase=implied
+			;;
+		esac
+		shift
+	done
+
+	if [ "$interactive_rebase" = implied ]; then
+		GIT_EDITOR=:
+		export GIT_EDITOR
+	fi
+
+	test -n "$interactive_rebase" || test -f "$dotest"/interactive
+}
+
+run_pre_rebase_hook () {
+	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+	   test -x "$GIT_DIR/hooks/pre-rebase"
+	then
+		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
+			echo >&2 "The pre-rebase hook refused to rebase."
+			exit 1
+		}
+	fi
+}
+
+test -f "$GIT_DIR"/rebase-apply/applying &&
+	die 'It looks like git-am is in progress. Cannot rebase.'
+
+is_interactive "$@" && exec git-rebase--interactive "$@"
+
+if test $# -eq 0
+then
+	test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
+	test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
+		die 'A rebase is in progress, try --continue, --skip or --abort.'
+	die "No arguments given and $GIT_DIR/rebase-apply already exists."
+fi
+
+while test $# != 0
+do
+	case "$1" in
+	--no-verify)
+		OK_TO_SKIP_PRE_REBASE=yes
+		;;
+	--continue)
+		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
+			die "No rebase in progress?"
+
+		git diff-files --quiet --ignore-submodules || {
+			echo "You must edit all merge conflicts and then"
+			echo "mark them as resolved using git add"
+			exit 1
+		}
+		if test -d "$dotest"
+		then
+			prev_head=$(cat "$dotest/prev_head")
+			end=$(cat "$dotest/end")
+			msgnum=$(cat "$dotest/msgnum")
+			onto=$(cat "$dotest/onto")
+			continue_merge
+			while test "$msgnum" -le "$end"
+			do
+				call_merge "$msgnum"
+				continue_merge
+			done
+			finish_rb_merge
+			exit
+		fi
+		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
+		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
+		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
+		git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
+		move_to_original_branch
+		exit
+		;;
+	--skip)
+		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
+			die "No rebase in progress?"
+
+		git reset --hard HEAD || exit $?
+		if test -d "$dotest"
+		then
+			git rerere clear
+			prev_head=$(cat "$dotest/prev_head")
+			end=$(cat "$dotest/end")
+			msgnum=$(cat "$dotest/msgnum")
+			msgnum=$(($msgnum + 1))
+			onto=$(cat "$dotest/onto")
+			while test "$msgnum" -le "$end"
+			do
+				call_merge "$msgnum"
+				continue_merge
+			done
+			finish_rb_merge
+			exit
+		fi
+		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
+		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
+		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
+		git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
+		move_to_original_branch
+		exit
+		;;
+	--abort)
+		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
+			die "No rebase in progress?"
+
+		git rerere clear
+		if test -d "$dotest"
+		then
+			move_to_original_branch
+		else
+			dotest="$GIT_DIR"/rebase-apply
+			move_to_original_branch
+		fi
+		git reset --hard $(cat "$dotest/orig-head")
+		rm -r "$dotest"
+		exit
+		;;
+	--onto)
+		test 2 -le "$#" || usage
+		newbase="$2"
+		shift
+		;;
+	-M|-m|--m|--me|--mer|--merg|--merge)
+		do_merge=t
+		;;
+	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
+		--strateg=*|--strategy=*|\
+	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
+		case "$#,$1" in
+		*,*=*)
+			strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			strategy="$2"
+			shift ;;
+		esac
+		do_merge=t
+		;;
+	-n|--no-stat)
+		diffstat=
+		;;
+	--stat)
+		diffstat=t
+		;;
+	-v|--verbose)
+		verbose=t
+		diffstat=t
+		;;
+	--whitespace=*)
+		git_am_opt="$git_am_opt $1"
+		case "$1" in
+		--whitespace=fix|--whitespace=strip)
+			force_rebase=t
+			;;
+		esac
+		;;
+	--committer-date-is-author-date|--ignore-date)
+		git_am_opt="$git_am_opt $1"
+		force_rebase=t
+		;;
+	-C*)
+		git_am_opt="$git_am_opt $1"
+		;;
+	--root)
+		rebase_root=t
+		;;
+	-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
+		force_rebase=t
+		;;
+	-*)
+		usage
+		;;
+	*)
+		break
+		;;
+	esac
+	shift
+done
+test $# -gt 2 && usage
+
+# Make sure we do not have $GIT_DIR/rebase-apply
+if test -z "$do_merge"
+then
+	if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null
+	then
+		rmdir "$GIT_DIR"/rebase-apply
+	else
+		echo >&2 '
+It seems that I cannot create a rebase-apply directory, and
+I wonder if you are in the middle of patch application or another
+rebase.  If that is not the case, please
+	rm -fr '"$GIT_DIR"'/rebase-apply
+and run me again.  I am stopping in case you still have something
+valuable there.'
+		exit 1
+	fi
+else
+	if test -d "$dotest"
+	then
+		die "previous rebase directory $dotest still exists." \
+			'Try git rebase (--continue | --abort | --skip)'
+	fi
+fi
+
+# The tree must be really really clean.
+if ! git update-index --ignore-submodules --refresh; then
+	echo >&2 "cannot rebase: you have unstaged changes"
+	exit 1
+fi
+diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
+case "$diff" in
+?*)	echo >&2 "cannot rebase: your index contains uncommitted changes"
+	echo >&2 "$diff"
+	exit 1
+	;;
+esac
+
+if test -z "$rebase_root"
+then
+	# The upstream head must be given.  Make sure it is valid.
+	upstream_name="$1"
+	shift
+	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+	die "invalid upstream $upstream_name"
+	unset root_flag
+	upstream_arg="$upstream_name"
+else
+	test -z "$newbase" && die "--root must be used with --onto"
+	unset upstream_name
+	unset upstream
+	root_flag="--root"
+	upstream_arg="$root_flag"
+fi
+
+# Make sure the branch to rebase onto is valid.
+onto_name=${newbase-"$upstream_name"}
+onto=$(git rev-parse --verify "${onto_name}^0") || exit
+
+# If a hook exists, give it a chance to interrupt
+run_pre_rebase_hook "$upstream_arg" "$@"
+
+# If the branch to rebase is given, that is the branch we will rebase
+# $branch_name -- branch being rebased, or HEAD (already detached)
+# $orig_head -- commit object name of tip of the branch before rebasing
+# $head_name -- refs/heads/<that-branch> or "detached HEAD"
+switch_to=
+case "$#" in
+1)
+	# Is it "rebase other $branchname" or "rebase other $commit"?
+	branch_name="$1"
+	switch_to="$1"
+
+	if git show-ref --verify --quiet -- "refs/heads/$1" &&
+	   branch=$(git rev-parse -q --verify "refs/heads/$1")
+	then
+		head_name="refs/heads/$1"
+	elif branch=$(git rev-parse -q --verify "$1")
+	then
+		head_name="detached HEAD"
+	else
+		usage
+	fi
+	;;
+*)
+	# Do not need to switch branches, we are already on it.
+	if branch_name=`git symbolic-ref -q HEAD`
+	then
+		head_name=$branch_name
+		branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
+	else
+		head_name="detached HEAD"
+		branch_name=HEAD ;# detached
+	fi
+	branch=$(git rev-parse --verify "${branch_name}^0") || exit
+	;;
+esac
+orig_head=$branch
+
+# Now we are rebasing commits $upstream..$branch (or with --root,
+# everything leading up to $branch) on top of $onto
+
+# Check if we are already based on $onto with linear history,
+# but this should be done only when upstream and onto are the same.
+mb=$(git merge-base "$onto" "$branch")
+if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
+	# linear history?
+	! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
+then
+	if test -z "$force_rebase"
+	then
+		# Lazily switch to the target branch if needed...
+		test -z "$switch_to" || git checkout "$switch_to"
+		echo >&2 "Current branch $branch_name is up to date."
+		exit 0
+	else
+		echo "Current branch $branch_name is up to date, rebase forced."
+	fi
+fi
+
+# Detach HEAD and reset the tree
+echo "First, rewinding head to replay your work on top of it..."
+git checkout -q "$onto^0" || die "could not detach HEAD"
+git update-ref ORIG_HEAD $branch
+
+if test -n "$diffstat"
+then
+	if test -n "$verbose"
+	then
+		echo "Changes from $mb to $onto:"
+	fi
+	# We want color (if set), but no pager
+	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+
+# If the $onto is a proper descendant of the tip of the branch, then
+# we just fast forwarded.
+if test "$mb" = "$branch"
+then
+	echo >&2 "Fast-forwarded $branch_name to $onto_name."
+	move_to_original_branch
+	exit 0
+fi
+
+if test -n "$rebase_root"
+then
+	revisions="$onto..$orig_head"
+else
+	revisions="$upstream..$orig_head"
+fi
+
+if test -z "$do_merge"
+then
+	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+		$root_flag "$revisions" |
+	git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
+	move_to_original_branch
+	ret=$?
+	test 0 != $ret -a -d "$GIT_DIR"/rebase-apply &&
+		echo $head_name > "$GIT_DIR"/rebase-apply/head-name &&
+		echo $onto > "$GIT_DIR"/rebase-apply/onto &&
+		echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head
+	exit $ret
+fi
+
+# start doing a rebase with git-merge
+# this is rename-aware if the recursive (default) strategy is used
+
+mkdir -p "$dotest"
+echo "$onto" > "$dotest/onto"
+echo "$onto_name" > "$dotest/onto_name"
+prev_head=$orig_head
+echo "$prev_head" > "$dotest/prev_head"
+echo "$orig_head" > "$dotest/orig-head"
+echo "$head_name" > "$dotest/head-name"
+
+msgnum=0
+for cmt in `git rev-list --reverse --no-merges "$revisions"`
+do
+	msgnum=$(($msgnum + 1))
+	echo "$cmt" > "$dotest/cmt.$msgnum"
+done
+
+echo 1 >"$dotest/msgnum"
+echo $msgnum >"$dotest/end"
+
+end=$msgnum
+msgnum=1
+
+while test "$msgnum" -le "$end"
+do
+	call_merge "$msgnum"
+	continue_merge
+done
+
+finish_rb_merge
diff --git a/git-rebase.sh b/git-rebase.sh
deleted file mode 100755
index b83fd3f..0000000
--- a/git-rebase.sh
+++ /dev/null
@@ -1,530 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]'
-LONG_USAGE='git-rebase replaces <branch> with a new branch of the
-same name.  When the --onto option is provided the new branch starts
-out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
-It then attempts to create a new commit for each commit from the original
-<branch> that does not exist in the <upstream> branch.
-
-It is possible that a merge failure will prevent this process from being
-completely automatic.  You will have to resolve any such merge failure
-and run git rebase --continue.  Another option is to bypass the commit
-that caused the merge failure with git rebase --skip.  To restore the
-original <branch> and remove the .git/rebase-apply working files, use the
-command git rebase --abort instead.
-
-Note that if <branch> is not specified on the command line, the
-currently checked out branch is used.
-
-Example:       git-rebase master~1 topic
-
-        A---B---C topic                   A'\''--B'\''--C'\'' topic
-       /                   -->           /
-  D---E---F---G master          D---E---F---G master
-'
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_SPEC=
-. git-sh-setup
-set_reflog_action rebase
-require_work_tree
-cd_to_toplevel
-
-OK_TO_SKIP_PRE_REBASE=
-RESOLVEMSG="
-When you have resolved this problem run \"git rebase --continue\".
-If you would prefer to skip this patch, instead run \"git rebase --skip\".
-To restore the original branch and stop rebasing run \"git rebase --abort\".
-"
-unset newbase
-strategy=recursive
-do_merge=
-dotest="$GIT_DIR"/rebase-merge
-prec=4
-verbose=
-diffstat=$(git config --bool rebase.stat)
-git_am_opt=
-rebase_root=
-force_rebase=
-
-continue_merge () {
-	test -n "$prev_head" || die "prev_head must be defined"
-	test -d "$dotest" || die "$dotest directory does not exist"
-
-	unmerged=$(git ls-files -u)
-	if test -n "$unmerged"
-	then
-		echo "You still have unmerged paths in your index"
-		echo "did you forget to use git add?"
-		die "$RESOLVEMSG"
-	fi
-
-	cmt=`cat "$dotest/current"`
-	if ! git diff-index --quiet --ignore-submodules HEAD --
-	then
-		if ! git commit --no-verify -C "$cmt"
-		then
-			echo "Commit failed, please do not call \"git commit\""
-			echo "directly, but instead do one of the following: "
-			die "$RESOLVEMSG"
-		fi
-		printf "Committed: %0${prec}d " $msgnum
-	else
-		printf "Already applied: %0${prec}d " $msgnum
-	fi
-	git rev-list --pretty=oneline -1 "$cmt" | sed -e 's/^[^ ]* //'
-
-	prev_head=`git rev-parse HEAD^0`
-	# save the resulting commit so we can read-tree on it later
-	echo "$prev_head" > "$dotest/prev_head"
-
-	# onto the next patch:
-	msgnum=$(($msgnum + 1))
-	echo "$msgnum" >"$dotest/msgnum"
-}
-
-call_merge () {
-	cmt="$(cat "$dotest/cmt.$1")"
-	echo "$cmt" > "$dotest/current"
-	hd=$(git rev-parse --verify HEAD)
-	cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
-	msgnum=$(cat "$dotest/msgnum")
-	end=$(cat "$dotest/end")
-	eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
-	eval GITHEAD_$hd='$(cat "$dotest/onto_name")'
-	export GITHEAD_$cmt GITHEAD_$hd
-	git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
-	rv=$?
-	case "$rv" in
-	0)
-		unset GITHEAD_$cmt GITHEAD_$hd
-		return
-		;;
-	1)
-		git rerere
-		die "$RESOLVEMSG"
-		;;
-	2)
-		echo "Strategy: $rv $strategy failed, try another" 1>&2
-		die "$RESOLVEMSG"
-		;;
-	*)
-		die "Unknown exit code ($rv) from command:" \
-			"git-merge-$strategy $cmt^ -- HEAD $cmt"
-		;;
-	esac
-}
-
-move_to_original_branch () {
-	test -z "$head_name" &&
-		head_name="$(cat "$dotest"/head-name)" &&
-		onto="$(cat "$dotest"/onto)" &&
-		orig_head="$(cat "$dotest"/orig-head)"
-	case "$head_name" in
-	refs/*)
-		message="rebase finished: $head_name onto $onto"
-		git update-ref -m "$message" \
-			$head_name $(git rev-parse HEAD) $orig_head &&
-		git symbolic-ref HEAD $head_name ||
-		die "Could not move back to $head_name"
-		;;
-	esac
-}
-
-finish_rb_merge () {
-	move_to_original_branch
-	rm -r "$dotest"
-	echo "All done."
-}
-
-is_interactive () {
-	while test $# != 0
-	do
-		case "$1" in
-			-i|--interactive)
-				interactive_rebase=explicit
-				break
-			;;
-			-p|--preserve-merges)
-				interactive_rebase=implied
-			;;
-		esac
-		shift
-	done
-
-	if [ "$interactive_rebase" = implied ]; then
-		GIT_EDITOR=:
-		export GIT_EDITOR
-	fi
-
-	test -n "$interactive_rebase" || test -f "$dotest"/interactive
-}
-
-run_pre_rebase_hook () {
-	if test -z "$OK_TO_SKIP_PRE_REBASE" &&
-	   test -x "$GIT_DIR/hooks/pre-rebase"
-	then
-		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
-			echo >&2 "The pre-rebase hook refused to rebase."
-			exit 1
-		}
-	fi
-}
-
-test -f "$GIT_DIR"/rebase-apply/applying &&
-	die 'It looks like git-am is in progress. Cannot rebase.'
-
-is_interactive "$@" && exec git-rebase--interactive "$@"
-
-if test $# -eq 0
-then
-	test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
-	test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
-		die 'A rebase is in progress, try --continue, --skip or --abort.'
-	die "No arguments given and $GIT_DIR/rebase-apply already exists."
-fi
-
-while test $# != 0
-do
-	case "$1" in
-	--no-verify)
-		OK_TO_SKIP_PRE_REBASE=yes
-		;;
-	--continue)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git diff-files --quiet --ignore-submodules || {
-			echo "You must edit all merge conflicts and then"
-			echo "mark them as resolved using git add"
-			exit 1
-		}
-		if test -d "$dotest"
-		then
-			prev_head=$(cat "$dotest/prev_head")
-			end=$(cat "$dotest/end")
-			msgnum=$(cat "$dotest/msgnum")
-			onto=$(cat "$dotest/onto")
-			continue_merge
-			while test "$msgnum" -le "$end"
-			do
-				call_merge "$msgnum"
-				continue_merge
-			done
-			finish_rb_merge
-			exit
-		fi
-		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
-		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
-		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
-		git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
-		move_to_original_branch
-		exit
-		;;
-	--skip)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git reset --hard HEAD || exit $?
-		if test -d "$dotest"
-		then
-			git rerere clear
-			prev_head=$(cat "$dotest/prev_head")
-			end=$(cat "$dotest/end")
-			msgnum=$(cat "$dotest/msgnum")
-			msgnum=$(($msgnum + 1))
-			onto=$(cat "$dotest/onto")
-			while test "$msgnum" -le "$end"
-			do
-				call_merge "$msgnum"
-				continue_merge
-			done
-			finish_rb_merge
-			exit
-		fi
-		head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) &&
-		onto=$(cat "$GIT_DIR"/rebase-apply/onto) &&
-		orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) &&
-		git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
-		move_to_original_branch
-		exit
-		;;
-	--abort)
-		test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
-			die "No rebase in progress?"
-
-		git rerere clear
-		if test -d "$dotest"
-		then
-			move_to_original_branch
-		else
-			dotest="$GIT_DIR"/rebase-apply
-			move_to_original_branch
-		fi
-		git reset --hard $(cat "$dotest/orig-head")
-		rm -r "$dotest"
-		exit
-		;;
-	--onto)
-		test 2 -le "$#" || usage
-		newbase="$2"
-		shift
-		;;
-	-M|-m|--m|--me|--mer|--merg|--merge)
-		do_merge=t
-		;;
-	-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
-		--strateg=*|--strategy=*|\
-	-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
-		case "$#,$1" in
-		*,*=*)
-			strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;;
-		1,*)
-			usage ;;
-		*)
-			strategy="$2"
-			shift ;;
-		esac
-		do_merge=t
-		;;
-	-n|--no-stat)
-		diffstat=
-		;;
-	--stat)
-		diffstat=t
-		;;
-	-v|--verbose)
-		verbose=t
-		diffstat=t
-		;;
-	--whitespace=*)
-		git_am_opt="$git_am_opt $1"
-		case "$1" in
-		--whitespace=fix|--whitespace=strip)
-			force_rebase=t
-			;;
-		esac
-		;;
-	--committer-date-is-author-date|--ignore-date)
-		git_am_opt="$git_am_opt $1"
-		force_rebase=t
-		;;
-	-C*)
-		git_am_opt="$git_am_opt $1"
-		;;
-	--root)
-		rebase_root=t
-		;;
-	-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
-		force_rebase=t
-		;;
-	-*)
-		usage
-		;;
-	*)
-		break
-		;;
-	esac
-	shift
-done
-test $# -gt 2 && usage
-
-# Make sure we do not have $GIT_DIR/rebase-apply
-if test -z "$do_merge"
-then
-	if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null
-	then
-		rmdir "$GIT_DIR"/rebase-apply
-	else
-		echo >&2 '
-It seems that I cannot create a rebase-apply directory, and
-I wonder if you are in the middle of patch application or another
-rebase.  If that is not the case, please
-	rm -fr '"$GIT_DIR"'/rebase-apply
-and run me again.  I am stopping in case you still have something
-valuable there.'
-		exit 1
-	fi
-else
-	if test -d "$dotest"
-	then
-		die "previous rebase directory $dotest still exists." \
-			'Try git rebase (--continue | --abort | --skip)'
-	fi
-fi
-
-# The tree must be really really clean.
-if ! git update-index --ignore-submodules --refresh; then
-	echo >&2 "cannot rebase: you have unstaged changes"
-	exit 1
-fi
-diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
-case "$diff" in
-?*)	echo >&2 "cannot rebase: your index contains uncommitted changes"
-	echo >&2 "$diff"
-	exit 1
-	;;
-esac
-
-if test -z "$rebase_root"
-then
-	# The upstream head must be given.  Make sure it is valid.
-	upstream_name="$1"
-	shift
-	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
-	die "invalid upstream $upstream_name"
-	unset root_flag
-	upstream_arg="$upstream_name"
-else
-	test -z "$newbase" && die "--root must be used with --onto"
-	unset upstream_name
-	unset upstream
-	root_flag="--root"
-	upstream_arg="$root_flag"
-fi
-
-# Make sure the branch to rebase onto is valid.
-onto_name=${newbase-"$upstream_name"}
-onto=$(git rev-parse --verify "${onto_name}^0") || exit
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
-
-# If the branch to rebase is given, that is the branch we will rebase
-# $branch_name -- branch being rebased, or HEAD (already detached)
-# $orig_head -- commit object name of tip of the branch before rebasing
-# $head_name -- refs/heads/<that-branch> or "detached HEAD"
-switch_to=
-case "$#" in
-1)
-	# Is it "rebase other $branchname" or "rebase other $commit"?
-	branch_name="$1"
-	switch_to="$1"
-
-	if git show-ref --verify --quiet -- "refs/heads/$1" &&
-	   branch=$(git rev-parse -q --verify "refs/heads/$1")
-	then
-		head_name="refs/heads/$1"
-	elif branch=$(git rev-parse -q --verify "$1")
-	then
-		head_name="detached HEAD"
-	else
-		usage
-	fi
-	;;
-*)
-	# Do not need to switch branches, we are already on it.
-	if branch_name=`git symbolic-ref -q HEAD`
-	then
-		head_name=$branch_name
-		branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
-	else
-		head_name="detached HEAD"
-		branch_name=HEAD ;# detached
-	fi
-	branch=$(git rev-parse --verify "${branch_name}^0") || exit
-	;;
-esac
-orig_head=$branch
-
-# Now we are rebasing commits $upstream..$branch (or with --root,
-# everything leading up to $branch) on top of $onto
-
-# Check if we are already based on $onto with linear history,
-# but this should be done only when upstream and onto are the same.
-mb=$(git merge-base "$onto" "$branch")
-if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
-	# linear history?
-	! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
-then
-	if test -z "$force_rebase"
-	then
-		# Lazily switch to the target branch if needed...
-		test -z "$switch_to" || git checkout "$switch_to"
-		echo >&2 "Current branch $branch_name is up to date."
-		exit 0
-	else
-		echo "Current branch $branch_name is up to date, rebase forced."
-	fi
-fi
-
-# Detach HEAD and reset the tree
-echo "First, rewinding head to replay your work on top of it..."
-git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $branch
-
-if test -n "$diffstat"
-then
-	if test -n "$verbose"
-	then
-		echo "Changes from $mb to $onto:"
-	fi
-	# We want color (if set), but no pager
-	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
-fi
-
-# If the $onto is a proper descendant of the tip of the branch, then
-# we just fast forwarded.
-if test "$mb" = "$branch"
-then
-	echo >&2 "Fast-forwarded $branch_name to $onto_name."
-	move_to_original_branch
-	exit 0
-fi
-
-if test -n "$rebase_root"
-then
-	revisions="$onto..$orig_head"
-else
-	revisions="$upstream..$orig_head"
-fi
-
-if test -z "$do_merge"
-then
-	git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-		$root_flag "$revisions" |
-	git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
-	move_to_original_branch
-	ret=$?
-	test 0 != $ret -a -d "$GIT_DIR"/rebase-apply &&
-		echo $head_name > "$GIT_DIR"/rebase-apply/head-name &&
-		echo $onto > "$GIT_DIR"/rebase-apply/onto &&
-		echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head
-	exit $ret
-fi
-
-# start doing a rebase with git-merge
-# this is rename-aware if the recursive (default) strategy is used
-
-mkdir -p "$dotest"
-echo "$onto" > "$dotest/onto"
-echo "$onto_name" > "$dotest/onto_name"
-prev_head=$orig_head
-echo "$prev_head" > "$dotest/prev_head"
-echo "$orig_head" > "$dotest/orig-head"
-echo "$head_name" > "$dotest/head-name"
-
-msgnum=0
-for cmt in `git rev-list --reverse --no-merges "$revisions"`
-do
-	msgnum=$(($msgnum + 1))
-	echo "$cmt" > "$dotest/cmt.$msgnum"
-done
-
-echo 1 >"$dotest/msgnum"
-echo $msgnum >"$dotest/end"
-
-end=$msgnum
-msgnum=1
-
-while test "$msgnum" -le "$end"
-do
-	call_merge "$msgnum"
-	continue_merge
-done
-
-finish_rb_merge
diff --git a/git.c b/git.c
index 5a00726..39456a0 100644
--- a/git.c
+++ b/git.c
@@ -336,6 +336,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
 		{ "push", cmd_push, RUN_SETUP },
 		{ "read-tree", cmd_read_tree, RUN_SETUP },
+		{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
 		{ "receive-pack", cmd_receive_pack },
 		{ "reflog", cmd_reflog, RUN_SETUP },
 		{ "remote", cmd_remote, RUN_SETUP },
-- 
test

  reply	other threads:[~2009-05-21  9:48 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-21  9:47 [PATCH v0 1/3] doc/git-rebase.txt: remove mention of multiple strategies Nguyễn Thái Ngọc Duy
2009-05-21  9:47 ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Nguyễn Thái Ngọc Duy
2009-05-21  9:47   ` Nguyễn Thái Ngọc Duy [this message]
2009-05-21 10:25     ` [PATCH v0 3/3] Build in git-rebase.sh Jakub Narebski
2009-05-21 10:44       ` Nguyen Thai Ngoc Duy
2009-05-21 12:29         ` Jakub Narebski
2009-05-21 22:57           ` Nguyen Thai Ngoc Duy
2009-05-21 14:39     ` Junio C Hamano
2009-05-22  6:56     ` Johannes Sixt
2009-05-22  7:30       ` Nguyen Thai Ngoc Duy
2009-05-23  9:26         ` Nguyen Thai Ngoc Duy
2009-05-23 14:50           ` Nguyen Thai Ngoc Duy
2009-05-25  6:16             ` Johannes Sixt
2009-05-25  6:34               ` Nguyen Thai Ngoc Duy
2009-05-25  6:47                 ` Johannes Sixt
2009-05-25  7:00                   ` Nguyen Thai Ngoc Duy
2009-05-25  7:39                     ` Nguyen Thai Ngoc Duy
2009-05-21 10:22   ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Jakub Narebski
2009-05-21 10:39     ` Nguyen Thai Ngoc Duy
2009-05-21 14:22   ` Junio C Hamano
2009-05-23 15:31     ` [PATCH " Nguyễn Thái Ngọc Duy

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=1242899229-27603-3-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.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.