git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/WIP 0/8] Convert git-rebase.sh to C
@ 2015-03-18  9:55 Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

In the spirit of sharing code proactively [1], despite my embarrassment,
this is what I got for converting git-rebase.sh to C. Note that this
is only about git-rebase.sh, not git-rebase--*.sh. Some changes in
git-rebase.sh are pushed back to git-rebase--*.sh. The idea is we
convert git-rebase.sh first, then the rest later.

Anybody who wants to base their work on this code, feel free to
--reset-author (and take all the bugs with you, I'm sure there are
lots of them). This work is from 2013. git-rebase.sh has received lots
of updates since then, so there's still lots of work to do.

[1] http://article.gmane.org/gmane.comp.version-control.git/265699

Nguyễn Thái Ngọc Duy (8):
  strbuf: add and use strbuf_read_file_or_die()
  Move reset_tree from builtin/checkout.c to unpack-trees.c
  rebase: turn rebase--am into a separate program
  rebase: turn rebase--merge into a separate program
  rebase: turn rebase--interactive into a separate program
  rebase: remove unused function
  rebase: move resolvemsg to rebase--* scripts
  Build in rebase

 Makefile                                |   8 +-
 builtin.h                               |   1 +
 builtin/blame.c                         |   4 +-
 builtin/checkout.c                      |  40 +-
 builtin/commit.c                        |  16 +-
 builtin/merge.c                         |  32 +-
 builtin/notes.c                         |   4 +-
 builtin/rebase.c (new)                  | 752 ++++++++++++++++++++++++++++++++
 builtin/tag.c                           |   7 +-
 commit.c                                |   4 +-
 commit.h                                |   4 +-
 contrib/examples/git-rebase.sh (new +x) | 532 ++++++++++++++++++++++
 git-rebase--am.sh (mode +x)             |  39 ++
 git-rebase--interactive.sh (mode +x)    |  41 +-
 git-rebase--merge.sh (mode +x)          |  39 ++
 git-rebase.sh (gone)                    | 544 -----------------------
 git.c                                   |   1 +
 strbuf.c                                |   8 +
 strbuf.h                                |   1 +
 unpack-trees.c                          |  33 ++
 unpack-trees.h                          |   4 +
 21 files changed, 1480 insertions(+), 634 deletions(-)
 create mode 100644 builtin/rebase.c
 create mode 100755 contrib/examples/git-rebase.sh
 mode change 100644 => 100755 git-rebase--am.sh
 mode change 100644 => 100755 git-rebase--interactive.sh
 mode change 100644 => 100755 git-rebase--merge.sh
 delete mode 100755 git-rebase.sh

-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die()
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 builtin/blame.c  |  4 ++--
 builtin/commit.c | 16 +++++-----------
 builtin/merge.c  |  3 +--
 builtin/notes.c  |  4 ++--
 builtin/tag.c    |  7 ++-----
 strbuf.c         |  8 ++++++++
 strbuf.h         |  1 +
 7 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index bc6c899..503595c 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2193,8 +2193,8 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
 			if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 			    textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
 				strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
-			else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
-				die_errno("cannot open or read '%s'", read_from);
+			else
+				strbuf_read_file_or_die(&buf, read_from, st.st_size);
 			break;
 		case S_IFLNK:
 			if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
diff --git a/builtin/commit.c b/builtin/commit.c
index d6dd3df..dad9acf 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -612,9 +612,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 			die_errno(_("could not read log from standard input"));
 		hook_arg1 = "message";
 	} else if (logfile) {
-		if (strbuf_read_file(&sb, logfile, 0) < 0)
-			die_errno(_("could not read log file '%s'"),
-				  logfile);
+		strbuf_read_file_or_die(&sb, logfile, 0);
 		hook_arg1 = "message";
 	} else if (use_message) {
 		buffer = strstr(use_message_buffer, "\n\n");
@@ -634,16 +632,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 				      &sb, &ctx);
 		hook_arg1 = "message";
 	} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
-		if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
-			die_errno(_("could not read MERGE_MSG"));
+		strbuf_read_file_or_die(&sb, git_path("MERGE_MSG"), 0);
 		hook_arg1 = "merge";
 	} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
-		if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
-			die_errno(_("could not read SQUASH_MSG"));
+		strbuf_read_file_or_die(&sb, git_path("SQUASH_MSG"), 0);
 		hook_arg1 = "squash";
 	} else if (template_file) {
-		if (strbuf_read_file(&sb, template_file, 0) < 0)
-			die_errno(_("could not read '%s'"), template_file);
+		strbuf_read_file_or_die(&sb, template_file, 0);
 		hook_arg1 = "template";
 		clean_message_contents = 0;
 	}
@@ -1497,8 +1492,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 		fclose(fp);
 		strbuf_release(&m);
 		if (!stat(git_path("MERGE_MODE"), &statbuf)) {
-			if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
-				die_errno(_("could not read MERGE_MODE"));
+			strbuf_read_file_or_die(&sb, git_path("MERGE_MODE"), 0);
 			if (!strcmp(sb.buf, "no-ff"))
 				allow_fast_forward = 0;
 		}
diff --git a/builtin/merge.c b/builtin/merge.c
index 9307e9c..6babf39 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -769,8 +769,7 @@ static void read_merge_msg(struct strbuf *msg)
 {
 	const char *filename = git_path("MERGE_MSG");
 	strbuf_reset(msg);
-	if (strbuf_read_file(msg, filename, 0) < 0)
-		die_errno(_("Could not read from '%s'"), filename);
+	strbuf_read_file_or_die(msg, filename, 0);
 }
 
 static void write_merge_state(struct commit_list *);
diff --git a/builtin/notes.c b/builtin/notes.c
index 453457a..3210c7f 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -252,8 +252,8 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
 	if (!strcmp(arg, "-")) {
 		if (strbuf_read(&(msg->buf), 0, 1024) < 0)
 			die_errno(_("cannot read '%s'"), arg);
-	} else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
-		die_errno(_("could not open or read '%s'"), arg);
+	} else
+		strbuf_read_file_or_die(&(msg->buf), arg, 0);
 	stripspace(&(msg->buf), 0);
 
 	msg->given = 1;
diff --git a/builtin/tag.c b/builtin/tag.c
index 9c3e067..69f4ea3 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -540,11 +540,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			if (!strcmp(msgfile, "-")) {
 				if (strbuf_read(&buf, 0, 1024) < 0)
 					die_errno(_("cannot read '%s'"), msgfile);
-			} else {
-				if (strbuf_read_file(&buf, msgfile, 1024) < 0)
-					die_errno(_("could not open or read '%s'"),
-						msgfile);
-			}
+			} else
+				strbuf_read_file_or_die(&buf, msgfile, 0);
 		}
 	}
 
diff --git a/strbuf.c b/strbuf.c
index 9a373be..9f50478 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -411,6 +411,14 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
 	return len;
 }
 
+void strbuf_read_file_or_die(struct strbuf *sb, const char *path, size_t size)
+{
+	int ret;
+	ret = strbuf_read_file(sb, path, size);
+	if (ret < 0 || (size && ret != size))
+		die_errno(_("could not open or read '%s'"), path);
+}
+
 void strbuf_add_lines(struct strbuf *out, const char *prefix,
 		      const char *buf, size_t size)
 {
diff --git a/strbuf.h b/strbuf.h
index ecae4e2..c1f012d 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -152,6 +152,7 @@ extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 /* XXX: if read fails, any partial read is undone */
 extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
 extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+extern void strbuf_read_file_or_die(struct strbuf *sb, const char *path, size_t size);
 extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 
 extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 builtin/checkout.c | 40 +++-------------------------------------
 builtin/merge.c    | 29 +++++++----------------------
 unpack-trees.c     | 33 +++++++++++++++++++++++++++++++++
 unpack-trees.h     |  4 ++++
 4 files changed, 47 insertions(+), 59 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index a9c1b5a..93b18ad 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -362,40 +362,6 @@ static void describe_detached_head(const char *msg, struct commit *commit)
 	strbuf_release(&sb);
 }
 
