diff mbox series

[v3,5/5] remote: announce removal of "branches/" and "remotes/"

Message ID 20250120-pks-remote-branches-deprecation-v3-5-c7e539b6a84f@pks.im (mailing list archive)
State Superseded
Headers show
Series remote: announce removal of "branches/" and "remotes/" | expand

Commit Message

Patrick Steinhardt Jan. 20, 2025, 7:43 a.m. UTC
Back when Git was in its infancy, remotes were configured via separate
files in "branches/" (back in 2005). This mechanism was replaced later
that year with the "remotes/" directory. Both mechanisms have eventually
been replaced by config-based remotes, and it is very unlikely that
anybody still uses these directories to configure their remotes.

Both of these directories have been marked as deprecated, one in 2005
and the other one in 2011. Follow through with the deprecation and
finally announce the removal of these features in Git 3.0.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/BreakingChanges.txt      | 25 ++++++++++++++++++
 Documentation/gitrepository-layout.txt |  7 +++--
 builtin/remote.c                       |  2 ++
 remote.c                               | 19 ++++++++++++++
 remote.h                               |  2 ++
 t/t5505-remote.sh                      |  6 ++---
 t/t5510-fetch.sh                       | 13 ++++------
 t/t5515-fetch-merge-logic.sh           | 47 ++++++++++++++++++----------------
 t/t5516-fetch-push.sh                  | 14 +++++-----
 9 files changed, 92 insertions(+), 43 deletions(-)

Comments

Junio C Hamano Jan. 21, 2025, 9:25 p.m. UTC | #1
Patrick Steinhardt <ps@pks.im> writes:

> +repositories at all and most users aren't even aware of these mechanisms. They
> +have been deprecated for almost 20 years and 14 years respectively, and we are
> +not aware of any reason why anybody would want to use these mechanisms.

I am aware of one reason why some folks may prefer being able to say

    $ ls .git/branches/*pattern*
    $ echo "$URL#branch" >".git/branches/$shortname"
    $ git fetch $shortname

over the configuration file based mechanism, especially when they
have to deal with dozens of remotes that change the branch name to
be pulled from.  And as I already said the above while reviewing the
previous round of this series, _we_ are now aware of it.

I however am in favor of deprecating and removing the support, but
that is not because I am not aware how useful they could be.  I am
and we are aware, but we haven't heard anybody jumping up and down
to advocate for its undeprecation for a long time, and that is why
I am personally OK with this removal.

>  branches::
> -	A slightly deprecated way to store shorthands to be used
> +	A deprecated way to store shorthands to be used
>  	to specify a URL to 'git fetch', 'git pull' and 'git push'.
>  	A file can be stored as `branches/<name>` and then
>  	'name' can be given to these commands in place of
> @@ -162,7 +162,8 @@ branches::
>  	and not likely to be found in modern repositories. This
>  	directory is ignored if $GIT_COMMON_DIR is set and
>  	"$GIT_COMMON_DIR/branches" will be used instead.
> -
> ++
> +Git will stop reading remotes from this directory in Git 3.0.
>  
>  hooks::
>  	Hooks are customization scripts used by various Git
> @@ -238,6 +239,8 @@ remotes::
>  	and not likely to be found in modern repositories. This
>  	directory is ignored if $GIT_COMMON_DIR is set and
>  	"$GIT_COMMON_DIR/remotes" will be used instead.
> ++
> +Git will stop reading remotes from this directory in Git 3.0.

OK.

> diff --git a/builtin/remote.c b/builtin/remote.c
> index 1ad3e70a6b..e565b2b3fe 100644
> --- a/builtin/remote.c
> +++ b/builtin/remote.c
> @@ -640,10 +640,12 @@ static int migrate_file(struct remote *remote)
>  	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
>  	for (i = 0; i < remote->fetch.nr; i++)
>  		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
> +#ifndef WITH_BREAKING_CHANGES
>  	if (remote->origin == REMOTE_REMOTES)
>  		unlink_or_warn(git_path("remotes/%s", remote->name));
>  	else if (remote->origin == REMOTE_BRANCHES)
>  		unlink_or_warn(git_path("branches/%s", remote->name));
> +#endif /* WITH_BREAKING_CHANGES */
>  	strbuf_release(&buf);

