diff mbox series

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

Message ID 20250106-pks-remote-branches-deprecation-v2-5-2ce87c053536@pks.im (mailing list archive)
State New
Headers show
Series remote: announce removal of "branches/" and "remotes/" | expand

Commit Message

Patrick Steinhardt Jan. 6, 2025, 7:51 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. These mechanism 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                               |  6 +++++
 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, 79 insertions(+), 43 deletions(-)

Comments

Christian Couder Jan. 6, 2025, 1:24 p.m. UTC | #1
On Mon, Jan 6, 2025 at 8:52 AM Patrick Steinhardt <ps@pks.im> wrote:
>
> 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. These mechanism have eventually

s/mechanism/mechanisms/

> 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.

What I like about the removal of git-pack-redundant(1) in the previous
patch is that we started to emit a user-visible warning in 2020 and
now users even have to pass an `--i-still-use-this` option to be able
to use the command. This really makes sure users cannot ignore the
fact that the command is deprecated.

Accordingly I think it would be nice if we started to emit warnings
(that could possibly be disabled) when we find a repo still uses stuff
in "branches/" and "remotes/". These would be much more difficult to
miss or ignore than doc changes.
Junio C Hamano Jan. 6, 2025, 3:53 p.m. UTC | #2
Christian Couder <christian.couder@gmail.com> writes:

> What I like about the removal of git-pack-redundant(1) in the previous
> patch is that we started to emit a user-visible warning in 2020 and
> now users even have to pass an `--i-still-use-this` option to be able
> to use the command. This really makes sure users cannot ignore the
> fact that the command is deprecated.
>
> Accordingly I think it would be nice if we started to emit warnings
> (that could possibly be disabled) when we find a repo still uses stuff
> in "branches/" and "remotes/". These would be much more difficult to
> miss or ignore than doc changes.

That's an excellent suggestion.  Even though this topic is about
introducing breaking changes, saying "we waited for long enough",
making sure we have prepared the user base for such changes to
lesson the impact of "breaking" changes is a very prudent thing to
do.

I guess everything is contained within remote.c these days?
Patches welcome ;-)

Thanks.
Patrick Steinhardt Jan. 7, 2025, 12:48 p.m. UTC | #3
On Mon, Jan 06, 2025 at 07:53:46AM -0800, Junio C Hamano wrote:
> Christian Couder <christian.couder@gmail.com> writes:
> 
> > What I like about the removal of git-pack-redundant(1) in the previous
> > patch is that we started to emit a user-visible warning in 2020 and
> > now users even have to pass an `--i-still-use-this` option to be able
> > to use the command. This really makes sure users cannot ignore the
> > fact that the command is deprecated.
> >
> > Accordingly I think it would be nice if we started to emit warnings
> > (that could possibly be disabled) when we find a repo still uses stuff
> > in "branches/" and "remotes/". These would be much more difficult to
> > miss or ignore than doc changes.
> 
> That's an excellent suggestion.  Even though this topic is about
> introducing breaking changes, saying "we waited for long enough",
> making sure we have prepared the user base for such changes to
> lesson the impact of "breaking" changes is a very prudent thing to
> do.
> 
> I guess everything is contained within remote.c these days?
> Patches welcome ;-)

Makes sense indeed. We can easily add for something like below diff.
I'll roll that into the next version, thanks!

Patrick

diff --git a/remote.c b/remote.c
index 55e91fab47..8c104c6ee1 100644
--- a/remote.c
+++ b/remote.c
@@ -309,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) {
@@ -338,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);
Junio C Hamano Jan. 7, 2025, 4:40 p.m. UTC | #4
Patrick Steinhardt <ps@pks.im> writes:

> Makes sense indeed. We can easily add for something like below diff.
> I'll roll that into the next version, thanks!

It is a good start, but is probably a bit too noisy.  Can we make
them appear ONLY when the definitions read from these older sources
are actually USED?

Thanks.

>
> Patrick
>
> diff --git a/remote.c b/remote.c
> index 55e91fab47..8c104c6ee1 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -309,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) {
> @@ -338,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);
Junio C Hamano Jan. 7, 2025, 4:49 p.m. UTC | #5
Junio C Hamano <gitster@pobox.com> writes:

