diff mbox series

[v2,14/15] replay: add --contained to rebase contained branches

Message ID 20230509175347.1714141-15-christian.couder@gmail.com (mailing list archive)
State Superseded
Headers show
Series Introduce new `git replay` command | expand

Commit Message

Christian Couder May 9, 2023, 5:53 p.m. UTC
From: Elijah Newren <newren@gmail.com>

Let's add a `--contained` option that can be used along with
`--onto` to rebase all the branches contained in the <revision-range>
argument.

Co-authored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 Documentation/git-replay.txt | 16 +++++++++++++++-
 builtin/replay.c             | 12 ++++++++++--
 t/t3650-replay-basics.sh     | 29 +++++++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 3 deletions(-)

Comments

Elijah Newren May 16, 2023, 4:26 a.m. UTC | #1
On Tue, May 9, 2023 at 10:54 AM Christian Couder
<christian.couder@gmail.com> wrote:
>
> From: Elijah Newren <newren@gmail.com>
>
> Let's add a `--contained` option that can be used along with
> `--onto` to rebase all the branches contained in the <revision-range>
> argument.
>
> Co-authored-by: Christian Couder <chriscool@tuxfamily.org>
> Signed-off-by: Elijah Newren <newren@gmail.com>
> Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
> ---
>  Documentation/git-replay.txt | 16 +++++++++++++++-
>  builtin/replay.c             | 12 ++++++++++--
>  t/t3650-replay-basics.sh     | 29 +++++++++++++++++++++++++++++
>  3 files changed, 54 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
> index 439b2f92e7..3e06ab2f5e 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> | --advance <branch>) <revision-range>...
> +'git replay' [--contained] (--onto <newbase> | --advance <branch>) <revision-range>...

Should this be

  'git replay' ([--contained] --onto <newbase> | --advance <branch>)
<revision-range>...

?

>
>  DESCRIPTION
>  -----------
> @@ -90,6 +90,20 @@ top of the exact same new base, they only differ in that the first
>  provides instructions to make mybranch point at the new commits and
>  the second provides instructions to make target point at them.
>
> +What if you have a stack of branches, one depending upon another, and
> +you'd really like to rebase the whole set?
> +
> +------------
> +$ git replay --contained --onto origin/main origin/main..tipbranch
> +update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
> +update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
> +update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
> +------------
> +
> +In contrast, trying to do this with rebase would require 3 separate
> +rebases, eacho of which involves a different <ONTO> and <UPSTREAM> and

s/eacho/each/

> +forces you to first check out each branch in turn.

This paragraph isn't true anymore with rebase's --update-refs, right?

> +
>  When calling `git replay`, one does not need to specify a range of
>  commits to replay using the syntax `A..B`; any range expression will
>  do:
> diff --git a/builtin/replay.c b/builtin/replay.c
> index c146f38f58..4d24eb95d8 100644
> --- a/builtin/replay.c
> +++ b/builtin/replay.c
> @@ -256,6 +256,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
>         const char *advance_name = NULL;
>         struct commit *onto = NULL;
>         const char *onto_name = NULL;
> +       int contained = 0;
>
>         struct rev_info revs;
>         struct commit *last_commit = NULL;
> @@ -266,7 +267,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
>         int ret = 0, i;
>
>         const char * const replay_usage[] = {
> -               N_("git replay (--onto <newbase> | --advance <branch>) <revision-range>..."),
> +               N_("git replay [--contained] (--onto <newbase> | --advance <branch>) <revision-range>..."),

Possibly need to update this here too.

>                 NULL
>         };
>         struct option replay_options[] = {
> @@ -276,6 +277,8 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
>                 OPT_STRING(0, "onto", &onto_name,
>                            N_("revision"),
>                            N_("replay onto given commit")),
> +               OPT_BOOL(0, "contained", &contained,
> +                        N_("advance all branches contained in revision-range")),
>                 OPT_END()
>         };
>
> @@ -301,6 +304,10 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
>                 usage_with_options(replay_usage, replay_options);
>         }
>
> +       if (advance_name && contained)
> +               die(_("options '%s' and '%s' cannot be used together"),
> +                   "--advance", "--contained");

But the code does check that these are incompatible.  Good.


