Skip to content

Commit cea667e

Browse files
committed
Merge branch 'jk/maint-remote-mirror-safer'
* jk/maint-remote-mirror-safer: remote: deprecate --mirror remote: separate the concept of push and fetch mirrors remote: disallow some nonsensical option combinations
2 parents 7e3ead1 + 0990248 commit cea667e

File tree

3 files changed

+136
-19
lines changed

3 files changed

+136
-19
lines changed

Documentation/git-remote.txt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git remote' [-v | --verbose]
13-
'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror] <name> <url>
13+
'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>
1414
'git remote rename' <old> <new>
1515
'git remote rm' <name>
1616
'git remote set-head' <name> (-a | -d | <branch>)
@@ -67,11 +67,14 @@ multiple branches without grabbing all branches.
6767
With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
6868
up to point at remote's `<master>` branch. See also the set-head command.
6969
+
70-
In mirror mode, enabled with `\--mirror`, the refs will not be stored
71-
in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option
72-
only makes sense in bare repositories. If a remote uses mirror
73-
mode, furthermore, `git push` will always behave as if `\--mirror`
74-
was passed.
70+
When a fetch mirror is created with `\--mirror=fetch`, the refs will not
71+
be stored in the 'refs/remotes/' namespace, but rather everything in
72+
'refs/' on the remote will be directly mirrored into 'refs/' in the
73+
local repository. This option only makes sense in bare repositories,
74+
because a fetch would overwrite any local commits.
75+
+
76+
When a push mirror is created with `\--mirror=push`, then `git push`
77+
will always behave as if `\--mirror` was passed.
7578

7679
'rename'::
7780

