git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v0 1/3] doc/git-rebase.txt: remove mention of multiple strategies
@ 2009-05-21  9:47 Nguyễn Thái Ngọc Duy
  2009-05-21  9:47 ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Nguyễn Thái Ngọc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2009-05-21  9:47 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy

git-rebase.sh does not seem to support this.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 I have starred at git-rebase.sh many times and still don't know how
 it can do multiple strategies. So assume it's wrong, remove it.

 Documentation/git-rebase.txt |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 3d5a066..26f3b7b 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -231,8 +231,7 @@ OPTIONS
 
 -s <strategy>::
 --strategy=<strategy>::
-	Use the given merge strategy; can be supplied more than
-	once to specify them in the order they should be tried.
+	Use the given merge strategy.
 	If there is no `-s` option, a built-in list of strategies
 	is used instead ('git-merge-recursive' when merging a single
 	head, 'git-merge-octopus' otherwise).  This implies --merge.
-- 
test

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

* [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C
  2009-05-21  9:47 [PATCH v0 1/3] doc/git-rebase.txt: remove mention of multiple strategies Nguyễn Thái Ngọc Duy
@ 2009-05-21  9:47 ` Nguyễn Thái Ngọc Duy
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
                     ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2009-05-21  9:47 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy

These new tests make sure I don't miss any check being performed before
rebase is proceeded (which is well tested by other tests)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 t/t3400-rebase.sh |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 6e391a3..37f86ab 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -41,9 +41,37 @@ test_expect_success \
      git tag topic
 '
 
+test_expect_success 'rebase on dirty worktree' '
+     echo dirty >> A &&
+     ! git rebase master'
+
+test_expect_success 'rebase on dirty cache' '
+     git add  A &&
+     ! git rebase master'
+
 test_expect_success 'rebase against master' '
+     git reset HEAD &&
+     git checkout -f &&
      git rebase master'
 
+test_expect_success 'rebase against master twice' '
+	git rebase master 2>&1|grep "Current branch my-topic-branch is up to date\\."
+'
+
+test_expect_success 'rebase against master twice with --force' '
+	git rebase --force-rebase master 2>&1|grep "Current branch my-topic-branch is up to date, rebase forced"
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+	git checkout my-topic-branch^ &&
+	git rebase master my-topic-branch 2>&1|grep "Current branch my-topic-branch is up to date\\."
+'
+
+test_expect_success 'rebase fast-forward to master' '
+	git checkout my-topic-branch^ &&
+	git rebase my-topic-branch 2>&1|grep "Fast-forwarded HEAD to my-topic-branch"
+'
+
 test_expect_success \
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
-- 
test

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

* [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21  9:47 ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Nguyễn Thái Ngọc Duy
@ 2009-05-21  9:47   ` Nguyễn Thái Ngọc Duy
  2009-05-21 10:25     ` Jakub Narebski
                       ` (2 more replies)
  2009-05-21 10:22   ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Jakub Narebski
  2009-05-21 14:22   ` Junio C Hamano
  2 siblings, 3 replies; 21+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2009-05-21  9:47 UTC (permalink / raw)
  To: git, Junio C Hamano; +Cc: Nguyễn Thái Ngọc Duy


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

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

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

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

* Re: [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C
  2009-05-21  9:47 ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Nguyễn Thái Ngọc Duy
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
@ 2009-05-21 10:22   ` Jakub Narebski
  2009-05-21 10:39     ` Nguyen Thai Ngoc Duy
  2009-05-21 14:22   ` Junio C Hamano
  2 siblings, 1 reply; 21+ messages in thread
From: Jakub Narebski @ 2009-05-21 10:22 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy    <pclouds@gmail.com> writes:

> These new tests make sure I don't miss any check being performed before
> rebase is proceeded (which is well tested by other tests)
> 
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  t/t3400-rebase.sh |   28 ++++++++++++++++++++++++++++
>  1 files changed, 28 insertions(+), 0 deletions(-)
> 
> diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
> index 6e391a3..37f86ab 100755
> --- a/t/t3400-rebase.sh
> +++ b/t/t3400-rebase.sh
> @@ -41,9 +41,37 @@ test_expect_success \
>       git tag topic
>  '
>  
> +test_expect_success 'rebase on dirty worktree' '
> +     echo dirty >> A &&
> +     ! git rebase master'

Shouldn't you use test_must_fail instead? From t/test-lib.sh
(paraphrasing):

  Writing this as "! git rebase master" is wrong, because
  the failure could be due to a segv.  We want a controlled failure.

> +
> +test_expect_success 'rebase on dirty cache' '
> +     git add  A &&
> +     ! git rebase master'
> +
>  test_expect_success 'rebase against master' '
> +     git reset HEAD &&
> +     git checkout -f &&
>       git rebase master'
>  
> +test_expect_success 'rebase against master twice' '
> +	git rebase master 2>&1|grep "Current branch my-topic-branch is up to date\\."
> +'

It would be more readable to split this line on '|', i.e.:

+test_expect_success 'rebase against master twice' '
+	git rebase master 2>&1 |
+	grep "Current branch my-topic-branch is up to date\\."
+'

I think we prefer to save output to a file, and compare this file with
expected output (using test_cmp).  Additionally you can check if the
message you want is in correct stream (is in STDERR, and STDOUT is
empty).

> +
> +test_expect_success 'rebase against master twice with --force' '
> +	git rebase --force-rebase master 2>&1|grep "Current branch my-topic-branch is up to date, rebase forced"
> +'
> +
> +test_expect_success 'rebase against master twice from another branch' '
> +	git checkout my-topic-branch^ &&
> +	git rebase master my-topic-branch 2>&1|grep "Current branch my-topic-branch is up to date\\."
> +'
> +
> +test_expect_success 'rebase fast-forward to master' '
> +	git checkout my-topic-branch^ &&
> +	git rebase my-topic-branch 2>&1|grep "Fast-forwarded HEAD to my-topic-branch"
> +'

Same as above.

> +
>  test_expect_success \
>      'the rebase operation should not have destroyed author information' \
>      '! (git log | grep "Author:" | grep "<>")'

Errrr... what?  Why git-log and not git-cat-file?  Why grep twice?
Additionally you do not check here that author is unchanged, only that
is not destroyed.

-- 
Jakub Narebski
Poland
ShadeHawk on #git

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
@ 2009-05-21 10:25     ` Jakub Narebski
  2009-05-21 10:44       ` Nguyen Thai Ngoc Duy
  2009-05-21 14:39     ` Junio C Hamano
  2009-05-22  6:56     ` Johannes Sixt
  2 siblings, 1 reply; 21+ messages in thread
From: Jakub Narebski @ 2009-05-21 10:25 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy    <pclouds@gmail.com> writes:

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> ---
>  Regression: "-M" is gone. Don't really want to mess up struct option for "-M"
> 
>  Makefile                       |    2 +-
>  builtin-rebase.c               |  992 ++++++++++++++++++++++++++++++++++++++++
>  builtin.h                      |    1 +
>  contrib/examples/git-rebase.sh |  530 +++++++++++++++++++++
>  git-rebase.sh                  |  530 ---------------------
>  git.c                          |    1 +
>  6 files changed, 1525 insertions(+), 531 deletions(-)
>  create mode 100644 builtin-rebase.c
>  create mode 100755 contrib/examples/git-rebase.sh
>  delete mode 100755 git-rebase.sh

You should have used -M option to git-format-patch to make it clear
that this patch moves git-rebase.sh to contrib/examples/git-rebase.sh
without changes.
 

[...]
> +#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

I see misaligns here...

[...]

Couldn't you use parseopt also in subcommands?

-- 
Jakub Narebski
Poland
ShadeHawk on #git

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

* Re: [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help  migrating git-rebase.sh to C
  2009-05-21 10:22   ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Jakub Narebski
@ 2009-05-21 10:39     ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-21 10:39 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano

2009/5/21 Jakub Narebski <jnareb@gmail.com>:
> Nguyễn Thái Ngọc Duy    <pclouds@gmail.com> writes:
>
>> These new tests make sure I don't miss any check being performed before
>> rebase is proceeded (which is well tested by other tests)
>>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>>  t/t3400-rebase.sh |   28 ++++++++++++++++++++++++++++
>>  1 files changed, 28 insertions(+), 0 deletions(-)
>>
>> diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
>> index 6e391a3..37f86ab 100755
>> --- a/t/t3400-rebase.sh
>> +++ b/t/t3400-rebase.sh
>> @@ -41,9 +41,37 @@ test_expect_success \
>>       git tag topic
>>  '
>>
>> +test_expect_success 'rebase on dirty worktree' '
>> +     echo dirty >> A &&
>> +     ! git rebase master'
>
> Shouldn't you use test_must_fail instead? From t/test-lib.sh
> (paraphrasing):
>
>  Writing this as "! git rebase master" is wrong, because
>  the failure could be due to a segv.  We want a controlled failure.

Right. Shouldn't we have another patch to fix this once and for all? I
did "grep -F '! git'" and find a few places applicable too.

>> +
>>  test_expect_success \
>>      'the rebase operation should not have destroyed author information' \
>>      '! (git log | grep "Author:" | grep "<>")'
>
> Errrr... what?  Why git-log and not git-cat-file?  Why grep twice?
> Additionally you do not check here that author is unchanged, only that
> is not destroyed.

Errr.. this is not from me. No idea why.
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21 10:25     ` Jakub Narebski
@ 2009-05-21 10:44       ` Nguyen Thai Ngoc Duy
  2009-05-21 12:29         ` Jakub Narebski
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-21 10:44 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano

2009/5/21 Jakub Narebski <jnareb@gmail.com>:
> Nguyễn Thái Ngọc Duy    <pclouds@gmail.com> writes:
>
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>> ---
>>  Regression: "-M" is gone. Don't really want to mess up struct option for "-M"
>>
>>  Makefile                       |    2 +-
>>  builtin-rebase.c               |  992 ++++++++++++++++++++++++++++++++++++++++
>>  builtin.h                      |    1 +
>>  contrib/examples/git-rebase.sh |  530 +++++++++++++++++++++
>>  git-rebase.sh                  |  530 ---------------------
>>  git.c                          |    1 +
>>  6 files changed, 1525 insertions(+), 531 deletions(-)
>>  create mode 100644 builtin-rebase.c
>>  create mode 100755 contrib/examples/git-rebase.sh
>>  delete mode 100755 git-rebase.sh
>
> You should have used -M option to git-format-patch to make it clear
> that this patch moves git-rebase.sh to contrib/examples/git-rebase.sh
> without changes.

Yes, much shorter patch. I recall some issues about reversable patch
long ago so I did not bother finding this option.

> [...]
>> +#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
>
> I see misaligns here...

Hmm.. it's perfectly aligned in complete source form. Probably because of TABs.

> [...]
>
> Couldn't you use parseopt also in subcommands?

I don't understand. You mean parseopt for --continue, --skip and --abort?
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21 10:44       ` Nguyen Thai Ngoc Duy
@ 2009-05-21 12:29         ` Jakub Narebski
  2009-05-21 22:57           ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Jakub Narebski @ 2009-05-21 12:29 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano

On Thu, 21 May 2009, Nguyen Thai Ngoc Duy wrote:
> 2009/5/21 Jakub Narebski <jnareb@gmail.com>:
>> Nguyễn Thái Ngọc Duy    <pclouds@gmail.com> writes:

>> [...]
>>> +#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
>>
>> I see misaligns here...
> 
> Hmm.. it's perfectly aligned in complete source form. Probably
> because of TABs. 

Yes, it is because of using TABs to align (and not only to indent),
and probably because of changing tabstop because of extra character
('+' at beginning).  I don't know good solution for this problem.
 
>> [...]
>>
>> Couldn't you use parseopt also in subcommands?
> 
> I don't understand. You mean parseopt for --continue, --skip and
> --abort? 

Yes. I don't know if it possible, and if it would make sense, but it
seems strange to me having both parseopts in main, and strcmp
comparisons for other parts...

-- 
Jakub Narebski
Poland

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

* Re: [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C
  2009-05-21  9:47 ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Nguyễn Thái Ngọc Duy
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
  2009-05-21 10:22   ` [PATCH v0 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C Jakub Narebski
@ 2009-05-21 14:22   ` Junio C Hamano
  2009-05-23 15:31     ` [PATCH " Nguyễn Thái Ngọc Duy
  2 siblings, 1 reply; 21+ messages in thread
From: Junio C Hamano @ 2009-05-21 14:22 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> +test_expect_success 'rebase on dirty worktree' '
> +     echo dirty >> A &&
> +     ! git rebase master'

It is a good defensive way to use "test_must_fail" instead of "!" when
testing git itself; test_must_fail does not allow the program to terminate
with non-zero value by segfaulting, while "!" does.

> +test_expect_success 'rebase on dirty cache' '
> +     git add  A &&

Two-spaces?

> +     ! git rebase master'
> +
>  test_expect_success 'rebase against master' '
> +     git reset HEAD &&
> +     git checkout -f &&

Hmm, why not "reset --hard HEAD".  Not asking to change (yet), but just
asking if there is a reason.

>       git rebase master'
>  
> +test_expect_success 'rebase against master twice' '
> +	git rebase master 2>&1|grep "Current branch my-topic-branch is up to date\\."
> +'
> +
> +test_expect_success 'rebase against master twice with --force' '
> +	git rebase --force-rebase master 2>&1|grep "Current branch my-topic-branch is up to date, rebase forced"

Do not to use any pipe while testing, i.e.

	git rebase >out 2>err &&
        grep "what you expect in 'out'" out &&
        grep "what you expect in 'err'" err

so that you can catch exit status from the command you placed in the
upstream of the pipe.

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
  2009-05-21 10:25     ` Jakub Narebski
@ 2009-05-21 14:39     ` Junio C Hamano
  2009-05-22  6:56     ` Johannes Sixt
  2 siblings, 0 replies; 21+ messages in thread
From: Junio C Hamano @ 2009-05-21 14:39 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git

Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:

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

Good riddance ;-).  It's not documented, nor I even knew it existed.

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21 12:29         ` Jakub Narebski
@ 2009-05-21 22:57           ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-21 22:57 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, Junio C Hamano

On Thu, May 21, 2009 at 10:29 PM, Jakub Narebski <jnareb@gmail.com> wrote:
>>> [...]
>>>
>>> Couldn't you use parseopt also in subcommands?
>>
>> I don't understand. You mean parseopt for --continue, --skip and
>> --abort?
>
> Yes. I don't know if it possible, and if it would make sense, but it
> seems strange to me having both parseopts in main, and strcmp
> comparisons for other parts...

Ah you meant --interactive. It's because git-rebase--interactive.sh is
a separate command and it needs full arguments. parseopts eats
arguments. I'm too lazy to rebuild argv after being eaten by
parseopts. So just search for "interactive" sign, then pass them all
to git-rebase--interactive.sh (until git-rebase--interactive.sh is
built in)
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-21  9:47   ` [PATCH v0 3/3] Build in git-rebase.sh Nguyễn Thái Ngọc Duy
  2009-05-21 10:25     ` Jakub Narebski
  2009-05-21 14:39     ` Junio C Hamano
@ 2009-05-22  6:56     ` Johannes Sixt
  2009-05-22  7:30       ` Nguyen Thai Ngoc Duy
  2 siblings, 1 reply; 21+ messages in thread
From: Johannes Sixt @ 2009-05-22  6:56 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Junio C Hamano

Nguyễn Thái Ngọc Duy schrieb:
> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

Is it possible for you to test this series on Windows? Many rebase tests
fail, but I haven't investigated why.

-- Hannes

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-22  6:56     ` Johannes Sixt
@ 2009-05-22  7:30       ` Nguyen Thai Ngoc Duy
  2009-05-23  9:26         ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-22  7:30 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano

2009/5/22 Johannes Sixt <j.sixt@viscovery.net>:
> Nguyễn Thái Ngọc Duy schrieb:
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>
> Is it possible for you to test this series on Windows? Many rebase tests
> fail, but I haven't investigated why.

I'll try it this weekend.
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-22  7:30       ` Nguyen Thai Ngoc Duy
@ 2009-05-23  9:26         ` Nguyen Thai Ngoc Duy
  2009-05-23 14:50           ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-23  9:26 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano

On Fri, May 22, 2009 at 05:30:31PM +1000, Nguyen Thai Ngoc Duy wrote:
> 2009/5/22 Johannes Sixt <j.sixt@viscovery.net>:
> > Nguyễn Thái Ngọc Duy schrieb:
> >> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> >
> > Is it possible for you to test this series on Windows? Many rebase tests
> > fail, but I haven't investigated why.
> 
> I'll try it this weekend.

This patch makes t3*rebase*.sh pass for me except t3412 (more exactly
t3412.8). That test failed even with git-rebase.sh. Hmm... Anyway
could you try again to see what tests still fail?

--<--
diff --git a/builtin-rebase.c b/builtin-rebase.c
index 2acea59..5582f1d 100644
--- a/builtin-rebase.c
+++ b/builtin-rebase.c
@@ -56,7 +56,8 @@ static int rebase_config(const char *var, const char *value, void *data)
 			((struct rebase_opt*)data)->flags |= REBASE_STAT;
 		return 0;
 	}
-	return 0;
+
+	return git_default_config(var, value, data);
 }
 
 /* utility functions */
@@ -504,7 +505,7 @@ static void run_interactive(int argc, const char **argv)
 	ret = run_command(&cp);
 	if (IS_RUN_COMMAND_ERR(ret))
 		die("Failed to run git rebase--interactive");
-	exit(ret);
+	exit(-ret);
 }
 
 /* from diff.c */
--<--

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-23  9:26         ` Nguyen Thai Ngoc Duy
@ 2009-05-23 14:50           ` Nguyen Thai Ngoc Duy
  2009-05-25  6:16             ` Johannes Sixt
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-23 14:50 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Brandon Casey

On Sat, May 23, 2009 at 07:26:03PM +1000, Nguyen Thai Ngoc Duy wrote:
> On Fri, May 22, 2009 at 05:30:31PM +1000, Nguyen Thai Ngoc Duy wrote:
> > 2009/5/22 Johannes Sixt <j.sixt@viscovery.net>:
> > > Nguyễn Thái Ngọc Duy schrieb:
> > >> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> > >
> > > Is it possible for you to test this series on Windows? Many rebase tests
> > > fail, but I haven't investigated why.
> > 
> > I'll try it this weekend.
> 
> This patch makes t3*rebase*.sh pass for me except t3412 (more exactly
> t3412.8). That test failed even with git-rebase.sh. Hmm... Anyway
> could you try again to see what tests still fail?

Someone with better Windows knowledge than me should explain how this works. Windows'
snprintf() just cuts out the last character in this statement:

snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'

All rebase tests now pass for me on Windows (Vista something, I have
yet to find where it hides its "uname" command)

-->--
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 357e733..1cea768 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -13,7 +13,7 @@
 int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
 {
 	char *s;
-	int ret = -1;
+	int size, ret = -1;
 
 	if (maxsize > 0) {
 		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
@@ -26,18 +26,19 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
 		return ret;
 
 	s = NULL;
-	if (maxsize < 128)
-		maxsize = 128;
+	size = maxsize < 128 ? 128 : maxsize;
 
 	while (ret == -1) {
-		maxsize *= 4;
-		str = realloc(s, maxsize);
-		if (! str)
+		size *= 4;
+		s = realloc(s, size);
+		if (!s)
 			break;
-		s = str;
-		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
-		if (ret == maxsize-1)
+		s = s;
+		ret = vsnprintf(s, size-SNPRINTF_SIZE_CORR, format, ap);
+		if (ret == size-1)
 			ret = -1;
+		else
+			memcpy(str, s, maxsize-1);
 	}
 	free(s);
 	return ret;
-->--

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

* [PATCH 2/3] t/t3400-rebase.sh: add more tests to help migrating git-rebase.sh to C
  2009-05-21 14:22   ` Junio C Hamano
@ 2009-05-23 15:31     ` Nguyễn Thái Ngọc Duy
  0 siblings, 0 replies; 21+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2009-05-23 15:31 UTC (permalink / raw)
  To: git, Junio C Hamano, Jakub Narebski; +Cc: Nguyễn Thái Ngọc Duy

These new tests make sure I don't miss any check being performed before
rebase is proceeded (which is well tested by other tests)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Fixed pipe stuff, test_must_fail and spaces

 t/t3400-rebase.sh |   31 +++++++++++++++++++++++++++++++
 1 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 6e391a3..7f62bfb 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -41,9 +41,40 @@ test_expect_success \
      git tag topic
 '
 
+test_expect_success 'rebase on dirty worktree' '
+     echo dirty >> A &&
+     test_must_fail git rebase master'
+
+test_expect_success 'rebase on dirty cache' '
+     git add A &&
+     test_must_fail git rebase master'
+
 test_expect_success 'rebase against master' '
+     git reset --hard HEAD &&
      git rebase master'
 
+test_expect_success 'rebase against master twice' '
+     git rebase master 2>err &&
+     grep "Current branch my-topic-branch is up to date" err
+'
+
+test_expect_success 'rebase against master twice with --force' '
+     git rebase --force-rebase master >out &&
+     grep "Current branch my-topic-branch is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+     git checkout my-topic-branch^ &&
+     git rebase master my-topic-branch 2>err &&
+     grep "Current branch my-topic-branch is up to date" err
+'
+
+test_expect_success 'rebase fast-forward to master' '
+     git checkout my-topic-branch^ &&
+     git rebase my-topic-branch 2>err &&
+     grep "Fast-forwarded HEAD to my-topic-branch" err
+'
+
 test_expect_success \
     'the rebase operation should not have destroyed author information' \
     '! (git log | grep "Author:" | grep "<>")'
-- 
test

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-23 14:50           ` Nguyen Thai Ngoc Duy
@ 2009-05-25  6:16             ` Johannes Sixt
  2009-05-25  6:34               ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Johannes Sixt @ 2009-05-25  6:16 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano, Brandon Casey

Nguyen Thai Ngoc Duy schrieb:
> On Sat, May 23, 2009 at 07:26:03PM +1000, Nguyen Thai Ngoc Duy wrote:
>> On Fri, May 22, 2009 at 05:30:31PM +1000, Nguyen Thai Ngoc Duy wrote:
>>> 2009/5/22 Johannes Sixt <j.sixt@viscovery.net>:
>>>> Nguyễn Thái Ngọc Duy schrieb:
>>>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>>> Is it possible for you to test this series on Windows? Many rebase tests
>>>> fail, but I haven't investigated why.
>>> I'll try it this weekend.
>> This patch makes t3*rebase*.sh pass for me except t3412 (more exactly
>> t3412.8). That test failed even with git-rebase.sh. Hmm... Anyway
>> could you try again to see what tests still fail?
> 
> Someone with better Windows knowledge than me should explain how this works. Windows'
> snprintf() just cuts out the last character in this statement:
> 
> snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'

This doesn't happen for me: neither with Windows's original snprintf nor
with the version from compat/. Are you using the latest msysgit
environment to compile, i.e. gcc 4.4? There was a change regarding
SNPRINTF_SIZE_CORR; perhaps that's the culprit?

I don't undertand what this patch does, anyway. Where is the detail that I
am missing?

> All rebase tests now pass for me on Windows (Vista something, I have
> yet to find where it hides its "uname" command)

They also pass for me with your earlier fix-up patch, but with or without
this patch to compat/snprintf.c.

> diff --git a/compat/snprintf.c b/compat/snprintf.c
> index 357e733..1cea768 100644
> --- a/compat/snprintf.c
> +++ b/compat/snprintf.c
> @@ -13,7 +13,7 @@
>  int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
>  {
>  	char *s;
> -	int ret = -1;
> +	int size, ret = -1;
>  
>  	if (maxsize > 0) {
>  		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
> @@ -26,18 +26,19 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
>  		return ret;
>  
>  	s = NULL;
> -	if (maxsize < 128)
> -		maxsize = 128;
> +	size = maxsize < 128 ? 128 : maxsize;
>  
>  	while (ret == -1) {
> -		maxsize *= 4;
> -		str = realloc(s, maxsize);
> -		if (! str)
> +		size *= 4;
> +		s = realloc(s, size);
> +		if (!s)
>  			break;
> -		s = str;
> -		ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
> -		if (ret == maxsize-1)
> +		s = s;
> +		ret = vsnprintf(s, size-SNPRINTF_SIZE_CORR, format, ap);
> +		if (ret == size-1)
>  			ret = -1;
> +		else
> +			memcpy(str, s, maxsize-1);
>  	}
>  	free(s);
>  	return ret;

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-25  6:16             ` Johannes Sixt
@ 2009-05-25  6:34               ` Nguyen Thai Ngoc Duy
  2009-05-25  6:47                 ` Johannes Sixt
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-25  6:34 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Brandon Casey

On Mon, May 25, 2009 at 4:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
> Nguyen Thai Ngoc Duy schrieb:
>> On Sat, May 23, 2009 at 07:26:03PM +1000, Nguyen Thai Ngoc Duy wrote:
>>> On Fri, May 22, 2009 at 05:30:31PM +1000, Nguyen Thai Ngoc Duy wrote:
>>>> 2009/5/22 Johannes Sixt <j.sixt@viscovery.net>:
>>>>> Nguyễn Thái Ngọc Duy schrieb:
>>>>>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
>>>>> Is it possible for you to test this series on Windows? Many rebase tests
>>>>> fail, but I haven't investigated why.
>>>> I'll try it this weekend.
>>> This patch makes t3*rebase*.sh pass for me except t3412 (more exactly
>>> t3412.8). That test failed even with git-rebase.sh. Hmm... Anyway
>>> could you try again to see what tests still fail?
>>
>> Someone with better Windows knowledge than me should explain how this works. Windows'
>> snprintf() just cuts out the last character in this statement:
>>
>> snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'
>
> This doesn't happen for me: neither with Windows's original snprintf nor
> with the version from compat/. Are you using the latest msysgit
> environment to compile, i.e. gcc 4.4? There was a change regarding
> SNPRINTF_SIZE_CORR; perhaps that's the culprit?

It could be. I used git.git's next branch, not msys4git or mingw.git
repositories.

> I don't undertand what this patch does, anyway. Where is the detail that I
> am missing?

The first vsnprintf() in "onto" case above did not produce " --onto"
as it should have been. I tried to recover by filling "str" with the
second vsnprintf() in the loop.

>> All rebase tests now pass for me on Windows (Vista something, I have
>> yet to find where it hides its "uname" command)
>
> They also pass for me with your earlier fix-up patch, but with or without
> this patch to compat/snprintf.c.

Good to know.
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-25  6:34               ` Nguyen Thai Ngoc Duy
@ 2009-05-25  6:47                 ` Johannes Sixt
  2009-05-25  7:00                   ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Johannes Sixt @ 2009-05-25  6:47 UTC (permalink / raw)
  To: Nguyen Thai Ngoc Duy; +Cc: git, Junio C Hamano, Brandon Casey

Nguyen Thai Ngoc Duy schrieb:
> On Mon, May 25, 2009 at 4:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>> Nguyen Thai Ngoc Duy schrieb:
>>> Someone with better Windows knowledge than me should explain how this works. Windows'
>>> snprintf() just cuts out the last character in this statement:
>>>
>>> snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'
>> This doesn't happen for me: neither with Windows's original snprintf nor
>> with the version from compat/. Are you using the latest msysgit
>> environment to compile, i.e. gcc 4.4? There was a change regarding
>> SNPRINTF_SIZE_CORR; perhaps that's the culprit?
> 
> It could be. I used git.git's next branch, not msys4git or mingw.git
> repositories.

OK. Assuming you are using the latest msysgit compiler, which is gcc 4.4,
then you need the SNPRINTF_SIZE_CORR related patch from 4msysgit.git.

-- Hannes

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-25  6:47                 ` Johannes Sixt
@ 2009-05-25  7:00                   ` Nguyen Thai Ngoc Duy
  2009-05-25  7:39                     ` Nguyen Thai Ngoc Duy
  0 siblings, 1 reply; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-25  7:00 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Brandon Casey

On Mon, May 25, 2009 at 4:47 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
> Nguyen Thai Ngoc Duy schrieb:
>> On Mon, May 25, 2009 at 4:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>>> Nguyen Thai Ngoc Duy schrieb:
>>>> Someone with better Windows knowledge than me should explain how this works. Windows'
>>>> snprintf() just cuts out the last character in this statement:
>>>>
>>>> snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'
>>> This doesn't happen for me: neither with Windows's original snprintf nor
>>> with the version from compat/. Are you using the latest msysgit
>>> environment to compile, i.e. gcc 4.4? There was a change regarding
>>> SNPRINTF_SIZE_CORR; perhaps that's the culprit?
>>
>> It could be. I used git.git's next branch, not msys4git or mingw.git
>> repositories.
>
> OK. Assuming you are using the latest msysgit compiler, which is gcc 4.4,
> then you need the SNPRINTF_SIZE_CORR related patch from 4msysgit.git.

It's cross compiler 4.3. Will try the patch.
-- 
Duy

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

* Re: [PATCH v0 3/3] Build in git-rebase.sh
  2009-05-25  7:00                   ` Nguyen Thai Ngoc Duy
@ 2009-05-25  7:39                     ` Nguyen Thai Ngoc Duy
  0 siblings, 0 replies; 21+ messages in thread
From: Nguyen Thai Ngoc Duy @ 2009-05-25  7:39 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: git, Junio C Hamano, Brandon Casey

On Mon, May 25, 2009 at 5:00 PM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:
> On Mon, May 25, 2009 at 4:47 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>> Nguyen Thai Ngoc Duy schrieb:
>>> On Mon, May 25, 2009 at 4:16 PM, Johannes Sixt <j.sixt@viscovery.net> wrote:
>>>> Nguyen Thai Ngoc Duy schrieb:
>>>>> Someone with better Windows knowledge than me should explain how this works. Windows'
>>>>> snprintf() just cuts out the last character in this statement:
>>>>>
>>>>> snprintf(buf, 8, "--%s", "onto"); // result: '--ont', expected: '--onto'
>>>> This doesn't happen for me: neither with Windows's original snprintf nor
>>>> with the version from compat/. Are you using the latest msysgit
>>>> environment to compile, i.e. gcc 4.4? There was a change regarding
>>>> SNPRINTF_SIZE_CORR; perhaps that's the culprit?
>>>
>>> It could be. I used git.git's next branch, not msys4git or mingw.git
>>> repositories.
>>
>> OK. Assuming you are using the latest msysgit compiler, which is gcc 4.4,
>> then you need the SNPRINTF_SIZE_CORR related patch from 4msysgit.git.
>
> It's cross compiler 4.3. Will try the patch.

It is because of SNPRINTF_SIZE_CORR. Thank you.
-- 
Duy

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

end of thread, other threads:[~2009-05-25  7:46 UTC | newest]

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

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).