Subject: [PATCH v0 3/3] Build in git-rebase.sh

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Thu, 21 May 2009 19:47:09 +1000

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 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