builtin/remote.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
static const char * const builtin_remote_usage[] = {
1111
"git remote [-v | --verbose]",
12-
"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>",
12+
"git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>",
1313
"git remote rename <old> <new>",
1414
"git remote rm <name>",
1515
"git remote set-head <name> (-a | -d | <branch>)",
@@ -117,6 +117,11 @@ enum {
117117
TAGS_SET = 2
118118
};
119119

120+
#define MIRROR_NONE 0
121+
#define MIRROR_FETCH 1
122+
#define MIRROR_PUSH 2
123+
#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)
124+
120125
static int add_branch(const char *key, const char *branchname,
121126
const char *remotename, int mirror, struct strbuf *tmp)
122127
{
@@ -131,9 +136,32 @@ static int add_branch(const char *key, const char *branchname,
131136
return git_config_set_multivar(key, tmp->buf, "^$", 0);
132137
}
133138

139+
static const char mirror_advice[] =
140+
"--mirror is dangerous and deprecated; please\n"
141+
"\t use --mirror=fetch or --mirror=push instead";
142+
143+
static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
144+
{
145+
unsigned *mirror = opt->value;
146+
if (not)
147+
*mirror = MIRROR_NONE;
148+
else if (!arg) {
149+
warning("%s", mirror_advice);
150+
*mirror = MIRROR_BOTH;
151+
}
152+
else if (!strcmp(arg, "fetch"))
153+
*mirror = MIRROR_FETCH;
154+
else if (!strcmp(arg, "push"))
155+
*mirror = MIRROR_PUSH;
156+
else
157+
return error("unknown mirror argument: %s", arg);
158+
return 0;
159+
}
160+
134161
static int add(int argc, const char **argv)
135162
{
136-
int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
163+
int fetch = 0, fetch_tags = TAGS_DEFAULT;
164+
unsigned mirror = MIRROR_NONE;
137165
struct string_list track = STRING_LIST_INIT_NODUP;
138166
const char *master = NULL;
139167
struct remote *remote;
@@ -151,7 +179,9 @@ static int add(int argc, const char **argv)
151179
OPT_CALLBACK('t', "track", &track, "branch",
152180
"branch(es) to track", opt_parse_track),
153181
OPT_STRING('m', "master", &master, "branch", "master branch"),
154-
OPT_BOOLEAN(0, "mirror", &mirror, "no separate remotes"),
182+
{ OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
183+
"set up remote as a mirror to push to or fetch from",
184+
PARSE_OPT_OPTARG, parse_mirror_opt },
155185
OPT_END()
156186
};
157187

@@ -161,6 +191,11 @@ static int add(int argc, const char **argv)
161191
if (argc < 2)
162192
usage_with_options(builtin_remote_add_usage, options);
163193

194+
if (mirror && master)
195+
die("specifying a master branch makes no sense with --mirror");
196+
if (mirror && track.nr)
197+
die("specifying branches to track makes no sense with --mirror");
198+
164199
name = argv[0];
165200
url = argv[1];
166201

@@ -177,18 +212,19 @@ static int add(int argc, const char **argv)
177212
if (git_config_set(buf.buf, url))
178213
return 1;
179214

180-
strbuf_reset(&buf);
181-
strbuf_addf(&buf, "remote.%s.fetch", name);
182-
183-
if (track.nr == 0)
184-
string_list_append(&track, "*");
185-
for (i = 0; i < track.nr; i++) {
186-
if (add_branch(buf.buf, track.items[i].string,
187-
name, mirror, &buf2))
188-
return 1;
215+
if (!mirror || mirror & MIRROR_FETCH) {
216+
strbuf_reset(&buf);
217+
strbuf_addf(&buf, "remote.%s.fetch", name);
218+
if (track.nr == 0)
219+
string_list_append(&track, "*");
220+
for (i = 0; i < track.nr; i++) {
221+
if (add_branch(buf.buf, track.items[i].string,
222+
name, mirror, &buf2))
223+
return 1;
224+
}
189225
}
190226

191-
if (mirror) {
227+
if (mirror & MIRROR_PUSH) {
192228
strbuf_reset(&buf);
193229
strbuf_addf(&buf, "remote.%s.mirror", name);
194230
if (git_config_set(buf.buf, "true"))

t/t5505-remote.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,84 @@ test_expect_success 'add --mirror && prune' '
304304
git rev-parse --verify refs/heads/side)
305305
'
306306

307+
test_expect_success 'add --mirror=fetch' '
308+
mkdir mirror-fetch &&
309+
git init mirror-fetch/parent &&
310+
(cd mirror-fetch/parent &&
311+
test_commit one) &&
312+
git init --bare mirror-fetch/child &&
313+
(cd mirror-fetch/child &&
314+
git remote add --mirror=fetch -f parent ../parent)
315+
'
316+
317+
test_expect_success 'fetch mirrors act as mirrors during fetch' '
318+
(cd mirror-fetch/parent &&
319+
git branch new &&
320+
git branch -m master renamed
321+
) &&
322+
(cd mirror-fetch/child &&
323+
git fetch parent &&
324+
git rev-parse --verify refs/heads/new &&
325+
git rev-parse --verify refs/heads/renamed
326+
)
327+
'
328+
329+
test_expect_success 'fetch mirrors can prune' '
330+
(cd mirror-fetch/child &&
331+
git remote prune parent &&
332+
test_must_fail git rev-parse --verify refs/heads/master
333+
)
334+
'
335+
336+
test_expect_success 'fetch mirrors do not act as mirrors during push' '
337+
(cd mirror-fetch/parent &&
338+
git checkout HEAD^0
339+
) &&
340+
(cd mirror-fetch/child &&
341+
git branch -m renamed renamed2 &&
342+
git push parent
343+
) &&
344+
(cd mirror-fetch/parent &&
345+
git rev-parse --verify renamed &&
346+
test_must_fail git rev-parse --verify refs/heads/renamed2
347+
)
348+
'
349+
350+
test_expect_success 'add --mirror=push' '
351+
mkdir mirror-push &&
352+
git init --bare mirror-push/public &&
353+
git init mirror-push/private &&
354+
(cd mirror-push/private &&
355+
test_commit one &&
356+
git remote add --mirror=push public ../public
357+
)
358+
'
359+
360+
test_expect_success 'push mirrors act as mirrors during push' '
361+
(cd mirror-push/private &&
362+
git branch new &&
363+
git branch -m master renamed &&
364+
git push public
365+
) &&
366+
(cd mirror-push/private &&
367+
git rev-parse --verify refs/heads/new &&
368+
git rev-parse --verify refs/heads/renamed &&
369+
test_must_fail git rev-parse --verify refs/heads/master
370+
)
371+
'
372+
373+
test_expect_success 'push mirrors do not act as mirrors during fetch' '
374+
(cd mirror-push/public &&
375+
git branch -m renamed renamed2 &&
376+
git symbolic-ref HEAD refs/heads/renamed2
377+
) &&
378+
(cd mirror-push/private &&
379+
git fetch public &&
380+
git rev-parse --verify refs/heads/renamed &&
381+
test_must_fail git rev-parse --verify refs/heads/renamed2
382+
)
383+
'
384+
307385
test_expect_success 'add alt && prune' '
308386
(mkdir alttst &&
309387
cd alttst &&

0 commit comments

Comments
 (0)