diff mbox series

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

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

Commit Message

Christian Couder June 2, 2023, 10:25 a.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 | 12 +++++++++++-
 builtin/replay.c             | 12 ++++++++++--
 t/t3650-replay-basics.sh     | 29 +++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 3 deletions(-)

Comments

Toon Claes June 22, 2023, 10:10 a.m. UTC | #1
Christian Couder <christian.couder@gmail.com> writes:

> diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
> index 439b2f92e7..6fcaa44ef2 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>...

I'm not sure we need this, or at least not right now.
I've been testing with a repo having:

* a13d9c4 (another-feature) yet another commit
* c7afc2e (HEAD -> feature) third commit
* e95cecc second commit
* f141e77 first commit
| * 7bb62ac (main) later commit
| * 506cb0a another commit
|/
* e7acac6 initial commit

I tried both commands:

$ git replay --onto main main..feature main..another-feature
$ git replay --onto main --contained main..another-feature

and they both give the same result (especially with the commit following
up this one). What is the upside of having this --contained option?

Maybe it's better to defer this patch to a separate series.

And another question, in git-rebase(1) a similar option is called
--update-refs. Would you consider reusing that name here is a good idea
for consistency?

> diff --git a/builtin/replay.c b/builtin/replay.c
> index 4b64d15159..9b182eaa90 100644
> --- a/builtin/replay.c
> +++ b/builtin/replay.c
> @@ -303,6 +306,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");

Is there a technical issue why we don't support this? I don't really see
why we couldn't.

--
Toon
Christian Couder Sept. 7, 2023, 8:37 a.m. UTC | #2
On Thu, Jun 22, 2023 at 12:13 PM Toon Claes <toon@iotcl.com> wrote:
>
>
> Christian Couder <christian.couder@gmail.com> writes:
>
> > diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
> > index 439b2f92e7..6fcaa44ef2 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>...
>
> I'm not sure we need this, or at least not right now.
> I've been testing with a repo having:
>
> * a13d9c4 (another-feature) yet another commit
> * c7afc2e (HEAD -> feature) third commit
> * e95cecc second commit
> * f141e77 first commit
> | * 7bb62ac (main) later commit
> | * 506cb0a another commit
> |/
> * e7acac6 initial commit
>
> I tried both commands:
>
> $ git replay --onto main main..feature main..another-feature
> $ git replay --onto main --contained main..another-feature
>
> and they both give the same result (especially with the commit following
> up this one). What is the upside of having this --contained option?

This is expected. The thing is that:

$ git replay --onto main main..another-feature

will only output something to update 'another-feature'

while:

$ git replay --onto main --contained main..another-feature

will output something to update 'another-feature' and also something
to update 'feature'.

So when you use --contained you don't need to first find the other
branches like 'feature' that point to commits between 'main' and
'another-feature', as --contained will find them for you.

> Maybe it's better to defer this patch to a separate series.

I am not sure why you are proposing this. It's true that there are
other means to achieve the same thing as --contained, but that doesn't
mean that it cannot be useful already. If there were things that
needed to be more polished in this feature, then maybe leaving it for
a separate series later might allow this series to graduate while
--contained is polished, but I don't think we are in this case.

> And another question, in git-rebase(1) a similar option is called
> --update-refs. Would you consider reusing that name here is a good idea
> for consistency?

`git replay` outputs commands that can be passed to `git update-ref
--stdin`, but for now it doesn't run that command itself. So no refs
are actually updated. If we ever add an option for git replay to also
update the refs, it would have a name probably quite similar to
--update-refs so it would be unfortunate if --update-refs is already
used for something else.

--contained also tells about the fact that the branches affected by
the option are "contained" in the revision range that is passed, which
is nice.

In short I think it's just unfortunate that git rebase already uses
--update-refs for the "contained" branches, as it would be likely to
confuse people if we would use it here for that.
diff mbox series

Patch

diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
index 439b2f92e7..6fcaa44ef2 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,16 @@  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}
+------------
+
 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 4b64d15159..9b182eaa90 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -258,6 +258,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;
@@ -268,7 +269,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[] = {
@@ -278,6 +279,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()
 	};
 
@@ -303,6 +306,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);
@@ -364,7 +371,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