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