From: Elijah Newren <newren@xxxxxxxxx> In many cases the `--onto` option is not necessary as we can guess the branch we would like to replay onto. So let's introduce guess_new_base() for that purpose and make `--onto` optional. Co-authored-by: Christian Couder <chriscool@xxxxxxxxxxxxx> Signed-off-by: Elijah Newren <newren@xxxxxxxxx> Signed-off-by: Christian Couder <chriscool@xxxxxxxxxxxxx> --- Documentation/git-replay.txt | 8 ++++- builtin/replay.c | 61 +++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt index 7a83f70343..ce2cafc42e 100644 --- a/Documentation/git-replay.txt +++ b/Documentation/git-replay.txt @@ -9,7 +9,7 @@ git-replay - Replay commits on a different base, without touching working tree SYNOPSIS -------- [verse] -'git replay' --onto <newbase> <revision-range>... +'git replay' [--onto <newbase>] <revision-range>... DESCRIPTION ----------- @@ -20,6 +20,12 @@ references. However, the output of this command is meant to be used as input to `git update-ref --stdin`, which would update the relevant branches. +When the `--onto <newbase>` option is not passed, the commits will be +replayed onto a base guessed from the `<revision-range>`. For example +if the `<revision-range>` is `origin/main..mybranch` then `mybranch` +was probably based on an old version of `origin/main`, so we will +replay it on the newest version of that branch. + OPTIONS ------- diff --git a/builtin/replay.c b/builtin/replay.c index 63513ea6f1..af948af73c 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -75,6 +75,54 @@ static struct commit *create_commit(struct tree *tree, return (struct commit *)obj; } +static struct commit *guess_new_base(struct rev_cmdline_info *info) +{ + struct commit *new_base = NULL; + int i, bottom_commits = 0; + + /* + * When the user specifies e.g. + * git replay origin/main..mybranch + * git replay ^origin/next mybranch1 mybranch2 + * we want to be able to determine where to replay the commits. In + * these examples, the branches are probably based on an old version + * of either origin/main or origin/next, so we want to replay on the + * newest version of that branch. In contrast we would want to error + * out if they ran + * git replay ^origin/master ^origin/next mybranch + * git replay mybranch~2..mybranch + * the first of those because there's no unique base to choose, and + * the second because they'd likely just be replaying commits on top + * of the same commit and not making any difference. + */ + for (i = 0; i < info->nr; i++) { + struct rev_cmdline_entry *e = info->rev + i; + struct object_id oid; + char *fullname = NULL; + + if (!(e->flags & BOTTOM)) + continue; + + /* + * We need a unique base commit to know where to replay; error + * out if not unique. + * + * Also, we usually don't want to replay commits on the same + * base they started on, so only accept this as the base if + * it uniquely names some ref. + */ + if (bottom_commits++ || + dwim_ref(e->name, strlen(e->name), &oid, &fullname, 0) != 1) + die(_("cannot determine where to replay commits; please specify --onto")); + + free(fullname); + new_base = lookup_commit_reference_gently(the_repository, + &e->item->oid, 1); + } + + return new_base; +} + static struct commit *pick_regular_commit(struct commit *pickme, struct commit *last_commit, struct merge_options *merge_opt, @@ -117,7 +165,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) int ret = 0; const char * const replay_usage[] = { - N_("git replay --onto <newbase> <revision-range>..."), + N_("git replay [--onto <newbase>] <revision-range>..."), NULL }; struct option replay_options[] = { @@ -130,12 +178,6 @@ int cmd_replay(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, replay_options, replay_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); - if (!onto_name) { - error(_("option --onto is mandatory")); - usage_with_options(replay_usage, replay_options); - } - - onto = peel_committish(onto_name); repo_init_revisions(the_repository, &revs, prefix); @@ -151,6 +193,11 @@ int cmd_replay(int argc, const char **argv, const char *prefix) revs.topo_order = 1; revs.simplify_history = 0; + if (onto_name) + onto = peel_committish(onto_name); + else + onto = guess_new_base(&revs.cmdline); + if (prepare_revision_walk(&revs) < 0) { ret = error(_("error preparing revisions")); goto cleanup; -- 2.40.0.228.gb2eb5bb98e