* [PATCH/WIP 0/8] Convert git-rebase.sh to C
@ 2015-03-18 9:55 Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
In the spirit of sharing code proactively [1], despite my embarrassment,
this is what I got for converting git-rebase.sh to C. Note that this
is only about git-rebase.sh, not git-rebase--*.sh. Some changes in
git-rebase.sh are pushed back to git-rebase--*.sh. The idea is we
convert git-rebase.sh first, then the rest later.
Anybody who wants to base their work on this code, feel free to
--reset-author (and take all the bugs with you, I'm sure there are
lots of them). This work is from 2013. git-rebase.sh has received lots
of updates since then, so there's still lots of work to do.
[1] http://article.gmane.org/gmane.comp.version-control.git/265699
Nguyễn Thái Ngọc Duy (8):
strbuf: add and use strbuf_read_file_or_die()
Move reset_tree from builtin/checkout.c to unpack-trees.c
rebase: turn rebase--am into a separate program
rebase: turn rebase--merge into a separate program
rebase: turn rebase--interactive into a separate program
rebase: remove unused function
rebase: move resolvemsg to rebase--* scripts
Build in rebase
Makefile | 8 +-
builtin.h | 1 +
builtin/blame.c | 4 +-
builtin/checkout.c | 40 +-
builtin/commit.c | 16 +-
builtin/merge.c | 32 +-
builtin/notes.c | 4 +-
builtin/rebase.c (new) | 752 ++++++++++++++++++++++++++++++++
builtin/tag.c | 7 +-
commit.c | 4 +-
commit.h | 4 +-
contrib/examples/git-rebase.sh (new +x) | 532 ++++++++++++++++++++++
git-rebase--am.sh (mode +x) | 39 ++
git-rebase--interactive.sh (mode +x) | 41 +-
git-rebase--merge.sh (mode +x) | 39 ++
git-rebase.sh (gone) | 544 -----------------------
git.c | 1 +
strbuf.c | 8 +
strbuf.h | 1 +
unpack-trees.c | 33 ++
unpack-trees.h | 4 +
21 files changed, 1480 insertions(+), 634 deletions(-)
create mode 100644 builtin/rebase.c
create mode 100755 contrib/examples/git-rebase.sh
mode change 100644 => 100755 git-rebase--am.sh
mode change 100644 => 100755 git-rebase--interactive.sh
mode change 100644 => 100755 git-rebase--merge.sh
delete mode 100755 git-rebase.sh
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die()
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
builtin/blame.c | 4 ++--
builtin/commit.c | 16 +++++-----------
builtin/merge.c | 3 +--
builtin/notes.c | 4 ++--
builtin/tag.c | 7 ++-----
strbuf.c | 8 ++++++++
strbuf.h | 1 +
7 files changed, 21 insertions(+), 22 deletions(-)
diff --git a/builtin/blame.c b/builtin/blame.c
index bc6c899..503595c 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -2193,8 +2193,8 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
- else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
- die_errno("cannot open or read '%s'", read_from);
+ else
+ strbuf_read_file_or_die(&buf, read_from, st.st_size);
break;
case S_IFLNK:
if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
diff --git a/builtin/commit.c b/builtin/commit.c
index d6dd3df..dad9acf 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -612,9 +612,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
die_errno(_("could not read log from standard input"));
hook_arg1 = "message";
} else if (logfile) {
- if (strbuf_read_file(&sb, logfile, 0) < 0)
- die_errno(_("could not read log file '%s'"),
- logfile);
+ strbuf_read_file_or_die(&sb, logfile, 0);
hook_arg1 = "message";
} else if (use_message) {
buffer = strstr(use_message_buffer, "\n\n");
@@ -634,16 +632,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
&sb, &ctx);
hook_arg1 = "message";
} else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
- die_errno(_("could not read MERGE_MSG"));
+ strbuf_read_file_or_die(&sb, git_path("MERGE_MSG"), 0);
hook_arg1 = "merge";
} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
- die_errno(_("could not read SQUASH_MSG"));
+ strbuf_read_file_or_die(&sb, git_path("SQUASH_MSG"), 0);
hook_arg1 = "squash";
} else if (template_file) {
- if (strbuf_read_file(&sb, template_file, 0) < 0)
- die_errno(_("could not read '%s'"), template_file);
+ strbuf_read_file_or_die(&sb, template_file, 0);
hook_arg1 = "template";
clean_message_contents = 0;
}
@@ -1497,8 +1492,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
fclose(fp);
strbuf_release(&m);
if (!stat(git_path("MERGE_MODE"), &statbuf)) {
- if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
- die_errno(_("could not read MERGE_MODE"));
+ strbuf_read_file_or_die(&sb, git_path("MERGE_MODE"), 0);
if (!strcmp(sb.buf, "no-ff"))
allow_fast_forward = 0;
}
diff --git a/builtin/merge.c b/builtin/merge.c
index 9307e9c..6babf39 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -769,8 +769,7 @@ static void read_merge_msg(struct strbuf *msg)
{
const char *filename = git_path("MERGE_MSG");
strbuf_reset(msg);
- if (strbuf_read_file(msg, filename, 0) < 0)
- die_errno(_("Could not read from '%s'"), filename);
+ strbuf_read_file_or_die(msg, filename, 0);
}
static void write_merge_state(struct commit_list *);
diff --git a/builtin/notes.c b/builtin/notes.c
index 453457a..3210c7f 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -252,8 +252,8 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
if (!strcmp(arg, "-")) {
if (strbuf_read(&(msg->buf), 0, 1024) < 0)
die_errno(_("cannot read '%s'"), arg);
- } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
- die_errno(_("could not open or read '%s'"), arg);
+ } else
+ strbuf_read_file_or_die(&(msg->buf), arg, 0);
stripspace(&(msg->buf), 0);
msg->given = 1;
diff --git a/builtin/tag.c b/builtin/tag.c
index 9c3e067..69f4ea3 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -540,11 +540,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (!strcmp(msgfile, "-")) {
if (strbuf_read(&buf, 0, 1024) < 0)
die_errno(_("cannot read '%s'"), msgfile);
- } else {
- if (strbuf_read_file(&buf, msgfile, 1024) < 0)
- die_errno(_("could not open or read '%s'"),
- msgfile);
- }
+ } else
+ strbuf_read_file_or_die(&buf, msgfile, 0);
}
}
diff --git a/strbuf.c b/strbuf.c
index 9a373be..9f50478 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -411,6 +411,14 @@ int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
return len;
}
+void strbuf_read_file_or_die(struct strbuf *sb, const char *path, size_t size)
+{
+ int ret;
+ ret = strbuf_read_file(sb, path, size);
+ if (ret < 0 || (size && ret != size))
+ die_errno(_("could not open or read '%s'"), path);
+}
+
void strbuf_add_lines(struct strbuf *out, const char *prefix,
const char *buf, size_t size)
{
diff --git a/strbuf.h b/strbuf.h
index ecae4e2..c1f012d 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -152,6 +152,7 @@ extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
/* XXX: if read fails, any partial read is undone */
extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
+extern void strbuf_read_file_or_die(struct strbuf *sb, const char *path, size_t size);
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
builtin/checkout.c | 40 +++-------------------------------------
builtin/merge.c | 29 +++++++----------------------
unpack-trees.c | 33 +++++++++++++++++++++++++++++++++
unpack-trees.h | 4 ++++
4 files changed, 47 insertions(+), 59 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index a9c1b5a..93b18ad 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -362,40 +362,6 @@ static void describe_detached_head(const char *msg, struct commit *commit)
strbuf_release(&sb);
}
-static int reset_tree(struct tree *tree, const struct checkout_opts *o,
- int worktree, int *writeout_error)
-{
- struct unpack_trees_options opts;
- struct tree_desc tree_desc;
-
- memset(&opts, 0, sizeof(opts));
- opts.head_idx = -1;
- opts.update = worktree;
- opts.skip_unmerged = !worktree;
- opts.reset = 1;
- opts.merge = 1;
- opts.fn = oneway_merge;
- opts.verbose_update = !o->quiet && isatty(2);
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
- parse_tree(tree);
- init_tree_desc(&tree_desc, tree->buffer, tree->size);
- switch (unpack_trees(1, &tree_desc, &opts)) {
- case -2:
- *writeout_error = 1;
- /*
- * We return 0 nevertheless, as the index is all right
- * and more importantly we have made best efforts to
- * update paths in the work tree, and we cannot revert
- * them.
- */
- case 0:
- return 0;
- default:
- return 128;
- }
-}
-
struct branch_info {
const char *name; /* The short name used */
const char *path; /* The full name of a real branch */
@@ -427,7 +393,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
resolve_undo_clear();
if (opts->force) {
- ret = reset_tree(new->commit->tree, opts, 1, writeout_error);
+ ret = reset_tree(new->commit->tree, opts->quiet, 1, writeout_error);
if (ret)
return ret;
} else {
@@ -513,7 +479,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.verbosity = 0;
work = write_tree_from_memory(&o);
- ret = reset_tree(new->commit->tree, opts, 1,
+ ret = reset_tree(new->commit->tree, opts->quiet, 1,
writeout_error);
if (ret)
return ret;
@@ -522,7 +488,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.branch2 = "local";
merge_trees(&o, new->commit->tree, work,
old->commit->tree, &result);
- ret = reset_tree(new->commit->tree, opts, 0,
+ ret = reset_tree(new->commit->tree, opts->quiet, 0,
writeout_error);
if (ret)
return ret;
diff --git a/builtin/merge.c b/builtin/merge.c
index 6babf39..c51e4f5 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -269,33 +269,18 @@ static void read_empty(unsigned const char *sha1, int verbose)
die(_("read-tree failed"));
}
-static void reset_hard(unsigned const char *sha1, int verbose)
-{
- int i = 0;
- const char *args[6];
-
- args[i++] = "read-tree";
- if (verbose)
- args[i++] = "-v";
- args[i++] = "--reset";
- args[i++] = "-u";
- args[i++] = sha1_to_hex(sha1);
- args[i] = NULL;
-
- if (run_command_v_opt(args, RUN_GIT_CMD))
- die(_("read-tree failed"));
-}
-
-static void restore_state(const unsigned char *head,
+static void restore_state(struct commit *head_commit,
const unsigned char *stash)
{
struct strbuf sb = STRBUF_INIT;
const char *args[] = { "stash", "apply", NULL, NULL };
+ int error = 0;
if (is_null_sha1(stash))
return;
- reset_hard(head, 1);
+ if (reset_tree(head_commit->tree, 0, 1, &error) || error)
+ die(_("read-tree failed"));
args[2] = sha1_to_hex(stash);
@@ -1409,7 +1394,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
int ret;
if (i) {
printf(_("Rewinding the tree to pristine...\n"));
- restore_state(head_commit->object.sha1, stash);
+ restore_state(head_commit, stash);
}
if (use_strategies_nr != 1)
printf(_("Trying merge strategy %s...\n"),
@@ -1475,7 +1460,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* it up.
*/
if (!best_strategy) {
- restore_state(head_commit->object.sha1, stash);
+ restore_state(head_commit, stash);
if (use_strategies_nr > 1)
fprintf(stderr,
_("No merge strategy handled the merge.\n"));
@@ -1488,7 +1473,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
; /* We already have its result in the working tree. */
else {
printf(_("Rewinding the tree to pristine...\n"));
- restore_state(head_commit->object.sha1, stash);
+ restore_state(head_commit, stash);
printf(_("Using the %s to prepare resolving by hand.\n"),
best_strategy);
try_merge_strategy(best_strategy, common, remoteheads,
diff --git a/unpack-trees.c b/unpack-trees.c
index 0e1a196..3c24a4d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1847,3 +1847,36 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
}
return merged_entry(a, old, o);
}
+
+int reset_tree(struct tree *tree, int quiet, int worktree, int *writeout_error)
+{
+ struct unpack_trees_options opts;
+ struct tree_desc tree_desc;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = -1;
+ opts.update = worktree;
+ opts.skip_unmerged = !worktree;
+ opts.reset = 1;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ opts.verbose_update = !quiet && isatty(2);
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ parse_tree(tree);
+ init_tree_desc(&tree_desc, tree->buffer, tree->size);
+ switch (unpack_trees(1, &tree_desc, &opts)) {
+ case -2:
+ *writeout_error = 1;
+ /*
+ * We return 0 nevertheless, as the index is all right
+ * and more importantly we have made best efforts to
+ * update paths in the work tree, and we cannot revert
+ * them.
+ */
+ case 0:
+ return 0;
+ default:
+ return 128;
+ }
+}
diff --git a/unpack-trees.h b/unpack-trees.h
index ec74a9f..f0850bb 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -83,4 +83,8 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+
+struct tree;
+extern int reset_tree(struct tree *tree, int quiet,
+ int worktree, int *writeout_error);
#endif
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
Makefile | 2 +-
git-rebase--am.sh (mode +x) | 34 ++++++++++++++++++++++++++++++++++
git-rebase.sh | 11 ++++++++++-
3 files changed, 45 insertions(+), 2 deletions(-)
mode change 100644 => 100755 git-rebase--am.sh
diff --git a/Makefile b/Makefile
index 1b30d7b..93151f4 100644
--- a/Makefile
+++ b/Makefile
@@ -445,6 +445,7 @@ SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-rebase--am.sh
SCRIPT_SH += git-repack.sh
SCRIPT_SH += git-request-pull.sh
SCRIPT_SH += git-stash.sh
@@ -453,7 +454,6 @@ SCRIPT_SH += git-web--browse.sh
SCRIPT_LIB += git-mergetool--lib
SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--am
SCRIPT_LIB += git-rebase--interactive
SCRIPT_LIB += git-rebase--merge
SCRIPT_LIB += git-sh-setup
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
old mode 100644
new mode 100755
index 97f31dc..ab84330
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -3,6 +3,40 @@
# Copyright (c) 2010 Junio C Hamano.
#
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
+write_basic_state () {
+ echo "$head_name" > "$state_dir"/head-name &&
+ echo "$onto" > "$state_dir"/onto &&
+ echo "$orig_head" > "$state_dir"/orig-head &&
+ echo "$GIT_QUIET" > "$state_dir"/quiet &&
+ test t = "$verbose" && : > "$state_dir"/verbose
+ test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+ test -n "$strategy_opts" && echo "$strategy_opts" > \
+ "$state_dir"/strategy_opts
+ test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+ "$state_dir"/allow_rerere_autoupdate
+}
+
+move_to_original_branch () {
+ case "$head_name" in
+ refs/*)
+ message="rebase finished: $head_name onto $onto"
+ git update-ref -m "$message" \
+ $head_name $(git rev-parse HEAD) $orig_head &&
+ git symbolic-ref \
+ -m "rebase finished: returning to $head_name" \
+ HEAD $head_name ||
+ die "$(gettext "Could not move back to $head_name")"
+ ;;
+ esac
+}
+
case "$action" in
continue)
git am --resolved --resolvemsg="$resolvemsg" &&
diff --git a/git-rebase.sh b/git-rebase.sh
index b2f1c76..42e868d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -149,7 +149,16 @@ run_specific_rebase () {
export GIT_EDITOR
autosquash=
fi
- . git-rebase--$type
+ git_quiet=$GIT_QUIET
+ export GIT_PAGER
+ export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
+ export onto orig_head rebase_root resolvemsg revisions
+ export state_dir verbose strategy strategy_opts
+ if [ "$type" = am ]; then
+ exec git-rebase--am
+ else
+ . git-rebase--$type
+ fi
}
run_pre_rebase_hook () {
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 4/8] rebase: turn rebase--merge into a separate program
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
` (2 preceding siblings ...)
2015-03-18 9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
Makefile | 2 +-
git-rebase--merge.sh (mode +x) | 34 ++++++++++++++++++++++++++++++++++
git-rebase.sh | 2 ++
3 files changed, 37 insertions(+), 1 deletion(-)
mode change 100644 => 100755 git-rebase--merge.sh
diff --git a/Makefile b/Makefile
index 93151f4..7ee8df7 100644
--- a/Makefile
+++ b/Makefile
@@ -446,6 +446,7 @@ SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
SCRIPT_SH += git-rebase.sh
SCRIPT_SH += git-rebase--am.sh
+SCRIPT_SH += git-rebase--merge.sh
SCRIPT_SH += git-repack.sh
SCRIPT_SH += git-request-pull.sh
SCRIPT_SH += git-stash.sh
@@ -455,7 +456,6 @@ SCRIPT_SH += git-web--browse.sh
SCRIPT_LIB += git-mergetool--lib
SCRIPT_LIB += git-parse-remote
SCRIPT_LIB += git-rebase--interactive
-SCRIPT_LIB += git-rebase--merge
SCRIPT_LIB += git-sh-setup
SCRIPT_LIB += git-sh-i18n
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
old mode 100644
new mode 100755
index b10f2cf..baaef41
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -3,8 +3,42 @@
# Copyright (c) 2010 Junio C Hamano.
#
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
prec=4
+write_basic_state () {
+ echo "$head_name" > "$state_dir"/head-name &&
+ echo "$onto" > "$state_dir"/onto &&
+ echo "$orig_head" > "$state_dir"/orig-head &&
+ echo "$GIT_QUIET" > "$state_dir"/quiet &&
+ test t = "$verbose" && : > "$state_dir"/verbose
+ test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+ test -n "$strategy_opts" && echo "$strategy_opts" > \
+ "$state_dir"/strategy_opts
+ test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+ "$state_dir"/allow_rerere_autoupdate
+}
+
+move_to_original_branch () {
+ case "$head_name" in
+ refs/*)
+ message="rebase finished: $head_name onto $onto"
+ git update-ref -m "$message" \
+ $head_name $(git rev-parse HEAD) $orig_head &&
+ git symbolic-ref \
+ -m "rebase finished: returning to $head_name" \
+ HEAD $head_name ||
+ die "$(gettext "Could not move back to $head_name")"
+ ;;
+ esac
+}
+
read_state () {
onto_name=$(cat "$state_dir"/onto_name) &&
end=$(cat "$state_dir"/end) &&
diff --git a/git-rebase.sh b/git-rebase.sh
index 42e868d..ff2c2ae 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -156,6 +156,8 @@ run_specific_rebase () {
export state_dir verbose strategy strategy_opts
if [ "$type" = am ]; then
exec git-rebase--am
+ elif [ "$type" = merge ]; then
+ exec git-rebase--merge
else
. git-rebase--$type
fi
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 5/8] rebase: turn rebase--interactive into a separate program
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
` (3 preceding siblings ...)
2015-03-18 9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
Makefile | 2 +-
git-rebase--interactive.sh (mode +x) | 36 +++++++++++++++++++++++++++++++++++-
git-rebase.sh | 9 ++-------
3 files changed, 38 insertions(+), 9 deletions(-)
mode change 100644 => 100755 git-rebase--interactive.sh
diff --git a/Makefile b/Makefile
index 7ee8df7..83972a2 100644
--- a/Makefile
+++ b/Makefile
@@ -446,6 +446,7 @@ SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
SCRIPT_SH += git-rebase.sh
SCRIPT_SH += git-rebase--am.sh
+SCRIPT_SH += git-rebase--interactive.sh
SCRIPT_SH += git-rebase--merge.sh
SCRIPT_SH += git-repack.sh
SCRIPT_SH += git-request-pull.sh
@@ -455,7 +456,6 @@ SCRIPT_SH += git-web--browse.sh
SCRIPT_LIB += git-mergetool--lib
SCRIPT_LIB += git-parse-remote
-SCRIPT_LIB += git-rebase--interactive
SCRIPT_LIB += git-sh-setup
SCRIPT_LIB += git-sh-i18n
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
old mode 100644
new mode 100755
index 44901d5..b1c71a9
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -9,7 +9,14 @@
#
# The original idea comes from Eric W. Biederman, in
# http://article.gmane.org/gmane.comp.version-control.git/22407
-#
+
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action "rebase -i ($action)"
+require_work_tree_exists
+
+GIT_QUIET=$git_quiet
+
# The file containing rebase commands, comments, and empty lines.
# This file is created by "git rebase -i" then edited by the user. As
# the lines are processed, they are removed from the front of this
@@ -80,6 +87,33 @@ rewritten_pending="$state_dir"/rewritten-pending
GIT_CHERRY_PICK_HELP="$resolvemsg"
export GIT_CHERRY_PICK_HELP
+write_basic_state () {
+ echo "$head_name" > "$state_dir"/head-name &&
+ echo "$onto" > "$state_dir"/onto &&
+ echo "$orig_head" > "$state_dir"/orig-head &&
+ echo "$GIT_QUIET" > "$state_dir"/quiet &&
+ test t = "$verbose" && : > "$state_dir"/verbose
+ test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
+ test -n "$strategy_opts" && echo "$strategy_opts" > \
+ "$state_dir"/strategy_opts
+ test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
+ "$state_dir"/allow_rerere_autoupdate
+}
+
+output () {
+ case "$verbose" in
+ '')
+ output=$("$@" 2>&1 )
+ status=$?
+ test $status != 0 && printf "%s\n" "$output"
+ return $status
+ ;;
+ *)
+ "$@"
+ ;;
+ esac
+}
+
warn () {
printf '%s\n' "$*" >&2
}
diff --git a/git-rebase.sh b/git-rebase.sh
index ff2c2ae..86119e7 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -154,13 +154,8 @@ run_specific_rebase () {
export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
export onto orig_head rebase_root resolvemsg revisions
export state_dir verbose strategy strategy_opts
- if [ "$type" = am ]; then
- exec git-rebase--am
- elif [ "$type" = merge ]; then
- exec git-rebase--merge
- else
- . git-rebase--$type
- fi
+ export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
+ exec git-rebase--$type
}
run_pre_rebase_hook () {
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 6/8] rebase: remove unused function
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
` (4 preceding siblings ...)
2015-03-18 9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
git-rebase.sh is no longer a dependency for rebase subscripts. This
function is only used by subscripts only, which now becomes useless.
---
git-rebase.sh | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/git-rebase.sh b/git-rebase.sh
index 86119e7..d941239 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -102,19 +102,6 @@ read_basic_state () {
allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
}
-write_basic_state () {
- echo "$head_name" > "$state_dir"/head-name &&
- echo "$onto" > "$state_dir"/onto &&
- echo "$orig_head" > "$state_dir"/orig-head &&
- echo "$GIT_QUIET" > "$state_dir"/quiet &&
- test t = "$verbose" && : > "$state_dir"/verbose
- test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy
- test -n "$strategy_opts" && echo "$strategy_opts" > \
- "$state_dir"/strategy_opts
- test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \
- "$state_dir"/allow_rerere_autoupdate
-}
-
output () {
case "$verbose" in
'')
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
` (5 preceding siblings ...)
2015-03-18 9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
git-rebase--am.sh | 5 +++++
git-rebase--interactive.sh | 5 +++++
git-rebase--merge.sh | 5 +++++
git-rebase.sh | 7 +------
4 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/git-rebase--am.sh b/git-rebase--am.sh
index ab84330..399956b 100755
--- a/git-rebase--am.sh
+++ b/git-rebase--am.sh
@@ -9,6 +9,11 @@ set_reflog_action rebase
require_work_tree_exists
GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
write_basic_state () {
echo "$head_name" > "$state_dir"/head-name &&
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b1c71a9..337dec3 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -16,6 +16,11 @@ set_reflog_action "rebase -i ($action)"
require_work_tree_exists
GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
# The file containing rebase commands, comments, and empty lines.
# This file is created by "git rebase -i" then edited by the user. As
diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh
index baaef41..7e240be 100755
--- a/git-rebase--merge.sh
+++ b/git-rebase--merge.sh
@@ -9,6 +9,11 @@ set_reflog_action rebase
require_work_tree_exists
GIT_QUIET=$git_quiet
+resolvemsg="
+$(gettext 'When you have resolved this problem, run "git rebase --continue".
+If you prefer to skip this patch, run "git rebase --skip" instead.
+To check out the original branch and stop rebasing, run "git rebase --abort".')
+"
prec=4
diff --git a/git-rebase.sh b/git-rebase.sh
index d941239..38530e8 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -49,11 +49,6 @@ cd_to_toplevel
LF='
'
ok_to_skip_pre_rebase=
-resolvemsg="
-$(gettext 'When you have resolved this problem, run "git rebase --continue".
-If you prefer to skip this patch, run "git rebase --skip" instead.
-To check out the original branch and stop rebasing, run "git rebase --abort".')
-"
unset onto
cmd=
strategy=
@@ -139,7 +134,7 @@ run_specific_rebase () {
git_quiet=$GIT_QUIET
export GIT_PAGER
export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
- export onto orig_head rebase_root resolvemsg revisions
+ export onto orig_head rebase_root revisions
export state_dir verbose strategy strategy_opts
export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
exec git-rebase--$type
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH/WIP 8/8] Build in rebase
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
` (6 preceding siblings ...)
2015-03-18 9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
@ 2015-03-18 9:55 ` Nguyễn Thái Ngọc Duy
7 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2015-03-18 9:55 UTC (permalink / raw)
To: git; +Cc: Nguyễn Thái Ngọc Duy
---
Makefile | 2 +-
builtin.h | 1 +
builtin/rebase.c (new) | 752 ++++++++++++++++++++++++++++++++
commit.c | 4 +-
commit.h | 4 +-
contrib/examples/git-rebase.sh (new +x) | 532 ++++++++++++++++++++++
git-rebase.sh (gone) | 532 ----------------------
git.c | 1 +
8 files changed, 1291 insertions(+), 537 deletions(-)
create mode 100644 builtin/rebase.c
create mode 100755 contrib/examples/git-rebase.sh
delete mode 100755 git-rebase.sh
diff --git a/Makefile b/Makefile
index 83972a2..223d50b 100644
--- a/Makefile
+++ b/Makefile
@@ -444,7 +444,6 @@ SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-rebase.sh
SCRIPT_SH += git-rebase--am.sh
SCRIPT_SH += git-rebase--interactive.sh
SCRIPT_SH += git-rebase--merge.sh
@@ -903,6 +902,7 @@ BUILTIN_OBJS += builtin/prune-packed.o
BUILTIN_OBJS += builtin/prune.o
BUILTIN_OBJS += builtin/push.o
BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
BUILTIN_OBJS += builtin/remote.o
diff --git a/builtin.h b/builtin.h
index 7e7bbd6..431bbbf 100644
--- a/builtin.h
+++ b/builtin.h
@@ -108,6 +108,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);
extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
extern int cmd_push(int argc, const char **argv, const char *prefix);
extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase(int argc, const char **argv, const char *prefix);
extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
extern int cmd_reflog(int argc, const char **argv, const char *prefix);
extern int cmd_remote(int argc, const char **argv, const char *prefix);
diff --git a/builtin/rebase.c b/builtin/rebase.c
new file mode 100644
index 0000000..29eff0e
--- /dev/null
+++ b/builtin/rebase.c
@@ -0,0 +1,752 @@
+#include "cache.h"
+#include "parse-options.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "tree-walk.h"
+#include "unpack-trees.h"
+#include "diff.h"
+#include "commit.h"
+#include "revision.h"
+#include "submodule.h"
+#include "commit.h"
+#include "dir.h"
+
+#define REBASE_AM 1
+#define REBASE_MERGE 2
+#define REBASE_INTERACTIVE 3
+
+static const char * const builtin_rebase_usage[] = {
+ N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]"),
+ N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]"),
+ N_("git rebase --continue | --abort | --skip | --edit-todo"),
+ NULL
+};
+
+/* These are exported as environment variables for git-rebase--*.sh */
+static int action_abort;
+static int action_continue;
+static int action_skip;
+static int autosquash;
+static int force_rebase;
+static int keep_empty;
+static int preserve_merges;
+static int quiet;
+static int rerere_autoupdate;
+static int root;
+static int verbose;
+static const char *exec_cmd;
+static const char *head_name;
+static const char *onto;
+static const char *orig_head;
+static struct strbuf revisions = STRBUF_INIT;
+static const char *state_basedir;
+static const char *strategy;
+static const char *strategy_opts;
+static const char *switch_to;
+static const char *squash_onto;
+
+static int do_merge;
+static int edit_todo;
+static int interactive_rebase;
+static int rebase_type;
+static int show_stat;
+
+static char *read_file(const char *name)
+{
+ struct strbuf sb = STRBUF_INIT;
+ if (strbuf_read_file(&sb,
+ git_path("%s/%s", state_basedir, name),
+ 0) >= 0)
+ return strbuf_detach(&sb, NULL);
+ else
+ return NULL;
+}
+
+static char *read_file_or_die(const char *name)
+{
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_read_file_or_die(&sb,
+ git_path("%s/%s", state_basedir, name),
+ 0);
+ return strbuf_detach(&sb, NULL);
+}
+
+static void read_bool(const char *name, int *var)
+{
+ char *buf = read_file(name);
+ if (buf) {
+ *var = buf[0] && !isspace(buf[0]);
+ free(buf);
+ }
+}
+
+static int read_bool_or_die(const char *name)
+{
+ char *buf = read_file_or_die(name);
+ int ret = buf[0] && !isspace(buf[0]);
+ free(buf);
+ return ret;
+}
+
+/*
+ * Note:
+ *
+ * After git-rebase--*.sh are integrated, we should probably adopt
+ * git-config format and store everything in one file instead of so
+ * many like this. state_dir will be something different to avoid
+ * misuse by old rebase versions. This code will stay for a few major
+ * releases before being phased out.
+ */
+static void read_basic_state()
+{
+ head_name = read_file_or_die("head-name");
+ onto = read_file_or_die("onto");
+ /*
+ * We always write to orig-head, but interactive rebase used
+ * to write to head. Fall back to reading from head to cover
+ * for the case that the user upgraded git with an ongoing
+ * interactive rebase.
+ */
+ if ((orig_head = read_file("orig-head")) == NULL)
+ orig_head = read_file_or_die("head");
+ quiet = read_bool_or_die("quiet");
+ read_bool("verbose", &verbose);
+ strategy = read_file("strategy");
+ strategy_opts = read_file("strategy_opts");
+ read_bool("allow_rerere_autoupdate", &rerere_autoupdate);
+}
+
+static void push_env_string(struct argv_array *argv,
+ const char *name,
+ const char *value)
+{
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "%s=%s", name, value);
+ argv_array_push(argv, sb.buf);
+ strbuf_release(&sb);
+}
+
+static void push_env_bool(struct argv_array *argv,
+ const char *name,
+ int value)
+{
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "%s=%s", name, value ? "t" : "");
+ argv_array_push(argv, sb.buf);
+ strbuf_release(&sb);
+}
+
+static int run_specific_rebase()
+{
+ struct child_process cmd;
+ struct argv_array env = ARGV_ARRAY_INIT;
+ const char *argv[2];
+ int ret;
+
+ if (interactive_rebase == -1) {
+ argv_array_push(&env, "GIT_EDITOR=:");
+ autosquash = 0;
+ }
+ push_env_bool(&env, "git_quiet", quiet);
+ if (action_continue)
+ argv_array_push(&env, "action=continue");
+ else if (action_skip)
+ argv_array_push(&env, "action=skip");
+ else if (action_abort)
+ argv_array_push(&env, "action=abort");
+ else if (edit_todo)
+ argv_array_push(&env, "action=edit-todo");
+ else
+ argv_array_push(&env, "action=");
+ push_env_bool(&env, "allow_rerere_autoupdate", rerere_autoupdate);
+ push_env_bool(&env, "autosquash", autosquash);
+ push_env_string(&env, "cmd", exec_cmd);
+ push_env_bool(&env, "force_rebase", force_rebase);
+ push_env_string(&env, "git_am_opt", ""/* git_am_opt */);
+ push_env_string(&env, "head_name", head_name);
+ push_env_bool(&env, "keep_empty", keep_empty);
+ push_env_string(&env, "onto", onto);
+ push_env_string(&env, "orig_head", orig_head);
+ push_env_bool(&env, "preserve_merges", preserve_merges);
+ push_env_bool(&env, "rebase_root", root);
+ push_env_string(&env, "revisions", revisions.buf);
+ push_env_string(&env, "squash_onto", squash_onto);
+ push_env_string(&env, "state_dir", git_path("%s", state_basedir));
+ push_env_string(&env, "strategy", strategy);
+ push_env_string(&env, "strategy_opts", strategy_opts);
+ push_env_string(&env, "switch_to", switch_to);
+ /* upstream */
+ push_env_bool(&env, "verbose", verbose);
+
+ switch (rebase_type) {
+ case REBASE_INTERACTIVE: argv[0] = "rebase--interactive"; break;
+ case REBASE_AM: argv[0] = "rebase--am"; break;
+ case REBASE_MERGE: argv[0] = "rebase--merge"; break;
+ default:
+ die("BUG: unsupported rebase type %d", rebase_type);
+ }
+ argv[1] = NULL;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.in = 0;
+ cmd.out = 1;
+ cmd.err = 2;
+ cmd.git_cmd = 1;
+ cmd.env = env.argv;
+ cmd.argv = argv;
+ ret = run_command(&cmd);
+ if (ret)
+ die_errno("failed to run %s", argv[0]);
+
+ argv_array_clear(&env);
+ return ret;
+}
+
+static int do_continue()
+{
+ unsigned char sha1[20];
+ if (get_sha1("HEAD", sha1))
+ die(_("Cannot read HEAD"));
+ if (read_cache_unmerged()) {
+ printf(_("You must edit all merge conflicts and then\n"
+ "mark them as resolved using 'git add'\n"));
+ return 1;
+ }
+ read_basic_state();
+ return run_specific_rebase();
+}
+
+static int do_skip()
+{
+ int ret, error = 0;
+ unsigned char sha1[20];
+ if (get_sha1("HEAD", sha1))
+ die(_("Cannot read HEAD"));
+
+ ret = reset_tree(lookup_commit(sha1)->tree, quiet, 1, &error);
+ if (ret)
+ return ret;
+ read_basic_state();
+ return run_specific_rebase();
+}
+
+static int do_abort()
+{
+ int ret, error = 0;
+ unsigned char sha1[20];
+ const char *rerere[] = { "rerere", "clear", NULL };
+ struct strbuf path = STRBUF_INIT;
+
+ ret = run_command_v_opt(rerere, RUN_GIT_CMD);
+ if (ret)
+ return ret;
+ read_basic_state();
+ if (!prefixcmp(head_name, "refs/"))
+ create_symref("HEAD", head_name, "rebase: aborting");
+
+ if (get_sha1(orig_head, sha1))
+ die(_("Cannot read %s"), orig_head);
+
+ ret = reset_tree(lookup_commit(sha1)->tree, quiet, 1, &error);
+ if (ret)
+ return ret;
+
+ strbuf_addstr(&path, git_path("%s", state_basedir));
+ remove_dir_recursively(&path, 0);
+ strbuf_release(&path);
+ return 0;
+}
+
+static int do_edit_todo()
+{
+ if (rebase_type != REBASE_INTERACTIVE)
+ die(_("The --edit-todo action can only be "
+ "used during interactive rebase."));
+ return run_specific_rebase();
+}
+
+static void error_on_missing_default_upstream()
+{
+ unsigned char sha1[20];
+ const char *head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+ if (!head)
+ die(_("Failed to resolve HEAD as a valid ref."));
+ if (!strcmp(head, "HEAD")) {
+ printf(_("You are not currently on a branch. Please specify which\n"
+ "branch you want to rebase against. See git-rebase(1) for details.\n"
+ "\n"
+ " git rebase <branch>\n"));
+ return;
+ }
+
+ if (!prefixcmp(head, "refs/heads/"))
+ head += strlen("refs/heads/");
+ printf(_("There is no tracking information for the current branch.\n"
+ "Please specify which branch you want to rebase against.\n"
+ "See git-rebase(1) for details\n"
+ "\n"
+ " git rebase <branch>\n"
+ "\n"
+ "If you wish to set tracking information for this branch you can do so with:\n"
+ "\n"
+ " git branch --set-upstream-to=$remote/<branch> %s\n"),
+ head);
+}
+
+static int determine_upstream(int argc, const char **argv,
+ const struct option *options,
+ const char **upstream_name,
+ const char **upstream_arg)
+{
+ int consumed = 0;
+ if (!root) {
+ unsigned char sha1[20];
+ if (argc) {
+ *upstream_name = argv[0];
+ argv++;
+ argc--;
+ consumed++;
+ } else {
+ *upstream_name = "@{upstream}";
+ if (get_sha1(*upstream_name, sha1)) {
+ error_on_missing_default_upstream();
+ exit(1);
+ }
+ }
+ if (get_sha1(*upstream_name, sha1))
+ die(_("Failed to resolve %s."), *upstream_name);
+ lookup_commit_or_die(sha1, *upstream_name);
+ *upstream_arg = *upstream_name;
+ return consumed;
+ }
+
+ if (!onto) {
+ unsigned char sha1[20];
+ struct strbuf sb = STRBUF_INIT;
+ if (commit_tree(&sb, EMPTY_TREE_SHA1_BIN, NULL, sha1, NULL, NULL))
+ die(_("failed to create empty-tree commit"));
+ onto = xstrdup(sha1_to_hex(sha1));
+ squash_onto = onto;
+ }
+ if (argc > 1)
+ usage_with_options(builtin_rebase_usage,
+ options);
+ *upstream_arg = "--root";
+ return consumed;
+}
+
+static void require_clean_work_tree()
+{
+ struct rev_info rev;
+ unsigned char sha1[20];
+ int err = 0;
+
+ if (get_sha1("HEAD", sha1))
+ die("HEAD is not a valid ref");
+
+ if (read_cache() < 0)
+ die(_("unable to read index file"));
+ refresh_cache(REFRESH_QUIET | REFRESH_IGNORE_SUBMODULES);
+
+ init_revisions(&rev, NULL);
+ DIFF_OPT_SET(&rev.diffopt, QUICK);
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+ handle_ignore_submodules_arg(&rev.diffopt, "all");
+ diff_setup_done(&rev.diffopt);
+ run_diff_files(&rev, 0);
+ if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+ fputs(_("Cannot rebase: You have unstaged changes.\n"), stderr);
+ err = 1;
+ }
+
+ init_revisions(&rev, NULL);
+ DIFF_OPT_SET(&rev.diffopt, QUICK);
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+ handle_ignore_submodules_arg(&rev.diffopt, "all");
+ diff_setup_done(&rev.diffopt);
+ add_head_to_pending(&rev);
+ run_diff_index(&rev, 1);
+ if (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES)) {
+ const char *msg = err ?
+ _("Additionally, your index contains uncommitted changes.\n") :
+ _("Cannot rebase: Your index contains uncommitted changes.\n");
+ fputs(msg, stderr);
+ err = 1;
+ }
+
+ if (err) {
+ fputs(_("Please commit or stash them.\n"), stderr);
+ exit(1);
+ }
+}
+
+static void run_pre_rebase_hook(int argc, const char **argv,
+ const char *upstream_arg)
+{
+ /*
+ if test -z "$ok_to_skip_pre_rebase" &&
+ test -x "$GIT_DIR/hooks/pre-rebase"
+ then
+ "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+ die "$(gettext "The pre-rebase hook refused to rebase.")"
+ fi
+ */
+}
+
+static int do_rebase(int argc, const char **argv,
+ const struct option *options)
+{
+ const char *upstream_name = NULL;
+ const char *upstream_arg = NULL;
+ const char *branch_name;
+ const char *onto_name;
+ unsigned char onto_sha1[20];
+
+ int n;
+ if (root && !onto && !interactive_rebase)
+ interactive_rebase = -1; /* implied */
+
+ if (interactive_rebase) {
+ rebase_type = REBASE_INTERACTIVE;
+ state_basedir = "rebase-merge";
+ } else if (do_merge) {
+ rebase_type = REBASE_MERGE;
+ state_basedir = "rebase-merge";
+ } else {
+ rebase_type = REBASE_AM;
+ state_basedir = "rebase-apply";
+ }
+
+ n = determine_upstream(argc, argv, options,
+ &upstream_name, &upstream_arg);
+ argc += n;
+ argv += n;
+
+ if (root)
+ upstream_arg = "--root";
+
+ /* Make sure the branch to rebase onto is valid. */
+ onto_name = onto ? onto : upstream_name;
+ if (strstr(onto, "...")) {
+ /*
+case "$onto_name" in
+*...*)
+ if left=${onto_name%...*} right=${onto_name#*...} &&
+ onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+ then
+ case "$onto" in
+ ?*"$LF"?*)
+ die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
+ ;;
+ '')
+ die "$(eval_gettext "\$onto_name: there is no merge base")"
+ ;;
+ esac
+ else
+ die "$(eval_gettext "\$onto_name: there is no merge base")"
+ fi
+ ;;
+ */
+ } else {
+ if (get_sha1(onto_name, onto_sha1) || !lookup_commit(onto_sha1))
+ die(_("Does not point to a valid commit: %s"), onto_name);
+ }
+
+ /*
+ * If the branch to rebase is given, that is the branch we
+ * will rebase
+ *
+ * $branch_name -- branch being rebased, or HEAD (already
+ * detached)
+ *
+ * $orig_head -- commit object name of tip of the branch
+ * before rebasing
+ *
+ * $head_name -- refs/heads/<that-branch> or "detached HEAD"
+ */
+ switch (argc) {
+ case 1:
+ /* Is it "rebase other $branchname" or "rebase other $commit"? */
+ branch_name = argv[0];
+ switch_to = argv[0];
+ /*
+ if git show-ref --verify --quiet -- "refs/heads/$1" &&
+ orig_head=$(git rev-parse -q --verify "refs/heads/$1")
+ then
+ head_name="refs/heads/$1"
+ elif orig_head=$(git rev-parse -q --verify "$1")
+ then
+ head_name="detached HEAD"
+ else
+ die "$(eval_gettext "fatal: no such branch: \$branch_name")"
+ fi
+ */
+ break;
+
+ case 0:
+ /*
+ # Do not need to switch branches, we are already on it.
+ if branch_name=`git symbolic-ref -q HEAD`
+ then
+ head_name=$branch_name
+ branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
+ else
+ head_name="detached HEAD"
+ branch_name=HEAD ;# detached
+ fi
+ orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+ */
+ break;
+ default:
+ die("BUG: unexpected number of arguments left to parse");
+ }
+
+ require_clean_work_tree();
+
+ /*
+ * Now we are rebasing commits $upstream..$orig_head (or with
+ * --root, everything leading up to $orig_head) on top of
+ * $onto
+ */
+
+ /*
+ * Check if we are already based on $onto with linear history,
+ * but this should be done only when upstream and onto are the
+ * same and if this is not an interactive rebase.
+ */
+
+ /*
+mb=$(git merge-base "$onto" "$orig_head")
+if test "$type" != interactive && test "$upstream" = "$onto" &&
+ test "$mb" = "$onto" &&
+ # linear history?
+ ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
+then
+ if test -z "$force_rebase"
+ then
+ # Lazily switch to the target branch if needed...
+ test -z "$switch_to" || git checkout "$switch_to" --
+ say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+ exit 0
+ else
+ say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+ fi
+fi
+ */
+
+ /* If a hook exists, give it a chance to interrupt */
+ run_pre_rebase_hook(argc, argv, upstream_arg);
+
+ /*
+if test -n "$diffstat"
+then
+ if test -n "$verbose"
+ then
+ echo "$(eval_gettext "Changes from \$mb to \$onto:")"
+ fi
+ # We want color (if set), but no pager
+ GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+ */
+
+ if (rebase_type == REBASE_INTERACTIVE)
+ return run_specific_rebase();
+
+ /* Detach HEAD and reset the tree */
+ /*
+say "$(gettext "First, rewinding head to replay your work on top of it...")"
+git checkout -q "$onto^0" || die "could not detach HEAD"
+git update-ref ORIG_HEAD $orig_head
+ */
+
+ /*
+ * If the $onto is a proper descendant of the tip of the
+ * branch, then we just fast-forwarded.
+ */
+ if (!hashcmp(mb, orig_head)) {
+ printf(_("Fast-forwarded %s to %s."), branch_name, onto_name);
+ move_to_original_branch();
+ return 0;
+ }
+
+ if (root)
+ strbuf_addf(&revisions, "%s..%s", onto, orig_head);
+ else
+ strbuf_addf(&revisions, "%s..%s", upstream, orig_head);
+
+ return run_specific_rebase();
+}
+
+static int git_rebase_config(const char *name, const char *value, void *data)
+{
+ if (!strcmp(name, "rebase.stat")) {
+ show_stat = git_config_bool(name, value);
+ return 0;
+ } else if (!strcmp(name, "rebase.autosquash")) {
+ autosquash = git_config_bool(name, value);
+ return 0;
+ }
+ return git_default_config(name, value, data);
+}
+
+int cmd_rebase(int argc, const char **argv, const char *prefix)
+{
+ const char *whitespace_opt = NULL;
+ int committer_date_is_author_date = 0;
+ int context_opt = 0;
+ int ignore_date = 0;
+ int ignore_whitespace = 0;
+ int no_ff = 0;
+ int pre_rebase = 1;
+ struct option options[] = {
+ OPT__VERBOSE(&verbose,
+ N_("display a diffstat of what changed upstream")),
+ OPT__QUIET(&quiet,
+ N_("be quiet. implies --no-stat")),
+ OPT_STRING(0, "onto", &onto, "ref",
+ N_("rebase onto given branch instead of upstream")),
+ OPT_BOOL('p', "preserve-merges", &preserve_merges,
+ N_("try to recreate merges instead of ignoring them")),
+ OPT_STRING('s', "stragegy", &strategy, "strategy",
+ N_("use the given merge strategy")),
+ { OPTION_SET_INT, 0, "no-ff", &no_ff, NULL,
+ N_("cherry-pick all commits, even if unchanged"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+ OPT_BOOL('m', "merge", &do_merge,
+ N_("use merging strategies to rebase")),
+ OPT_BOOL('i', "interactive", &interactive_rebase,
+ N_("let the user edit the list of commits to rebase")),
+ OPT_STRING('x', "exec", &exec_cmd, "command",
+ N_("add exec lines after each commit of the editable list")),
+ OPT_BOOL('k', "keep-empty", &keep_empty,
+ N_("preserve empty commits during rebase")),
+ OPT_BOOL('f', "force-rebase", &force_rebase,
+ N_("force rebase even if branch is up to date")),
+ OPT_STRING('X', "strategy-options", &strategy_opts, "options",
+ N_("pass the argument through to the merge strategy")),
+ { OPTION_SET_INT, 0, "stat", &show_stat, NULL,
+ N_("display a diffstat of what changed upstream"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
+ OPT_SET_INT('n', "no-stat", &show_stat,
+ N_("do not show diffstat of what changed upstream"), 0),
+ OPT_BOOL(0, "verify", &pre_rebase,
+ N_("allow pre-rebase hook to run")),
+ OPT_BOOL(0, "rerere-autoupdate", &rerere_autoupdate,
+ N_("allow rerere to update index with resolved conflicts")),
+ OPT_BOOL(0, "root", &root,
+ N_("rebase all reachable commits up to the root(s)")),
+ OPT_BOOL(0, "autosquash", &autosquash,
+ N_("move commits that begin with squash!/fixup! under -i")),
+ OPT_BOOL(0, "committer-date-is-author-date", &committer_date_is_author_date,
+ N_("lie about committer date")),
+ OPT_BOOL(0, "ignore-date", &ignore_date,
+ N_("use current timestamp for author date")),
+ OPT_STRING(0, "whitespace", &whitespace_opt, "mode",
+ N_("detect new or modified lines that have whitespace errors")),
+ OPT_INTEGER('C', NULL, &context_opt,
+ N_("ensure at least <n> lines of context match by 'git apply'")),
+ OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace,
+ N_("ignore changes in whitespace when finding context")),
+
+ OPT_GROUP("Actions"),
+ OPT_BOOL(0, "continue", &action_continue,
+ N_("continue")),
+ OPT_BOOL(0, "abort", &action_abort,
+ N_("abort and check out the original branch")),
+ OPT_BOOL(0, "skip", &action_skip,
+ N_("skip current patch and continue")),
+ OPT_BOOL(0, "edit-todo", &edit_todo,
+ N_("edit the todo list during an interactive rebase")),
+ OPT_END()
+ };
+ struct stat st;
+ int action_nr, in_progress;
+
+ if (!stat(git_path("rebase-apply"), &st) && S_ISDIR(st.st_mode)) {
+ if (!access(git_path("rebase-apply/applying"), F_OK))
+ die(_("It looks like git-am is in progress. Cannot rebase."));
+ rebase_type = REBASE_AM;
+ state_basedir = "rebase-apply";
+ in_progress = 1;
+ } else if (!stat(git_path("rebase-merge"), &st) &&
+ S_ISDIR(st.st_mode)) {
+ if (!access(git_path("rebase-merge/interactive"), F_OK)) {
+ rebase_type = REBASE_INTERACTIVE;
+ interactive_rebase = 1; /* explicit */
+ } else
+ rebase_type = REBASE_MERGE;
+ state_basedir = "rebase-merge";
+ in_progress = 1;
+ } else
+ in_progress = 0;
+
+ git_config(git_rebase_config, NULL);
+ gitmodules_config();
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_rebase_usage, 0);
+
+ action_nr = action_continue + action_skip + action_abort + edit_todo;
+ if ((action_nr != 1 && action_nr != 0) || argc > 2)
+ usage_with_options(builtin_rebase_usage, options);
+
+ if (verbose && quiet)
+ die(_("--quiet and --verbose are incompatible"));
+ else if (verbose)
+ show_stat = 1;
+ else if (quiet)
+ show_stat = 0;
+
+ if (preserve_merges && !interactive_rebase)
+ interactive_rebase = -1; /* implied */
+
+ if (strategy_opts) {
+ if (!strategy)
+ strategy = "recursive";
+ /* validate strategy options */
+ }
+
+ if (strategy)
+ do_merge = 1;
+
+ if (!strcmp(whitespace_opt, "fix") ||
+ !strcmp(whitespace_opt, "strip") ||
+ committer_date_is_author_date ||
+ no_ff)
+ force_rebase = 1;
+
+ if (exec_cmd && interactive_rebase != 1)
+ die(_("The --exec option must be used with the --interactive option"));
+
+ if (action_nr) {
+ if (!in_progress)
+ die(_("No rebase in progress?"));
+ if (rebase_type == REBASE_INTERACTIVE)
+ ; /* GIT_REFLOG_ACTION = "rebase -i ($action)" */
+
+ if (action_continue)
+ return do_continue();
+ else if (action_skip)
+ return do_skip();
+ else if (action_abort)
+ return do_abort();
+ else if (edit_todo)
+ return do_edit_todo();
+ else
+ die("BUG: how do you get here?");
+ }
+
+ if (in_progress)
+ die(_("It seems that there is already a %s directory, and\n"
+ "I wonder if you are in the middle of another rebase. If that is the\n"
+ "case, please try\n"
+ "\tgit rebase (--continue | --abort | --skip)\n"
+ "If that is not the case, please\n"
+ "\trm -rf \"%s/%s\""
+ "and run me again. I am stopping in case you still have something\n"
+ "valuable there."),
+ state_basedir,
+ get_git_dir(), state_basedir);
+
+ return do_rebase(argc, argv, options);
+}
diff --git a/commit.c b/commit.c
index e8eb0ae..395a860 100644
--- a/commit.c
+++ b/commit.c
@@ -1135,7 +1135,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
}
-int commit_tree(const struct strbuf *msg, unsigned char *tree,
+int commit_tree(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit)
{
@@ -1238,7 +1238,7 @@ static const char commit_utf8_warn[] =
"You may want to amend it after fixing the message, or set the config\n"
"variable i18n.commitencoding to the encoding your project uses.\n";
-int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *extra)
diff --git a/commit.h b/commit.h
index 0f469e5..058886f 100644
--- a/commit.h
+++ b/commit.h
@@ -193,11 +193,11 @@ struct commit_extra_header {
extern void append_merge_tag_headers(struct commit_list *parents,
struct commit_extra_header ***tail);
-extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit);
-extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
+extern int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
struct commit_list *parents, unsigned char *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *);
diff --git a/contrib/examples/git-rebase.sh b/contrib/examples/git-rebase.sh
new file mode 100755
index 0000000..38530e8
--- /dev/null
+++ b/contrib/examples/git-rebase.sh
@@ -0,0 +1,532 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano.
+#
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
+git-rebase --continue | --abort | --skip | --edit-todo
+--
+ Available options are
+v,verbose! display a diffstat of what changed upstream
+q,quiet! be quiet. implies --no-stat
+onto=! rebase onto given branch instead of upstream
+p,preserve-merges! try to recreate merges instead of ignoring them
+s,strategy=! use the given merge strategy
+no-ff! cherry-pick all commits, even if unchanged
+m,merge! use merging strategies to rebase
+i,interactive! let the user edit the list of commits to rebase
+x,exec=! add exec lines after each commit of the editable list
+k,keep-empty preserve empty commits during rebase
+f,force-rebase! force rebase even if branch is up to date
+X,strategy-option=! pass the argument through to the merge strategy
+stat! display a diffstat of what changed upstream
+n,no-stat! do not show diffstat of what changed upstream
+verify allow pre-rebase hook to run
+rerere-autoupdate allow rerere to update index with resolved conflicts
+root! rebase all reachable commits up to the root(s)
+autosquash move commits that begin with squash!/fixup! under -i
+committer-date-is-author-date! passed to 'git am'
+ignore-date! passed to 'git am'
+whitespace=! passed to 'git apply'
+ignore-whitespace! passed to 'git apply'
+C=! passed to 'git apply'
+ Actions:
+continue! continue
+abort! abort and check out the original branch
+skip! skip current patch and continue
+edit-todo! edit the todo list during an interactive rebase
+"
+. git-sh-setup
+. git-sh-i18n
+set_reflog_action rebase
+require_work_tree_exists
+cd_to_toplevel
+
+LF='
+'
+ok_to_skip_pre_rebase=
+unset onto
+cmd=
+strategy=
+strategy_opts=
+do_merge=
+merge_dir="$GIT_DIR"/rebase-merge
+apply_dir="$GIT_DIR"/rebase-apply
+verbose=
+diffstat=
+test "$(git config --bool rebase.stat)" = true && diffstat=t
+git_am_opt=
+rebase_root=
+force_rebase=
+allow_rerere_autoupdate=
+# Non-empty if a rebase was in progress when 'git rebase' was invoked
+in_progress=
+# One of {am, merge, interactive}
+type=
+# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
+state_dir=
+# One of {'', continue, skip, abort}, as parsed from command line
+action=
+preserve_merges=
+autosquash=
+keep_empty=
+test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
+
+read_basic_state () {
+ head_name=$(cat "$state_dir"/head-name) &&
+ onto=$(cat "$state_dir"/onto) &&
+ # We always write to orig-head, but interactive rebase used to write to
+ # head. Fall back to reading from head to cover for the case that the
+ # user upgraded git with an ongoing interactive rebase.
+ if test -f "$state_dir"/orig-head
+ then
+ orig_head=$(cat "$state_dir"/orig-head)
+ else
+ orig_head=$(cat "$state_dir"/head)
+ fi &&
+ GIT_QUIET=$(cat "$state_dir"/quiet) &&
+ test -f "$state_dir"/verbose && verbose=t
+ test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
+ test -f "$state_dir"/strategy_opts &&
+ strategy_opts="$(cat "$state_dir"/strategy_opts)"
+ test -f "$state_dir"/allow_rerere_autoupdate &&
+ allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
+}
+
+output () {
+ case "$verbose" in
+ '')
+ output=$("$@" 2>&1 )
+ status=$?
+ test $status != 0 && printf "%s\n" "$output"
+ return $status
+ ;;
+ *)
+ "$@"
+ ;;
+ esac
+}
+
+move_to_original_branch () {
+ case "$head_name" in
+ refs/*)
+ message="rebase finished: $head_name onto $onto"
+ git update-ref -m "$message" \
+ $head_name $(git rev-parse HEAD) $orig_head &&
+ git symbolic-ref \
+ -m "rebase finished: returning to $head_name" \
+ HEAD $head_name ||
+ die "$(gettext "Could not move back to $head_name")"
+ ;;
+ esac
+}
+
+run_specific_rebase () {
+ if [ "$interactive_rebase" = implied ]; then
+ GIT_EDITOR=:
+ export GIT_EDITOR
+ autosquash=
+ fi
+ git_quiet=$GIT_QUIET
+ export GIT_PAGER
+ export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
+ export onto orig_head rebase_root revisions
+ export state_dir verbose strategy strategy_opts
+ export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
+ exec git-rebase--$type
+}
+
+run_pre_rebase_hook () {
+ if test -z "$ok_to_skip_pre_rebase" &&
+ test -x "$GIT_DIR/hooks/pre-rebase"
+ then
+ "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
+ die "$(gettext "The pre-rebase hook refused to rebase.")"
+ fi
+}
+
+test -f "$apply_dir"/applying &&
+ die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
+
+if test -d "$apply_dir"
+then
+ type=am
+ state_dir="$apply_dir"
+elif test -d "$merge_dir"
+then
+ if test -f "$merge_dir"/interactive
+ then
+ type=interactive
+ interactive_rebase=explicit
+ else
+ type=merge
+ fi
+ state_dir="$merge_dir"
+fi
+test -n "$type" && in_progress=t
+
+total_argc=$#
+while test $# != 0
+do
+ case "$1" in
+ --no-verify)
+ ok_to_skip_pre_rebase=yes
+ ;;
+ --verify)
+ ok_to_skip_pre_rebase=
+ ;;
+ --continue|--skip|--abort|--edit-todo)
+ test $total_argc -eq 2 || usage
+ action=${1##--}
+ ;;
+ --onto)
+ test 2 -le "$#" || usage
+ onto="$2"
+ shift
+ ;;
+ -x)
+ test 2 -le "$#" || usage
+ cmd="${cmd}exec $2${LF}"
+ shift
+ ;;
+ -i)
+ interactive_rebase=explicit
+ ;;
+ -k)
+ keep_empty=yes
+ ;;
+ -p)
+ preserve_merges=t
+ test -z "$interactive_rebase" && interactive_rebase=implied
+ ;;
+ --autosquash)
+ autosquash=t
+ ;;
+ --no-autosquash)
+ autosquash=
+ ;;
+ -M|-m)
+ do_merge=t
+ ;;
+ -X)
+ shift
+ strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
+ do_merge=t
+ test -z "$strategy" && strategy=recursive
+ ;;
+ -s)
+ shift
+ strategy="$1"
+ do_merge=t
+ ;;
+ -n)
+ diffstat=
+ ;;
+ --stat)
+ diffstat=t
+ ;;
+ -v)
+ verbose=t
+ diffstat=t
+ GIT_QUIET=
+ ;;
+ -q)
+ GIT_QUIET=t
+ git_am_opt="$git_am_opt -q"
+ verbose=
+ diffstat=
+ ;;
+ --whitespace)
+ shift
+ git_am_opt="$git_am_opt --whitespace=$1"
+ case "$1" in
+ fix|strip)
+ force_rebase=t
+ ;;
+ esac
+ ;;
+ --ignore-whitespace)
+ git_am_opt="$git_am_opt $1"
+ ;;
+ --committer-date-is-author-date|--ignore-date)
+ git_am_opt="$git_am_opt $1"
+ force_rebase=t
+ ;;
+ -C)
+ shift
+ git_am_opt="$git_am_opt -C$1"
+ ;;
+ --root)
+ rebase_root=t
+ ;;
+ -f|--no-ff)
+ force_rebase=t
+ ;;
+ --rerere-autoupdate|--no-rerere-autoupdate)
+ allow_rerere_autoupdate="$1"
+ ;;
+ --)
+ shift
+ break
+ ;;
+ esac
+ shift
+done
+test $# -gt 2 && usage
+
+if test -n "$cmd" &&
+ test "$interactive_rebase" != explicit
+then
+ die "$(gettext "The --exec option must be used with the --interactive option")"
+fi
+
+if test -n "$action"
+then
+ test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
+ # Only interactive rebase uses detailed reflog messages
+ if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
+ then
+ GIT_REFLOG_ACTION="rebase -i ($action)"
+ export GIT_REFLOG_ACTION
+ fi
+fi
+
+if test "$action" = "edit-todo" && test "$type" != "interactive"
+then
+ die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
+fi
+
+case "$action" in
+continue)
+ # Sanity check
+ git rev-parse --verify HEAD >/dev/null ||
+ die "$(gettext "Cannot read HEAD")"
+ git update-index --ignore-submodules --refresh &&
+ git diff-files --quiet --ignore-submodules || {
+ echo "$(gettext "You must edit all merge conflicts and then
+mark them as resolved using git add")"
+ exit 1
+ }
+ read_basic_state
+ run_specific_rebase
+ ;;
+skip)
+ output git reset --hard HEAD || exit $?
+ read_basic_state
+ run_specific_rebase
+ ;;
+abort)
+ git rerere clear
+ read_basic_state
+ case "$head_name" in
+ refs/*)
+ git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
+ die "$(eval_gettext "Could not move back to \$head_name")"
+ ;;
+ esac
+ output git reset --hard $orig_head
+ rm -r "$state_dir"
+ exit
+ ;;
+edit-todo)
+ run_specific_rebase
+ ;;
+esac
+
+# Make sure no rebase is in progress
+if test -n "$in_progress"
+then
+ state_dir_base=${state_dir##*/}
+ cmd_live_rebase="git rebase (--continue | --abort | --skip)"
+ cmd_clear_stale_rebase="rm -fr \"$state_dir\""
+ die "
+$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
+I wonder if you are in the middle of another rebase. If that is the
+case, please try
+ $cmd_live_rebase
+If that is not the case, please
+ $cmd_clear_stale_rebase
+and run me again. I am stopping in case you still have something
+valuable there.')"
+fi
+
+if test -n "$rebase_root" && test -z "$onto"
+then
+ test -z "$interactive_rebase" && interactive_rebase=implied
+fi
+
+if test -n "$interactive_rebase"
+then
+ type=interactive
+ state_dir="$merge_dir"
+elif test -n "$do_merge"
+then
+ type=merge
+ state_dir="$merge_dir"
+else
+ type=am
+ state_dir="$apply_dir"
+fi
+
+if test -z "$rebase_root"
+then
+ case "$#" in
+ 0)
+ if ! upstream_name=$(git rev-parse --symbolic-full-name \
+ --verify -q @{upstream} 2>/dev/null)
+ then
+ . git-parse-remote
+ error_on_missing_default_upstream "rebase" "rebase" \
+ "against" "git rebase <branch>"
+ fi
+ ;;
+ *) upstream_name="$1"
+ shift
+ ;;
+ esac
+ upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+ die "$(eval_gettext "invalid upstream \$upstream_name")"
+ upstream_arg="$upstream_name"
+else
+ if test -z "$onto"
+ then
+ empty_tree=`git hash-object -t tree /dev/null`
+ onto=`git commit-tree $empty_tree </dev/null`
+ squash_onto="$onto"
+ fi
+ unset upstream_name
+ unset upstream
+ test $# -gt 1 && usage
+ upstream_arg=--root
+fi
+
+# Make sure the branch to rebase onto is valid.
+onto_name=${onto-"$upstream_name"}
+case "$onto_name" in
+*...*)
+ if left=${onto_name%...*} right=${onto_name#*...} &&
+ onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+ then
+ case "$onto" in
+ ?*"$LF"?*)
+ die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
+ ;;
+ '')
+ die "$(eval_gettext "\$onto_name: there is no merge base")"
+ ;;
+ esac
+ else
+ die "$(eval_gettext "\$onto_name: there is no merge base")"
+ fi
+ ;;
+*)
+ onto=$(git rev-parse --verify "${onto_name}^0") ||
+ die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
+ ;;
+esac
+
+# If the branch to rebase is given, that is the branch we will rebase
+# $branch_name -- branch being rebased, or HEAD (already detached)
+# $orig_head -- commit object name of tip of the branch before rebasing
+# $head_name -- refs/heads/<that-branch> or "detached HEAD"
+switch_to=
+case "$#" in
+1)
+ # Is it "rebase other $branchname" or "rebase other $commit"?
+ branch_name="$1"
+ switch_to="$1"
+
+ if git show-ref --verify --quiet -- "refs/heads/$1" &&
+ orig_head=$(git rev-parse -q --verify "refs/heads/$1")
+ then
+ head_name="refs/heads/$1"
+ elif orig_head=$(git rev-parse -q --verify "$1")
+ then
+ head_name="detached HEAD"
+ else
+ die "$(eval_gettext "fatal: no such branch: \$branch_name")"
+ fi
+ ;;
+0)
+ # Do not need to switch branches, we are already on it.
+ if branch_name=`git symbolic-ref -q HEAD`
+ then
+ head_name=$branch_name
+ branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
+ else
+ head_name="detached HEAD"
+ branch_name=HEAD ;# detached
+ fi
+ orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+ ;;
+*)
+ die "BUG: unexpected number of arguments left to parse"
+ ;;
+esac
+
+require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
+
+# Now we are rebasing commits $upstream..$orig_head (or with --root,
+# everything leading up to $orig_head) on top of $onto
+
+# Check if we are already based on $onto with linear history,
+# but this should be done only when upstream and onto are the same
+# and if this is not an interactive rebase.
+mb=$(git merge-base "$onto" "$orig_head")
+if test "$type" != interactive && test "$upstream" = "$onto" &&
+ test "$mb" = "$onto" &&
+ # linear history?
+ ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
+then
+ if test -z "$force_rebase"
+ then
+ # Lazily switch to the target branch if needed...
+ test -z "$switch_to" || git checkout "$switch_to" --
+ say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+ exit 0
+ else
+ say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+ fi
+fi
+
+# If a hook exists, give it a chance to interrupt
+run_pre_rebase_hook "$upstream_arg" "$@"
+
+if test -n "$diffstat"
+then
+ if test -n "$verbose"
+ then
+ echo "$(eval_gettext "Changes from \$mb to \$onto:")"
+ fi
+ # We want color (if set), but no pager
+ GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
+fi
+
+test "$type" = interactive && run_specific_rebase
+
+# Detach HEAD and reset the tree
+say "$(gettext "First, rewinding head to replay your work on top of it...")"
+git checkout -q "$onto^0" || die "could not detach HEAD"
+git update-ref ORIG_HEAD $orig_head
+
+# If the $onto is a proper descendant of the tip of the branch, then
+# we just fast-forwarded.
+if test "$mb" = "$orig_head"
+then
+ say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
+ move_to_original_branch
+ exit 0
+fi
+
+if test -n "$rebase_root"
+then
+ revisions="$onto..$orig_head"
+else
+ revisions="$upstream..$orig_head"
+fi
+
+run_specific_rebase
diff --git a/git-rebase.sh b/git-rebase.sh
deleted file mode 100755
index 38530e8..0000000
--- a/git-rebase.sh
+++ /dev/null
@@ -1,532 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_KEEPDASHDASH=
-OPTIONS_SPEC="\
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git-rebase --continue | --abort | --skip | --edit-todo
---
- Available options are
-v,verbose! display a diffstat of what changed upstream
-q,quiet! be quiet. implies --no-stat
-onto=! rebase onto given branch instead of upstream
-p,preserve-merges! try to recreate merges instead of ignoring them
-s,strategy=! use the given merge strategy
-no-ff! cherry-pick all commits, even if unchanged
-m,merge! use merging strategies to rebase
-i,interactive! let the user edit the list of commits to rebase
-x,exec=! add exec lines after each commit of the editable list
-k,keep-empty preserve empty commits during rebase
-f,force-rebase! force rebase even if branch is up to date
-X,strategy-option=! pass the argument through to the merge strategy
-stat! display a diffstat of what changed upstream
-n,no-stat! do not show diffstat of what changed upstream
-verify allow pre-rebase hook to run
-rerere-autoupdate allow rerere to update index with resolved conflicts
-root! rebase all reachable commits up to the root(s)
-autosquash move commits that begin with squash!/fixup! under -i
-committer-date-is-author-date! passed to 'git am'
-ignore-date! passed to 'git am'
-whitespace=! passed to 'git apply'
-ignore-whitespace! passed to 'git apply'
-C=! passed to 'git apply'
- Actions:
-continue! continue
-abort! abort and check out the original branch
-skip! skip current patch and continue
-edit-todo! edit the todo list during an interactive rebase
-"
-. git-sh-setup
-. git-sh-i18n
-set_reflog_action rebase
-require_work_tree_exists
-cd_to_toplevel
-
-LF='
-'
-ok_to_skip_pre_rebase=
-unset onto
-cmd=
-strategy=
-strategy_opts=
-do_merge=
-merge_dir="$GIT_DIR"/rebase-merge
-apply_dir="$GIT_DIR"/rebase-apply
-verbose=
-diffstat=
-test "$(git config --bool rebase.stat)" = true && diffstat=t
-git_am_opt=
-rebase_root=
-force_rebase=
-allow_rerere_autoupdate=
-# Non-empty if a rebase was in progress when 'git rebase' was invoked
-in_progress=
-# One of {am, merge, interactive}
-type=
-# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
-state_dir=
-# One of {'', continue, skip, abort}, as parsed from command line
-action=
-preserve_merges=
-autosquash=
-keep_empty=
-test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-
-read_basic_state () {
- head_name=$(cat "$state_dir"/head-name) &&
- onto=$(cat "$state_dir"/onto) &&
- # We always write to orig-head, but interactive rebase used to write to
- # head. Fall back to reading from head to cover for the case that the
- # user upgraded git with an ongoing interactive rebase.
- if test -f "$state_dir"/orig-head
- then
- orig_head=$(cat "$state_dir"/orig-head)
- else
- orig_head=$(cat "$state_dir"/head)
- fi &&
- GIT_QUIET=$(cat "$state_dir"/quiet) &&
- test -f "$state_dir"/verbose && verbose=t
- test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
- test -f "$state_dir"/strategy_opts &&
- strategy_opts="$(cat "$state_dir"/strategy_opts)"
- test -f "$state_dir"/allow_rerere_autoupdate &&
- allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
-}
-
-output () {
- case "$verbose" in
- '')
- output=$("$@" 2>&1 )
- status=$?
- test $status != 0 && printf "%s\n" "$output"
- return $status
- ;;
- *)
- "$@"
- ;;
- esac
-}
-
-move_to_original_branch () {
- case "$head_name" in
- refs/*)
- message="rebase finished: $head_name onto $onto"
- git update-ref -m "$message" \
- $head_name $(git rev-parse HEAD) $orig_head &&
- git symbolic-ref \
- -m "rebase finished: returning to $head_name" \
- HEAD $head_name ||
- die "$(gettext "Could not move back to $head_name")"
- ;;
- esac
-}
-
-run_specific_rebase () {
- if [ "$interactive_rebase" = implied ]; then
- GIT_EDITOR=:
- export GIT_EDITOR
- autosquash=
- fi
- git_quiet=$GIT_QUIET
- export GIT_PAGER
- export action allow_rerere_autoupdate git_am_opt git_quiet head_name keep_empty
- export onto orig_head rebase_root revisions
- export state_dir verbose strategy strategy_opts
- export autosquash cmd force_rebase preserve_merges squash_onto switch_to upstream
- exec git-rebase--$type
-}
-
-run_pre_rebase_hook () {
- if test -z "$ok_to_skip_pre_rebase" &&
- test -x "$GIT_DIR/hooks/pre-rebase"
- then
- "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} ||
- die "$(gettext "The pre-rebase hook refused to rebase.")"
- fi
-}
-
-test -f "$apply_dir"/applying &&
- die "$(gettext "It looks like git-am is in progress. Cannot rebase.")"
-
-if test -d "$apply_dir"
-then
- type=am
- state_dir="$apply_dir"
-elif test -d "$merge_dir"
-then
- if test -f "$merge_dir"/interactive
- then
- type=interactive
- interactive_rebase=explicit
- else
- type=merge
- fi
- state_dir="$merge_dir"
-fi
-test -n "$type" && in_progress=t
-
-total_argc=$#
-while test $# != 0
-do
- case "$1" in
- --no-verify)
- ok_to_skip_pre_rebase=yes
- ;;
- --verify)
- ok_to_skip_pre_rebase=
- ;;
- --continue|--skip|--abort|--edit-todo)
- test $total_argc -eq 2 || usage
- action=${1##--}
- ;;
- --onto)
- test 2 -le "$#" || usage
- onto="$2"
- shift
- ;;
- -x)
- test 2 -le "$#" || usage
- cmd="${cmd}exec $2${LF}"
- shift
- ;;
- -i)
- interactive_rebase=explicit
- ;;
- -k)
- keep_empty=yes
- ;;
- -p)
- preserve_merges=t
- test -z "$interactive_rebase" && interactive_rebase=implied
- ;;
- --autosquash)
- autosquash=t
- ;;
- --no-autosquash)
- autosquash=
- ;;
- -M|-m)
- do_merge=t
- ;;
- -X)
- shift
- strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")"
- do_merge=t
- test -z "$strategy" && strategy=recursive
- ;;
- -s)
- shift
- strategy="$1"
- do_merge=t
- ;;
- -n)
- diffstat=
- ;;
- --stat)
- diffstat=t
- ;;
- -v)
- verbose=t
- diffstat=t
- GIT_QUIET=
- ;;
- -q)
- GIT_QUIET=t
- git_am_opt="$git_am_opt -q"
- verbose=
- diffstat=
- ;;
- --whitespace)
- shift
- git_am_opt="$git_am_opt --whitespace=$1"
- case "$1" in
- fix|strip)
- force_rebase=t
- ;;
- esac
- ;;
- --ignore-whitespace)
- git_am_opt="$git_am_opt $1"
- ;;
- --committer-date-is-author-date|--ignore-date)
- git_am_opt="$git_am_opt $1"
- force_rebase=t
- ;;
- -C)
- shift
- git_am_opt="$git_am_opt -C$1"
- ;;
- --root)
- rebase_root=t
- ;;
- -f|--no-ff)
- force_rebase=t
- ;;
- --rerere-autoupdate|--no-rerere-autoupdate)
- allow_rerere_autoupdate="$1"
- ;;
- --)
- shift
- break
- ;;
- esac
- shift
-done
-test $# -gt 2 && usage
-
-if test -n "$cmd" &&
- test "$interactive_rebase" != explicit
-then
- die "$(gettext "The --exec option must be used with the --interactive option")"
-fi
-
-if test -n "$action"
-then
- test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
- # Only interactive rebase uses detailed reflog messages
- if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase
- then
- GIT_REFLOG_ACTION="rebase -i ($action)"
- export GIT_REFLOG_ACTION
- fi
-fi
-
-if test "$action" = "edit-todo" && test "$type" != "interactive"
-then
- die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
-fi
-
-case "$action" in
-continue)
- # Sanity check
- git rev-parse --verify HEAD >/dev/null ||
- die "$(gettext "Cannot read HEAD")"
- git update-index --ignore-submodules --refresh &&
- git diff-files --quiet --ignore-submodules || {
- echo "$(gettext "You must edit all merge conflicts and then
-mark them as resolved using git add")"
- exit 1
- }
- read_basic_state
- run_specific_rebase
- ;;
-skip)
- output git reset --hard HEAD || exit $?
- read_basic_state
- run_specific_rebase
- ;;
-abort)
- git rerere clear
- read_basic_state
- case "$head_name" in
- refs/*)
- git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
- die "$(eval_gettext "Could not move back to \$head_name")"
- ;;
- esac
- output git reset --hard $orig_head
- rm -r "$state_dir"
- exit
- ;;
-edit-todo)
- run_specific_rebase
- ;;
-esac
-
-# Make sure no rebase is in progress
-if test -n "$in_progress"
-then
- state_dir_base=${state_dir##*/}
- cmd_live_rebase="git rebase (--continue | --abort | --skip)"
- cmd_clear_stale_rebase="rm -fr \"$state_dir\""
- die "
-$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
-I wonder if you are in the middle of another rebase. If that is the
-case, please try
- $cmd_live_rebase
-If that is not the case, please
- $cmd_clear_stale_rebase
-and run me again. I am stopping in case you still have something
-valuable there.')"
-fi
-
-if test -n "$rebase_root" && test -z "$onto"
-then
- test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-if test -n "$interactive_rebase"
-then
- type=interactive
- state_dir="$merge_dir"
-elif test -n "$do_merge"
-then
- type=merge
- state_dir="$merge_dir"
-else
- type=am
- state_dir="$apply_dir"
-fi
-
-if test -z "$rebase_root"
-then
- case "$#" in
- 0)
- if ! upstream_name=$(git rev-parse --symbolic-full-name \
- --verify -q @{upstream} 2>/dev/null)
- then
- . git-parse-remote
- error_on_missing_default_upstream "rebase" "rebase" \
- "against" "git rebase <branch>"
- fi
- ;;
- *) upstream_name="$1"
- shift
- ;;
- esac
- upstream=`git rev-parse --verify "${upstream_name}^0"` ||
- die "$(eval_gettext "invalid upstream \$upstream_name")"
- upstream_arg="$upstream_name"
-else
- if test -z "$onto"
- then
- empty_tree=`git hash-object -t tree /dev/null`
- onto=`git commit-tree $empty_tree </dev/null`
- squash_onto="$onto"
- fi
- unset upstream_name
- unset upstream
- test $# -gt 1 && usage
- upstream_arg=--root
-fi
-
-# Make sure the branch to rebase onto is valid.
-onto_name=${onto-"$upstream_name"}
-case "$onto_name" in
-*...*)
- if left=${onto_name%...*} right=${onto_name#*...} &&
- onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
- then
- case "$onto" in
- ?*"$LF"?*)
- die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
- ;;
- '')
- die "$(eval_gettext "\$onto_name: there is no merge base")"
- ;;
- esac
- else
- die "$(eval_gettext "\$onto_name: there is no merge base")"
- fi
- ;;
-*)
- onto=$(git rev-parse --verify "${onto_name}^0") ||
- die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
- ;;
-esac
-
-# If the branch to rebase is given, that is the branch we will rebase
-# $branch_name -- branch being rebased, or HEAD (already detached)
-# $orig_head -- commit object name of tip of the branch before rebasing
-# $head_name -- refs/heads/<that-branch> or "detached HEAD"
-switch_to=
-case "$#" in
-1)
- # Is it "rebase other $branchname" or "rebase other $commit"?
- branch_name="$1"
- switch_to="$1"
-
- if git show-ref --verify --quiet -- "refs/heads/$1" &&
- orig_head=$(git rev-parse -q --verify "refs/heads/$1")
- then
- head_name="refs/heads/$1"
- elif orig_head=$(git rev-parse -q --verify "$1")
- then
- head_name="detached HEAD"
- else
- die "$(eval_gettext "fatal: no such branch: \$branch_name")"
- fi
- ;;
-0)
- # Do not need to switch branches, we are already on it.
- if branch_name=`git symbolic-ref -q HEAD`
- then
- head_name=$branch_name
- branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
- else
- head_name="detached HEAD"
- branch_name=HEAD ;# detached
- fi
- orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
- ;;
-*)
- die "BUG: unexpected number of arguments left to parse"
- ;;
-esac
-
-require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
-
-# Now we are rebasing commits $upstream..$orig_head (or with --root,
-# everything leading up to $orig_head) on top of $onto
-
-# Check if we are already based on $onto with linear history,
-# but this should be done only when upstream and onto are the same
-# and if this is not an interactive rebase.
-mb=$(git merge-base "$onto" "$orig_head")
-if test "$type" != interactive && test "$upstream" = "$onto" &&
- test "$mb" = "$onto" &&
- # linear history?
- ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
-then
- if test -z "$force_rebase"
- then
- # Lazily switch to the target branch if needed...
- test -z "$switch_to" || git checkout "$switch_to" --
- say "$(eval_gettext "Current branch \$branch_name is up to date.")"
- exit 0
- else
- say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
- fi
-fi
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
-
-if test -n "$diffstat"
-then
- if test -n "$verbose"
- then
- echo "$(eval_gettext "Changes from \$mb to \$onto:")"
- fi
- # We want color (if set), but no pager
- GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
-fi
-
-test "$type" = interactive && run_specific_rebase
-
-# Detach HEAD and reset the tree
-say "$(gettext "First, rewinding head to replay your work on top of it...")"
-git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $orig_head
-
-# If the $onto is a proper descendant of the tip of the branch, then
-# we just fast-forwarded.
-if test "$mb" = "$orig_head"
-then
- say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
- move_to_original_branch
- exit 0
-fi
-
-if test -n "$rebase_root"
-then
- revisions="$onto..$orig_head"
-else
- revisions="$upstream..$orig_head"
-fi
-
-run_specific_rebase
diff --git a/git.c b/git.c
index ed66c66..a4cb4a8 100644
--- a/git.c
+++ b/git.c
@@ -379,6 +379,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
{ "push", cmd_push, RUN_SETUP },
{ "read-tree", cmd_read_tree, RUN_SETUP },
+ { "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
--
2.3.0.rc1.137.g477eb31
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2015-03-18 9:57 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-18 9:55 [PATCH/WIP 0/8] Convert git-rebase.sh to C Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 1/8] strbuf: add and use strbuf_read_file_or_die() Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 2/8] Move reset_tree from builtin/checkout.c to unpack-trees.c Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 3/8] rebase: turn rebase--am into a separate program Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 4/8] rebase: turn rebase--merge " Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 5/8] rebase: turn rebase--interactive " Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 6/8] rebase: remove unused function Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 7/8] rebase: move resolvemsg to rebase--* scripts Nguyễn Thái Ngọc Duy
2015-03-18 9:55 ` [PATCH/WIP 8/8] Build in rebase Nguyễn Thái Ngọc Duy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).