> Patrick Steinhardt <ps@pks.im> writes:
>
>> Makes sense indeed. We can easily add for something like below diff.
>> I'll roll that into the next version, thanks!
>
> It is a good start, but is probably a bit too noisy.  Can we make
> them appear ONLY when the definitions read from these older sources
> are actually USED?
>
> Thanks.

Something along this line is what I had in mind.  Not even compile
tested, and I am not claiming that all the uses of remote will go
thourgh the code paths to use it with a transport, but you hopefully
got the idea.

 remote.c    | 20 ++++++++++++++++++++
 remote.h    |  2 ++
 transport.c |  2 ++
 3 files changed, 24 insertions(+)

diff --git c/remote.c w/remote.c
index f43cf5e7a4..1cca98215d 100644
--- c/remote.c
+++ w/remote.c
@@ -2904,3 +2904,23 @@ char *relative_url(const char *remote_url, const char *url,
 	free(out);
 	return strbuf_detach(&sb, NULL);
 }
+
+void remote_stale_warning(const struct remote *remote)
+{
+	const char *msg;
+
+	switch (remote->origin) {
+	case REMOTE_REMOTES:
+		msg = N_("Using remote '%s' read from the .git/remotes, "
+			 "whose support will be removed");
+		break;
+	case REMOTE_BRANCHES:
+		msg = N_("Using remote '%s' read from the .git/branches, "
+			 "whose support will be removed");
+		break;
+	default:
+		return;
+	}
+
+	warning(_(msg), remote->name);
+}
diff --git c/remote.h w/remote.h
index b901b56746..e29ceef3e4 100644
--- c/remote.h
+++ w/remote.h
@@ -445,4 +445,6 @@ void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
 char *relative_url(const char *remote_url, const char *url,
 		   const char *up_path);
 
+void remote_stale_warning(const struct remote *remote);
+
 #endif
diff --git c/transport.c w/transport.c
index 12cc5b4d96..c153be9100 100644
--- c/transport.c
+++ w/transport.c
@@ -1131,6 +1131,8 @@ struct transport *transport_get(struct remote *remote, const char *url)
 	ret->remote = remote;
 	helper = remote->foreign_vcs;
 
+	remote_stale_warning(remote);
+
 	if (!url)
 		url = remote->url.v[0];
 	ret->url = url;
Randall S. Becker Jan. 7, 2025, 4:55 p.m. UTC | #6
On January 7, 2025 11:50 AM, Junio C Hamano wrote:
>Junio C Hamano <gitster@pobox.com> writes:
>
>> Patrick Steinhardt <ps@pks.im> writes:
>>
>>> Makes sense indeed. We can easily add for something like below diff.
>>> I'll roll that into the next version, thanks!
>>
>> It is a good start, but is probably a bit too noisy.  Can we make them
>> appear ONLY when the definitions read from these older sources are
>> actually USED?
>>
>> Thanks.
>
>Something along this line is what I had in mind.  Not even compile tested,
and I am
>not claiming that all the uses of remote will go thourgh the code paths to
use it with
>a transport, but you hopefully got the idea.
>
> remote.c    | 20 ++++++++++++++++++++
> remote.h    |  2 ++
> transport.c |  2 ++
> 3 files changed, 24 insertions(+)
>
>diff --git c/remote.c w/remote.c
>index f43cf5e7a4..1cca98215d 100644
>--- c/remote.c
>+++ w/remote.c
>@@ -2904,3 +2904,23 @@ char *relative_url(const char *remote_url, const
char
>*url,
> 	free(out);
> 	return strbuf_detach(&sb, NULL);
> }
>+
>+void remote_stale_warning(const struct remote *remote) {
>+	const char *msg;
>+
>+	switch (remote->origin) {
>+	case REMOTE_REMOTES:
>+		msg = N_("Using remote '%s' read from the .git/remotes, "
>+			 "whose support will be removed");
>+		break;
>+	case REMOTE_BRANCHES:
>+		msg = N_("Using remote '%s' read from the .git/branches, "
>+			 "whose support will be removed");
>+		break;
>+	default:
>+		return;
>+	}
>+
>+	warning(_(msg), remote->name);
>+}
>diff --git c/remote.h w/remote.h
>index b901b56746..e29ceef3e4 100644
>--- c/remote.h
>+++ w/remote.h
>@@ -445,4 +445,6 @@ void apply_push_cas(struct push_cas_option *, struct
>remote *, struct ref *);  char *relative_url(const char *remote_url, const
char *url,
> 		   const char *up_path);
>
>+void remote_stale_warning(const struct remote *remote);
>+
> #endif
>diff --git c/transport.c w/transport.c
>index 12cc5b4d96..c153be9100 100644
>--- c/transport.c
>+++ w/transport.c
>@@ -1131,6 +1131,8 @@ struct transport *transport_get(struct remote
*remote,
>const char *url)
> 	ret->remote = remote;
> 	helper = remote->foreign_vcs;
>
>+	remote_stale_warning(remote);
>+
> 	if (!url)
> 		url = remote->url.v[0];
> 	ret->url = url;

