This patch series provides the bare minimum to run more than the trivial rebase (i.e. `git rebase <upstream>`). Here, I have implemented essential options needed to make this a builtin rebase. Ofcourse, to accomplish the task of builtin rebase, I had to do essential optimizations and add certain shield which weren't present in original rebase. It is based the latest iteration of pk/rebase-in-c, i.e. ac7f467fef8b (builtin/rebase: support running "git rebase <upstream>", 2018-08-07). This is the second patch series that brings us more closer to a builtin "git rebase". If you like to view the development branch, you can view (https://github.com/git/git/pull/505), where I have kept my commits up to date and leveraged Travis(there is sporadic failures in t5520 for macos gcc and isn't due to my patches) for extra testing other than my system. I plan on submitting the next patch series today, in this order: bultin rebase actions: The builtin rebase will add all the rebase actions. builtin rebase options: The builtin rebase will add all the options supported by original rebase. builtin rebase rest: The builtin rebase will convert all the remaining shell scripts from the original rebase to C. default to builtin rebase: This will turn on the feature-complete builtin rebase to on. These patch series are built on top of each other, i.e. they depend on this order. The motivation to organize these patch series is to make review easier and for pleasant read with the help of my GSoC mentors since, this is the final week of GSoC and other GSoC students most likely have submitted their respective works which will need lots of review. Pratik Karki (11): builtin rebase: support --onto builtin rebase: support `git rebase --onto A...B` builtin rebase: handle the pre-rebase hook (and add --no-verify) builtin rebase: support --quiet builtin rebase: support the `verbose` and `diffstat` options builtin rebase: require a clean worktree builtin rebase: try to fast forward when possible builtin rebase: support --force-rebase builtin rebase: start a new rebase only if none is in progress builtin rebase: only store fully-qualified refs in `options.head_name` builtin rebase: support `git rebase <upstream> <switch-to>` builtin/rebase.c | 333 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 320 insertions(+), 13 deletions(-) -- 2.18.0
The `--onto` option is important, as it allows to rebase a range of commits onto a different base commit (which gave the command its odd name: "rebase"). This commit introduces options parsing so that different options can be added in future commits. Note: As this commit introduces to the parse_options() call (which "eats" argv[0]), the argc is now expected to be lower by one after this patch, compared to before this patch: argv[0] no longer refers to the command name, but to the first (non-option) command-line parameter. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index e695d8a430..742ed31498 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -16,6 +16,16 @@ #include "cache-tree.h" #include "unpack-trees.h" #include "lockfile.h" +#include "parse-options.h" + +static char const * 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 +}; static GIT_PATH_FUNC(apply_dir, "rebase-apply") static GIT_PATH_FUNC(merge_dir, "rebase-merge") @@ -301,6 +311,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int ret, flags; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct option builtin_rebase_options[] = { + OPT_STRING(0, "onto", &options.onto_name, + N_("revision"), + N_("rebase onto given branch instead of upstream")), + OPT_END(), + }; /* * NEEDSWORK: Once the builtin rebase has been tested enough @@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) BUG("sane_execvp() returned???"); } - if (argc != 2) - die(_("Usage: %s <base>"), argv[0]); + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(builtin_rebase_usage, + builtin_rebase_options); + prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, + builtin_rebase_options, + builtin_rebase_usage, 0); + + if (argc > 2) + usage_with_options(builtin_rebase_usage, + builtin_rebase_options); switch (options.type) { case REBASE_MERGE: @@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if (!options.root) { - if (argc < 2) + if (argc < 1) die("TODO: handle @{upstream}"); else { - options.upstream_name = argv[1]; + options.upstream_name = argv[0]; argc--; argv++; if (!strcmp(options.upstream_name, "-")) @@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * orig_head -- commit object name of tip of the branch before rebasing * head_name -- refs/heads/<that-branch> or "detached HEAD" */ - if (argc > 1) + if (argc > 0) die("TODO: handle switch_to"); else { /* Do not need to switch branches, we are already on it. */ -- 2.18.0
This commit implements support for an --onto argument that is actually a "symmetric range" i.e. `<rev1>...<rev2>`. The equivalent shell script version of the code offers two different error messages for the cases where there is no merge base vs more than one merge base. Though following the similar approach would be nice, this would create more complexity than it is of current. Currently, for simple convenience, the `get_oid_mb()` function is used whose return value does not discern between those two error conditions. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 742ed31498..38c496dd10 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -17,6 +17,7 @@ #include "unpack-trees.h" #include "lockfile.h" #include "parse-options.h" +#include "commit.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -311,6 +312,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int ret, flags; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct object_id merge_base; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), @@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - die("TODO"); + if (get_oid_mb(options.onto_name, &merge_base) < 0) + die(_("'%s': need exactly one merge base"), + options.onto_name); + options.onto = lookup_commit_or_die(&merge_base, + options.onto_name); } else { options.onto = peel_committish(options.onto_name); if (!options.onto) -- 2.18.0
This commit converts the equivalent part of the shell script `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and to interrupt the rebase with error if the hook fails. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index 38c496dd10..b79f9b0a9f 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -70,6 +70,7 @@ struct rebase_options { const char *state_dir; struct commit *upstream; const char *upstream_name; + const char *upstream_arg; char *head_name; struct object_id orig_head; struct commit *onto; @@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) }; const char *branch_name; int ret, flags; + int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; struct object_id merge_base; @@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_STRING(0, "onto", &options.onto_name, N_("revision"), N_("rebase onto given branch instead of upstream")), + OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, + N_("allow pre-rebase hook to run")), OPT_END(), }; @@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.upstream = peel_committish(options.upstream_name); if (!options.upstream) die(_("invalid upstream '%s'"), options.upstream_name); + options.upstream_arg = options.upstream_name; } else die("TODO: upstream for --root"); @@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("Could not resolve HEAD to a revision")); } + /* If a hook exists, give it a chance to interrupt*/ + if (!ok_to_skip_pre_rebase && + run_hook_le(NULL, "pre-rebase", options.upstream_arg, + argc ? argv[0] : NULL, NULL)) + die(_("The pre-rebase hook refused to rebase.")); + strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) die(_("Could not detach HEAD")); -- 2.18.0
This commit introduces a rebase option `--quiet`. While `--quiet` is commonly perceived as opposite to `--verbose`, this is not the case for the rebase command: both `--quiet` and `--verbose` default to `false` if neither `--quiet` nor `--verbose` is present. This commit goes further and introduces `--no-quiet` which is the contrary of `--quiet` and it's introduction doesn't modify any behaviour. Note: The `flags` field in `rebase_options` will accumulate more bits in subsequent commits, in particular a verbose and a diffstat flag. And as --quoet inthe shell scripted version of the rebase command switches off --verbose and --stat, and as --verbose switches off --quiet, we use the (negated) REBASE_NO_QUIET instead of REBASE_QUIET: this allows us to turn off the quiet mode and turn on the verbose and diffstat mode in a single OPT_BIT(), and the opposite in a single OPT_NEGBIT(). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index b79f9b0a9f..19fa4d3fc4 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -79,6 +79,10 @@ struct rebase_options { int root; struct commit *restrict_revision; int dont_finish_rebase; + enum { + REBASE_NO_QUIET = 1<<0, + } flags; + struct strbuf git_am_opt; }; /* Returns the filename prefixed by the state_dir */ @@ -159,6 +163,9 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "revisions", opts->revisions); add_var(&script_snippet, "restrict_revision", opts->restrict_revision ? oid_to_hex(&opts->restrict_revision->object.oid) : NULL); + add_var(&script_snippet, "GIT_QUIET", + opts->flags & REBASE_NO_QUIET ? "" : "t"); + add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf); switch (opts->type) { case REBASE_AM: @@ -308,6 +315,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { .type = REBASE_UNSPECIFIED, + .flags = REBASE_NO_QUIET, + .git_am_opt = STRBUF_INIT, }; const char *branch_name; int ret, flags; @@ -321,6 +330,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) N_("rebase onto given branch instead of upstream")), OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, N_("allow pre-rebase hook to run")), + OPT_NEGBIT('q', "quiet", &options.flags, + N_("be quiet. implies --no-stat"), + REBASE_NO_QUIET), OPT_END(), }; @@ -357,6 +369,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) usage_with_options(builtin_rebase_usage, builtin_rebase_options); + if (!(options.flags & REBASE_NO_QUIET)) + strbuf_addstr(&options.git_am_opt, " -q"); + switch (options.type) { case REBASE_MERGE: case REBASE_INTERACTIVE: -- 2.18.0
This commit introduces support for the `-v` and `--stat` options of rebase. The --stat option can also be configured via the Git config setting rebase.stat. To support this, we also add a custom rebase_config() function in this commit that will be used instead of (and falls back to calling) git_default_config(). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 19fa4d3fc4..2d3f1d65fb 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -18,6 +18,7 @@ #include "lockfile.h" #include "parse-options.h" #include "commit.h" +#include "diff.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -81,6 +82,8 @@ struct rebase_options { int dont_finish_rebase; enum { REBASE_NO_QUIET = 1<<0, + REBASE_VERBOSE = 1<<1, + REBASE_DIFFSTAT = 1<<2, } flags; struct strbuf git_am_opt; }; @@ -166,6 +169,10 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "GIT_QUIET", opts->flags & REBASE_NO_QUIET ? "" : "t"); add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf); + add_var(&script_snippet, "verbose", + opts->flags & REBASE_VERBOSE ? "t" : ""); + add_var(&script_snippet, "diffstat", + opts->flags & REBASE_DIFFSTAT ? "t" : ""); switch (opts->type) { case REBASE_AM: @@ -311,6 +318,21 @@ static int reset_head(struct object_id *oid, const char *action, return ret; } +static int rebase_config(const char *var, const char *value, void *data) +{ + struct rebase_options *opts = data; + + if (!strcmp(var, "rebase.stat")) { + if (git_config_bool(var, value)) + opts->flags |= REBASE_DIFFSTAT; + else + opts->flags &= !REBASE_DIFFSTAT; + return 0; + } + + return git_default_config(var, value, data); +} + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@ -332,7 +354,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) N_("allow pre-rebase hook to run")), OPT_NEGBIT('q', "quiet", &options.flags, N_("be quiet. implies --no-stat"), - REBASE_NO_QUIET), + REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT), + OPT_BIT('v', "verbose", &options.flags, + N_("display a diffstat of what changed upstream"), + REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), + {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, + N_("do not show diffstat of what changed upstream"), + PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, OPT_END(), }; @@ -360,7 +388,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) trace_repo_setup(prefix); setup_work_tree(); - git_config(git_default_config, NULL); + git_config(rebase_config, &options); + argc = parse_options(argc, argv, prefix, builtin_rebase_options, builtin_rebase_usage, 0); @@ -456,6 +485,33 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) argc ? argv[0] : NULL, NULL)) die(_("The pre-rebase hook refused to rebase.")); + if (options.flags & REBASE_DIFFSTAT) { + struct diff_options opts; + + if (options.flags & REBASE_VERBOSE) + printf(_("Changes from %s to %s:\n"), + oid_to_hex(&merge_base), + oid_to_hex(&options.onto->object.oid)); + + /* We want color (if set), but no pager */ + diff_setup(&opts); + opts.stat_width = -1; /* use full terminal width */ + opts.stat_graph_width = -1; /* respect statGraphWidth config */ + opts.output_format |= + DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; + opts.detect_rename = DIFF_DETECT_RENAME; + diff_setup_done(&opts); + diff_tree_oid(&merge_base, &options.onto->object.oid, + "", &opts); + diffcore_std(&opts); + diff_flush(&opts); + } + + /* Detach HEAD and reset the tree */ + if (options.flags & REBASE_NO_QUIET) + printf(_("First, rewinding head to replay your work on top of " + "it...\n")); + strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) die(_("Could not detach HEAD")); -- 2.18.0
This commit reads the index of the repository for rebase and checks whether the repository is ready for rebase. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index 2d3f1d65fb..afef0b0046 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -19,6 +19,7 @@ #include "parse-options.h" #include "commit.h" #include "diff.h" +#include "wt-status.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -479,6 +480,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("Could not resolve HEAD to a revision")); } + if (read_index(the_repository->index) < 0) + die(_("could not read index")); + + if (require_clean_work_tree("rebase", + _("Please commit or stash them."), 1, 1)) { + ret = 1; + goto cleanup; + } + /* If a hook exists, give it a chance to interrupt*/ if (!ok_to_skip_pre_rebase && run_hook_le(NULL, "pre-rebase", options.upstream_arg, @@ -528,6 +538,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) ret = !!run_specific_rebase(&options); +cleanup: strbuf_release(&revisions); free(options.head_name); return ret; -- 2.18.0
In this commit, we add support to fast forward. Note: we will need the merge base later, therefore the call to can_fast_forward() really needs to be the first one when testing whether we can skip the rebase entirely (otherwise, it would make more sense to skip the possibly expensive operation if, say, running an interactive rebase). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index afef0b0046..52a218cd18 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -20,6 +20,7 @@ #include "commit.h" #include "diff.h" #include "wt-status.h" +#include "revision.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -89,6 +90,12 @@ struct rebase_options { struct strbuf git_am_opt; }; +static int is_interactive(struct rebase_options *opts) +{ + return opts->type == REBASE_INTERACTIVE || + opts->type == REBASE_PRESERVE_MERGES; +} + /* Returns the filename prefixed by the state_dir */ static const char *state_dir_path(const char *filename, struct rebase_options *opts) { @@ -334,6 +341,46 @@ static int rebase_config(const char *var, const char *value, void *data) return git_default_config(var, value, data); } +/* + * Determines whether the commits in from..to are linear, i.e. contain + * no merge commits. This function *expects* `from` to be an ancestor of + * `to`. + */ +static int is_linear_history(struct commit *from, struct commit *to) +{ + while (to && to != from) { + parse_commit(to); + if (!to->parents) + return 1; + if (to->parents->next) + return 0; + to = to->parents->item; + } + return 1; +} + +static int can_fast_forward(struct commit *onto, struct object_id *head_oid, + struct object_id *merge_base) +{ + struct commit *head = lookup_commit(the_repository, head_oid); + struct commit_list *merge_bases; + int res; + + if (!head) + return 0; + + merge_bases = get_merge_bases(onto, head); + if (merge_bases && !merge_bases->next) { + oidcpy(merge_base, &merge_bases->item->object.oid); + res = !oidcmp(merge_base, &onto->object.oid); + } else { + oidcpy(merge_base, &null_oid); + res = 0; + } + free_commit_list(merge_bases); + return res && is_linear_history(onto, head); +} + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@ -489,6 +536,31 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } + /* + * 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. + */ + if (can_fast_forward(options.onto, &options.orig_head, &merge_base) && + !is_interactive(&options) && !options.restrict_revision && + !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { + int flag; + + if (!(options.flags & REBASE_NO_QUIET)) + ; /* be quiet */ + else if (!strcmp(branch_name, "HEAD") && + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + puts(_("HEAD is up to date, rebase forced.")); + else + printf(_("Current branch %s is up to date, rebase " + "forced.\n"), branch_name); + } + /* If a hook exists, give it a chance to interrupt*/ if (!ok_to_skip_pre_rebase && run_hook_le(NULL, "pre-rebase", options.upstream_arg, -- 2.18.0
In this commit, we add support to `--force-rebase` option. The equivalent part of the shell script found in `git-legacy-rebase.sh` is converted as faithfully as possible to C. The --force-rebase option ensures that the rebase does not simply fast-forward even if it could. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 52a218cd18..8a7bf3d468 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -86,6 +86,7 @@ struct rebase_options { REBASE_NO_QUIET = 1<<0, REBASE_VERBOSE = 1<<1, REBASE_DIFFSTAT = 1<<2, + REBASE_FORCE = 1<<3, } flags; struct strbuf git_am_opt; }; @@ -181,6 +182,8 @@ static int run_specific_rebase(struct rebase_options *opts) opts->flags & REBASE_VERBOSE ? "t" : ""); add_var(&script_snippet, "diffstat", opts->flags & REBASE_DIFFSTAT ? "t" : ""); + add_var(&script_snippet, "force_rebase", + opts->flags & REBASE_FORCE ? "t" : ""); switch (opts->type) { case REBASE_AM: @@ -409,6 +412,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, N_("do not show diffstat of what changed upstream"), PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, + OPT_BIT('f', "force-rebase", &options.flags, + N_("cherry-pick all commits, even if unchanged"), + REBASE_FORCE), + OPT_BIT(0, "no-ff", &options.flags, + N_("cherry-pick all commits, even if unchanged"), + REBASE_FORCE), OPT_END(), }; @@ -551,10 +560,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { int flag; - if (!(options.flags & REBASE_NO_QUIET)) + if (!(options.flags & REBASE_FORCE)) { + if (!(options.flags & REBASE_NO_QUIET)) + ; /* be quiet */ + else if (!strcmp(branch_name, "HEAD") && + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + puts(_("HEAD is up to date.")); + else + printf(_("Current branch %s is up to date.\n"), + branch_name); + ret = !!finish_rebase(&options); + goto cleanup; + } else if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) puts(_("HEAD is up to date, rebase forced.")); else printf(_("Current branch %s is up to date, rebase " -- 2.18.0
To run a new rebase, there needs to be a check to assure that no other rebase is in progress. New rebase operation cannot start until an ongoing rebase operation completes or is terminated. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 8a7bf3d468..a261f552f1 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -87,6 +87,7 @@ struct rebase_options { REBASE_VERBOSE = 1<<1, REBASE_DIFFSTAT = 1<<2, REBASE_FORCE = 1<<3, + REBASE_INTERACTIVE_EXPLICIT = 1<<4, } flags; struct strbuf git_am_opt; }; @@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) .git_am_opt = STRBUF_INIT, }; const char *branch_name; - int ret, flags; + int ret, flags, in_progress = 0; int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; struct object_id merge_base; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, @@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) git_config(rebase_config, &options); + if (is_directory(apply_dir())) { + options.type = REBASE_AM; + options.state_dir = apply_dir(); + } else if (is_directory(merge_dir())) { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/rewritten", merge_dir()); + if (is_directory(buf.buf)) { + options.type = REBASE_PRESERVE_MERGES; + options.flags |= REBASE_INTERACTIVE_EXPLICIT; + } else { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/interactive", merge_dir()); + if(file_exists(buf.buf)) { + options.type = REBASE_INTERACTIVE; + options.flags |= REBASE_INTERACTIVE_EXPLICIT; + } else + options.type = REBASE_MERGE; + } + options.state_dir = merge_dir(); + } + + if (options.type != REBASE_UNSPECIFIED) + in_progress = 1; + argc = parse_options(argc, argv, prefix, builtin_rebase_options, builtin_rebase_usage, 0); @@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) usage_with_options(builtin_rebase_usage, builtin_rebase_options); + /* Make sure no rebase is in progress */ + if (in_progress) { + const char *last_slash = strrchr(options.state_dir, '/'); + const char *state_dir_base = + last_slash ? last_slash + 1 : options.state_dir; + const char *cmd_live_rebase = + "git rebase (--continue | --abort | --skip)"; + strbuf_reset(&buf); + strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir); + 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\t%s\n" + "If that is not the case, please\n\t%s\n" + "and run me again. I am stopping in case you still " + "have something\n" + "valuable there.\n"), + state_dir_base, cmd_live_rebase,buf.buf); + } + if (!(options.flags & REBASE_NO_QUIET)) strbuf_addstr(&options.git_am_opt, " -q"); -- 2.18.0
When running a rebase on a detached HEAD, we currently store the string "detached HEAD" in options.head_name. That is a faithful translation of the shell script version, and we still kind of need it for the purposes of the scripted backends. It is poor style for C, though, where we would really only want a valid, fully-qualified ref name as value, and NULL for detached HEADs, using "detached HEAD" for display only. Make it so. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index a261f552f1..63634210c0 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -169,7 +169,8 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "upstream_name", opts->upstream_name); add_var(&script_snippet, "upstream", oid_to_hex(&opts->upstream->object.oid)); - add_var(&script_snippet, "head_name", opts->head_name); + add_var(&script_snippet, "head_name", + opts->head_name ? opts->head_name : "detached HEAD"); add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head)); add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid)); add_var(&script_snippet, "onto_name", opts->onto_name); @@ -251,6 +252,9 @@ static int reset_head(struct object_id *oid, const char *action, *old_orig = NULL, oid_old_orig; int ret = 0; + if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) + BUG("Not a fully qualified branch: '%s'", switch_to_branch); + if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) return -1; @@ -558,7 +562,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * branch_name -- branch/commit 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" + * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) */ if (argc > 0) die("TODO: handle switch_to"); @@ -575,7 +579,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) branch_name = options.head_name; } else { - options.head_name = xstrdup("detached HEAD"); + free(options.head_name); + options.head_name = NULL; branch_name = "HEAD"; } if (get_oid("HEAD", &options.orig_head)) -- 2.18.0
This commit adds support for `switch-to` which is used to switch to the target branch if needed. The equivalent codes found in shell script `git-legacy-rebase.sh` is converted to builtin `rebase.c`. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> --- builtin/rebase.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 63634210c0..b2ddfa8dbf 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -79,6 +79,7 @@ struct rebase_options { struct commit *onto; const char *onto_name; const char *revisions; + const char *switch_to; int root; struct commit *restrict_revision; int dont_finish_rebase; @@ -186,6 +187,8 @@ static int run_specific_rebase(struct rebase_options *opts) opts->flags & REBASE_DIFFSTAT ? "t" : ""); add_var(&script_snippet, "force_rebase", opts->flags & REBASE_FORCE ? "t" : ""); + if (opts->switch_to) + add_var(&script_snippet, "switch_to", opts->switch_to); switch (opts->type) { case REBASE_AM: @@ -564,9 +567,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * orig_head -- commit object name of tip of the branch before rebasing * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) */ - if (argc > 0) - die("TODO: handle switch_to"); - else { + if (argc == 1) { + /* Is it "rebase other branchname" or "rebase other commit"? */ + branch_name = argv[0]; + options.switch_to = argv[0]; + + /* Is it a local branch? */ + strbuf_reset(&buf); + strbuf_addf(&buf, "refs/heads/%s", branch_name); + if (!read_ref(buf.buf, &options.orig_head)) + options.head_name = xstrdup(buf.buf); + /* If not is it a valid ref (branch or commit)? */ + else if (!get_oid(branch_name, &options.orig_head)) + options.head_name = NULL; + else + die(_("fatal: no such branch/commit '%s'"), + branch_name); + } else if (argc == 0) { /* Do not need to switch branches, we are already on it. */ options.head_name = xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, @@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if (get_oid("HEAD", &options.orig_head)) die(_("Could not resolve HEAD to a revision")); - } + } else + BUG("unexpected number of arguments left to parse"); if (read_index(the_repository->index) < 0) die(_("could not read index")); @@ -612,6 +630,28 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int flag; if (!(options.flags & REBASE_FORCE)) { + /* Lazily switch to the target branch if needed... */ + if (options.switch_to) { + struct object_id oid; + + if (get_oid(options.switch_to, &oid) < 0) { + ret = !!error(_("could not parse '%s'"), + options.switch_to); + goto cleanup; + } + + strbuf_reset(&buf); + strbuf_addf(&buf, "rebase: checkout %s", + options.switch_to); + if (reset_head(&oid, "checkout", + options.head_name, 0) < 0) { + ret = !!error(_("could not switch to " + "%s"), + options.switch_to); + goto cleanup; + } + } + if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && -- 2.18.0
(not really a review, this patch just happens to catch my eyes) On Wed, Aug 8, 2018 at 3:55 PM Pratik Karki <predatoramigo@gmail.com> wrote: > > This commit adds support for `switch-to` which is used to switch to the > target branch if needed. The equivalent codes found in shell script > `git-legacy-rebase.sh` is converted to builtin `rebase.c`. > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > --- > builtin/rebase.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- > 1 file changed, 44 insertions(+), 4 deletions(-) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 63634210c0..b2ddfa8dbf 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -79,6 +79,7 @@ struct rebase_options { > struct commit *onto; > const char *onto_name; > const char *revisions; > + const char *switch_to; > int root; > struct commit *restrict_revision; > int dont_finish_rebase; > @@ -186,6 +187,8 @@ static int run_specific_rebase(struct rebase_options *opts) > opts->flags & REBASE_DIFFSTAT ? "t" : ""); > add_var(&script_snippet, "force_rebase", > opts->flags & REBASE_FORCE ? "t" : ""); > + if (opts->switch_to) > + add_var(&script_snippet, "switch_to", opts->switch_to); > > switch (opts->type) { > case REBASE_AM: > @@ -564,9 +567,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > * orig_head -- commit object name of tip of the branch before rebasing > * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) > */ > - if (argc > 0) > - die("TODO: handle switch_to"); > - else { > + if (argc == 1) { > + /* Is it "rebase other branchname" or "rebase other commit"? */ > + branch_name = argv[0]; > + options.switch_to = argv[0]; > + > + /* Is it a local branch? */ > + strbuf_reset(&buf); > + strbuf_addf(&buf, "refs/heads/%s", branch_name); > + if (!read_ref(buf.buf, &options.orig_head)) > + options.head_name = xstrdup(buf.buf); > + /* If not is it a valid ref (branch or commit)? */ > + else if (!get_oid(branch_name, &options.orig_head)) > + options.head_name = NULL; > + else > + die(_("fatal: no such branch/commit '%s'"), die() automatically adds "fatal:" so you should not add it yourself here > + branch_name); > + } else if (argc == 0) { > /* Do not need to switch branches, we are already on it. */ > options.head_name = > xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, > @@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > } > if (get_oid("HEAD", &options.orig_head)) > die(_("Could not resolve HEAD to a revision")); > - } > + } else > + BUG("unexpected number of arguments left to parse"); Does this mean "git base one two three" triggers this BUG? If so, this should be a die() instead. I did not real the full source code, so maybe this case is already caught higher up. -- Duy
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote:
>
> This commit introduces a rebase option `--quiet`. While `--quiet` is
> commonly perceived as opposite to `--verbose`, this is not the case for
> the rebase command: both `--quiet` and `--verbose` default to `false` if
> neither `--quiet` nor `--verbose` is present.
>
> This commit goes further and introduces `--no-quiet` which is the
> contrary of `--quiet` and it's introduction doesn't modify any
> behaviour.
>
> Note: The `flags` field in `rebase_options` will accumulate more bits in
> subsequent commits, in particular a verbose and a diffstat flag. And as
> --quoet inthe shell scripted version of the rebase command switches off
--quote in the
(in case a resend is needed)
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote: > @@ -551,10 +560,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) [...] > ; /* be quiet */ > else if (!strcmp(branch_name, "HEAD") && > - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) > + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) This line is changing only the indentation whitespace? Would it make sense to have it in the previous patch?
Hi Duy, On Wed, 8 Aug 2018, Duy Nguyen wrote: > On Wed, Aug 8, 2018 at 3:55 PM Pratik Karki <predatoramigo@gmail.com> wrote: > > > > diff --git a/builtin/rebase.c b/builtin/rebase.c > > index 63634210c0..b2ddfa8dbf 100644 > > --- a/builtin/rebase.c > > +++ b/builtin/rebase.c > > @@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > > } > > if (get_oid("HEAD", &options.orig_head)) > > die(_("Could not resolve HEAD to a revision")); > > - } > > + } else > > + BUG("unexpected number of arguments left to parse"); > > Does this mean "git base one two three" triggers this BUG? If so, this > should be a die() instead. I did not real the full source code, so > maybe this case is already caught higher up. As you can see from https://github.com/git/git/blob/v2.18.0/git-rebase.sh#L615 the original, Unix shell script version of `git rebase` also says "BUG" here. And if you care to look at https://github.com/git/git/blob/3358abdcb/builtin/rebase.c#L870-L872 you will see that there is a proper check for the correct amount of command-line parameters. So at this point, it would indeed indicate a bug if the `argc` had an unexpected value. Ciao, Dscho
On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote: > > To run a new rebase, there needs to be a check to assure that no other > rebase is in progress. New rebase operation cannot start until an > ongoing rebase operation completes or is terminated. > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > --- > builtin/rebase.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 47 insertions(+), 1 deletion(-) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 8a7bf3d468..a261f552f1 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -87,6 +87,7 @@ struct rebase_options { > REBASE_VERBOSE = 1<<1, > REBASE_DIFFSTAT = 1<<2, > REBASE_FORCE = 1<<3, > + REBASE_INTERACTIVE_EXPLICIT = 1<<4, > } flags; > struct strbuf git_am_opt; > }; > @@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > .git_am_opt = STRBUF_INIT, > }; > const char *branch_name; > - int ret, flags; > + int ret, flags, in_progress = 0; > int ok_to_skip_pre_rebase = 0; > struct strbuf msg = STRBUF_INIT; > struct strbuf revisions = STRBUF_INIT; > + struct strbuf buf = STRBUF_INIT; > struct object_id merge_base; > struct option builtin_rebase_options[] = { > OPT_STRING(0, "onto", &options.onto_name, > @@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > > git_config(rebase_config, &options); > > + if (is_directory(apply_dir())) { > + options.type = REBASE_AM; > + options.state_dir = apply_dir(); > + } else if (is_directory(merge_dir())) { > + strbuf_reset(&buf); > + strbuf_addf(&buf, "%s/rewritten", merge_dir()); > + if (is_directory(buf.buf)) { > + options.type = REBASE_PRESERVE_MERGES; > + options.flags |= REBASE_INTERACTIVE_EXPLICIT; > + } else { > + strbuf_reset(&buf); > + strbuf_addf(&buf, "%s/interactive", merge_dir()); > + if(file_exists(buf.buf)) { > + options.type = REBASE_INTERACTIVE; > + options.flags |= REBASE_INTERACTIVE_EXPLICIT; > + } else > + options.type = REBASE_MERGE; > + } > + options.state_dir = merge_dir(); > + } > + > + if (options.type != REBASE_UNSPECIFIED) > + in_progress = 1; > + > argc = parse_options(argc, argv, prefix, > builtin_rebase_options, > builtin_rebase_usage, 0); > @@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > usage_with_options(builtin_rebase_usage, > builtin_rebase_options); > > + /* Make sure no rebase is in progress */ The faithful conversion doesn't even stop at the comments. ;-) I shortly wondered if this is the best place for this comment, but let's just keep it here to have the 1:1 rewrite. > + if (in_progress) { [...] > + state_dir_base, cmd_live_rebase,buf.buf); In case a resend is needed, add a whitespace after the comma and buf.buf, please. So far I have not seen anything major that would warrant a resend. Thanks, Stefan
Pratik Karki <predatoramigo@gmail.com> writes: > The `--onto` option is important, as it allows to rebase a range of > commits onto a different base commit (which gave the command its odd > name: "rebase"). Is there anything unimportant? A rhetorical question, of course. The quite casual and natural use of "to rebase" as a verb in the first sentence contradicts with what the parenthetical "its odd name" comment says. Perhaps drop everything after "(which..."? i.e. The `--onto` option allows to rebase a range of commits onto a different base commit. Port the support for the option to the C re-implementation. > This commit introduces options parsing so that different options can > be added in future commits. We usually do not say "This commit does X", or (worse) "I do X in this commit". Instead, order the codebase to be like so, e.g. "Support command line options by adding a call to parse_options(); later commits will add more options by building on top." or something like that. > @@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > BUG("sane_execvp() returned???"); > } > > - if (argc != 2) > - die(_("Usage: %s <base>"), argv[0]); > + if (argc == 2 && !strcmp(argv[1], "-h")) > + usage_with_options(builtin_rebase_usage, > + builtin_rebase_options); > + > prefix = setup_git_directory(); > trace_repo_setup(prefix); > setup_work_tree(); > > git_config(git_default_config, NULL); > + argc = parse_options(argc, argv, prefix, > + builtin_rebase_options, > + builtin_rebase_usage, 0); > + > + if (argc > 2) > + usage_with_options(builtin_rebase_usage, > + builtin_rebase_options); OK. This correctly calls the parser after repository setup. > switch (options.type) { > case REBASE_MERGE: > @@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > } > > if (!options.root) { > - if (argc < 2) > + if (argc < 1) > die("TODO: handle @{upstream}"); > else { > - options.upstream_name = argv[1]; > + options.upstream_name = argv[0]; > argc--; > argv++; > if (!strcmp(options.upstream_name, "-")) > @@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > * orig_head -- commit object name of tip of the branch before rebasing > * head_name -- refs/heads/<that-branch> or "detached HEAD" > */ > - if (argc > 1) > + if (argc > 0) > die("TODO: handle switch_to"); > else { > /* Do not need to switch branches, we are already on it. */
Pratik Karki <predatoramigo@gmail.com> writes: > This commit implements support for an --onto argument that is actually a > "symmetric range" i.e. `<rev1>...<rev2>`. > > The equivalent shell script version of the code offers two different > error messages for the cases where there is no merge base vs more than > one merge base. Though following the similar approach would be nice, > this would create more complexity than it is of current. Currently, for Sorry, but it is unclear what you mean by "than it is of current." Do you mean we leave it broken at this step in the series for now for expediency, with the intention to later revisit and fix it, or do you mean something else? > simple convenience, the `get_oid_mb()` function is used whose return > value does not discern between those two error conditions. > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > ... > @@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > if (!options.onto_name) > options.onto_name = options.upstream_name; > if (strstr(options.onto_name, "...")) { > - die("TODO"); > + if (get_oid_mb(options.onto_name, &merge_base) < 0) > + die(_("'%s': need exactly one merge base"), > + options.onto_name); > + options.onto = lookup_commit_or_die(&merge_base, > + options.onto_name); The original is slightly sloppy in that it will misparse rebase --onto 'master^{/log ... message}' and this shares the same, which I think is probably OK. When this actually becomes problematic, the original can easily be salvaged by making it to fall back to the same peel_committish in its else clause; I am not sure if this C rewrite is as easily be fixed the same way, though. > } else { > options.onto = peel_committish(options.onto_name); > if (!options.onto)
Pratik Karki <predatoramigo@gmail.com> writes: > This commit converts the equivalent part of the shell script > `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and > to interrupt the rebase with error if the hook fails. > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > --- Introduction of upstream_arg in this step looked a bit surprising, but the hook invocation is the only thing that uses it, so it is understandable. "rebase: handle the re-rebase hook and --no-verify" would have been sufficient, without "add" or parentheses. > builtin/rebase.c | 11 +++++++++++ > 1 file changed, 11 insertions(+) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index 38c496dd10..b79f9b0a9f 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -70,6 +70,7 @@ struct rebase_options { > const char *state_dir; > struct commit *upstream; > const char *upstream_name; > + const char *upstream_arg; > char *head_name; > struct object_id orig_head; > struct commit *onto; > @@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > }; > const char *branch_name; > int ret, flags; > + int ok_to_skip_pre_rebase = 0; > struct strbuf msg = STRBUF_INIT; > struct strbuf revisions = STRBUF_INIT; > struct object_id merge_base; > @@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > OPT_STRING(0, "onto", &options.onto_name, > N_("revision"), > N_("rebase onto given branch instead of upstream")), > + OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, > + N_("allow pre-rebase hook to run")), Do we need to catch "--no-no-verify" ourselves with NONEG bit, or is this sufficient to tell parse_options() machinery to take care of it? > OPT_END(), > }; > > @@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > options.upstream = peel_committish(options.upstream_name); > if (!options.upstream) > die(_("invalid upstream '%s'"), options.upstream_name); > + options.upstream_arg = options.upstream_name; > } else > die("TODO: upstream for --root"); > > @@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > die(_("Could not resolve HEAD to a revision")); > } > > + /* If a hook exists, give it a chance to interrupt*/ > + if (!ok_to_skip_pre_rebase && > + run_hook_le(NULL, "pre-rebase", options.upstream_arg, > + argc ? argv[0] : NULL, NULL)) > + die(_("The pre-rebase hook refused to rebase.")); > + > strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); > if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) > die(_("Could not detach HEAD"));
Stefan Beller <sbeller@google.com> writes: > On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote: >> >> This commit introduces a rebase option `--quiet`. While `--quiet` is >> commonly perceived as opposite to `--verbose`, this is not the case for >> the rebase command: both `--quiet` and `--verbose` default to `false` if >> neither `--quiet` nor `--verbose` is present. >> >> This commit goes further and introduces `--no-quiet` which is the >> contrary of `--quiet` and it's introduction doesn't modify any >> behaviour. Why? Is it for completeness (i.e. does the scripted version take such an option and addition of --no-quiet makes the C rewrite behave the same)? >> Note: The `flags` field in `rebase_options` will accumulate more bits in >> subsequent commits, in particular a verbose and a diffstat flag. And as >> --quoet inthe shell scripted version of the rebase command switches off > > --quote in the > > (in case a resend is needed) Meaning --quiet?
Hi Stefan,
On Wed, 8 Aug 2018, Stefan Beller wrote:
> On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote:
>
> > @@ -551,10 +560,21 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
> [...]
> > ; /* be quiet */
> > else if (!strcmp(branch_name, "HEAD") &&
> > - resolve_ref_unsafe("HEAD", 0, NULL, &flag))
> > + resolve_ref_unsafe("HEAD", 0, NULL, &flag))
>
> This line is changing only the indentation whitespace?
> Would it make sense to have it in the previous patch?
Correct. I will fix this before sending the next iteration,
Dscho
Hi Stefan, On Wed, 8 Aug 2018, Stefan Beller wrote: > On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote: > > > > diff --git a/builtin/rebase.c b/builtin/rebase.c > > index 8a7bf3d468..a261f552f1 100644 > > --- a/builtin/rebase.c > > +++ b/builtin/rebase.c > > @@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > > usage_with_options(builtin_rebase_usage, > > builtin_rebase_options); > > > > + /* Make sure no rebase is in progress */ > > The faithful conversion doesn't even stop at the comments. ;-) Yes, I insisted on it. TBH it is a bit of a shame that we cannot fix all those error messages going to stdout, but... you know... One step after the other. > I shortly wondered if this is the best place for this comment, > but let's just keep it here to have the 1:1 rewrite. It should probably be inside the conditional block, but as you say: the original had it in a funny spot, and so does the converted code. > > + if (in_progress) { > [...] > > + state_dir_base, cmd_live_rebase,buf.buf); > > In case a resend is needed, add a whitespace after the > comma and buf.buf, please. I will fix this before sending the next iteration. Thanks for the review! Dscho
Hi Junio, On Wed, 8 Aug 2018, Junio C Hamano wrote: > Pratik Karki <predatoramigo@gmail.com> writes: > > > The `--onto` option is important, as it allows to rebase a range of > > commits onto a different base commit (which gave the command its odd > > name: "rebase"). > > Is there anything unimportant? A rhetorical question, of course. You might think it is a rhetorical question, but obviously it is not, as your reaction testifies. But certainly there are more important options and less important options! The most important options are those that are frequently used. > The quite casual and natural use of "to rebase" as a verb in the > first sentence contradicts with what the parenthetical "its odd > name" comment says. Perhaps drop everything after "(which..."? > > i.e. > > The `--onto` option allows to rebase a range of commits onto > a different base commit. Port the support for the option to > the C re-implementation. I'd rather keep it. Remember, a story is easier to read than a dull academic treatise. I want to have a little bit of a personal touch when I stumble over these commit messages again. And I know I will. > > This commit introduces options parsing so that different options can > > be added in future commits. > > We usually do not say "This commit does X", or (worse) "I do X in > this commit". Oh, don't we now? ;-) (This *was* a rhetorical question, as *I* use this tense all the time, and unless you have quietly rewritten my commit messages without my knowledge nor consent, the Git commit history is full of these instances.) > Instead, order the codebase to be like so, e.g. "Support command line > options by adding a call to parse_options(); later commits will add more > options by building on top." or something like that. To be quite frank with you, I hoped for a review that would focus a teeny tiny bit on the correctness of the code. If you want to continue to nit-pick the commit messages, that's fine, of course, but do understand that I am not really prepared to change a whole lot there, unless you point out outright errors or false statements. Those naturally need fixing. Also, please note that I will now *definitely* focus on bug fixes, as I am really eager to get those speed improvements into Git for Windows v2.19.0. And I don't know whether I have said this publicly yet: I will send the next iterations of Pratik's patch series. He is busy with exams (GSoC really caters for US schedules, students who are in countries with very different university schedules are a bit out of luck here), and I really want these patches. Ciao, Dscho
Hi Junio, On Wed, 8 Aug 2018, Junio C Hamano wrote: > Pratik Karki <predatoramigo@gmail.com> writes: > > > This commit implements support for an --onto argument that is actually a > > "symmetric range" i.e. `<rev1>...<rev2>`. > > > > The equivalent shell script version of the code offers two different > > error messages for the cases where there is no merge base vs more than > > one merge base. Though following the similar approach would be nice, > > this would create more complexity than it is of current. Currently, for > > Sorry, but it is unclear what you mean by "than it is of current." > Do you mean we leave it broken at this step in the series for now > for expediency, with the intention to later revisit and fix it, or > do you mean something else? I suggested to drop the distinction, in favor of simpler code. Not for the time being, but for good. I reworded the commit message thusly: builtin rebase: support `git rebase --onto A...B` This commit implements support for an --onto argument that is actually a "symmetric range" i.e. `<rev1>...<rev2>`. The equivalent shell script version of the code offers two different error messages for the cases where there is no merge base vs more than one merge base. Though it would be nice to retain this distinction, dropping it makes it possible to simply use the `get_oid_mb()` function. Besides, it happens rarely in real-world scenarios. Therefore, in the interest of keeping the code less complex, let's just use that function, and live with an error message that does not distinguish between those two error conditions. > > @@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > > if (!options.onto_name) > > options.onto_name = options.upstream_name; > > if (strstr(options.onto_name, "...")) { > > - die("TODO"); > > + if (get_oid_mb(options.onto_name, &merge_base) < 0) > > + die(_("'%s': need exactly one merge base"), > > + options.onto_name); > > + options.onto = lookup_commit_or_die(&merge_base, > > + options.onto_name); > > The original is slightly sloppy in that it will misparse > > rebase --onto 'master^{/log ... message}' > > and this shares the same, which I think is probably OK. I did run into this recently, but not with an `--onto` option. I forgot the details (I meant to write it down, and forgot that, too). Sorry for musing, back on the topic. Yes, it shares the same, and *that* makes it okay. Remember: this patch series is not about improving `git rebase` at all. It is about converting from shell script to builtin. > When this actually becomes problematic, the original can easily be > salvaged by making it to fall back to the same peel_committish in its > else clause; I am not sure if this C rewrite is as easily be fixed the > same way, though. I will make a note so that I hopefully won't forget. Thanks, Dscho > > > } else { > > options.onto = peel_committish(options.onto_name); > > if (!options.onto) >
Hi Junio, On Wed, 8 Aug 2018, Junio C Hamano wrote: > Pratik Karki <predatoramigo@gmail.com> writes: > > > This commit converts the equivalent part of the shell script > > `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and > > to interrupt the rebase with error if the hook fails. > > > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > > --- > > Introduction of upstream_arg in this step looked a bit > surprising, but the hook invocation is the only thing that uses it, > so it is understandable. Yep, that's literally all that `upstream_arg` is used for: $ git grep upstream_arg v2.19.0-rc0 v2.19.0-rc0:git-rebase.sh: upstream_arg="$upstream_name" v2.19.0-rc0:git-rebase.sh: upstream_arg=--root v2.19.0-rc0:git-rebase.sh:run_pre_rebase_hook "$upstream_arg" "$@" > "rebase: handle the re-rebase hook and --no-verify" would have been > sufficient, without "add" or parentheses. Fixed. > > diff --git a/builtin/rebase.c b/builtin/rebase.c > > index 38c496dd10..b79f9b0a9f 100644 > > --- a/builtin/rebase.c > > +++ b/builtin/rebase.c > > @@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > > OPT_STRING(0, "onto", &options.onto_name, > > N_("revision"), > > N_("rebase onto given branch instead of upstream")), > > + OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, > > + N_("allow pre-rebase hook to run")), > > Do we need to catch "--no-no-verify" ourselves with NONEG bit, or is > this sufficient to tell parse_options() machinery to take care of > it? I just issued $ ./git rebase --verify --no-no-verify --xyz and it showed error: unknown option `xyz' [... usage ...] I vaguely remembered that the parse_options() machinery special-cases "no-" prefixes, and my test seems to confirm it. Holler if you want a more in-depth analysis. Ciao, Dscho
Hi Junio, On Wed, 8 Aug 2018, Junio C Hamano wrote: > Stefan Beller <sbeller@google.com> writes: > > > On Wed, Aug 8, 2018 at 6:51 AM Pratik Karki <predatoramigo@gmail.com> wrote: > >> > >> This commit introduces a rebase option `--quiet`. While `--quiet` is > >> commonly perceived as opposite to `--verbose`, this is not the case for > >> the rebase command: both `--quiet` and `--verbose` default to `false` if > >> neither `--quiet` nor `--verbose` is present. > >> > >> This commit goes further and introduces `--no-quiet` which is the > >> contrary of `--quiet` and it's introduction doesn't modify any > >> behaviour. > > Why? Is it for completeness (i.e. does the scripted version take > such an option and addition of --no-quiet makes the C rewrite behave > the same)? Ah. I mentioned that an explanation for this is needed in the commit message, and I guess that it is a bit too subtle. The part you clipped from your quoted text says: [... `--quiet`] switches off --verbose and --stat, and as --verbose switches off --quiet, we use the (negated) REBASE_NO_QUIET instead of REBASE_QUIET: this allows us to turn off the quiet mode and turn on the verbose and diffstat mode in a single OPT_BIT(), and the opposite in a single OPT_NEGBIT(). I agree that this is a pretty convoluted way to express the issue. See below for an attempt at a clearer commit message. > >> Note: The `flags` field in `rebase_options` will accumulate more bits in > >> subsequent commits, in particular a verbose and a diffstat flag. And as > >> --quoet inthe shell scripted version of the rebase command switches off > > > > --quote in the > > > > (in case a resend is needed) > > Meaning --quiet? Yep. I should have paid more attention in my pre-submission review, sorry. I changed the commit message to read like this: builtin rebase: support --quiet This commit introduces a rebase option `--quiet`. While `--quiet` is commonly perceived as opposite to `--verbose`, this is not the case for the rebase command: both `--quiet` and `--verbose` default to `false` if neither `--quiet` nor `--verbose` is present. Despite the default being `false` for both verbose and quiet mode, passing the `--quiet` option will turn off verbose mode, and `--verbose` will turn off quiet mode. This patch introduces the `flags` bit field, with `REBASE_NO_QUIET` as first user (with many more to come). We do *not* use `REBASE_QUIET` here for an important reason: To keep the implementation simple, this commit introduces `--no-quiet` instead of `--quiet`, so that a single `OPT_NEGBIT()` can turn on quiet mode and turn off verbose and diffstat mode at the same time. Likewise, the companion commit which will introduce support for `--verbose` will have a single `OPT_BIT()` that turns off quiet mode and turns on verbose and diffstat mode at the same time. Ciao, Dscho
This patch series provides the bare minimum to run more than just the trivial rebase (i.e. git rebase <upstream>): it implements the most common options such as --onto. It is based the latest iteration of pk/rebase-in-c. This is the second patch series that brings us more closer to a builtin "git rebase". Changes since v1: * Many commit messages were reworded. * An indentation fix was folded into the commit that introduces the incorrect indentation. * A missing space after a comma was inserted. Pratik Karki (11): builtin rebase: support --onto builtin rebase: support `git rebase --onto A...B` builtin rebase: handle the pre-rebase hook and --no-verify builtin rebase: support --quiet builtin rebase: support the `verbose` and `diffstat` options builtin rebase: require a clean worktree builtin rebase: try to fast forward when possible builtin rebase: support --force-rebase builtin rebase: start a new rebase only if none is in progress builtin rebase: only store fully-qualified refs in `options.head_name` builtin rebase: support `git rebase <upstream> <switch-to>` builtin/rebase.c | 333 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 320 insertions(+), 13 deletions(-) base-commit: ac7f467fef8b836084afdce5eded047c79a6858d Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-32%2Fdscho%2Frebase-in-c-2-basic-v2 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-32/dscho/rebase-in-c-2-basic-v2 Pull-Request: https://github.com/gitgitgadget/git/pull/32 Range-diff vs v1: 1: c5f67c35ea ! 1: fba1b3e2a9 builtin rebase: support --onto @@ -15,6 +15,7 @@ command name, but to the first (non-option) command-line parameter. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 2: 35d141c32a ! 2: f9826ab58f builtin rebase: support `git rebase --onto A...B` @@ -7,12 +7,18 @@ The equivalent shell script version of the code offers two different error messages for the cases where there is no merge base vs more than - one merge base. Though following the similar approach would be nice, - this would create more complexity than it is of current. Currently, for - simple convenience, the `get_oid_mb()` function is used whose return - value does not discern between those two error conditions. + one merge base. + + Though it would be nice to retain this distinction, dropping it makes it + possible to simply use the `get_oid_mb()` function. Besides, it happens + rarely in real-world scenarios. + + Therefore, in the interest of keeping the code less complex, let's just + use that function, and live with an error message that does not + distinguish between those two error conditions. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 3: e223f2209d ! 3: 7100820def builtin rebase: handle the pre-rebase hook (and add --no-verify) @@ -1,12 +1,13 @@ Author: Pratik Karki <predatoramigo@gmail.com> - builtin rebase: handle the pre-rebase hook (and add --no-verify) + builtin rebase: handle the pre-rebase hook and --no-verify This commit converts the equivalent part of the shell script `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and to interrupt the rebase with error if the hook fails. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 4: 19919e7e24 ! 4: 5034f53024 builtin rebase: support --quiet @@ -7,19 +7,23 @@ the rebase command: both `--quiet` and `--verbose` default to `false` if neither `--quiet` nor `--verbose` is present. - This commit goes further and introduces `--no-quiet` which is the - contrary of `--quiet` and it's introduction doesn't modify any - behaviour. + Despite the default being `false` for both verbose and quiet mode, + passing the `--quiet` option will turn off verbose mode, and `--verbose` + will turn off quiet mode. - Note: The `flags` field in `rebase_options` will accumulate more bits in - subsequent commits, in particular a verbose and a diffstat flag. And as - --quoet inthe shell scripted version of the rebase command switches off - --verbose and --stat, and as --verbose switches off --quiet, we use the - (negated) REBASE_NO_QUIET instead of REBASE_QUIET: this allows us to - turn off the quiet mode and turn on the verbose and diffstat mode in a - single OPT_BIT(), and the opposite in a single OPT_NEGBIT(). + This patch introduces the `flags` bit field, with `REBASE_NO_QUIET` + as first user (with many more to come). + + We do *not* use `REBASE_QUIET` here for an important reason: To keep the + implementation simple, this commit introduces `--no-quiet` instead of + `--quiet`, so that a single `OPT_NEGBIT()` can turn on quiet mode and + turn off verbose and diffstat mode at the same time. Likewise, the + companion commit which will introduce support for `--verbose` will have + a single `OPT_BIT()` that turns off quiet mode and turns on verbose and + diffstat mode at the same time. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 5: cbf318d0de ! 5: ce1e1f266a builtin rebase: support the `verbose` and `diffstat` options @@ -11,6 +11,7 @@ calling) git_default_config(). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 6: b440bf9884 ! 6: f11f21d5c6 builtin rebase: require a clean worktree @@ -6,6 +6,7 @@ whether the repository is ready for rebase. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 7: 0efe9b41f0 ! 7: 2ec0b744bf builtin rebase: try to fast forward when possible @@ -11,6 +11,7 @@ rebase). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c @@ -105,7 +106,7 @@ + if (!(options.flags & REBASE_NO_QUIET)) + ; /* be quiet */ + else if (!strcmp(branch_name, "HEAD") && -+ resolve_ref_unsafe("HEAD", 0, NULL, &flag)) ++ resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + puts(_("HEAD is up to date, rebase forced.")); + else + printf(_("Current branch %s is up to date, rebase " 8: ae019dec3f ! 8: 78d90e67de builtin rebase: support --force-rebase @@ -10,6 +10,7 @@ fast-forward even if it could. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c @@ -63,8 +64,4 @@ + } else if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && -- resolve_ref_unsafe("HEAD", 0, NULL, &flag)) -+ resolve_ref_unsafe("HEAD", 0, NULL, &flag)) - puts(_("HEAD is up to date, rebase forced.")); - else - printf(_("Current branch %s is up to date, rebase " + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) 9: d58d504c03 ! 9: b639bfa5a8 builtin rebase: start a new rebase only if none is in progress @@ -7,6 +7,7 @@ ongoing rebase operation completes or is terminated. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c @@ -84,7 +85,7 @@ + "and run me again. I am stopping in case you still " + "have something\n" + "valuable there.\n"), -+ state_dir_base, cmd_live_rebase,buf.buf); ++ state_dir_base, cmd_live_rebase, buf.buf); + } + if (!(options.flags & REBASE_NO_QUIET)) 10: ef468bf3d7 ! 10: aab01f0b8e builtin rebase: only store fully-qualified refs in `options.head_name` @@ -12,6 +12,7 @@ "detached HEAD" for display only. Make it so. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c 11: 9a26fc3fac ! 11: e64190d8ed builtin rebase: support `git rebase <upstream> <switch-to>` @@ -7,6 +7,7 @@ `git-legacy-rebase.sh` is converted to builtin `rebase.c`. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> + Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> diff --git a/builtin/rebase.c b/builtin/rebase.c --- a/builtin/rebase.c -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> The `--onto` option is important, as it allows to rebase a range of commits onto a different base commit (which gave the command its odd name: "rebase"). This commit introduces options parsing so that different options can be added in future commits. Note: As this commit introduces to the parse_options() call (which "eats" argv[0]), the argc is now expected to be lower by one after this patch, compared to before this patch: argv[0] no longer refers to the command name, but to the first (non-option) command-line parameter. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index e695d8a430..742ed31498 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -16,6 +16,16 @@ #include "cache-tree.h" #include "unpack-trees.h" #include "lockfile.h" +#include "parse-options.h" + +static char const * 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 +}; static GIT_PATH_FUNC(apply_dir, "rebase-apply") static GIT_PATH_FUNC(merge_dir, "rebase-merge") @@ -301,6 +311,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int ret, flags; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct option builtin_rebase_options[] = { + OPT_STRING(0, "onto", &options.onto_name, + N_("revision"), + N_("rebase onto given branch instead of upstream")), + OPT_END(), + }; /* * NEEDSWORK: Once the builtin rebase has been tested enough @@ -318,13 +334,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) BUG("sane_execvp() returned???"); } - if (argc != 2) - die(_("Usage: %s <base>"), argv[0]); + if (argc == 2 && !strcmp(argv[1], "-h")) + usage_with_options(builtin_rebase_usage, + builtin_rebase_options); + prefix = setup_git_directory(); trace_repo_setup(prefix); setup_work_tree(); git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, + builtin_rebase_options, + builtin_rebase_usage, 0); + + if (argc > 2) + usage_with_options(builtin_rebase_usage, + builtin_rebase_options); switch (options.type) { case REBASE_MERGE: @@ -343,10 +368,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if (!options.root) { - if (argc < 2) + if (argc < 1) die("TODO: handle @{upstream}"); else { - options.upstream_name = argv[1]; + options.upstream_name = argv[0]; argc--; argv++; if (!strcmp(options.upstream_name, "-")) @@ -377,7 +402,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * orig_head -- commit object name of tip of the branch before rebasing * head_name -- refs/heads/<that-branch> or "detached HEAD" */ - if (argc > 1) + if (argc > 0) die("TODO: handle switch_to"); else { /* Do not need to switch branches, we are already on it. */ -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit implements support for an --onto argument that is actually a "symmetric range" i.e. `<rev1>...<rev2>`. The equivalent shell script version of the code offers two different error messages for the cases where there is no merge base vs more than one merge base. Though it would be nice to retain this distinction, dropping it makes it possible to simply use the `get_oid_mb()` function. Besides, it happens rarely in real-world scenarios. Therefore, in the interest of keeping the code less complex, let's just use that function, and live with an error message that does not distinguish between those two error conditions. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 742ed31498..38c496dd10 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -17,6 +17,7 @@ #include "unpack-trees.h" #include "lockfile.h" #include "parse-options.h" +#include "commit.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -311,6 +312,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int ret, flags; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct object_id merge_base; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, N_("revision"), @@ -387,7 +389,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!options.onto_name) options.onto_name = options.upstream_name; if (strstr(options.onto_name, "...")) { - die("TODO"); + if (get_oid_mb(options.onto_name, &merge_base) < 0) + die(_("'%s': need exactly one merge base"), + options.onto_name); + options.onto = lookup_commit_or_die(&merge_base, + options.onto_name); } else { options.onto = peel_committish(options.onto_name); if (!options.onto) -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit converts the equivalent part of the shell script `git-legacy-rebase.sh` to run the pre-rebase hook (unless disabled), and to interrupt the rebase with error if the hook fails. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index 38c496dd10..b79f9b0a9f 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -70,6 +70,7 @@ struct rebase_options { const char *state_dir; struct commit *upstream; const char *upstream_name; + const char *upstream_arg; char *head_name; struct object_id orig_head; struct commit *onto; @@ -310,6 +311,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) }; const char *branch_name; int ret, flags; + int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; struct object_id merge_base; @@ -317,6 +319,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_STRING(0, "onto", &options.onto_name, N_("revision"), N_("rebase onto given branch instead of upstream")), + OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, + N_("allow pre-rebase hook to run")), OPT_END(), }; @@ -382,6 +386,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.upstream = peel_committish(options.upstream_name); if (!options.upstream) die(_("invalid upstream '%s'"), options.upstream_name); + options.upstream_arg = options.upstream_name; } else die("TODO: upstream for --root"); @@ -430,6 +435,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("Could not resolve HEAD to a revision")); } + /* If a hook exists, give it a chance to interrupt*/ + if (!ok_to_skip_pre_rebase && + run_hook_le(NULL, "pre-rebase", options.upstream_arg, + argc ? argv[0] : NULL, NULL)) + die(_("The pre-rebase hook refused to rebase.")); + strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) die(_("Could not detach HEAD")); -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit introduces a rebase option `--quiet`. While `--quiet` is commonly perceived as opposite to `--verbose`, this is not the case for the rebase command: both `--quiet` and `--verbose` default to `false` if neither `--quiet` nor `--verbose` is present. Despite the default being `false` for both verbose and quiet mode, passing the `--quiet` option will turn off verbose mode, and `--verbose` will turn off quiet mode. This patch introduces the `flags` bit field, with `REBASE_NO_QUIET` as first user (with many more to come). We do *not* use `REBASE_QUIET` here for an important reason: To keep the implementation simple, this commit introduces `--no-quiet` instead of `--quiet`, so that a single `OPT_NEGBIT()` can turn on quiet mode and turn off verbose and diffstat mode at the same time. Likewise, the companion commit which will introduce support for `--verbose` will have a single `OPT_BIT()` that turns off quiet mode and turns on verbose and diffstat mode at the same time. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index b79f9b0a9f..19fa4d3fc4 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -79,6 +79,10 @@ struct rebase_options { int root; struct commit *restrict_revision; int dont_finish_rebase; + enum { + REBASE_NO_QUIET = 1<<0, + } flags; + struct strbuf git_am_opt; }; /* Returns the filename prefixed by the state_dir */ @@ -159,6 +163,9 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "revisions", opts->revisions); add_var(&script_snippet, "restrict_revision", opts->restrict_revision ? oid_to_hex(&opts->restrict_revision->object.oid) : NULL); + add_var(&script_snippet, "GIT_QUIET", + opts->flags & REBASE_NO_QUIET ? "" : "t"); + add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf); switch (opts->type) { case REBASE_AM: @@ -308,6 +315,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { .type = REBASE_UNSPECIFIED, + .flags = REBASE_NO_QUIET, + .git_am_opt = STRBUF_INIT, }; const char *branch_name; int ret, flags; @@ -321,6 +330,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) N_("rebase onto given branch instead of upstream")), OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, N_("allow pre-rebase hook to run")), + OPT_NEGBIT('q', "quiet", &options.flags, + N_("be quiet. implies --no-stat"), + REBASE_NO_QUIET), OPT_END(), }; @@ -357,6 +369,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) usage_with_options(builtin_rebase_usage, builtin_rebase_options); + if (!(options.flags & REBASE_NO_QUIET)) + strbuf_addstr(&options.git_am_opt, " -q"); + switch (options.type) { case REBASE_MERGE: case REBASE_INTERACTIVE: -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit introduces support for the `-v` and `--stat` options of rebase. The --stat option can also be configured via the Git config setting rebase.stat. To support this, we also add a custom rebase_config() function in this commit that will be used instead of (and falls back to calling) git_default_config(). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 19fa4d3fc4..2d3f1d65fb 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -18,6 +18,7 @@ #include "lockfile.h" #include "parse-options.h" #include "commit.h" +#include "diff.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -81,6 +82,8 @@ struct rebase_options { int dont_finish_rebase; enum { REBASE_NO_QUIET = 1<<0, + REBASE_VERBOSE = 1<<1, + REBASE_DIFFSTAT = 1<<2, } flags; struct strbuf git_am_opt; }; @@ -166,6 +169,10 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "GIT_QUIET", opts->flags & REBASE_NO_QUIET ? "" : "t"); add_var(&script_snippet, "git_am_opt", opts->git_am_opt.buf); + add_var(&script_snippet, "verbose", + opts->flags & REBASE_VERBOSE ? "t" : ""); + add_var(&script_snippet, "diffstat", + opts->flags & REBASE_DIFFSTAT ? "t" : ""); switch (opts->type) { case REBASE_AM: @@ -311,6 +318,21 @@ static int reset_head(struct object_id *oid, const char *action, return ret; } +static int rebase_config(const char *var, const char *value, void *data) +{ + struct rebase_options *opts = data; + + if (!strcmp(var, "rebase.stat")) { + if (git_config_bool(var, value)) + opts->flags |= REBASE_DIFFSTAT; + else + opts->flags &= !REBASE_DIFFSTAT; + return 0; + } + + return git_default_config(var, value, data); +} + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@ -332,7 +354,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) N_("allow pre-rebase hook to run")), OPT_NEGBIT('q', "quiet", &options.flags, N_("be quiet. implies --no-stat"), - REBASE_NO_QUIET), + REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT), + OPT_BIT('v', "verbose", &options.flags, + N_("display a diffstat of what changed upstream"), + REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), + {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, + N_("do not show diffstat of what changed upstream"), + PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, OPT_END(), }; @@ -360,7 +388,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) trace_repo_setup(prefix); setup_work_tree(); - git_config(git_default_config, NULL); + git_config(rebase_config, &options); + argc = parse_options(argc, argv, prefix, builtin_rebase_options, builtin_rebase_usage, 0); @@ -456,6 +485,33 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) argc ? argv[0] : NULL, NULL)) die(_("The pre-rebase hook refused to rebase.")); + if (options.flags & REBASE_DIFFSTAT) { + struct diff_options opts; + + if (options.flags & REBASE_VERBOSE) + printf(_("Changes from %s to %s:\n"), + oid_to_hex(&merge_base), + oid_to_hex(&options.onto->object.oid)); + + /* We want color (if set), but no pager */ + diff_setup(&opts); + opts.stat_width = -1; /* use full terminal width */ + opts.stat_graph_width = -1; /* respect statGraphWidth config */ + opts.output_format |= + DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; + opts.detect_rename = DIFF_DETECT_RENAME; + diff_setup_done(&opts); + diff_tree_oid(&merge_base, &options.onto->object.oid, + "", &opts); + diffcore_std(&opts); + diff_flush(&opts); + } + + /* Detach HEAD and reset the tree */ + if (options.flags & REBASE_NO_QUIET) + printf(_("First, rewinding head to replay your work on top of " + "it...\n")); + strbuf_addf(&msg, "rebase: checkout %s", options.onto_name); if (reset_head(&options.onto->object.oid, "checkout", NULL, 1)) die(_("Could not detach HEAD")); -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit reads the index of the repository for rebase and checks whether the repository is ready for rebase. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index 2d3f1d65fb..afef0b0046 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -19,6 +19,7 @@ #include "parse-options.h" #include "commit.h" #include "diff.h" +#include "wt-status.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -479,6 +480,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("Could not resolve HEAD to a revision")); } + if (read_index(the_repository->index) < 0) + die(_("could not read index")); + + if (require_clean_work_tree("rebase", + _("Please commit or stash them."), 1, 1)) { + ret = 1; + goto cleanup; + } + /* If a hook exists, give it a chance to interrupt*/ if (!ok_to_skip_pre_rebase && run_hook_le(NULL, "pre-rebase", options.upstream_arg, @@ -528,6 +538,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) ret = !!run_specific_rebase(&options); +cleanup: strbuf_release(&revisions); free(options.head_name); return ret; -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> In this commit, we add support to fast forward. Note: we will need the merge base later, therefore the call to can_fast_forward() really needs to be the first one when testing whether we can skip the rebase entirely (otherwise, it would make more sense to skip the possibly expensive operation if, say, running an interactive rebase). Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/builtin/rebase.c b/builtin/rebase.c index afef0b0046..d67df28efc 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -20,6 +20,7 @@ #include "commit.h" #include "diff.h" #include "wt-status.h" +#include "revision.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " @@ -89,6 +90,12 @@ struct rebase_options { struct strbuf git_am_opt; }; +static int is_interactive(struct rebase_options *opts) +{ + return opts->type == REBASE_INTERACTIVE || + opts->type == REBASE_PRESERVE_MERGES; +} + /* Returns the filename prefixed by the state_dir */ static const char *state_dir_path(const char *filename, struct rebase_options *opts) { @@ -334,6 +341,46 @@ static int rebase_config(const char *var, const char *value, void *data) return git_default_config(var, value, data); } +/* + * Determines whether the commits in from..to are linear, i.e. contain + * no merge commits. This function *expects* `from` to be an ancestor of + * `to`. + */ +static int is_linear_history(struct commit *from, struct commit *to) +{ + while (to && to != from) { + parse_commit(to); + if (!to->parents) + return 1; + if (to->parents->next) + return 0; + to = to->parents->item; + } + return 1; +} + +static int can_fast_forward(struct commit *onto, struct object_id *head_oid, + struct object_id *merge_base) +{ + struct commit *head = lookup_commit(the_repository, head_oid); + struct commit_list *merge_bases; + int res; + + if (!head) + return 0; + + merge_bases = get_merge_bases(onto, head); + if (merge_bases && !merge_bases->next) { + oidcpy(merge_base, &merge_bases->item->object.oid); + res = !oidcmp(merge_base, &onto->object.oid); + } else { + oidcpy(merge_base, &null_oid); + res = 0; + } + free_commit_list(merge_bases); + return res && is_linear_history(onto, head); +} + int cmd_rebase(int argc, const char **argv, const char *prefix) { struct rebase_options options = { @@ -489,6 +536,31 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) goto cleanup; } + /* + * 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. + */ + if (can_fast_forward(options.onto, &options.orig_head, &merge_base) && + !is_interactive(&options) && !options.restrict_revision && + !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { + int flag; + + if (!(options.flags & REBASE_NO_QUIET)) + ; /* be quiet */ + else if (!strcmp(branch_name, "HEAD") && + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + puts(_("HEAD is up to date, rebase forced.")); + else + printf(_("Current branch %s is up to date, rebase " + "forced.\n"), branch_name); + } + /* If a hook exists, give it a chance to interrupt*/ if (!ok_to_skip_pre_rebase && run_hook_le(NULL, "pre-rebase", options.upstream_arg, -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> In this commit, we add support to `--force-rebase` option. The equivalent part of the shell script found in `git-legacy-rebase.sh` is converted as faithfully as possible to C. The --force-rebase option ensures that the rebase does not simply fast-forward even if it could. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index d67df28efc..8a7bf3d468 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -86,6 +86,7 @@ struct rebase_options { REBASE_NO_QUIET = 1<<0, REBASE_VERBOSE = 1<<1, REBASE_DIFFSTAT = 1<<2, + REBASE_FORCE = 1<<3, } flags; struct strbuf git_am_opt; }; @@ -181,6 +182,8 @@ static int run_specific_rebase(struct rebase_options *opts) opts->flags & REBASE_VERBOSE ? "t" : ""); add_var(&script_snippet, "diffstat", opts->flags & REBASE_DIFFSTAT ? "t" : ""); + add_var(&script_snippet, "force_rebase", + opts->flags & REBASE_FORCE ? "t" : ""); switch (opts->type) { case REBASE_AM: @@ -409,6 +412,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, N_("do not show diffstat of what changed upstream"), PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, + OPT_BIT('f', "force-rebase", &options.flags, + N_("cherry-pick all commits, even if unchanged"), + REBASE_FORCE), + OPT_BIT(0, "no-ff", &options.flags, + N_("cherry-pick all commits, even if unchanged"), + REBASE_FORCE), OPT_END(), }; @@ -551,7 +560,18 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) { int flag; - if (!(options.flags & REBASE_NO_QUIET)) + if (!(options.flags & REBASE_FORCE)) { + if (!(options.flags & REBASE_NO_QUIET)) + ; /* be quiet */ + else if (!strcmp(branch_name, "HEAD") && + resolve_ref_unsafe("HEAD", 0, NULL, &flag)) + puts(_("HEAD is up to date.")); + else + printf(_("Current branch %s is up to date.\n"), + branch_name); + ret = !!finish_rebase(&options); + goto cleanup; + } else if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && resolve_ref_unsafe("HEAD", 0, NULL, &flag)) -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> To run a new rebase, there needs to be a check to assure that no other rebase is in progress. New rebase operation cannot start until an ongoing rebase operation completes or is terminated. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index 8a7bf3d468..d45f8f9008 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -87,6 +87,7 @@ struct rebase_options { REBASE_VERBOSE = 1<<1, REBASE_DIFFSTAT = 1<<2, REBASE_FORCE = 1<<3, + REBASE_INTERACTIVE_EXPLICIT = 1<<4, } flags; struct strbuf git_am_opt; }; @@ -392,10 +393,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) .git_am_opt = STRBUF_INIT, }; const char *branch_name; - int ret, flags; + int ret, flags, in_progress = 0; int ok_to_skip_pre_rebase = 0; struct strbuf msg = STRBUF_INIT; struct strbuf revisions = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; struct object_id merge_base; struct option builtin_rebase_options[] = { OPT_STRING(0, "onto", &options.onto_name, @@ -447,6 +449,30 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) git_config(rebase_config, &options); + if (is_directory(apply_dir())) { + options.type = REBASE_AM; + options.state_dir = apply_dir(); + } else if (is_directory(merge_dir())) { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/rewritten", merge_dir()); + if (is_directory(buf.buf)) { + options.type = REBASE_PRESERVE_MERGES; + options.flags |= REBASE_INTERACTIVE_EXPLICIT; + } else { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s/interactive", merge_dir()); + if(file_exists(buf.buf)) { + options.type = REBASE_INTERACTIVE; + options.flags |= REBASE_INTERACTIVE_EXPLICIT; + } else + options.type = REBASE_MERGE; + } + options.state_dir = merge_dir(); + } + + if (options.type != REBASE_UNSPECIFIED) + in_progress = 1; + argc = parse_options(argc, argv, prefix, builtin_rebase_options, builtin_rebase_usage, 0); @@ -455,6 +481,26 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) usage_with_options(builtin_rebase_usage, builtin_rebase_options); + /* Make sure no rebase is in progress */ + if (in_progress) { + const char *last_slash = strrchr(options.state_dir, '/'); + const char *state_dir_base = + last_slash ? last_slash + 1 : options.state_dir; + const char *cmd_live_rebase = + "git rebase (--continue | --abort | --skip)"; + strbuf_reset(&buf); + strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir); + 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\t%s\n" + "If that is not the case, please\n\t%s\n" + "and run me again. I am stopping in case you still " + "have something\n" + "valuable there.\n"), + state_dir_base, cmd_live_rebase, buf.buf); + } + if (!(options.flags & REBASE_NO_QUIET)) strbuf_addstr(&options.git_am_opt, " -q"); -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> When running a rebase on a detached HEAD, we currently store the string "detached HEAD" in options.head_name. That is a faithful translation of the shell script version, and we still kind of need it for the purposes of the scripted backends. It is poor style for C, though, where we would really only want a valid, fully-qualified ref name as value, and NULL for detached HEADs, using "detached HEAD" for display only. Make it so. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index d45f8f9008..afc75fe731 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -169,7 +169,8 @@ static int run_specific_rebase(struct rebase_options *opts) add_var(&script_snippet, "upstream_name", opts->upstream_name); add_var(&script_snippet, "upstream", oid_to_hex(&opts->upstream->object.oid)); - add_var(&script_snippet, "head_name", opts->head_name); + add_var(&script_snippet, "head_name", + opts->head_name ? opts->head_name : "detached HEAD"); add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head)); add_var(&script_snippet, "onto", oid_to_hex(&opts->onto->object.oid)); add_var(&script_snippet, "onto_name", opts->onto_name); @@ -251,6 +252,9 @@ static int reset_head(struct object_id *oid, const char *action, *old_orig = NULL, oid_old_orig; int ret = 0; + if (switch_to_branch && !starts_with(switch_to_branch, "refs/")) + BUG("Not a fully qualified branch: '%s'", switch_to_branch); + if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) return -1; @@ -558,7 +562,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * branch_name -- branch/commit 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" + * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) */ if (argc > 0) die("TODO: handle switch_to"); @@ -575,7 +579,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) branch_name = options.head_name; } else { - options.head_name = xstrdup("detached HEAD"); + free(options.head_name); + options.head_name = NULL; branch_name = "HEAD"; } if (get_oid("HEAD", &options.orig_head)) -- gitgitgadget
From: Pratik Karki <predatoramigo@gmail.com> This commit adds support for `switch-to` which is used to switch to the target branch if needed. The equivalent codes found in shell script `git-legacy-rebase.sh` is converted to builtin `rebase.c`. Signed-off-by: Pratik Karki <predatoramigo@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- builtin/rebase.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/builtin/rebase.c b/builtin/rebase.c index afc75fe731..e817956d96 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -79,6 +79,7 @@ struct rebase_options { struct commit *onto; const char *onto_name; const char *revisions; + const char *switch_to; int root; struct commit *restrict_revision; int dont_finish_rebase; @@ -186,6 +187,8 @@ static int run_specific_rebase(struct rebase_options *opts) opts->flags & REBASE_DIFFSTAT ? "t" : ""); add_var(&script_snippet, "force_rebase", opts->flags & REBASE_FORCE ? "t" : ""); + if (opts->switch_to) + add_var(&script_snippet, "switch_to", opts->switch_to); switch (opts->type) { case REBASE_AM: @@ -564,9 +567,23 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) * orig_head -- commit object name of tip of the branch before rebasing * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) */ - if (argc > 0) - die("TODO: handle switch_to"); - else { + if (argc == 1) { + /* Is it "rebase other branchname" or "rebase other commit"? */ + branch_name = argv[0]; + options.switch_to = argv[0]; + + /* Is it a local branch? */ + strbuf_reset(&buf); + strbuf_addf(&buf, "refs/heads/%s", branch_name); + if (!read_ref(buf.buf, &options.orig_head)) + options.head_name = xstrdup(buf.buf); + /* If not is it a valid ref (branch or commit)? */ + else if (!get_oid(branch_name, &options.orig_head)) + options.head_name = NULL; + else + die(_("fatal: no such branch/commit '%s'"), + branch_name); + } else if (argc == 0) { /* Do not need to switch branches, we are already on it. */ options.head_name = xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, @@ -585,7 +602,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) } if (get_oid("HEAD", &options.orig_head)) die(_("Could not resolve HEAD to a revision")); - } + } else + BUG("unexpected number of arguments left to parse"); if (read_index(the_repository->index) < 0) die(_("could not read index")); @@ -612,6 +630,28 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) int flag; if (!(options.flags & REBASE_FORCE)) { + /* Lazily switch to the target branch if needed... */ + if (options.switch_to) { + struct object_id oid; + + if (get_oid(options.switch_to, &oid) < 0) { + ret = !!error(_("could not parse '%s'"), + options.switch_to); + goto cleanup; + } + + strbuf_reset(&buf); + strbuf_addf(&buf, "rebase: checkout %s", + options.switch_to); + if (reset_head(&oid, "checkout", + options.head_name, 0) < 0) { + ret = !!error(_("could not switch to " + "%s"), + options.switch_to); + goto cleanup; + } + } + if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ else if (!strcmp(branch_name, "HEAD") && -- gitgitgadget
On Tue, Sep 04, 2018 at 02:27:20PM -0700, Pratik Karki via GitGitGadget wrote: > From: Pratik Karki <predatoramigo@gmail.com> > > When running a rebase on a detached HEAD, we currently store the string > "detached HEAD" in options.head_name. That is a faithful translation of > the shell script version, and we still kind of need it for the purposes of > the scripted backends. > > It is poor style for C, though, where we would really only want a valid, > fully-qualified ref name as value, and NULL for detached HEADs, using > "detached HEAD" for display only. Make it so. > > Signed-off-by: Pratik Karki <predatoramigo@gmail.com> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> > --- > builtin/rebase.c | 11 ++++++++--- > 1 file changed, 8 insertions(+), 3 deletions(-) > > diff --git a/builtin/rebase.c b/builtin/rebase.c > index d45f8f9008..afc75fe731 100644 > --- a/builtin/rebase.c > +++ b/builtin/rebase.c > @@ -575,7 +579,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) > branch_name = options.head_name; > > } else { > - options.head_name = xstrdup("detached HEAD"); > + free(options.head_name); > + options.head_name = NULL; Please use FREE_AND_NULL(options.head_name) here. > branch_name = "HEAD"; > } > if (get_oid("HEAD", &options.orig_head)) > -- > gitgitgadget >
SZEDER Gábor <szeder.dev@gmail.com> writes: >> } else { >> - options.head_name = xstrdup("detached HEAD"); >> + free(options.head_name); >> + options.head_name = NULL; > > Please use FREE_AND_NULL(options.head_name) here. Good; did contrib/coccinelle/free.cocci catch this? > >> branch_name = "HEAD"; >> } >> if (get_oid("HEAD", &options.orig_head)) >> -- >> gitgitgadget >>
On Mon, Sep 10, 2018 at 09:55:30AM -0700, Junio C Hamano wrote:
> SZEDER Gábor <szeder.dev@gmail.com> writes:
>
> >> } else {
> >> - options.head_name = xstrdup("detached HEAD");
> >> + free(options.head_name);
> >> + options.head_name = NULL;
> >
> > Please use FREE_AND_NULL(options.head_name) here.
>
> Good; did contrib/coccinelle/free.cocci catch this?
Yes.
But now that you mention it, I see that it didn't catch it in 'pu' or
in most of the later 'pk/rebase-in-c-X-...' branches, even though they
all have these lines. It bisects to 0073df2bd3 (builtin rebase:
support --rebase-merges[=[no-]rebase-cousins], 2018-09-04), which
doesn't touch these lines at all.
Strange; I have no idea what's going on.