remote: announce removal of "branches/" and "remotes/"
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> [jc: with a small tweak to the help message] Signed-off-by: Junio C Hamano <gitster@pobox.com>
Patrick Steinhardt committed
Jan 22, 2025 at 12:31 UTC
8ccc75c2452b5814d2445d60d54266293ca48674
9 files changed
+99
-43
Documentation/BreakingChanges.txt
+25
@@ -154,6 +154,31 @@ Cf. <xmqq1rjuz6n3.fsf_-_@gitster.c.googlers.com>,
154
<CAKvOHKAFXQwt4D8yUCCkf_TQL79mYaJ=KAKhtpDNTvHJFuX1NA@mail.gmail.com>,
155
<20230323204047.GA9290@coredump.intra.peff.net>,
156
157
+* Support for storing shorthands for remote URLs in "$GIT_COMMON_DIR/branches/"
158
+ and "$GIT_COMMON_DIR/remotes/" has been long superseded by storing remotes in
159
+ the repository configuration.
160
++
161
+The mechanism has originally been introduced in f170e4b39d ([PATCH] fetch/pull:
162
+short-hand notation for remote repositories., 2005-07-16) and was superseded by
163
+6687f8fea2 ([PATCH] Use .git/remote/origin, not .git/branches/origin.,
164
+2005-08-20), where we switched from ".git/branches/" to ".git/remotes/". That
165
+commit already mentions an upcoming deprecation of the ".git/branches/"
166
+directory, and starting with a1d4aa7424 (Add repository-layout document.,
167
+2005-09-01) we have also marked this layout as deprecated. Eventually we also
168
+started to migrate away from ".git/remotes/" in favor of config-based remotes,
169
+and we have marked the directory as legacy in 3d3d282146 (Documentation:
170
+Grammar correction, wording fixes and cleanup, 2011-08-23)
171
++
172
+As our documentation mentions, these directories are not to be found in modern
173
+repositories at all and most users aren't even aware of these mechanisms. They
174
+have been deprecated for almost 20 years and 14 years respectively, and we are
175
+not aware of any active users that have complained about this deprecation.
176
+Furthermore, the ".git/branches/" directory is nowadays misleadingly named and
177
+may cause confusion as "branches" are almost exclusively used in the context of
178
+references.
179
++
180
+These features will be removed.
181
+
182
== Superseded features that will not be deprecated
183
184
Some features have gained newer replacements that aim to improve the design in
Documentation/gitrepository-layout.txt
+5
-2
@@ -153,7 +153,7 @@ config.worktree::
153
linkgit:git-worktree[1]).
154
155
branches::
156
- A slightly deprecated way to store shorthands to be used
156
+ A deprecated way to store shorthands to be used
157
to specify a URL to 'git fetch', 'git pull' and 'git push'.
158
A file can be stored as `branches/<name>` and then
159
'name' can be given to these commands in place of
@@ -162,7 +162,8 @@ branches::
162
and not likely to be found in modern repositories. This
163
directory is ignored if $GIT_COMMON_DIR is set and
164
"$GIT_COMMON_DIR/branches" will be used instead.
165
-
165
++
166
+Git will stop reading remotes from this directory in Git 3.0.
167
168
hooks::
169
Hooks are customization scripts used by various Git
@@ -238,6 +239,8 @@ remotes::
239
and not likely to be found in modern repositories. This
240
directory is ignored if $GIT_COMMON_DIR is set and
241
"$GIT_COMMON_DIR/remotes" will be used instead.
242
++
243
+Git will stop reading remotes from this directory in Git 3.0.
244
245
logs::
246
Records of changes made to refs are stored in this directory.
builtin/remote.c
+2
@@ -640,10 +640,12 @@ static int migrate_file(struct remote *remote)
640
strbuf_addf(&buf, "remote.%s.fetch", remote->name);
641
for (i = 0; i < remote->fetch.nr; i++)
642
git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
643
+#ifndef WITH_BREAKING_CHANGES
644
if (remote->origin == REMOTE_REMOTES)
645
unlink_or_warn(git_path("remotes/%s", remote->name));
646
else if (remote->origin == REMOTE_BRANCHES)
647
unlink_or_warn(git_path("branches/%s", remote->name));
648
+#endif /* WITH_BREAKING_CHANGES */
649
strbuf_release(&buf);
650
651
return 0;
remote.c
+26
@@ -293,6 +293,7 @@ static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
293
rewrite->instead_of_nr++;
294
}
295
296
+#ifndef WITH_BREAKING_CHANGES
297
static const char *skip_spaces(const char *s)
298
{
299
while (isspace(*s))
@@ -300,6 +301,21 @@ static const char *skip_spaces(const char *s)
301
return s;
302
}
303
304
+static void warn_about_deprecated_remote_type(const char *type,
305
+ const struct remote *remote)
306
+{
307
+ warning(_("reading remote from \"%s/%s\", which is nominated for removal.\n"
308
+ "\n"
309
+ "If you still use the \"remotes/\" directory it is recommended to\n"
310
+ "migrate to config-based remotes:\n"
311
+ "\n"
312
+ "\tgit remote rename %s %s\n"
313
+ "\n"
314
+ "If you cannot, please let us know why you still need to use it by\n"
315
+ "sending an e-mail to <git@vger.kernel.org>."),
316
+ type, remote->name, remote->name, remote->name);
317
+}
318
+
319
static void read_remotes_file(struct remote_state *remote_state,
320
struct remote *remote)
321
{
@@ -308,6 +324,9 @@ static void read_remotes_file(struct remote_state *remote_state,
324
325
if (!f)
326
return;
327
+
328
+ warn_about_deprecated_remote_type("remotes", remote);
329
+
330
remote->configured_in_repo = 1;
331
remote->origin = REMOTE_REMOTES;
332
while (strbuf_getline(&buf, f) != EOF) {
@@ -337,6 +356,8 @@ static void read_branches_file(struct remote_state *remote_state,
356
if (!f)
357
return;
358
359
+ warn_about_deprecated_remote_type("branches", remote);
360
+
361
strbuf_getline_lf(&buf, f);
362
fclose(f);
363
strbuf_trim(&buf);
@@ -374,6 +395,7 @@ static void read_branches_file(struct remote_state *remote_state,
395
strbuf_release(&buf);
396
free(to_free);
397
}
398
+#endif /* WITH_BREAKING_CHANGES */
399
400
static int handle_config(const char *key, const char *value,
401
const struct config_context *ctx, void *cb)
@@ -572,6 +594,7 @@ static void read_config(struct repository *repo, int early)
594
alias_all_urls(repo->remote_state);
595
}
596
597
+#ifndef WITH_BREAKING_CHANGES
598
static int valid_remote_nick(const char *name)
599
{
600
if (!name[0] || is_dot_or_dotdot(name))
@@ -583,6 +606,7 @@ static int valid_remote_nick(const char *name)
606
return 0;
607
return 1;
608
}
609
+#endif /* WITH_BREAKING_CHANGES */
610
611
static const char *remotes_remote_for_branch(struct remote_state *remote_state,
612
struct branch *branch,
@@ -725,12 +749,14 @@ remotes_remote_get_1(struct remote_state *remote_state, const char *name,
749
&name_given);
750
751
ret = make_remote(remote_state, name, 0);
752
+#ifndef WITH_BREAKING_CHANGES
753
if (valid_remote_nick(name) && have_git_dir()) {
754
if (!valid_remote(ret))
755
read_remotes_file(remote_state, ret);
756
if (!valid_remote(ret))
757
read_branches_file(remote_state, ret);
758
}
759
+#endif /* WITH_BREAKING_CHANGES */
760
if (name_given && !valid_remote(ret))
761
add_url_alias(remote_state, ret, name);
762
if (!valid_remote(ret))
remote.h
+2
@@ -21,8 +21,10 @@ struct transport_ls_refs_options;
21
enum {
22
REMOTE_UNCONFIGURED = 0,
23
REMOTE_CONFIG,
24
+#ifndef WITH_BREAKING_CHANGES
25
REMOTE_REMOTES,
26
REMOTE_BRANCHES
27
+#endif /* WITH_BREAKING_CHANGES */
28
};
29
30
struct rewrite {
t/t5505-remote.sh
+3
-3
@@ -1007,7 +1007,7 @@ Pull: refs/heads/main:refs/heads/origin
1007
Pull: refs/heads/next:refs/heads/origin2
1008
EOF
1009
1010
-test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
1010
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
1011
git clone one five &&
1012
origin_url=$(pwd)/one &&
1013
(
@@ -1033,7 +1033,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
1033
)
1034
'
1035
1036
-test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
1036
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
1037
git clone --template= one six &&
1038
origin_url=$(pwd)/one &&
1039
(
@@ -1049,7 +1049,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
1049
)
1050
'
1051
1052
-test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
1052
+test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
1053
git clone --template= one seven &&
1054
(
1055
cd seven &&
t/t5510-fetch.sh
+5
-8
@@ -34,14 +34,11 @@ test_expect_success "clone and setup child repos" '
34
git clone . three &&
35
(
36
cd three &&
37
- git config branch.main.remote two &&
38
- git config branch.main.merge refs/heads/one &&
39
- mkdir -p .git/remotes &&
40
- cat >.git/remotes/two <<-\EOF
41
- URL: ../two/.git/
42
- Pull: refs/heads/main:refs/heads/two
43
- Pull: refs/heads/one:refs/heads/one
44
- EOF
37
+ git config set remote.two.url ../two/.git/ &&
38
+ git config set remote.two.fetch refs/heads/main:refs/heads/two &&
39
+ git config set --append remote.two.fetch refs/heads/one:refs/heads/one &&
40
+ git config set branch.main.remote two &&
41
+ git config set branch.main.merge refs/heads/one
42
) &&
43
git clone . bundle &&
44
git clone . seven
t/t5515-fetch-merge-logic.sh
+25
-22
@@ -104,28 +104,31 @@ test_expect_success setup '
104
git config remote.config-glob.fetch refs/heads/*:refs/remotes/rem/* &&
105
remotes="$remotes config-glob" &&
106
107
- mkdir -p .git/remotes &&
108
- cat >.git/remotes/remote-explicit <<-\EOF &&
109
- URL: ../.git/
110
- Pull: refs/heads/main:remotes/rem/main
111
- Pull: refs/heads/one:remotes/rem/one
112
- Pull: two:remotes/rem/two
113
- Pull: refs/heads/three:remotes/rem/three
114
- EOF
115
- remotes="$remotes remote-explicit" &&
116
-
117
- cat >.git/remotes/remote-glob <<-\EOF &&
118
- URL: ../.git/
119
- Pull: refs/heads/*:refs/remotes/rem/*
120
- EOF
121
- remotes="$remotes remote-glob" &&
122
-
123
- mkdir -p .git/branches &&
124
- echo "../.git" > .git/branches/branches-default &&
125
- remotes="$remotes branches-default" &&
126
-
127
- echo "../.git#one" > .git/branches/branches-one &&
128
- remotes="$remotes branches-one" &&
107
+ if test_have_prereq WITHOUT_BREAKING_CHANGES
108
+ then
109
+ mkdir -p .git/remotes &&
110
+ cat >.git/remotes/remote-explicit <<-\EOF &&
111
+ URL: ../.git/
112
+ Pull: refs/heads/main:remotes/rem/main
113
+ Pull: refs/heads/one:remotes/rem/one
114
+ Pull: two:remotes/rem/two
115
+ Pull: refs/heads/three:remotes/rem/three
116
+ EOF
117
+ remotes="$remotes remote-explicit" &&
118
+
119
+ cat >.git/remotes/remote-glob <<-\EOF &&
120
+ URL: ../.git/
121
+ Pull: refs/heads/*:refs/remotes/rem/*
122
+ EOF
123
+ remotes="$remotes remote-glob" &&
124
+
125
+ mkdir -p .git/branches &&
126
+ echo "../.git" > .git/branches/branches-default &&
127
+ remotes="$remotes branches-default" &&
128
+
129
+ echo "../.git#one" > .git/branches/branches-one &&
130
+ remotes="$remotes branches-one"
131
+ fi &&
132
133
for remote in $remotes ; do
134
git config branch.br-$remote.remote $remote &&
t/t5516-fetch-push.sh
+6
-8
@@ -975,7 +975,7 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' '
975
! grep "warning: updating the current branch" stderr
976
'
977
978
-test_expect_success 'fetch with branches' '
978
+test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
979
mk_empty testrepo &&
980
git branch second $the_first_commit &&
981
git checkout second &&
@@ -991,7 +991,7 @@ test_expect_success 'fetch with branches' '
991
git checkout main
992
'
993
994
-test_expect_success 'fetch with branches containing #' '
994
+test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #' '
995
mk_empty testrepo &&
996
mkdir testrepo/.git/branches &&
997
echo "..#second" > testrepo/.git/branches/branch2 &&
@@ -1005,7 +1005,7 @@ test_expect_success 'fetch with branches containing #' '
1005
git checkout main
1006
'
1007
1008
-test_expect_success 'push with branches' '
1008
+test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
1009
mk_empty testrepo &&
1010
git checkout second &&
1011
@@ -1022,7 +1022,7 @@ test_expect_success 'push with branches' '
1022
)
1023
'
1024
1025
-test_expect_success 'push with branches containing #' '
1025
+test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches containing #' '
1026
mk_empty testrepo &&
1027
1028
test_when_finished "rm -rf .git/branches" &&
@@ -1211,18 +1211,16 @@ test_expect_success 'push --porcelain --dry-run rejected' '
1211
'
1212
1213
test_expect_success 'push --prune' '
1214
- mk_test testrepo heads/main heads/second heads/foo heads/bar &&
1214
+ mk_test testrepo heads/main heads/foo heads/bar &&
1215
git push --prune testrepo : &&
1216
check_push_result testrepo $the_commit heads/main &&
1217
- check_push_result testrepo $the_first_commit heads/second &&
1217
! check_push_result testrepo $the_first_commit heads/foo heads/bar
1218
'
1219
1220
test_expect_success 'push --prune refspec' '
1222
- mk_test testrepo tmp/main tmp/second tmp/foo tmp/bar &&
1221
+ mk_test testrepo tmp/main tmp/foo tmp/bar &&
1222
git push --prune testrepo "refs/heads/*:refs/tmp/*" &&
1223
check_push_result testrepo $the_commit tmp/main &&
1225
- check_push_result testrepo $the_first_commit tmp/second &&
1224
! check_push_result testrepo $the_first_commit tmp/foo tmp/bar
1225
'
1226