Interesting.  I wonder if our new warning should talk about whatever
end-user facing interface that triggers this code path.  It would
help them wean themselves away from the old interface, no?

> diff --git a/remote.c b/remote.c
> index 10104d11e3..5feb0ae886 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -293,6 +293,7 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
>  	rewrite->instead_of_nr++;
>  }
>  
> +#ifndef WITH_BREAKING_CHANGES
>  static const char *skip_spaces(const char *s)
>  {
>  	while (isspace(*s))
> @@ -308,6 +309,13 @@ static void read_remotes_file(struct remote_state *remote_state,
>  
>  	if (!f)
>  		return;
> +
> +	warning(_("Reading remote from \"remotes/%s\", which is nominated\n"
> +		  "for removal. If you still use the \"remotes/\" directory\n"
> +		  "it is recommended to migrate to config-based remotes. If\n"

Do we have a way to concisely say "how" to do this?  If I am reading
the caller of migrate_file() in builtin/remote.c, it would be

    $ git remote mv foo foo

for any foo in .git/remotes/* or .git/branches/* hierarchy?

Of course they may be an ancient leftover file that the user even no
longer is aware of having, in which case

    $ rm .git/remotes/foo

might be an OK answer, but even then

    $ git remote rm foo

would probably be more appropriate.

> +		  "you cannot, please let us know you still use it by sending\n"

I do not think we care to receive a piece of e-mail that only says
"I still use it".  We may want to learn _why_ they cannot switch
away, though.

The same comment applies to the other side.

Everything else in this patch looked superb.

Thanks.
Patrick Steinhardt Jan. 22, 2025, 11:05 a.m. UTC | #2
On Tue, Jan 21, 2025 at 01:25:56PM -0800, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> > diff --git a/builtin/remote.c b/builtin/remote.c
> > index 1ad3e70a6b..e565b2b3fe 100644
> > --- a/builtin/remote.c
> > +++ b/builtin/remote.c
> > @@ -640,10 +640,12 @@ static int migrate_file(struct remote *remote)
> >  	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
> >  	for (i = 0; i < remote->fetch.nr; i++)
> >  		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
> > +#ifndef WITH_BREAKING_CHANGES
> >  	if (remote->origin == REMOTE_REMOTES)
> >  		unlink_or_warn(git_path("remotes/%s", remote->name));
> >  	else if (remote->origin == REMOTE_BRANCHES)
> >  		unlink_or_warn(git_path("branches/%s", remote->name));
> > +#endif /* WITH_BREAKING_CHANGES */
> >  	strbuf_release(&buf);
> 
> Interesting.  I wonder if our new warning should talk about whatever
> end-user facing interface that triggers this code path.  It would
> help them wean themselves away from the old interface, no?

Not quite sure that I understand what you're saying. Is it that we
should tell whether we were reading from "branches/" or "remotes/"? If
so we already do that.

> > diff --git a/remote.c b/remote.c
> > index 10104d11e3..5feb0ae886 100644
> > --- a/remote.c
> > +++ b/remote.c
> > @@ -293,6 +293,7 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
> >  	rewrite->instead_of_nr++;
> >  }
> >  
> > +#ifndef WITH_BREAKING_CHANGES
> >  static const char *skip_spaces(const char *s)
> >  {
> >  	while (isspace(*s))
> > @@ -308,6 +309,13 @@ static void read_remotes_file(struct remote_state *remote_state,
> >  
> >  	if (!f)
> >  		return;
> > +
> > +	warning(_("Reading remote from \"remotes/%s\", which is nominated\n"
> > +		  "for removal. If you still use the \"remotes/\" directory\n"
> > +		  "it is recommended to migrate to config-based remotes. If\n"
> 
> Do we have a way to concisely say "how" to do this?  If I am reading
> the caller of migrate_file() in builtin/remote.c, it would be
> 
>     $ git remote mv foo foo
> 
> for any foo in .git/remotes/* or .git/branches/* hierarchy?
> 
> Of course they may be an ancient leftover file that the user even no
> longer is aware of having, in which case
> 
>     $ rm .git/remotes/foo
> 
> might be an OK answer, but even then
> 
>     $ git remote rm foo
> 
> would probably be more appropriate.

Very good idea indeed. We also have tests that exercise this behaviour
in t5505 (with the slight exception that it is `git remote rename`, not
`git remote mv`).

Patrick
Junio C Hamano Jan. 22, 2025, 5:58 p.m. UTC | #3
Patrick Steinhardt <ps@pks.im> writes:

> On Tue, Jan 21, 2025 at 01:25:56PM -0800, Junio C Hamano wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>> > diff --git a/builtin/remote.c b/builtin/remote.c
>> > index 1ad3e70a6b..e565b2b3fe 100644
>> > --- a/builtin/remote.c
>> > +++ b/builtin/remote.c
>> > @@ -640,10 +640,12 @@ static int migrate_file(struct remote *remote)
>> >  	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
>> >  	for (i = 0; i < remote->fetch.nr; i++)
>> >  		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
>> > +#ifndef WITH_BREAKING_CHANGES
>> >  	if (remote->origin == REMOTE_REMOTES)
>> >  		unlink_or_warn(git_path("remotes/%s", remote->name));
>> >  	else if (remote->origin == REMOTE_BRANCHES)
>> >  		unlink_or_warn(git_path("branches/%s", remote->name));
>> > +#endif /* WITH_BREAKING_CHANGES */
>> >  	strbuf_release(&buf);
>> 
>> Interesting.  I wonder if our new warning should talk about whatever
>> end-user facing interface that triggers this code path.  It would
>> help them wean themselves away from the old interface, no?
>
> Not quite sure that I understand what you're saying. Is it that we
> should tell whether we were reading from "branches/" or "remotes/"? If
> so we already do that.

No, what I meant was to say "You are using outdated remotes/
hierarchy to describe this remote.  You can run 'remote mv %s %s'
to migrate its definition to the more modern config-based system".

The message already says the first sentence, but not the latter.
diff mbox series

Patch

diff --git a/Documentation/BreakingChanges.txt b/Documentation/BreakingChanges.txt
index 27acff86db..a91eb3bc55 100644
--- a/Documentation/BreakingChanges.txt
+++ b/Documentation/BreakingChanges.txt
@@ -154,6 +154,31 @@  Cf. <xmqq1rjuz6n3.fsf_-_@gitster.c.googlers.com>,
     <CAKvOHKAFXQwt4D8yUCCkf_TQL79mYaJ=KAKhtpDNTvHJFuX1NA@mail.gmail.com>,
     <20230323204047.GA9290@coredump.intra.peff.net>,
 
+* Support for storing shorthands for remote URLs in "$GIT_COMMON_DIR/branches/"
+  and "$GIT_COMMON_DIR/remotes/" has been long superseded by storing remotes in
+  the repository configuration.
++
+The mechanism has originally been introduced in f170e4b39d ([PATCH] fetch/pull:
+short-hand notation for remote repositories., 2005-07-16) and was superseded by
+6687f8fea2 ([PATCH] Use .git/remote/origin, not .git/branches/origin.,
+2005-08-20), where we switched from ".git/branches/" to ".git/remotes/". That
+commit already mentions an upcoming deprecation of the ".git/branches/"
+directory, and starting with a1d4aa7424 (Add repository-layout document.,
+2005-09-01) we have also marked this layout as deprecated. Eventually we also
+started to migrate away from ".git/remotes/" in favor of config-based remotes,
+and we have marked the directory as legacy in 3d3d282146 (Documentation:
+Grammar correction, wording fixes and cleanup, 2011-08-23)
++
+As our documentation mentions, these directories are not to be found in modern
+repositories at all and most users aren't even aware of these mechanisms. They
+have been deprecated for almost 20 years and 14 years respectively, and we are
+not aware of any reason why anybody would want to use these mechanisms.
+Furthermore, the ".git/branches/" directory is nowadays misleadingly named and
+may cause confusion as "branches" are almost exclusively used in the context of
+references.
++
+These features will be removed.
+
 == Superseded features that will not be deprecated
 
 Some features have gained newer replacements that aim to improve the design in
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index fa8b51daf0..85911ca8ea 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -153,7 +153,7 @@  config.worktree::
 	linkgit:git-worktree[1]).
 
 branches::
-	A slightly deprecated way to store shorthands to be used
+	A deprecated way to store shorthands to be used
 	to specify a URL to 'git fetch', 'git pull' and 'git push'.
 	A file can be stored as `branches/<name>` and then
 	'name' can be given to these commands in place of
@@ -162,7 +162,8 @@  branches::
 	and not likely to be found in modern repositories. This
 	directory is ignored if $GIT_COMMON_DIR is set and
 	"$GIT_COMMON_DIR/branches" will be used instead.
-
++
+Git will stop reading remotes from this directory in Git 3.0.
 
 hooks::
 	Hooks are customization scripts used by various Git
@@ -238,6 +239,8 @@  remotes::
 	and not likely to be found in modern repositories. This
 	directory is ignored if $GIT_COMMON_DIR is set and
 	"$GIT_COMMON_DIR/remotes" will be used instead.
++
+Git will stop reading remotes from this directory in Git 3.0.
 
 logs::
 	Records of changes made to refs are stored in this directory.
diff --git a/builtin/remote.c b/builtin/remote.c
index 1ad3e70a6b..e565b2b3fe 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -640,10 +640,12 @@  static int migrate_file(struct remote *remote)
 	strbuf_addf(&buf, "remote.%s.fetch", remote->name);
 	for (i = 0; i < remote->fetch.nr; i++)
 		git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
+#ifndef WITH_BREAKING_CHANGES
 	if (remote->origin == REMOTE_REMOTES)
 		unlink_or_warn(git_path("remotes/%s", remote->name));
 	else if (remote->origin == REMOTE_BRANCHES)
 		unlink_or_warn(git_path("branches/%s", remote->name));
+#endif /* WITH_BREAKING_CHANGES */
 	strbuf_release(&buf);
 
 	return 0;
diff --git a/remote.c b/remote.c
index 10104d11e3..5feb0ae886 100644
--- a/remote.c
+++ b/remote.c
@@ -293,6 +293,7 @@  static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
 	rewrite->instead_of_nr++;
 }
 
+#ifndef WITH_BREAKING_CHANGES
 static const char *skip_spaces(const char *s)
 {
 	while (isspace(*s))
@@ -308,6 +309,13 @@  static void read_remotes_file(struct remote_state *remote_state,
 
 	if (!f)
 		return;
+
+	warning(_("Reading remote from \"remotes/%s\", which is nominated\n"
+		  "for removal. If you still use the \"remotes/\" directory\n"
+		  "it is recommended to migrate to config-based remotes. If\n"
+		  "you cannot, please let us know you still use it by sending\n"
+		  "an e-mail to <git@vger.kernel.org>."), remote->name);
+
 	remote->configured_in_repo = 1;
 	remote->origin = REMOTE_REMOTES;
 	while (strbuf_getline(&buf, f) != EOF) {
@@ -337,6 +345,12 @@  static void read_branches_file(struct remote_state *remote_state,
 	if (!f)
 		return;
 
+	warning(_("Reading remote from \"branches/%s\", which is nominated\n"
+		  "for removal. If you still use the \"branches/\" directory\n"
+		  "it is recommended to migrate to config-based remotes. If\n"
+		  "you cannot, please let us know you still use it by sending\n"
+		  "an e-mail to <git@vger.kernel.org>."), remote->name);
+
 	strbuf_getline_lf(&buf, f);
 	fclose(f);
 	strbuf_trim(&buf);
@@ -374,6 +388,7 @@  static void read_branches_file(struct remote_state *remote_state,
 	strbuf_release(&buf);
 	free(to_free);
 }
+#endif /* WITH_BREAKING_CHANGES */
 
 static int handle_config(const char *key, const char *value,
 			 const struct config_context *ctx, void *cb)
@@ -572,6 +587,7 @@  static void read_config(struct repository *repo, int early)
 	alias_all_urls(repo->remote_state);
 }
 
+#ifndef WITH_BREAKING_CHANGES
 static int valid_remote_nick(const char *name)
 {
 	if (!name[0] || is_dot_or_dotdot(name))
@@ -583,6 +599,7 @@  static int valid_remote_nick(const char *name)
 			return 0;
 	return 1;
 }
+#endif /* WITH_BREAKING_CHANGES */
 
 static const char *remotes_remote_for_branch(struct remote_state *remote_state,
 					     struct branch *branch,
@@ -725,12 +742,14 @@  remotes_remote_get_1(struct remote_state *remote_state, const char *name,
 				   &name_given);
 
 	ret = make_remote(remote_state, name, 0);
+#ifndef WITH_BREAKING_CHANGES
 	if (valid_remote_nick(name) && have_git_dir()) {
 		if (!valid_remote(ret))
 			read_remotes_file(remote_state, ret);
 		if (!valid_remote(ret))
 			read_branches_file(remote_state, ret);
 	}
+#endif /* WITH_BREAKING_CHANGES */
 	if (name_given && !valid_remote(ret))
 		add_url_alias(remote_state, ret, name);
 	if (!valid_remote(ret))
diff --git a/remote.h b/remote.h
index a7e5c4e07c..45b0c9babb 100644
--- a/remote.h
+++ b/remote.h
@@ -21,8 +21,10 @@  struct transport_ls_refs_options;
 enum {
 	REMOTE_UNCONFIGURED = 0,
 	REMOTE_CONFIG,
+#ifndef WITH_BREAKING_CHANGES
 	REMOTE_REMOTES,
 	REMOTE_BRANCHES
+#endif /* WITH_BREAKING_CHANGES */
 };
 
 struct rewrite {
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 08424e878e..e96ac8c767 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1007,7 +1007,7 @@  Pull: refs/heads/main:refs/heads/origin
 Pull: refs/heads/next:refs/heads/origin2
 EOF
 
-test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
 	git clone one five &&
 	origin_url=$(pwd)/one &&
 	(
@@ -1033,7 +1033,7 @@  test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
 	)
 '
 
-test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
 	git clone --template= one six &&
 	origin_url=$(pwd)/one &&
 	(
@@ -1049,7 +1049,7 @@  test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
 	)
 '
 
-test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
 	git clone --template= one seven &&
 	(
 		cd seven &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 3b3991ab86..04d8a96367 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -34,14 +34,11 @@  test_expect_success "clone and setup child repos" '
 	git clone . three &&
 	(
 		cd three &&
-		git config branch.main.remote two &&
-		git config branch.main.merge refs/heads/one &&
-		mkdir -p .git/remotes &&
-		cat >.git/remotes/two <<-\EOF
-		URL: ../two/.git/
-		Pull: refs/heads/main:refs/heads/two
-		Pull: refs/heads/one:refs/heads/one
-		EOF
+		git config set remote.two.url ../two/.git/ &&
+		git config set remote.two.fetch refs/heads/main:refs/heads/two &&
+		git config set --append remote.two.fetch refs/heads/one:refs/heads/one &&
+		git config set branch.main.remote two &&
+		git config set branch.main.merge refs/heads/one
 	) &&
 	git clone . bundle &&
 	git clone . seven
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d..4e6026c611 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -104,28 +104,31 @@  test_expect_success setup '
 	git config remote.config-glob.fetch refs/heads/*:refs/remotes/rem/* &&
 	remotes="$remotes config-glob" &&
 
-	mkdir -p .git/remotes &&
-	cat >.git/remotes/remote-explicit <<-\EOF &&
-	URL: ../.git/
-	Pull: refs/heads/main:remotes/rem/main
-	Pull: refs/heads/one:remotes/rem/one
-	Pull: two:remotes/rem/two
-	Pull: refs/heads/three:remotes/rem/three
-	EOF
-	remotes="$remotes remote-explicit" &&
-
-	cat >.git/remotes/remote-glob <<-\EOF &&
-	URL: ../.git/
-	Pull: refs/heads/*:refs/remotes/rem/*
-	EOF
-	remotes="$remotes remote-glob" &&
-
-	mkdir -p .git/branches &&
-	echo "../.git" > .git/branches/branches-default &&
-	remotes="$remotes branches-default" &&
-
-	echo "../.git#one" > .git/branches/branches-one &&
-	remotes="$remotes branches-one" &&
+	if test_have_prereq WITHOUT_BREAKING_CHANGES
+	then
+		mkdir -p .git/remotes &&
+		cat >.git/remotes/remote-explicit <<-\EOF &&
+		URL: ../.git/
+		Pull: refs/heads/main:remotes/rem/main
+		Pull: refs/heads/one:remotes/rem/one
+		Pull: two:remotes/rem/two
+		Pull: refs/heads/three:remotes/rem/three
+		EOF
+		remotes="$remotes remote-explicit" &&
+
+		cat >.git/remotes/remote-glob <<-\EOF &&
+		URL: ../.git/
+		Pull: refs/heads/*:refs/remotes/rem/*
+		EOF
+		remotes="$remotes remote-glob" &&
+
+		mkdir -p .git/branches &&
+		echo "../.git" > .git/branches/branches-default &&
+		remotes="$remotes branches-default" &&
+
+		echo "../.git#one" > .git/branches/branches-one &&
+		remotes="$remotes branches-one"
+	fi &&
 
 	for remote in $remotes ; do
 		git config branch.br-$remote.remote $remote &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 9d693eb57f..e705aedbf4 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -975,7 +975,7 @@  test_expect_success 'allow push to HEAD of non-bare repository (config)' '
 	! grep "warning: updating the current branch" stderr
 '
 
-test_expect_success 'fetch with branches' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
 	mk_empty testrepo &&
 	git branch second $the_first_commit &&
 	git checkout second &&
@@ -991,7 +991,7 @@  test_expect_success 'fetch with branches' '
 	git checkout main
 '
 
-test_expect_success 'fetch with branches containing #' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #' '
 	mk_empty testrepo &&
 	mkdir testrepo/.git/branches &&
 	echo "..#second" > testrepo/.git/branches/branch2 &&
@@ -1005,7 +1005,7 @@  test_expect_success 'fetch with branches containing #' '
 	git checkout main
 '
 
-test_expect_success 'push with branches' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
 	mk_empty testrepo &&
 	git checkout second &&
 
@@ -1022,7 +1022,7 @@  test_expect_success 'push with branches' '
 	)
 '
 
-test_expect_success 'push with branches containing #' '
+test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches containing #' '
 	mk_empty testrepo &&
 
 	test_when_finished "rm -rf .git/branches" &&
@@ -1211,18 +1211,16 @@  test_expect_success 'push --porcelain --dry-run rejected' '
 '
 
 test_expect_success 'push --prune' '
-	mk_test testrepo heads/main heads/second heads/foo heads/bar &&
+	mk_test testrepo heads/main heads/foo heads/bar &&
 	git push --prune testrepo : &&
 	check_push_result testrepo $the_commit heads/main &&
-	check_push_result testrepo $the_first_commit heads/second &&
 	! check_push_result testrepo $the_first_commit heads/foo heads/bar
 '
 
 test_expect_success 'push --prune refspec' '
-	mk_test testrepo tmp/main tmp/second tmp/foo tmp/bar &&
+	mk_test testrepo tmp/main tmp/foo tmp/bar &&
 	git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
 	check_push_result testrepo $the_commit tmp/main &&
-	check_push_result testrepo $the_first_commit tmp/second &&
 	! check_push_result testrepo $the_first_commit tmp/foo tmp/bar
 '