> +
>         repo_init_revisions(the_repository, &revs, prefix);
>
>         argc = setup_revisions(argc, argv, &revs, NULL);
> @@ -363,7 +370,8 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
>                         continue;
>                 while (decoration) {
>                         if (decoration->type == DECORATION_REF_LOCAL &&
> -                           strset_contains(update_refs, decoration->name)) {
> +                           (contained || strset_contains(update_refs,
> +                                                         decoration->name))) {
>                                 printf("update %s %s %s\n",
>                                        decoration->name,
>                                        oid_to_hex(&last_commit->object.oid),
> diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
> index bca405c431..3fb4167e69 100755
> --- a/t/t3650-replay-basics.sh
> +++ b/t/t3650-replay-basics.sh
> @@ -122,4 +122,33 @@ test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
>         test_cmp expect result-bare
>  '
>
> +test_expect_success 'using replay to also rebase a contained branch' '
> +       git replay --contained --onto main main..topic3 >result &&
> +
> +       test_line_count = 2 result &&
> +       cut -f 3 -d " " result >new-branch-tips &&
> +
> +       git log --format=%s $(head -n 1 new-branch-tips) >actual &&
> +       test_write_lines F C M L B A >expect &&
> +       test_cmp expect actual &&
> +
> +       git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
> +       test_write_lines H G F C M L B A >expect &&
> +       test_cmp expect actual &&
> +
> +       printf "update refs/heads/topic1 " >expect &&
> +       printf "%s " $(head -n 1 new-branch-tips) >>expect &&
> +       git rev-parse topic1 >>expect &&
> +       printf "update refs/heads/topic3 " >>expect &&
> +       printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
> +       git rev-parse topic3 >>expect &&
> +
> +       test_cmp expect result
> +'
> +
> +test_expect_success 'using replay on bare repo to also rebase a contained branch' '
> +       git -C bare replay --contained --onto main main..topic3 >result-bare &&
> +       test_cmp expect result-bare
> +'
> +
>  test_done
> --
> 2.40.1.491.gdff9a222ea
>
diff mbox series

Patch

diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
index 439b2f92e7..3e06ab2f5e 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> | --advance <branch>) <revision-range>...
+'git replay' [--contained] (--onto <newbase> | --advance <branch>) <revision-range>...
 
 DESCRIPTION
 -----------
@@ -90,6 +90,20 @@  top of the exact same new base, they only differ in that the first
 provides instructions to make mybranch point at the new commits and
 the second provides instructions to make target point at them.
 
+What if you have a stack of branches, one depending upon another, and
+you'd really like to rebase the whole set?
+
+------------
+$ git replay --contained --onto origin/main origin/main..tipbranch
+update refs/heads/branch1 ${NEW_branch1_HASH} ${OLD_branch1_HASH}
+update refs/heads/branch2 ${NEW_branch2_HASH} ${OLD_branch2_HASH}
+update refs/heads/tipbranch ${NEW_tipbranch_HASH} ${OLD_tipbranch_HASH}
+------------
+
+In contrast, trying to do this with rebase would require 3 separate
+rebases, eacho of which involves a different <ONTO> and <UPSTREAM> and
+forces you to first check out each branch in turn.
+
 When calling `git replay`, one does not need to specify a range of
 commits to replay using the syntax `A..B`; any range expression will
 do:
diff --git a/builtin/replay.c b/builtin/replay.c
index c146f38f58..4d24eb95d8 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -256,6 +256,7 @@  int cmd_replay(int argc, const char **argv, const char *prefix)
 	const char *advance_name = NULL;
 	struct commit *onto = NULL;
 	const char *onto_name = NULL;
+	int contained = 0;
 
 	struct rev_info revs;
 	struct commit *last_commit = NULL;
@@ -266,7 +267,7 @@  int cmd_replay(int argc, const char **argv, const char *prefix)
 	int ret = 0, i;
 
 	const char * const replay_usage[] = {
-		N_("git replay (--onto <newbase> | --advance <branch>) <revision-range>..."),
+		N_("git replay [--contained] (--onto <newbase> | --advance <branch>) <revision-range>..."),
 		NULL
 	};
 	struct option replay_options[] = {
@@ -276,6 +277,8 @@  int cmd_replay(int argc, const char **argv, const char *prefix)
 		OPT_STRING(0, "onto", &onto_name,
 			   N_("revision"),
 			   N_("replay onto given commit")),
+		OPT_BOOL(0, "contained", &contained,
+			 N_("advance all branches contained in revision-range")),
 		OPT_END()
 	};
 
@@ -301,6 +304,10 @@  int cmd_replay(int argc, const char **argv, const char *prefix)
 		usage_with_options(replay_usage, replay_options);
 	}
 
+	if (advance_name && contained)
+		die(_("options '%s' and '%s' cannot be used together"),
+		    "--advance", "--contained");
+
 	repo_init_revisions(the_repository, &revs, prefix);
 
 	argc = setup_revisions(argc, argv, &revs, NULL);
@@ -363,7 +370,8 @@  int cmd_replay(int argc, const char **argv, const char *prefix)
 			continue;
 		while (decoration) {
 			if (decoration->type == DECORATION_REF_LOCAL &&
-			    strset_contains(update_refs, decoration->name)) {
+			    (contained || strset_contains(update_refs,
+							  decoration->name))) {
 				printf("update %s %s %s\n",
 				       decoration->name,
 				       oid_to_hex(&last_commit->object.oid),
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index bca405c431..3fb4167e69 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -122,4 +122,33 @@  test_expect_success 'using replay on bare repo to perform basic cherry-pick' '
 	test_cmp expect result-bare
 '
 
+test_expect_success 'using replay to also rebase a contained branch' '
+	git replay --contained --onto main main..topic3 >result &&
+
+	test_line_count = 2 result &&
+	cut -f 3 -d " " result >new-branch-tips &&
+
+	git log --format=%s $(head -n 1 new-branch-tips) >actual &&
+	test_write_lines F C M L B A >expect &&
+	test_cmp expect actual &&
+
+	git log --format=%s $(tail -n 1 new-branch-tips) >actual &&
+	test_write_lines H G F C M L B A >expect &&
+	test_cmp expect actual &&
+
+	printf "update refs/heads/topic1 " >expect &&
+	printf "%s " $(head -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic1 >>expect &&
+	printf "update refs/heads/topic3 " >>expect &&
+	printf "%s " $(tail -n 1 new-branch-tips) >>expect &&
+	git rev-parse topic3 >>expect &&
+
+	test_cmp expect result
+'
+
+test_expect_success 'using replay on bare repo to also rebase a contained branch' '
+	git -C bare replay --contained --onto main main..topic3 >result-bare &&
+	test_cmp expect result-bare
+'
+
 test_done