-static int reset_tree(struct tree *tree, const struct checkout_opts *o,
-		      int worktree, int *writeout_error)
-{
-	struct unpack_trees_options opts;
-	struct tree_desc tree_desc;
-
-	memset(&opts, 0, sizeof(opts));
-	opts.head_idx = -1;
-	opts.update = worktree;
-	opts.skip_unmerged = !worktree;
-	opts.reset = 1;
-	opts.merge = 1;
-	opts.fn = oneway_merge;
-	opts.verbose_update = !o->quiet && isatty(2);
-	opts.src_index = &the_index;
-	opts.dst_index = &the_index;
-	parse_tree(tree);
-	init_tree_desc(&tree_desc, tree->buffer, tree->size);
-	switch (unpack_trees(1, &tree_desc, &opts)) {
-	case -2:
-		*writeout_error = 1;
-		/*
-		 * We return 0 nevertheless, as the index is all right
-		 * and more importantly we have made best efforts to
-		 * update paths in the work tree, and we cannot revert
-		 * them.
-		 */
-	case 0:
-		return 0;
-	default:
-		return 128;
-	}
-}
-
 struct branch_info {
 	const char *name; /* The short name used */
 	const char *path; /* The full name of a real branch */
@@ -427,7 +393,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 
 	resolve_undo_clear();
 	if (opts->force) {
-		ret = reset_tree(new->commit->tree, opts, 1, writeout_error);
+		ret = reset_tree(new->commit->tree, opts->quiet, 1, writeout_error);
 		if (ret)
 			return ret;
 	} else {
@@ -513,7 +479,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.verbosity = 0;
 			work = write_tree_from_memory(&o);
 
-			ret = reset_tree(new->commit->tree, opts, 1,
+			ret = reset_tree(new->commit->tree, opts->quiet, 1,
 					 writeout_error);
 			if (ret)
 				return ret;
@@ -522,7 +488,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			o.branch2 = "local";
 			merge_trees(&o, new->commit->tree, work,
 				old->commit->tree, &result);
-			ret = reset_tree(new->commit->tree, opts, 0,
+			ret = reset_tree(new->commit->tree, opts->quiet, 0,
 					 writeout_error);
 			if (ret)
 				return ret;
diff --git a/builtin/merge.c b/builtin/merge.c
index 6babf39..c51e4f5 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -269,33 +269,18 @@ static void read_empty(unsigned const char *sha1, int verbose)
 		die(_("read-tree failed"));
 }
 
-static void reset_hard(unsigned const char *sha1, int verbose)
-{
-	int i = 0;
-	const char *args[6];
-
-	args[i++] = "read-tree";
-	if (verbose)
-		args[i++] = "-v";
-	args[i++] = "--reset";
-	args[i++] = "-u";
-	args[i++] = sha1_to_hex(sha1);
-	args[i] = NULL;
-
-	if (run_command_v_opt(args, RUN_GIT_CMD))
-		die(_("read-tree failed"));
-}
-
-static void restore_state(const unsigned char *head,
+static void restore_state(struct commit *head_commit,
 			  const unsigned char *stash)
 {
 	struct strbuf sb = STRBUF_INIT;
 	const char *args[] = { "stash", "apply", NULL, NULL };
+	int error = 0;
 
 	if (is_null_sha1(stash))
 		return;
 
-	reset_hard(head, 1);
+	if (reset_tree(head_commit->tree, 0, 1, &error) || error)
+		die(_("read-tree failed"));
 
 	args[2] = sha1_to_hex(stash);
 
@@ -1409,7 +1394,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		int ret;
 		if (i) {
 			printf(_("Rewinding the tree to pristine...\n"));
-			restore_state(head_commit->object.sha1, stash);
+			restore_state(head_commit, stash);
 		}
 		if (use_strategies_nr != 1)
 			printf(_("Trying merge strategy %s...\n"),
@@ -1475,7 +1460,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * it up.
 	 */
 	if (!best_strategy) {
-		restore_state(head_commit->object.sha1, stash);
+		restore_state(head_commit, stash);
 		if (use_strategies_nr > 1)
 			fprintf(stderr,
 				_("No merge strategy handled the merge.\n"));
@@ -1488,7 +1473,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		; /* We already have its result in the working tree. */
 	else {
 		printf(_("Rewinding the tree to pristine...\n"));
-		restore_state(head_commit->object.sha1, stash);
+		restore_state(head_commit, stash);
 		printf(_("Using the %s to prepare resolving by hand.\n"),
 			best_strategy);
 		try_merge_strategy(best_strategy, common, remoteheads,
diff --git a/unpack-trees.c b/unpack-trees.c
index 0e1a196..3c24a4d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1847,3 +1847,36 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 	}
 	return merged_entry(a, old, o);
 }
+
+int reset_tree(struct tree *tree, int quiet, int worktree, int *writeout_error)
+{
+	struct unpack_trees_options opts;
+	struct tree_desc tree_desc;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.head_idx = -1;
+	opts.update = worktree;
+	opts.skip_unmerged = !worktree;
+	opts.reset = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = !quiet && isatty(2);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+	parse_tree(tree);
+	init_tree_desc(&tree_desc, tree->buffer, tree->size);
+	switch (unpack_trees(1, &tree_desc, &opts)) {
+	case -2:
+		*writeout_error = 1;
+		/*
+		 * We return 0 nevertheless, as the index is all right
+		 * and more importantly we have made best efforts to
+		 * update paths in the work tree, and we cannot revert
+		 * them.
+		 */
+	case 0:
+		return 0;
+	default:
+		return 128;
+	}
+}
diff --git a/unpack-trees.h b/unpack-trees.h
index ec74a9f..f0850bb 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -83,4 +83,8 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
 int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
 int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
 
+
+struct tree;
+extern int reset_tree(struct tree *tree, int quiet,
+		      int worktree, int *writeout_error);
 #endif
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 Makefile                    |  2 +-
 git-rebase--am.sh (mode +x) | 34 ++++++++++++++++++++++++++++++++++
 git-rebase.sh               | 11 ++++++++++-
 3 files changed, 45 insertions(+), 2 deletions(-)
 mode change 100644 => 100755 git-rebase--am.sh

diff --git a/Makefile b/Makefile
index 1b30d7b..93151f4 100644
--- a/Makefile
+++ b/Makefile
@@ -445,6 +445,7 @@ SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-rebase--am.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -453,7 +454,6 @@ SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--am
 SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
old mode 100644
new mode 100755
index 97f31dc..ab84330
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -3,6 +3,40 @@
 # Copyright (c) 2010 Junio C Hamano.
 #
 
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
+write_basic_state () {
+	echo "$head_name" > "$state_dir"/head-name &&
+	echo "$onto" > "$state_dir"/onto &&
+	echo "$orig_head" > "$state_dir"/orig-head &&
+	echo "$GIT_QUIET" > "$state_dir"/quiet &&
+	test t = "$verbose" && : > "$state_dir"/verbose
+	test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+	test -n "$strategy_opts" && echo "$strategy_opts" > \
+		"$state_dir"/strategy_opts
+	test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+		"$state_dir"/allow_rerere_autoupdate
+}
+
+move_to_original_branch () {
+	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 \
+			-m "rebase finished: returning to $head_name" \
+			HEAD $head_name ||
+		die "$(gettext "Could not move back to $head_name")"
+		;;
+	esac
+}
+
 case "$action" in
 continue)
 	git am --resolved --resolvemsg="$resolvemsg" &&
diff --git a/git-rebase.sh b/git-rebase.sh
index b2f1c76..42e868d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -149,7 +149,16 @@ run_specific_rebase () {
 		export GIT_EDITOR
 		autosquash=
 	fi
-	. git-rebase--$type
+	git_quiet=$GIT_QUIET
+	export GIT_PAGER
+	export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
+	export onto orig_head rebase_root resolvemsg revisions
+	export state_dir verbose strategy strategy_opts
+	if [ "$type" = am ]; then
+		exec git-rebase--am
+	else
+		. git-rebase--$type
+	fi
 }
 
 run_pre_rebase_hook () {
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 4/8] rebase: turn rebase--merge into a separate program
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
                   ` (2 preceding siblings ...)
  2015-03-18  9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 Makefile                       |  2 +-
 git-rebase--merge.sh (mode +x) | 34 ++++++++++++++++++++++++++++++++++
 git-rebase.sh                  |  2 ++
 3 files changed, 37 insertions(+), 1 deletion(-)
 mode change 100644 => 100755 git-rebase--merge.sh

diff --git a/Makefile b/Makefile
index 93151f4..7ee8df7 100644
--- a/Makefile
+++ b/Makefile
@@ -446,6 +446,7 @@ SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-rebase--am.sh
+SCRIPT_SH += git-rebase--merge.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -455,7 +456,6 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--interactive
-SCRIPT_LIB += git-rebase--merge
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
 
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
old mode 100644
new mode 100755
index b10f2cf..baaef41
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -3,8 +3,42 @@
 # Copyright (c) 2010 Junio C Hamano.
 #
 
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
 prec=4
 
+write_basic_state () {
+	echo "$head_name" > "$state_dir"/head-name &&
+	echo "$onto" > "$state_dir"/onto &&
+	echo "$orig_head" > "$state_dir"/orig-head &&
+	echo "$GIT_QUIET" > "$state_dir"/quiet &&
+	test t = "$verbose" && : > "$state_dir"/verbose
+	test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+	test -n "$strategy_opts" && echo "$strategy_opts" > \
+		"$state_dir"/strategy_opts
+	test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+		"$state_dir"/allow_rerere_autoupdate
+}
+
+move_to_original_branch () {
+	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 \
+			-m "rebase finished: returning to $head_name" \
+			HEAD $head_name ||
+		die "$(gettext "Could not move back to $head_name")"
+		;;
+	esac
+}
+
 read_state () {
 	onto_name=$(cat "$state_dir"/onto_name) &&
 	end=$(cat "$state_dir"/end) &&
diff --git a/git-rebase.sh b/git-rebase.sh
index 42e868d..ff2c2ae 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -156,6 +156,8 @@ run_specific_rebase () {
 	export state_dir verbose strategy strategy_opts
 	if [ "$type" = am ]; then
 		exec git-rebase--am
+	elif [ "$type" = merge ]; then
+		exec git-rebase--merge
 	else
 		. git-rebase--$type
 	fi
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 5/8] rebase: turn rebase--interactive into a separate program
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
                   ` (3 preceding siblings ...)
  2015-03-18  9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 Makefile                             |  2 +-
 git-rebase--interactive.sh (mode +x) | 36 +++++++++++++++++++++++++++++++++++-
 git-rebase.sh                        |  9 ++-------
 3 files changed, 38 insertions(+), 9 deletions(-)
 mode change 100644 => 100755 git-rebase--interactive.sh

diff --git a/Makefile b/Makefile
index 7ee8df7..83972a2 100644
--- a/Makefile
+++ b/Makefile
@@ -446,6 +446,7 @@ SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-rebase--am.sh
+SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase--merge.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
@@ -455,7 +456,6 @@ SCRIPT_SH += git-web--browse.sh
 
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--interactive
 SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
 
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
old mode 100644
new mode 100755
index 44901d5..b1c71a9
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -9,7 +9,14 @@
 #
 # The original idea comes from Eric W. Biederman, in
 # http://article.gmane.org/gmane.comp.version-control.git/22407
-#
+
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action "rebase -i ($action)"
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
 # The file containing rebase commands, comments, and empty lines.
 # This file is created by "git rebase -i" then edited by the user.  As
 # the lines are processed, they are removed from the front of this
@@ -80,6 +87,33 @@ rewritten_pending="$state_dir"/rewritten-pending
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
+write_basic_state () {
+	echo "$head_name" > "$state_dir"/head-name &&
+	echo "$onto" > "$state_dir"/onto &&
+	echo "$orig_head" > "$state_dir"/orig-head &&
+	echo "$GIT_QUIET" > "$state_dir"/quiet &&
+	test t = "$verbose" && : > "$state_dir"/verbose
+	test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+	test -n "$strategy_opts" && echo "$strategy_opts" > \
+		"$state_dir"/strategy_opts
+	test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+		"$state_dir"/allow_rerere_autoupdate
+}
+
+output () {
+	case "$verbose" in
+	'')
+		output=$("$@" 2>&1 )
+		status=$?
+		test $status != 0 && printf "%s\n" "$output"
+		return $status
+		;;
+	*)
+		"$@"
+		;;
+	esac
+}
+
 warn () {
 	printf '%s\n' "$*" >&2
 }
diff --git a/git-rebase.sh b/git-rebase.sh
index ff2c2ae..86119e7 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -154,13 +154,8 @@ run_specific_rebase () {
 	export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
 	export onto orig_head rebase_root resolvemsg revisions
 	export state_dir verbose strategy strategy_opts
-	if [ "$type" = am ]; then
-		exec git-rebase--am
-	elif [ "$type" = merge ]; then
-		exec git-rebase--merge
-	else
-		. git-rebase--$type
-	fi
+	export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
+	exec git-rebase--$type
 }
 
 run_pre_rebase_hook () {
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 6/8] rebase: remove unused function
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
                   ` (4 preceding siblings ...)
  2015-03-18  9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

git-rebase.sh is no longer a dependency for rebase subscripts. This
function is only used by subscripts only, which now becomes useless.
---
 git-rebase.sh | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/git-rebase.sh b/git-rebase.sh
index 86119e7..d941239 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -102,19 +102,6 @@ read_basic_state () {
 		allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
 }
 
-write_basic_state () {
-	echo "$head_name" > "$state_dir"/head-name &&
-	echo "$onto" > "$state_dir"/onto &&
-	echo "$orig_head" > "$state_dir"/orig-head &&
-	echo "$GIT_QUIET" > "$state_dir"/quiet &&
-	test t = "$verbose" && : > "$state_dir"/verbose
-	test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
-	test -n "$strategy_opts" && echo "$strategy_opts" > \
-		"$state_dir"/strategy_opts
-	test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
-		"$state_dir"/allow_rerere_autoupdate
-}
-
 output () {
 	case "$verbose" in
 	'')
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
                   ` (5 preceding siblings ...)
  2015-03-18  9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  2015-03-18  9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 git-rebase--am.sh          | 5 +++++
 git-rebase--interactive.sh | 5 +++++
 git-rebase--merge.sh       | 5 +++++
 git-rebase.sh              | 7 +------
 4 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index ab84330..399956b 100755
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -9,6 +9,11 @@ set_reflog_action rebase
 require_work_tree_exists
 
 GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
 
 write_basic_state () {
 	echo "$head_name" > "$state_dir"/head-name &&
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b1c71a9..337dec3 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -16,6 +16,11 @@ set_reflog_action "rebase -i ($action)"
 require_work_tree_exists
 
 GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
 
 # The file containing rebase commands, comments, and empty lines.
 # This file is created by "git rebase -i" then edited by the user.  As
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index baaef41..7e240be 100755
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -9,6 +9,11 @@ set_reflog_action rebase
 require_work_tree_exists
 
 GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
 
 prec=4
 
diff --git a/git-rebase.sh b/git-rebase.sh
index d941239..38530e8 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -49,11 +49,6 @@ cd_to_toplevel
 LF='
 '
 ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'When you have resolved this problem, run "git rebase --continue".
-If you prefer to skip this patch, run "git rebase --skip" instead.
-To check out the original branch and stop rebasing, run "git rebase --abort".')
-"
 unset onto
 cmd=
 strategy=
@@ -139,7 +134,7 @@ run_specific_rebase () {
 	git_quiet=$GIT_QUIET
 	export GIT_PAGER
 	export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
-	export onto orig_head rebase_root resolvemsg revisions
+	export onto orig_head rebase_root revisions
 	export state_dir verbose strategy strategy_opts
 	export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
 	exec git-rebase--$type
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH/WIP 8/8] Build in rebase
  2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
                   ` (6 preceding siblings ...)
  2015-03-18  9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
@ 2015-03-18  9:55 ` Nguyễn Thái Ngọc Duy
  7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18  9:55 UTC (permalink / raw)
  To: git; +Cc: Nguyễn Thái Ngọc Duy

---
 Makefile                                |   2 +-
 builtin.h                               |   1 +
 builtin/rebase.c (new)                  | 752 ++++++++++++++++++++++++++++++++
 commit.c                                |   4 +-
 commit.h                                |   4 +-
 contrib/examples/git-rebase.sh (new +x) | 532 ++++++++++++++++++++++
 git-rebase.sh (gone)                    | 532 ----------------------
 git.c                                   |   1 +
 8 files changed, 1291 insertions(+), 537 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 83972a2..223d50b 100644
--- a/Makefile
+++ b/Makefile
@@ -444,7 +444,6 @@ SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-rebase.sh
 SCRIPT_SH += git-rebase--am.sh
 SCRIPT_SH += git-rebase--interactive.sh
 SCRIPT_SH += git-rebase--merge.sh
@@ -903,6 +902,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.h b/builtin.h
index 7e7bbd6..431bbbf 100644
--- a/builtin.h
+++ b/builtin.h
@@ -108,6 +108,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/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 0000000..29eff0e
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,752 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "submodule.h"
+#include "commit.h"
+#include "dir.h"
+
+#define REBASE_AM		1
+#define REBASE_MERGE		2
+#define REBASE_INTERACTIVE	3
+
+static const char * const builtin_rebase_usage[] = {
+	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]"),
+	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"),
+	N_("git rebase --continue | --abort | --skip | --edit-todo"),
+	NULL
+};
+
+/* These are exported as environment variables for git-rebase--*.sh */
+static int action_abort;
+static int action_continue;
+static int action_skip;
+static int autosquash;
+static int force_rebase;
+static int keep_empty;
+static int preserve_merges;
+static int quiet;
+static int rerere_autoupdate;
+static int root;
+static int verbose;
+static const char *exec_cmd;
+static const char *head_name;
+static const char *onto;
+static const char *orig_head;
+static struct strbuf revisions = STRBUF_INIT;
+static const char *state_basedir;
+static const char *strategy;
+static const char *strategy_opts;
+static const char *switch_to;
+static const char *squash_onto;
+
+static int do_merge;
+static int edit_todo;
+static int interactive_rebase;
+static int rebase_type;
+static int show_stat;
+
+static char *read_file(const char *name)
+{
+	struct strbuf sb = STRBUF_INIT;
+	if (strbuf_read_file(&sb,
+			     git_path("%s/%s", state_basedir, name),
+			     0) >= 0)
+		return strbuf_detach(&sb, NULL);
+	else
+		return NULL;
+}
+
+static char *read_file_or_die(const char *name)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_read_file_or_die(&sb,
+				git_path("%s/%s", state_basedir, name),
+				0);
+	return strbuf_detach(&sb, NULL);
+}
+
+static void read_bool(const char *name, int *var)
+{
+	char *buf = read_file(name);
+	if (buf) {
+		*var = buf[0] && !isspace(buf[0]);
+		free(buf);
+	}
+}
+
+static int read_bool_or_die(const char *name)
+{
+	char *buf = read_file_or_die(name);
+	int ret = buf[0] && !isspace(buf[0]);
+	free(buf);
+	return ret;
+}
+
+/*
+ * Note:
+ *
+ * After git-rebase--*.sh are integrated, we should probably adopt
+ * git-config format and store everything in one file instead of so
+ * many like this. state_dir will be something different to avoid
+ * misuse by old rebase versions. This code will stay for a few major
+ * releases before being phased out.
+ */
+static void read_basic_state()
+{
+	head_name = read_file_or_die("head-name");
+	onto = read_file_or_die("onto");
+	/*
+	 * We always write to orig-head, but interactive rebase used
+	 * to write to head. Fall back to reading from head to cover
+	 * for the case that the user upgraded git with an ongoing
+	 * interactive rebase.
+	 */
+	if ((orig_head = read_file("orig-head")) == NULL)
+		orig_head = read_file_or_die("head");
+	quiet = read_bool_or_die("quiet");
+	read_bool("verbose", &verbose);
+	strategy = read_file("strategy");
+	strategy_opts = read_file("strategy_opts");
+	read_bool("allow_rerere_autoupdate", &rerere_autoupdate);
+}
+
+static void push_env_string(struct argv_array *argv,
+			    const char *name,
+			    const char *value)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addf(&sb, "%s=%s", name, value);
+	argv_array_push(argv, sb.buf);
+	strbuf_release(&sb);
+}
+
+static void push_env_bool(struct argv_array *argv,
+			  const char *name,
+			  int value)
+{
+	struct strbuf sb = STRBUF_INIT;
+	strbuf_addf(&sb, "%s=%s", name, value ? "t" : "");
+	argv_array_push(argv, sb.buf);
+	strbuf_release(&sb);
+}
+
+static int run_specific_rebase()
+{
+	struct child_process cmd;
+	struct argv_array env = ARGV_ARRAY_INIT;
+	const char *argv[2];
+	int ret;
+
+	if (interactive_rebase == -1) {
+		argv_array_push(&env, "GIT_EDITOR=:");
+		autosquash = 0;
+	}
+	push_env_bool(&env, "git_quiet", quiet);
+	if (action_continue)
+		argv_array_push(&env, "action=continue");
+	else if (action_skip)
+		argv_array_push(&env, "action=skip");
+	else if (action_abort)
+		argv_array_push(&env, "action=abort");
+	else if (edit_todo)
+		argv_array_push(&env, "action=edit-todo");
+	else
+		argv_array_push(&env, "action=");
+	push_env_bool(&env,   "allow_rerere_autoupdate", rerere_autoupdate);
+	push_env_bool(&env,   "autosquash",              autosquash);
+	push_env_string(&env, "cmd",                     exec_cmd);
+	push_env_bool(&env,   "force_rebase",            force_rebase);
+	push_env_string(&env, "git_am_opt", ""/* git_am_opt */);
+	push_env_string(&env, "head_name",               head_name);
+	push_env_bool(&env,   "keep_empty",              keep_empty);
+	push_env_string(&env, "onto",                    onto);
+	push_env_string(&env, "orig_head",               orig_head);
+	push_env_bool(&env,   "preserve_merges",         preserve_merges);
+	push_env_bool(&env,   "rebase_root",             root);
+	push_env_string(&env, "revisions",               revisions.buf);
+	push_env_string(&env, "squash_onto",             squash_onto);
+	push_env_string(&env, "state_dir", git_path("%s", state_basedir));
+	push_env_string(&env, "strategy", strategy);
+	push_env_string(&env, "strategy_opts", strategy_opts);
+	push_env_string(&env, "switch_to", switch_to);
+	/* upstream */
+	push_env_bool(&env, "verbose", verbose);
+
+	switch (rebase_type) {
+	case REBASE_INTERACTIVE: argv[0] = "rebase--interactive"; break;
+	case REBASE_AM:		 argv[0] = "rebase--am";	  break;
+	case REBASE_MERGE:	 argv[0] = "rebase--merge";	  break;
+	default:
+		die("BUG: unsupported rebase type %d", rebase_type);
+	}
+	argv[1] = NULL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.in  = 0;
+	cmd.out = 1;
+	cmd.err = 2;
+	cmd.git_cmd = 1;
+	cmd.env = env.argv;
+	cmd.argv = argv;
+	ret = run_command(&cmd);
+	if (ret)
+		die_errno("failed to run %s", argv[0]);
+
+	argv_array_clear(&env);
+	return ret;
+}
+
+static int do_continue()
+{
+	unsigned char sha1[20];
+	if (get_sha1("HEAD", sha1))
+		die(_("Cannot read HEAD"));
+	if (read_cache_unmerged()) {
+		printf(_("You must edit all merge conflicts and then\n"
+			 "mark them as resolved using 'git add'\n"));
+		return 1;
+	}
+	read_basic_state();
+	return run_specific_rebase();
+}
+
+static int do_skip()
+{
+	int ret, error = 0;
+	unsigned char sha1[20];
+	if (get_sha1("HEAD", sha1))
+		die(_("Cannot read HEAD"));
+
+	ret = reset_tree(lookup_commit(sha1)->tree, quiet, 1, &error);
+	if (ret)
+		return ret;
+	read_basic_state();
+	return run_specific_rebase();
+}
+
+static int do_abort()
+{
+	int ret, error = 0;
+	unsigned char sha1[20];
+	const char *rerere[] = { "rerere", "clear", NULL };
+	struct strbuf path = STRBUF_INIT;
+
+	ret = run_command_v_opt(rerere, RUN_GIT_CMD);
+	if (ret)
+		return ret;
+	read_basic_state();
+	if (!prefixcmp(head_name, "refs/"))
+		create_symref("HEAD", head_name, "rebase: aborting");
+
+	if (get_sha1(orig_head, sha1))
+		die(_("Cannot read %s"), orig_head);
+
+	ret = reset_tree(lookup_commit(sha1)->tree, quiet, 1, &error);
+	if (ret)
+		return ret;
+
+	strbuf_addstr(&path, git_path("%s", state_basedir));
+	remove_dir_recursively(&path, 0);
+	strbuf_release(&path);
+	return 0;
+}
+
+static int do_edit_todo()
+{
+	if (rebase_type != REBASE_INTERACTIVE)
+		die(_("The --edit-todo action can only be "
+		      "used during interactive rebase."));
+	return run_specific_rebase();
+}
+
+static void error_on_missing_default_upstream()
+{
+	unsigned char sha1[20];
+	const char *head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+	if (!head)
+		die(_("Failed to resolve HEAD as a valid ref."));
+	if (!strcmp(head, "HEAD")) {
+		printf(_("You are not currently on a branch. Please specify which\n"
+			 "branch you want to rebase against. See git-rebase(1) for details.\n"
+			 "\n"
+			 "    git rebase <branch>\n"));
+		return;
+	}
+
+	if (!prefixcmp(head, "refs/heads/"))
+		head += strlen("refs/heads/");
+	printf(_("There is no tracking information for the current branch.\n"
+		 "Please specify which branch you want to rebase against.\n"
+		 "See git-rebase(1) for details\n"
+		 "\n"
+		 "    git rebase <branch>\n"
+		 "\n"
+		 "If you wish to set tracking information for this branch you can do so with:\n"
+		 "\n"
+		 "    git branch --set-upstream-to=$remote/<branch> %s\n"),
+	       head);
+}
+
+static int determine_upstream(int argc, const char **argv,
+			      const struct option *options,
+			      const char **upstream_name,
+			      const char **upstream_arg)
+{
+	int consumed = 0;
+	if (!root) {
+		unsigned char sha1[20];
+		if (argc) {
+			*upstream_name = argv[0];
+			argv++;
+			argc--;
+			consumed++;
+		} else {
+			*upstream_name = "@{upstream}";
+			if (get_sha1(*upstream_name, sha1)) {
+				error_on_missing_default_upstream();
+				exit(1);
+			}
+		}
+		if (get_sha1(*upstream_name, sha1))
+			die(_("Failed to resolve %s."), *upstream_name);
+		lookup_commit_or_die(sha1, *upstream_name);
+		*upstream_arg = *upstream_name;
+		return consumed;
+	}
+
+	if (!onto) {
+		unsigned char sha1[20];
+		struct strbuf sb = STRBUF_INIT;
+		if (commit_tree(&sb, EMPTY_TREE_SHA1_BIN, NULL, sha1, NULL, NULL))
+			die(_("failed to create empty-tree commit"));
+		onto = xstrdup(sha1_to_hex(sha1));
+		squash_onto = onto;
+	}
+	if (argc > 1)
+		usage_with_options(builtin_rebase_usage,
+				   options);
+	*upstream_arg = "--root";
+	return consumed;
+}
+
+static void require_clean_work_tree()
+{
+	struct rev_info rev;
+	unsigned char sha1[20];
+	int err = 0;
+
+	if (get_sha1("HEAD", sha1))
+		die("HEAD is not a valid ref");
+
+	if (read_cache() < 0)
+		die(_("unable to read index file"));
+	refresh_cache(REFRESH_QUIET | REFRESH_IGNORE_SUBMODULES);
+
+	init_revisions(&rev, NULL);
+	DIFF_OPT_SET(&rev.diffopt, QUICK);
+	DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+	handle_ignore_submodules_arg(&rev.diffopt, "all");
+	diff_setup_done(&rev.diffopt);
+	run_diff_files(&rev, 0);
+	if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+		fputs(_("Cannot rebase: You have unstaged changes.\n"), stderr);
+		err = 1;
+	}
+
+	init_revisions(&rev, NULL);
+	DIFF_OPT_SET(&rev.diffopt, QUICK);
+	DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+	handle_ignore_submodules_arg(&rev.diffopt, "all");
+	diff_setup_done(&rev.diffopt);
+	add_head_to_pending(&rev);
+	run_diff_index(&rev, 1);
+	if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+		const char *msg = err ?
+			_("Additionally, your index contains uncommitted changes.\n") :
+			_("Cannot rebase: Your index contains uncommitted changes.\n");
+		fputs(msg, stderr);
+		err = 1;
+	}
+
+	if (err) {
+		fputs(_("Please commit or stash them.\n"), stderr);
+		exit(1);
+	}
+}
+
+static void run_pre_rebase_hook(int argc, const char **argv,
+				const char *upstream_arg)
+{
+	/*
+	if test -z "$ok_to_skip_pre_rebase" &&
+	   test -x "$GIT_DIR/hooks/pre-rebase"
+	then
+		"$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+		die "$(gettext "The pre-rebase hook refused to rebase.")"
+	fi
+	 */
+}
+
+static int do_rebase(int argc, const char **argv,
+		     const struct option *options)
+{
+	const char *upstream_name = NULL;
+	const char *upstream_arg = NULL;
+	const char *branch_name;
+	const char *onto_name;
+	unsigned char onto_sha1[20];
+
+	int n;
+	if (root && !onto && !interactive_rebase)
+		interactive_rebase = -1; /* implied */
+
+	if (interactive_rebase) {
+		rebase_type = REBASE_INTERACTIVE;
+		state_basedir = "rebase-merge";
+	} else if (do_merge) {
+		rebase_type = REBASE_MERGE;
+		state_basedir = "rebase-merge";
+	} else {
+		rebase_type = REBASE_AM;
+		state_basedir = "rebase-apply";
+	}
+
+	n = determine_upstream(argc, argv, options,
+			       &upstream_name, &upstream_arg);
+	argc += n;
+	argv += n;
+
+	if (root)
+		upstream_arg = "--root";
+
+	/* Make sure the branch to rebase onto is valid. */
+	onto_name = onto ? onto : upstream_name;
+	if (strstr(onto, "...")) {
+		/*
+case "$onto_name" in
+*...*)
+	if	left=${onto_name%...*} right=${onto_name#*...} &&
+		onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+	then
+		case "$onto" in
+		?*"$LF"?*)
+			die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
+			;;
+		'')
+			die "$(eval_gettext "\$onto_name: there is no merge base")"
+			;;
+		esac
+	else
+		die "$(eval_gettext "\$onto_name: there is no merge base")"
+	fi
+	;;
+		*/
+	} else {
+		if (get_sha1(onto_name, onto_sha1) || !lookup_commit(onto_sha1))
+			die(_("Does not point to a valid commit: %s"), onto_name);
+	}
+
+	/*
+	 * 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 (argc) {
+	case 1:
+		/* Is it "rebase other $branchname" or "rebase other $commit"? */
+		branch_name = argv[0];
+		switch_to = argv[0];
+		/*
+	if git show-ref --verify --quiet -- "refs/heads/$1" &&
+	   orig_head=$(git rev-parse -q --verify "refs/heads/$1")
+	then
+		head_name="refs/heads/$1"
+	elif orig_head=$(git rev-parse -q --verify "$1")
+	then
+		head_name="detached HEAD"
+	else
+		die "$(eval_gettext "fatal: no such branch: \$branch_name")"
+	fi
+	 */
+		break;
+
+	case 0:
+		/*
+	# 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
+	orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+		 */
+		break;
+	default:
+		die("BUG: unexpected number of arguments left to parse");
+	}
+
+	require_clean_work_tree();
+
+	/*
+	 * Now we are rebasing commits $upstream..$orig_head (or with
+	 * --root, everything leading up to $orig_head) 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 and if this is not an interactive rebase.
+	 */
+
+	/*
+mb=$(git merge-base "$onto" "$orig_head")
+if test "$type" != interactive && test "$upstream" = "$onto" &&
+	test "$mb" = "$onto" &&
+	# linear history?
+	! (git rev-list --parents "$onto".."$orig_head" | sane_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" --
+		say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+		exit 0
+	else
+		say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+	fi
+fi
+	*/
+
+	/* If a hook exists, give it a chance to interrupt */
+	run_pre_rebase_hook(argc, argv, upstream_arg);
+
+	/*
+if test -n "$diffstat"
+then
+	if test -n "$verbose"
+	then
+		echo "$(eval_gettext "Changes from \$mb to \$onto:")"
+	fi
+	# We want color (if set), but no pager
+	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+	*/
+
+	if (rebase_type == REBASE_INTERACTIVE)
+		return run_specific_rebase();
+
+	/* Detach HEAD and reset the tree */
+	/*
+say "$(gettext "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 $orig_head
+	*/
+
+	/*
+	 * If the $onto is a proper descendant of the tip of the
+	 * branch, then we just fast-forwarded.
+	 */
+	if (!hashcmp(mb, orig_head)) {
+		printf(_("Fast-forwarded %s to %s."), branch_name, onto_name);
+		move_to_original_branch();
+		return 0;
+	}
+
+	if (root)
+		strbuf_addf(&revisions, "%s..%s", onto, orig_head);
+	else
+		strbuf_addf(&revisions, "%s..%s", upstream, orig_head);
+
+	return run_specific_rebase();
+}
+
+static int git_rebase_config(const char *name, const char *value, void *data)
+{
+	if (!strcmp(name, "rebase.stat")) {
+		show_stat = git_config_bool(name, value);
+		return 0;
+	} else if (!strcmp(name, "rebase.autosquash")) {
+		autosquash = git_config_bool(name, value);
+		return 0;
+	}
+	return git_default_config(name, value, data);
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+	const char *whitespace_opt = NULL;
+	int committer_date_is_author_date = 0;
+	int context_opt = 0;
+	int ignore_date = 0;
+	int ignore_whitespace = 0;
+	int no_ff = 0;
+	int pre_rebase = 1;
+	struct option options[] = {
+		OPT__VERBOSE(&verbose,
+			     N_("display a diffstat of what changed upstream")),
+		OPT__QUIET(&quiet,
+			   N_("be quiet. implies --no-stat")),
+		OPT_STRING(0, "onto", &onto, "ref",
+			   N_("rebase onto given branch instead of upstream")),
+		OPT_BOOL('p', "preserve-merges", &preserve_merges,
+			 N_("try to recreate merges instead of ignoring them")),
+		OPT_STRING('s', "stragegy", &strategy, "strategy",
+			   N_("use the given merge strategy")),
+		{ OPTION_SET_INT, 0, "no-ff", &no_ff, NULL,
+		  N_("cherry-pick all commits, even if unchanged"),
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		OPT_BOOL('m', "merge", &do_merge,
+			 N_("use merging strategies to rebase")),
+		OPT_BOOL('i', "interactive", &interactive_rebase,
+			 N_("let the user edit the list of commits to rebase")),
+		OPT_STRING('x', "exec", &exec_cmd, "command",
+			   N_("add exec lines after each commit of the editable list")),
+		OPT_BOOL('k', "keep-empty", &keep_empty,
+			 N_("preserve empty commits during rebase")),
+		OPT_BOOL('f', "force-rebase", &force_rebase,
+			 N_("force rebase even if branch is up to date")),
+		OPT_STRING('X', "strategy-options", &strategy_opts, "options",
+			   N_("pass the argument through to the merge strategy")),
+		{ OPTION_SET_INT, 0, "stat", &show_stat, NULL,
+		  N_("display a diffstat of what changed upstream"),
+		  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+		OPT_SET_INT('n', "no-stat", &show_stat,
+			    N_("do not show diffstat of what changed upstream"), 0),
+		OPT_BOOL(0, "verify", &pre_rebase,
+			 N_("allow pre-rebase hook to run")),
+		OPT_BOOL(0, "rerere-autoupdate", &rerere_autoupdate,
+			 N_("allow rerere to update index with resolved conflicts")),
+		OPT_BOOL(0, "root", &root,
+			 N_("rebase all reachable commits up to the root(s)")),
+		OPT_BOOL(0, "autosquash", &autosquash,
+			 N_("move commits that begin with squash!/fixup! under -i")),
+		OPT_BOOL(0, "committer-date-is-author-date", &committer_date_is_author_date,
+			 N_("lie about committer date")),
+		OPT_BOOL(0, "ignore-date", &ignore_date,
+			 N_("use current timestamp for author date")),
+		OPT_STRING(0, "whitespace", &whitespace_opt, "mode",
+			   N_("detect new or modified lines that have whitespace errors")),
+		OPT_INTEGER('C', NULL, &context_opt,
+			    N_("ensure at least <n> lines of context match by 'git apply'")),
+		OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
+			 N_("ignore changes in whitespace when finding context")),
+
+		OPT_GROUP("Actions"),
+		OPT_BOOL(0, "continue", &action_continue,
+			 N_("continue")),
+		OPT_BOOL(0, "abort", &action_abort,
+			 N_("abort and check out the original branch")),
+		OPT_BOOL(0, "skip", &action_skip,
+			 N_("skip current patch and continue")),
+		OPT_BOOL(0, "edit-todo", &edit_todo,
+			 N_("edit the todo list during an interactive rebase")),
+		OPT_END()
+	};
+	struct stat st;
+	int action_nr, in_progress;
+
+	if (!stat(git_path("rebase-apply"), &st) && S_ISDIR(st.st_mode)) {
+		if (!access(git_path("rebase-apply/applying"), F_OK))
+			die(_("It looks like git-am is in progress. Cannot rebase."));
+		rebase_type = REBASE_AM;
+		state_basedir = "rebase-apply";
+		in_progress = 1;
+	} else if (!stat(git_path("rebase-merge"), &st) &&
+		   S_ISDIR(st.st_mode)) {
+		if (!access(git_path("rebase-merge/interactive"), F_OK)) {
+			rebase_type = REBASE_INTERACTIVE;
+			interactive_rebase = 1; /* explicit */
+		} else
+			rebase_type = REBASE_MERGE;
+		state_basedir = "rebase-merge";
+		in_progress = 1;
+	} else
+		in_progress = 0;
+
+	git_config(git_rebase_config, NULL);
+	gitmodules_config();
+
+	argc = parse_options(argc, argv, prefix, options,
+			     builtin_rebase_usage, 0);
+
+	action_nr = action_continue + action_skip + action_abort + edit_todo;
+	if ((action_nr != 1 && action_nr != 0) || argc > 2)
+		usage_with_options(builtin_rebase_usage, options);
+
+	if (verbose && quiet)
+		die(_("--quiet and --verbose are incompatible"));
+	else if (verbose)
+		show_stat = 1;
+	else if (quiet)
+		show_stat = 0;
+
+	if (preserve_merges && !interactive_rebase)
+		interactive_rebase = -1; /* implied */
+
+	if (strategy_opts) {
+		if (!strategy)
+			strategy = "recursive";
+		/* validate strategy options */
+	}
+
+	if (strategy)
+		do_merge = 1;
+
+	if (!strcmp(whitespace_opt, "fix") ||
+	    !strcmp(whitespace_opt, "strip") ||
+	    committer_date_is_author_date ||
+	    no_ff)
+		force_rebase = 1;
+
+	if (exec_cmd && interactive_rebase != 1)
+		die(_("The --exec option must be used with the --interactive option"));
+
+	if (action_nr) {
+		if (!in_progress)
+			die(_("No rebase in progress?"));
+		if (rebase_type == REBASE_INTERACTIVE)
+			; /* GIT_REFLOG_ACTION = "rebase -i ($action)" */
+
+		if (action_continue)
+			return do_continue();
+		else if (action_skip)
+			return do_skip();
+		else if (action_abort)
+			return do_abort();
+		else if (edit_todo)
+			return do_edit_todo();
+		else
+			die("BUG: how do you get here?");
+	}
+
+	if (in_progress)
+		die(_("It seems that there is already a %s directory, and\n"
+		      "I wonder if you are in the middle of another rebase. If that is the\n"
+		      "case, please try\n"
+		      "\tgit rebase (--continue | --abort | --skip)\n"
+		      "If that is not the case, please\n"
+		      "\trm -rf \"%s/%s\""
+		      "and run me again. I am stopping in case you still have something\n"
+		      "valuable there."),
+		    state_basedir,
+		    get_git_dir(), state_basedir);
+
+	return do_rebase(argc, argv, options);
+}
diff --git a/commit.c b/commit.c
index e8eb0ae..395a860 100644
--- a/commit.c
+++ b/commit.c
@@ -1135,7 +1135,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
 	}
 }
 
