mirror of
https://github.com/git-for-windows/git.git
synced 2026-06-13 08:57:56 -05:00
replay: offer an option to linearize the commit topology
One of the stated goals of git-replay(1) is to allow implementing the git-rebase(1) functionality on the server side. The default mode of git-rebase(1) is to act as if `--no-rebase-merges` was given. This mode drops merge commits instead of replaying them, and linearizes the commit history into a sequence of the regular (single-parent) commits. Add option `--linearize` to git-replay(1) to do the same. Co-authored-by: Toon Claes <toon@iotcl.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
committed by
Junio C Hamano
parent
dbec23af84
commit
ae260e765e
@@ -88,6 +88,11 @@ incompatible with `--contained` (which is a modifier for `--onto` only).
|
||||
+
|
||||
The default mode can be configured via the `replay.refAction` configuration variable.
|
||||
|
||||
--linearize::
|
||||
In this mode, `git replay` imitates `git rebase --no-rebase-merges`,
|
||||
i.e. it cherry-picks only non-merge commits, each one on top of the
|
||||
previous one.
|
||||
|
||||
<revision-range>::
|
||||
Range of commits to replay; see "Specifying Ranges" in
|
||||
linkgit:git-rev-parse[1]. In `--advance=<branch>` or
|
||||
|
||||
@@ -111,6 +111,8 @@ int cmd_replay(int argc,
|
||||
N_("mode"),
|
||||
N_("control ref update behavior (update|print)"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_BOOL(0, "linearize", &opts.linearize,
|
||||
N_("ignore merge commits instead of replaying them")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -132,6 +134,8 @@ int cmd_replay(int argc,
|
||||
opts.contained, "--contained");
|
||||
die_for_incompatible_opt2(!!opts.ref, "--ref",
|
||||
!!opts.contained, "--contained");
|
||||
die_for_incompatible_opt2(!!opts.revert, "--revert",
|
||||
opts.linearize, "--linearize");
|
||||
|
||||
/* Parse ref action mode from command line or config */
|
||||
ref_mode = get_ref_action_mode(repo, ref_action);
|
||||
|
||||
30
replay.c
30
replay.c
@@ -277,12 +277,16 @@ static struct commit *pick_regular_commit(struct repository *repo,
|
||||
struct commit *onto,
|
||||
struct merge_options *merge_opt,
|
||||
struct merge_result *result,
|
||||
struct commit *replayed_base,
|
||||
bool reverse,
|
||||
enum replay_empty_commit_action empty)
|
||||
{
|
||||
struct commit *base, *replayed_base;
|
||||
struct commit *base;
|
||||
struct tree *pickme_tree, *base_tree, *replayed_base_tree;
|
||||
|
||||
if (replayed_base && reverse)
|
||||
BUG("Linearizing commits is not supported when replaying in reverse");
|
||||
|
||||
if (pickme->parents) {
|
||||
base = pickme->parents->item;
|
||||
base_tree = repo_get_commit_tree(repo, base);
|
||||
@@ -291,7 +295,8 @@ static struct commit *pick_regular_commit(struct repository *repo,
|
||||
base_tree = lookup_tree(repo, repo->hash_algo->empty_tree);
|
||||
}
|
||||
|
||||
replayed_base = get_mapped_commit(replayed_commits, base, onto);
|
||||
if (!replayed_base)
|
||||
replayed_base = get_mapped_commit(replayed_commits, base, onto);
|
||||
replayed_base_tree = repo_get_commit_tree(repo, replayed_base);
|
||||
pickme_tree = repo_get_commit_tree(repo, pickme);
|
||||
|
||||
@@ -430,12 +435,23 @@ int replay_revisions(struct rev_info *revs,
|
||||
while ((commit = get_revision(revs))) {
|
||||
const struct name_decoration *decoration;
|
||||
|
||||
if (commit->parents && commit->parents->next)
|
||||
die(_("replaying merge commits is not supported yet!"));
|
||||
if (commit->parents && commit->parents->next) {
|
||||
if (!opts->linearize)
|
||||
die(_("replaying merge commits is not supported yet!"));
|
||||
/*
|
||||
* When linearizing, a merge commit itself is not picked,
|
||||
* but refs that point to it might need updating.
|
||||
*/
|
||||
} else {
|
||||
struct commit *to_pick = reverse ? last_commit : onto;
|
||||
last_commit =
|
||||
pick_regular_commit(revs->repo, commit,
|
||||
replayed_commits, to_pick,
|
||||
&merge_opt, &result,
|
||||
opts->linearize ? last_commit : NULL,
|
||||
reverse, opts->empty);
|
||||
}
|
||||
|
||||
last_commit = pick_regular_commit(revs->repo, commit, replayed_commits,
|
||||
reverse ? last_commit : onto,
|
||||
&merge_opt, &result, reverse, opts->empty);
|
||||
if (!last_commit)
|
||||
break;
|
||||
|
||||
|
||||
5
replay.h
5
replay.h
@@ -62,6 +62,11 @@ struct replay_revisions_options {
|
||||
* Defaults to REPLAY_EMPTY_COMMIT_DROP.
|
||||
*/
|
||||
enum replay_empty_commit_action empty;
|
||||
|
||||
/*
|
||||
* Whether to linearize the commits (i.e. drop merge commits).
|
||||
*/
|
||||
int linearize;
|
||||
};
|
||||
|
||||
/* This struct is used as an out-parameter by `replay_revisions()`. */
|
||||
|
||||
@@ -565,4 +565,30 @@ test_expect_success '--onto with --ref rejects multiple revision ranges' '
|
||||
test_grep "cannot be used with multiple revision ranges" err
|
||||
'
|
||||
|
||||
test_expect_success 'replay merge commit fails' '
|
||||
echo "fatal: replaying merge commits is not supported yet!" >expect &&
|
||||
test_must_fail git replay --ref-action=print --onto main I..P 2>actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'replay to rebase merge commit with --linearize' '
|
||||
git replay --ref-action=print --linearize --onto main I..topic-with-merge >result &&
|
||||
|
||||
test_line_count = 1 result &&
|
||||
|
||||
git log --format=%s $(cut -f 3 -d " " result) >actual &&
|
||||
test_write_lines O N J M L B A >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'replay to rebase merge commit with --linearize down to root commit' '
|
||||
git replay --ref-action=print --linearize --onto main A..topic-with-merge >result &&
|
||||
|
||||
test_line_count = 1 result &&
|
||||
|
||||
git log --format=%s $(cut -f 3 -d " " result) >actual &&
|
||||
test_write_lines O N J I M L B A >expect &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
Reference in New Issue
Block a user