I like this but wonder whether there might be some way to inhibit the
warnings
one a user gets it and decides they will act but do not want to see the
warnings
any longer? I have had requests like this on other products. Just a thought.
Patrick Steinhardt Jan. 8, 2025, 6:36 a.m. UTC | #7
On Tue, Jan 07, 2025 at 11:55:16AM -0500, rsbecker@nexbridge.com wrote:
> On January 7, 2025 11:50 AM, Junio C Hamano wrote:
> >Junio C Hamano <gitster@pobox.com> writes:
> >
> >> Patrick Steinhardt <ps@pks.im> writes:
> >>
> >>> Makes sense indeed. We can easily add for something like below diff.
> >>> I'll roll that into the next version, thanks!
> >>
> >> It is a good start, but is probably a bit too noisy.  Can we make them
> >> appear ONLY when the definitions read from these older sources are
> >> actually USED?
> >>
> >> Thanks.

Fair enough, can do.

> I like this but wonder whether there might be some way to inhibit the
> warnings one a user gets it and decides they will act but do not want
> to see the warnings any longer? I have had requests like this on other
> products. Just a thought.

I guess the best idea I have here is to use an environment variable,
e.g. "GIT_ALLOW_DEPRECATED_REMOTES=true", along with a hint for how to
enable it.

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

> On Tue, Jan 07, 2025 at 11:55:16AM -0500, rsbecker@nexbridge.com wrote:
>
>> I like this but wonder whether there might be some way to inhibit the
>> warnings one a user gets it and decides they will act but do not want
>> to see the warnings any longer? I have had requests like this on other
>> products. Just a thought.
>
> I guess the best idea I have here is to use an environment variable,
> e.g. "GIT_ALLOW_DEPRECATED_REMOTES=true", along with a hint for how to
> enable it.

Hmph.

I may be missing something, but wouldn't the whole point of the
warning be noisy and pesky as long as the user _uses_ that
configuration?  It is not like "you can set this knob and delay the
removal past Git 3.0".  If the user migrates away from the mechanism
that is being removed, we would stop bugging the user about the
stale setting, so I do not see why we want to add anything extra
(other than possibly telling them how to migrate away from
$GIT_DIR/{branches,remotes}/ using "git remote" in the warning
message itself).

Thanks.
diff mbox series

Patch

diff --git a/Documentation/BreakingChanges.txt b/Documentation/BreakingChanges.txt
index 27acff86db4883a7d7967343c61711959b75a473..a91eb3bc5518d09b39c49ce07964ca3aba7b11dd 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 fa8b51daf08775f3d666a910d9b00486627e02af..85911ca8ea0b222ab9cc3dc2dd99c5136c72bd2b 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 1ad3e70a6b438a3c4446b16c02dc5c23c7fa14be..e565b2b3fec8bf2e4182e85d8d08953a14416881 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 10104d11e3cba1908cd35f22b54e167755770404..55e91fab47197313fd8c2c1c5f73fcd46b4df4f7 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))
@@ -374,6 +375,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 +574,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 +586,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 +729,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 a7e5c4e07c53ce5a0f6d852ce9f9233e6bb61550..45b0c9babb1374a05471e86de2c248972adb2aae 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 08424e878e104cc19a43960b987cf868f542cad2..e96ac8c7676ceeb7299c8f0839d9654d527ae084 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 3b3991ab8678a57fce3ad371e37900fb3c6c426a..04d8a96367910d4687b18a8f725dc365cf2ceedb 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 320d26796d24d8a2281d37220a7bdf73cafaa503..4e6026c6114fb8367136173ea2b7fdbc0baa37fc 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 9d693eb57f7790ddb81cee0b905a101719069562..e705aedbf4a8d57ed188918077b9f82ed8e77b51 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
 '