Skip to content

Commit 0dab246

Browse files
avargitster
authored andcommitted
clone: add a --no-tags option to clone without tags
Add a --no-tags option to clone without fetching any tags. Without this change there's no easy way to clone a repository without also fetching its tags. When supplying --single-branch the primary remote branch will be cloned, but in addition tags will be followed & retrieved. Now --no-tags can be added --single-branch to clone a repository without tags, and which only tracks a single upstream branch. This option works without --single-branch as well, and will do a normal clone but not fetch any tags. Many git commands pay some fixed overhead as a function of the number of references. E.g. creating ~40k tags in linux.git will cause a command like `git log -1 >/dev/null` to run in over a second instead of in a matter of milliseconds, in addition numerous other things will slow down, e.g. "git log <TAB>" with the bash completion will slowly show ~40k references instead of 1. The user might want to avoid all of that overhead to simply use a repository like that to browse the "master" branch, or something like a CI tool might want to keep that one branch up-to-date without caring about any other references. Without this change the only way of accomplishing this was either by manually tweaking the config in a fresh repository: git init git && cat >git/.git/config <<EOF && [remote "origin"] url = git@github.com:git/git.git tagOpt = --no-tags fetch = +refs/heads/master:refs/remotes/origin/master [branch "master"] remote = origin merge = refs/heads/master EOF cd git && git pull Which requires hardcoding the "master" name, which may not be the main --single-branch would have retrieved, or alternatively by setting tagOpt=--no-tags right after cloning & deleting any existing tags: git clone --single-branch git@github.com:git/git.git && cd git && git config remote.origin.tagOpt --no-tags && git tag -l | xargs git tag -d Which of course was also subtly buggy if --branch was pointed at a tag, leaving the user in a detached head: git clone --single-branch --branch v2.12.0 git@github.com:git/git.git && cd git && git config remote.origin.tagOpt --no-tags && git tag -l | xargs git tag -d Now all this complexity becomes the much simpler: git clone --single-branch --no-tags git@github.com:git/git.git Or in the case of cloning a single tag "branch": git clone --single-branch --branch v2.12.0 --no-tags git@github.com:git/git.git Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 28d67d9 commit 0dab246

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

Documentation/git-clone.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ SYNOPSIS
1313
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
1414
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
1515
[--dissociate] [--separate-git-dir <git dir>]
16-
[--depth <depth>] [--[no-]single-branch]
16+
[--depth <depth>] [--[no-]single-branch] [--no-tags]
1717
[--recurse-submodules] [--[no-]shallow-submodules]
1818
[--jobs <n>] [--] <repository> [<directory>]
1919

@@ -215,6 +215,18 @@ objects from the source repository into a pack in the cloned repository.
215215
branch when `--single-branch` clone was made, no remote-tracking
216216
branch is created.
217217

218+
--no-tags::
219+
Don't clone any tags, and set
220+
`remote.<remote>.tagOpt=--no-tags` in the config, ensuring
221+
that future `git pull` and `git fetch` operations won't follow
222+
any tags. Subsequent explicit tag fetches will still work,
223+
(see linkgit:git-fetch[1]).
224+
+
225+
Can be used in conjunction with `--single-branch` to clone and
226+
maintain a branch with no references other than a single cloned
227+
branch. This is useful e.g. to maintain minimal clones of the default
228+
branch of some repository for search indexing.
229+
218230
--recurse-submodules[=<pathspec]::
219231
After the clone is created, initialize and clone submodules
220232
within based on the provided pathspec. If no pathspec is

builtin/clone.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
4040

4141
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
4242
static int option_local = -1, option_no_hardlinks, option_shared;
43+
static int option_no_tags;
4344
static int option_shallow_submodules;
4445
static int deepen;
4546
static char *option_template, *option_depth, *option_since;
@@ -120,6 +121,8 @@ static struct option builtin_clone_options[] = {
120121
N_("deepen history of shallow clone, excluding rev")),
121122
OPT_BOOL(0, "single-branch", &option_single_branch,
122123
N_("clone only one branch, HEAD or --branch")),
124+
OPT_BOOL(0, "no-tags", &option_no_tags,
125+
N_("don't clone any tags, and make later fetches not to follow them")),
123126
OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
124127
N_("any cloned submodules will be shallow")),
125128
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
@@ -563,7 +566,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
563566
} else
564567
get_fetch_map(refs, refspec, &tail, 0);
565568

566-
if (!option_mirror && !option_single_branch)
569+
if (!option_mirror && !option_single_branch && !option_no_tags)
567570
get_fetch_map(refs, tag_refspec, &tail, 0);
568571