-int commit_tree(const struct strbuf *msg, unsigned char *tree,
+int commit_tree(const struct strbuf *msg, const unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret,
 		const char *author, const char *sign_commit)
 {
@@ -1238,7 +1238,7 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 			 struct commit_list *parents, unsigned char *ret,
 			 const char *author, const char *sign_commit,
 			 struct commit_extra_header *extra)
diff --git a/commit.h b/commit.h
index 0f469e5..058886f 100644
--- a/commit.h
+++ b/commit.h
@@ -193,11 +193,11 @@ struct commit_extra_header {
 extern void append_merge_tag_headers(struct commit_list *parents,
 				     struct commit_extra_header ***tail);
 
-extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
 		       struct commit_list *parents, unsigned char *ret,
 		       const char *author, const char *sign_commit);
 
-extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 				struct commit_list *parents, unsigned char *ret,
 				const char *author, const char *sign_commit,
 				struct commit_extra_header *);
diff --git a/contrib/examples/git-rebase.sh b/contrib/examples/git-rebase.sh
new file mode 100755
index 0000000..38530e8
--- /dev/null
+++ b/contrib/examples/git-rebase.sh
@@ -0,0 +1,532 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano.
+#
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
+git-rebase --continue | --abort | --skip | --edit-todo
+--
+ Available options are
+v,verbose!         display a diffstat of what changed upstream
+q,quiet!           be quiet. implies --no-stat
+onto=!             rebase onto given branch instead of upstream
+p,preserve-merges! try to recreate merges instead of ignoring them
+s,strategy=!       use the given merge strategy
+no-ff!             cherry-pick all commits, even if unchanged
+m,merge!           use merging strategies to rebase
+i,interactive!     let the user edit the list of commits to rebase
+x,exec=!           add exec lines after each commit of the editable list
+k,keep-empty	   preserve empty commits during rebase
+f,force-rebase!    force rebase even if branch is up to date
+X,strategy-option=! pass the argument through to the merge strategy
+stat!              display a diffstat of what changed upstream
+n,no-stat!         do not show diffstat of what changed upstream
+verify             allow pre-rebase hook to run
+rerere-autoupdate  allow rerere to update index with resolved conflicts
+root!              rebase all reachable commits up to the root(s)
+autosquash         move commits that begin with squash!/fixup! under -i
+committer-date-is-author-date! passed to 'git am'
+ignore-date!       passed to 'git am'
+whitespace=!       passed to 'git apply'
+ignore-whitespace! passed to 'git apply'
+C=!                passed to 'git apply'
+ Actions:
+continue!          continue
+abort!             abort and check out the original branch
+skip!              skip current patch and continue
+edit-todo!         edit the todo list during an interactive rebase
+"
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+cd_to_toplevel
+
+LF='
+'
+ok_to_skip_pre_rebase=
+unset onto
+cmd=
+strategy=
+strategy_opts=
+do_merge=
+merge_dir="$GIT_DIR"/rebase-merge
+apply_dir="$GIT_DIR"/rebase-apply
+verbose=
+diffstat=
+test "$(git config --bool rebase.stat)" = true && diffstat=t
+git_am_opt=
+rebase_root=
+force_rebase=
+allow_rerere_autoupdate=
+# Non-empty if a rebase was in progress when 'git rebase' was invoked
+in_progress=
+# One of {am, merge, interactive}
+type=
+# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
+state_dir=
+# One of {'', continue, skip, abort}, as parsed from command line
+action=
+preserve_merges=
+autosquash=
+keep_empty=
+test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
+
+read_basic_state () {
+	head_name=$(cat "$state_dir"/head-name) &&
+	onto=$(cat "$state_dir"/onto) &&
+	# We always write to orig-head, but interactive rebase used to write to
+	# head. Fall back to reading from head to cover for the case that the
+	# user upgraded git with an ongoing interactive rebase.
+	if test -f "$state_dir"/orig-head
+	then
+		orig_head=$(cat "$state_dir"/orig-head)
+	else
+		orig_head=$(cat "$state_dir"/head)
+	fi &&
+	GIT_QUIET=$(cat "$state_dir"/quiet) &&
+	test -f "$state_dir"/verbose && verbose=t
+	test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
+	test -f "$state_dir"/strategy_opts &&
+		strategy_opts="$(cat "$state_dir"/strategy_opts)"
+	test -f "$state_dir"/allow_rerere_autoupdate &&
+		allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
+}
+
+output () {
+	case "$verbose" in
+	'')
+		output=$("$@" 2>&1 )
+		status=$?
+		test $status != 0 && printf "%s\n" "$output"
+		return $status
+		;;
+	*)
+		"$@"
+		;;
+	esac
+}
+
+move_to_original_branch () {
+	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 \
+			-m "rebase finished: returning to $head_name" \
+			HEAD $head_name ||
+		die "$(gettext "Could not move back to $head_name")"
+		;;
+	esac
+}
+
+run_specific_rebase () {
+	if [ "$interactive_rebase" = implied ]; then
+		GIT_EDITOR=:
+		export GIT_EDITOR
+		autosquash=
+	fi
+	git_quiet=$GIT_QUIET
+	export GIT_PAGER
+	export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
+	export onto orig_head rebase_root revisions
+	export state_dir verbose strategy strategy_opts
+	export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
+	exec git-rebase--$type
+}
+
+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+"$@"} ||
+		die "$(gettext "The pre-rebase hook refused to rebase.")"
+	fi
+}
+
+test -f "$apply_dir"/applying &&
+	die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
+
+if test -d "$apply_dir"
+then
+	type=am
+	state_dir="$apply_dir"
+elif test -d "$merge_dir"
+then
+	if test -f "$merge_dir"/interactive
+	then
+		type=interactive
+		interactive_rebase=explicit
+	else
+		type=merge
+	fi
+	state_dir="$merge_dir"
+fi
+test -n "$type" && in_progress=t
+
+total_argc=$#
+while test $# != 0
+do
+	case "$1" in
+	--no-verify)
+		ok_to_skip_pre_rebase=yes
+		;;
+	--verify)
+		ok_to_skip_pre_rebase=
+		;;
+	--continue|--skip|--abort|--edit-todo)
+		test $total_argc -eq 2 || usage
+		action=${1##--}
+		;;
+	--onto)
+		test 2 -le "$#" || usage
+		onto="$2"
+		shift
+		;;
+	-x)
+		test 2 -le "$#" || usage
+		cmd="${cmd}exec $2${LF}"
+		shift
+		;;
+	-i)
+		interactive_rebase=explicit
+		;;
+	-k)
+		keep_empty=yes
+		;;
+	-p)
+		preserve_merges=t
+		test -z "$interactive_rebase" && interactive_rebase=implied
+		;;
+	--autosquash)
+		autosquash=t
+		;;
+	--no-autosquash)
+		autosquash=
+		;;
+	-M|-m)
+		do_merge=t
+		;;
+	-X)
+		shift
+		strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
+		do_merge=t
+		test -z "$strategy" && strategy=recursive
+		;;
+	-s)
+		shift
+		strategy="$1"
+		do_merge=t
+		;;
+	-n)
+		diffstat=
+		;;
+	--stat)
+		diffstat=t
+		;;
+	-v)
+		verbose=t
+		diffstat=t
+		GIT_QUIET=
+		;;
+	-q)
+		GIT_QUIET=t
+		git_am_opt="$git_am_opt -q"
+		verbose=
+		diffstat=
+		;;
+	--whitespace)
+		shift
+		git_am_opt="$git_am_opt --whitespace=$1"
+		case "$1" in
+		fix|strip)
+			force_rebase=t
+			;;
+		esac
+		;;
+	--ignore-whitespace)
+		git_am_opt="$git_am_opt $1"
+		;;
+	--committer-date-is-author-date|--ignore-date)
+		git_am_opt="$git_am_opt $1"
+		force_rebase=t
+		;;
+	-C)
+		shift
+		git_am_opt="$git_am_opt -C$1"
+		;;
+	--root)
+		rebase_root=t
+		;;
+	-f|--no-ff)
+		force_rebase=t
+		;;
+	--rerere-autoupdate|--no-rerere-autoupdate)
+		allow_rerere_autoupdate="$1"
+		;;
+	--)
+		shift
+		break
+		;;
+	esac
+	shift
+done
+test $# -gt 2 && usage
+
+if test -n "$cmd" &&
+   test "$interactive_rebase" != explicit
+then
+	die "$(gettext "The --exec option must be used with the --interactive option")"
+fi
+
+if test -n "$action"
+then
+	test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
+	# Only interactive rebase uses detailed reflog messages
+	if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
+	then
+		GIT_REFLOG_ACTION="rebase -i ($action)"
+		export GIT_REFLOG_ACTION
+	fi
+fi
+
+if test "$action" = "edit-todo" && test "$type" != "interactive"
+then
+	die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
+fi
+
+case "$action" in
+continue)
+	# Sanity check
+	git rev-parse --verify HEAD >/dev/null ||
+		die "$(gettext "Cannot read HEAD")"
+	git update-index --ignore-submodules --refresh &&
+	git diff-files --quiet --ignore-submodules || {
+		echo "$(gettext "You must edit all merge conflicts and then
+mark them as resolved using git add")"
+		exit 1
+	}
+	read_basic_state
+	run_specific_rebase
+	;;
+skip)
+	output git reset --hard HEAD || exit $?
+	read_basic_state
+	run_specific_rebase
+	;;
+abort)
+	git rerere clear
+	read_basic_state
+	case "$head_name" in
+	refs/*)
+		git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
+		die "$(eval_gettext "Could not move back to \$head_name")"
+		;;
+	esac
+	output git reset --hard $orig_head
+	rm -r "$state_dir"
+	exit
+	;;
+edit-todo)
+	run_specific_rebase
+	;;
+esac
+
+# Make sure no rebase is in progress
+if test -n "$in_progress"
+then
+	state_dir_base=${state_dir##*/}
+	cmd_live_rebase="git rebase (--continue | --abort | --skip)"
+	cmd_clear_stale_rebase="rm -fr \"$state_dir\""
+	die "
+$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
+I wonder if you are in the middle of another rebase.  If that is the
+case, please try
+	$cmd_live_rebase
+If that is not the case, please
+	$cmd_clear_stale_rebase
+and run me again.  I am stopping in case you still have something
+valuable there.')"
+fi
+
+if test -n "$rebase_root" && test -z "$onto"
+then
+	test -z "$interactive_rebase" && interactive_rebase=implied
+fi
+
+if test -n "$interactive_rebase"
+then
+	type=interactive
+	state_dir="$merge_dir"
+elif test -n "$do_merge"
+then
+	type=merge
+	state_dir="$merge_dir"
+else
+	type=am
+	state_dir="$apply_dir"
+fi
+
+if test -z "$rebase_root"
+then
+	case "$#" in
+	0)
+		if ! upstream_name=$(git rev-parse --symbolic-full-name \
+			--verify -q @{upstream} 2>/dev/null)
+		then
+			. git-parse-remote
+			error_on_missing_default_upstream "rebase" "rebase" \
+				"against" "git rebase <branch>"
+		fi
+		;;
+	*)	upstream_name="$1"
+		shift
+		;;
+	esac
+	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+	die "$(eval_gettext "invalid upstream \$upstream_name")"
+	upstream_arg="$upstream_name"
+else
+	if test -z "$onto"
+	then
+		empty_tree=`git hash-object -t tree /dev/null`
+		onto=`git commit-tree $empty_tree </dev/null`
+		squash_onto="$onto"
+	fi
+	unset upstream_name
+	unset upstream
+	test $# -gt 1 && usage
+	upstream_arg=--root
+fi
+
+# Make sure the branch to rebase onto is valid.
+onto_name=${onto-"$upstream_name"}
+case "$onto_name" in
+*...*)
+	if	left=${onto_name%...*} right=${onto_name#*...} &&
+		onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+	then
+		case "$onto" in
+		?*"$LF"?*)
+			die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
+			;;
+		'')
+			die "$(eval_gettext "\$onto_name: there is no merge base")"
+			;;
+		esac
+	else
+		die "$(eval_gettext "\$onto_name: there is no merge base")"
+	fi
+	;;
+*)
+	onto=$(git rev-parse --verify "${onto_name}^0") ||
+	die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
+	;;
+esac
+
+# 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" &&
+	   orig_head=$(git rev-parse -q --verify "refs/heads/$1")
+	then
+		head_name="refs/heads/$1"
+	elif orig_head=$(git rev-parse -q --verify "$1")
+	then
+		head_name="detached HEAD"
+	else
+		die "$(eval_gettext "fatal: no such branch: \$branch_name")"
+	fi
+	;;
+0)
+	# 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
+	orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+	;;
+*)
+	die "BUG: unexpected number of arguments left to parse"
+	;;
+esac
+
+require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
+
+# Now we are rebasing commits $upstream..$orig_head (or with --root,
+# everything leading up to $orig_head) 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
+# and if this is not an interactive rebase.
+mb=$(git merge-base "$onto" "$orig_head")
+if test "$type" != interactive && test "$upstream" = "$onto" &&
+	test "$mb" = "$onto" &&
+	# linear history?
+	! (git rev-list --parents "$onto".."$orig_head" | sane_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" --
+		say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+		exit 0
+	else
+		say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+	fi
+fi
+
+# If a hook exists, give it a chance to interrupt
+run_pre_rebase_hook "$upstream_arg" "$@"
+
+if test -n "$diffstat"
+then
+	if test -n "$verbose"
+	then
+		echo "$(eval_gettext "Changes from \$mb to \$onto:")"
+	fi
+	# We want color (if set), but no pager
+	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+
+test "$type" = interactive && run_specific_rebase
+
+# Detach HEAD and reset the tree
+say "$(gettext "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 $orig_head
+
+# If the $onto is a proper descendant of the tip of the branch, then
+# we just fast-forwarded.
+if test "$mb" = "$orig_head"
+then
+	say "$(eval_gettext "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
+
+run_specific_rebase
diff --git a/git-rebase.sh b/git-rebase.sh
deleted file mode 100755
index 38530e8..0000000
--- a/git-rebase.sh
+++ /dev/null
@@ -1,532 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_KEEPDASHDASH=
-OPTIONS_SPEC="\
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git-rebase --continue | --abort | --skip | --edit-todo
---
- Available options are
-v,verbose!         display a diffstat of what changed upstream
-q,quiet!           be quiet. implies --no-stat
-onto=!             rebase onto given branch instead of upstream
-p,preserve-merges! try to recreate merges instead of ignoring them
-s,strategy=!       use the given merge strategy
-no-ff!             cherry-pick all commits, even if unchanged
-m,merge!           use merging strategies to rebase
-i,interactive!     let the user edit the list of commits to rebase
-x,exec=!           add exec lines after each commit of the editable list
-k,keep-empty	   preserve empty commits during rebase
-f,force-rebase!    force rebase even if branch is up to date
-X,strategy-option=! pass the argument through to the merge strategy
-stat!              display a diffstat of what changed upstream
-n,no-stat!         do not show diffstat of what changed upstream
-verify             allow pre-rebase hook to run
-rerere-autoupdate  allow rerere to update index with resolved conflicts
-root!              rebase all reachable commits up to the root(s)
-autosquash         move commits that begin with squash!/fixup! under -i
-committer-date-is-author-date! passed to 'git am'
-ignore-date!       passed to 'git am'
-whitespace=!       passed to 'git apply'
-ignore-whitespace! passed to 'git apply'
-C=!                passed to 'git apply'
- Actions:
-continue!          continue
-abort!             abort and check out the original branch
-skip!              skip current patch and continue
-edit-todo!         edit the todo list during an interactive rebase
-"
-. git-sh-setup
-. git-sh-i18n
-set_reflog_action rebase
-require_work_tree_exists
-cd_to_toplevel
-
-LF='
-'
-ok_to_skip_pre_rebase=
-unset onto
-cmd=
-strategy=
-strategy_opts=
-do_merge=
-merge_dir="$GIT_DIR"/rebase-merge
-apply_dir="$GIT_DIR"/rebase-apply
-verbose=
-diffstat=
-test "$(git config --bool rebase.stat)" = true && diffstat=t
-git_am_opt=
-rebase_root=
-force_rebase=
-allow_rerere_autoupdate=
-# Non-empty if a rebase was in progress when 'git rebase' was invoked
-in_progress=
-# One of {am, merge, interactive}
-type=
-# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
-state_dir=
-# One of {'', continue, skip, abort}, as parsed from command line
-action=
-preserve_merges=
-autosquash=
-keep_empty=
-test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-
-read_basic_state () {
-	head_name=$(cat "$state_dir"/head-name) &&
-	onto=$(cat "$state_dir"/onto) &&
-	# We always write to orig-head, but interactive rebase used to write to
-	# head. Fall back to reading from head to cover for the case that the
-	# user upgraded git with an ongoing interactive rebase.
-	if test -f "$state_dir"/orig-head
-	then
-		orig_head=$(cat "$state_dir"/orig-head)
-	else
-		orig_head=$(cat "$state_dir"/head)
-	fi &&
-	GIT_QUIET=$(cat "$state_dir"/quiet) &&
-	test -f "$state_dir"/verbose && verbose=t
-	test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
-	test -f "$state_dir"/strategy_opts &&
-		strategy_opts="$(cat "$state_dir"/strategy_opts)"
-	test -f "$state_dir"/allow_rerere_autoupdate &&
-		allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
-}
-
-output () {
-	case "$verbose" in
-	'')
-		output=$("$@" 2>&1 )
-		status=$?
-		test $status != 0 && printf "%s\n" "$output"
-		return $status
-		;;
-	*)
-		"$@"
-		;;
-	esac
-}
-
-move_to_original_branch () {
-	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 \
-			-m "rebase finished: returning to $head_name" \
-			HEAD $head_name ||
-		die "$(gettext "Could not move back to $head_name")"
-		;;
-	esac
-}
-
-run_specific_rebase () {
-	if [ "$interactive_rebase" = implied ]; then
-		GIT_EDITOR=:
-		export GIT_EDITOR
-		autosquash=
-	fi
-	git_quiet=$GIT_QUIET
-	export GIT_PAGER
-	export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
-	export onto orig_head rebase_root revisions
-	export state_dir verbose strategy strategy_opts
-	export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
-	exec git-rebase--$type
-}
-
-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+"$@"} ||
-		die "$(gettext "The pre-rebase hook refused to rebase.")"
-	fi
-}
-
-test -f "$apply_dir"/applying &&
-	die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
-
-if test -d "$apply_dir"
-then
-	type=am
-	state_dir="$apply_dir"
-elif test -d "$merge_dir"
-then
-	if test -f "$merge_dir"/interactive
-	then
-		type=interactive
-		interactive_rebase=explicit
-	else
-		type=merge
-	fi
-	state_dir="$merge_dir"
-fi
-test -n "$type" && in_progress=t
-
-total_argc=$#
-while test $# != 0
-do
-	case "$1" in
-	--no-verify)
-		ok_to_skip_pre_rebase=yes
-		;;
-	--verify)
-		ok_to_skip_pre_rebase=
-		;;
-	--continue|--skip|--abort|--edit-todo)
-		test $total_argc -eq 2 || usage
-		action=${1##--}
-		;;
-	--onto)
-		test 2 -le "$#" || usage
-		onto="$2"
-		shift
-		;;
-	-x)
-		test 2 -le "$#" || usage
-		cmd="${cmd}exec $2${LF}"
-		shift
-		;;
-	-i)
-		interactive_rebase=explicit
-		;;
-	-k)
-		keep_empty=yes
-		;;
-	-p)
-		preserve_merges=t
-		test -z "$interactive_rebase" && interactive_rebase=implied
-		;;
-	--autosquash)
-		autosquash=t
-		;;
-	--no-autosquash)
-		autosquash=
-		;;
-	-M|-m)
-		do_merge=t
-		;;
-	-X)
-		shift
-		strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
-		do_merge=t
-		test -z "$strategy" && strategy=recursive
-		;;
-	-s)
-		shift
-		strategy="$1"
-		do_merge=t
-		;;
-	-n)
-		diffstat=
-		;;
-	--stat)
-		diffstat=t
-		;;
-	-v)
-		verbose=t
-		diffstat=t
-		GIT_QUIET=
-		;;
-	-q)
-		GIT_QUIET=t
-		git_am_opt="$git_am_opt -q"
-		verbose=
-		diffstat=
-		;;
-	--whitespace)
-		shift
-		git_am_opt="$git_am_opt --whitespace=$1"
-		case "$1" in
-		fix|strip)
-			force_rebase=t
-			;;
-		esac
-		;;
-	--ignore-whitespace)
-		git_am_opt="$git_am_opt $1"
-		;;
-	--committer-date-is-author-date|--ignore-date)
-		git_am_opt="$git_am_opt $1"
-		force_rebase=t
-		;;
-	-C)
-		shift
-		git_am_opt="$git_am_opt -C$1"
-		;;
-	--root)
-		rebase_root=t
-		;;
-	-f|--no-ff)
-		force_rebase=t
-		;;
-	--rerere-autoupdate|--no-rerere-autoupdate)
-		allow_rerere_autoupdate="$1"
-		;;
-	--)
-		shift
-		break
-		;;
-	esac
-	shift
-done
-test $# -gt 2 && usage
-
-if test -n "$cmd" &&
-   test "$interactive_rebase" != explicit
-then
-	die "$(gettext "The --exec option must be used with the --interactive option")"
-fi
-
-if test -n "$action"
-then
-	test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
-	# Only interactive rebase uses detailed reflog messages
-	if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
-	then
-		GIT_REFLOG_ACTION="rebase -i ($action)"
-		export GIT_REFLOG_ACTION
-	fi
-fi
-
-if test "$action" = "edit-todo" && test "$type" != "interactive"
-then
-	die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
-fi
-
-case "$action" in
-continue)
-	# Sanity check
-	git rev-parse --verify HEAD >/dev/null ||
-		die "$(gettext "Cannot read HEAD")"
-	git update-index --ignore-submodules --refresh &&
-	git diff-files --quiet --ignore-submodules || {
-		echo "$(gettext "You must edit all merge conflicts and then
-mark them as resolved using git add")"
-		exit 1
-	}
-	read_basic_state
-	run_specific_rebase
-	;;
-skip)
-	output git reset --hard HEAD || exit $?
-	read_basic_state
-	run_specific_rebase
-	;;
-abort)
-	git rerere clear
-	read_basic_state
-	case "$head_name" in
-	refs/*)
-		git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
-		die "$(eval_gettext "Could not move back to \$head_name")"
-		;;
-	esac
-	output git reset --hard $orig_head
-	rm -r "$state_dir"
-	exit
-	;;
-edit-todo)
-	run_specific_rebase
-	;;
-esac
-
-# Make sure no rebase is in progress
-if test -n "$in_progress"
-then
-	state_dir_base=${state_dir##*/}
-	cmd_live_rebase="git rebase (--continue | --abort | --skip)"
-	cmd_clear_stale_rebase="rm -fr \"$state_dir\""
-	die "
-$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
-I wonder if you are in the middle of another rebase.  If that is the
-case, please try
-	$cmd_live_rebase
-If that is not the case, please
-	$cmd_clear_stale_rebase
-and run me again.  I am stopping in case you still have something
-valuable there.')"
-fi
-
-if test -n "$rebase_root" && test -z "$onto"
-then
-	test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-if test -n "$interactive_rebase"
-then
-	type=interactive
-	state_dir="$merge_dir"
-elif test -n "$do_merge"
-then
-	type=merge
-	state_dir="$merge_dir"
-else
-	type=am
-	state_dir="$apply_dir"
-fi
-
-if test -z "$rebase_root"
-then
-	case "$#" in
-	0)
-		if ! upstream_name=$(git rev-parse --symbolic-full-name \
-			--verify -q @{upstream} 2>/dev/null)
-		then
-			. git-parse-remote
-			error_on_missing_default_upstream "rebase" "rebase" \
-				"against" "git rebase <branch>"
-		fi
-		;;
-	*)	upstream_name="$1"
-		shift
-		;;
-	esac
-	upstream=`git rev-parse --verify "${upstream_name}^0"` ||
-	die "$(eval_gettext "invalid upstream \$upstream_name")"
-	upstream_arg="$upstream_name"
-else
-	if test -z "$onto"
-	then
-		empty_tree=`git hash-object -t tree /dev/null`
-		onto=`git commit-tree $empty_tree </dev/null`
-		squash_onto="$onto"
-	fi
-	unset upstream_name
-	unset upstream
-	test $# -gt 1 && usage
-	upstream_arg=--root
-fi
-
-# Make sure the branch to rebase onto is valid.
-onto_name=${onto-"$upstream_name"}
-case "$onto_name" in
-*...*)
-	if	left=${onto_name%...*} right=${onto_name#*...} &&
-		onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
-	then
-		case "$onto" in
-		?*"$LF"?*)
-			die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
-			;;
-		'')
-			die "$(eval_gettext "\$onto_name: there is no merge base")"
-			;;
-		esac
-	else
-		die "$(eval_gettext "\$onto_name: there is no merge base")"
-	fi
-	;;
-*)
-	onto=$(git rev-parse --verify "${onto_name}^0") ||
-	die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
-	;;
-esac
-
-# 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" &&
-	   orig_head=$(git rev-parse -q --verify "refs/heads/$1")
-	then
-		head_name="refs/heads/$1"
-	elif orig_head=$(git rev-parse -q --verify "$1")
-	then
-		head_name="detached HEAD"
-	else
-		die "$(eval_gettext "fatal: no such branch: \$branch_name")"
-	fi
-	;;
-0)
-	# 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
-	orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
-	;;
-*)
-	die "BUG: unexpected number of arguments left to parse"
-	;;
-esac
-
-require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
-
-# Now we are rebasing commits $upstream..$orig_head (or with --root,
-# everything leading up to $orig_head) 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
-# and if this is not an interactive rebase.
-mb=$(git merge-base "$onto" "$orig_head")
-if test "$type" != interactive && test "$upstream" = "$onto" &&
-	test "$mb" = "$onto" &&
-	# linear history?
-	! (git rev-list --parents "$onto".."$orig_head" | sane_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" --
-		say "$(eval_gettext "Current branch \$branch_name is up to date.")"
-		exit 0
-	else
-		say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
-	fi
-fi
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
-
-if test -n "$diffstat"
-then
-	if test -n "$verbose"
-	then
-		echo "$(eval_gettext "Changes from \$mb to \$onto:")"
-	fi
-	# We want color (if set), but no pager
-	GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
-fi
-
-test "$type" = interactive && run_specific_rebase
-
-# Detach HEAD and reset the tree
-say "$(gettext "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 $orig_head
-
-# If the $onto is a proper descendant of the tip of the branch, then
-# we just fast-forwarded.
-if test "$mb" = "$orig_head"
-then
-	say "$(eval_gettext "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
-
-run_specific_rebase
diff --git a/git.c b/git.c
index ed66c66..a4cb4a8 100644
--- a/git.c
+++ b/git.c
@@ -379,6 +379,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 },
-- 
2.3.0.rc1.137.g477eb31

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2015-03-18  9:57 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-18  9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
2015-03-18  9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).