569572
return local_refs;
@@ -652,7 +655,7 @@ static void update_remote_refs(const struct ref *refs,
652655

653656
if (refs) {
654657
write_remote_refs(mapped_refs);
655-
if (option_single_branch)
658+
if (option_single_branch && !option_no_tags)
656659
write_followtags(refs, msg);
657660
}
658661

@@ -1035,6 +1038,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
10351038
git_config_set(key.buf, repo);
10361039
strbuf_reset(&key);
10371040

1041+
if (option_no_tags) {
1042+
strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
1043+
git_config_set(key.buf, "--no-tags");
1044+
strbuf_reset(&key);
1045+
}
1046+
10381047
if (option_required_reference.nr || option_optional_reference.nr)
10391048
setup_reference();
10401049

contrib/completion/git-completion.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,7 @@ _git_clone ()
13091309
--template=
13101310
--depth
13111311
--single-branch
1312+
--no-tags
13121313
--branch
13131314
--recurse-submodules
13141315
--no-single-branch

t/t5612-clone-refspec.sh

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,20 @@ test_expect_success 'setup' '
1717
echo four >file &&
1818
git commit -a -m four &&
1919
git checkout master &&
20+
git tag five &&
2021
2122
# default clone
2223
git clone . dir_all &&
2324
25+
# default clone --no-tags
26+
git clone --no-tags . dir_all_no_tags &&
27+
2428
# default --single that follows HEAD=master
2529
git clone --single-branch . dir_master &&
2630
31+
# default --single that follows HEAD=master with no tags
32+
git clone --single-branch --no-tags . dir_master_no_tags &&
33+
2734
# default --single that follows HEAD=side
2835
git checkout side &&
2936
git clone --single-branch . dir_side &&
@@ -45,6 +52,9 @@ test_expect_success 'setup' '
4552
# explicit --single with tag
4653
git clone --single-branch --branch two . dir_tag &&
4754
55+
# explicit --single with tag and --no-tags
56+
git clone --single-branch --no-tags --branch two . dir_tag_no_tags &&
57+
4858
# advance both "master" and "side" branches
4959
git checkout side &&
5060
echo five >file &&
@@ -77,7 +87,18 @@ test_expect_success 'by default no tags will be kept updated' '
7787
git for-each-ref refs/tags >../actual
7888
) &&
7989
git for-each-ref refs/tags >expect &&
80-
test_must_fail test_cmp expect actual
90+
test_must_fail test_cmp expect actual &&
91+
test_line_count = 2 actual
92+
'
93+
94+
test_expect_success 'clone with --no-tags' '
95+
(
96+
cd dir_all_no_tags &&
97+
git fetch &&
98+
git for-each-ref refs/tags >../actual
99+
) &&
100+
>expect &&
101+
test_cmp expect actual
81102
'
82103

83104
test_expect_success '--single-branch while HEAD pointing at master' '
@@ -90,7 +111,47 @@ test_expect_success '--single-branch while HEAD pointing at master' '
90111
) &&
91112
# only follow master
92113
git for-each-ref refs/heads/master >expect &&
93-
test_cmp expect actual
114+
# get & check latest tags
115+
test_cmp expect actual &&
116+
(
117+
cd dir_master &&
118+
git fetch --tags &&
119+
git for-each-ref refs/tags >../actual
120+
) &&
121+
git for-each-ref refs/tags >expect &&
122+
test_cmp expect actual &&
123+
test_line_count = 2 actual
124+
'
125+
126+
test_expect_success '--single-branch while HEAD pointing at master and --no-tags' '
127+
(
128+
cd dir_master_no_tags &&
129+
git fetch &&
130+
git for-each-ref refs/remotes/origin |
131+
sed -e "/HEAD$/d" \
132+
-e "s|/remotes/origin/|/heads/|" >../actual
133+
) &&
134+
# only follow master
135+
git for-each-ref refs/heads/master >expect &&
136+
test_cmp expect actual &&
137+
# get tags (noop)
138+
(
139+
cd dir_master_no_tags &&
140+
git fetch &&
141+
git for-each-ref refs/tags >../actual
142+
) &&
143+
>expect &&
144+
test_cmp expect actual &&
145+
test_line_count = 0 actual &&
146+
# get tags with --tags overrides tagOpt
147+
(
148+
cd dir_master_no_tags &&
149+
git fetch --tags &&
150+
git for-each-ref refs/tags >../actual
151+
) &&
152+
git for-each-ref refs/tags >expect &&
153+
test_cmp expect actual &&
154+
test_line_count = 2 actual
94155
'
95156

96157
test_expect_success '--single-branch while HEAD pointing at side' '
@@ -129,6 +190,17 @@ test_expect_success '--single-branch with explicit --branch with tag fetches upd
129190
test_cmp expect actual
130191
'
131192

193+
test_expect_success '--single-branch with explicit --branch with tag fetches updated tag despite --no-tags' '
194+
(
195+
cd dir_tag_no_tags &&
196+
git fetch &&
197+
git for-each-ref refs/tags >../actual
198+
) &&
199+
git for-each-ref refs/tags/two >expect &&
200+
test_cmp expect actual &&
201+
test_line_count = 1 actual
202+
'
203+
132204
test_expect_success '--single-branch with --mirror' '
133205
(
134206
cd dir_mirror &&

0 commit comments

Comments
 (0)