Add a --no-tags option to "git clone" to clone without tags. Currently
there's no easy way to clone a repository and end up with just a
"master" branch via --single-branch, or track all branches and no
tags. Now --no-tags can be added to "git clone" with or without
--single-branch to clone a repository without tags.

Before this the only way of doing this was either by manually tweaking
the config in a fresh repository:

    git init git &&
    cat >git/.git/config <<EOF &&
    [remote "origin"]
        url = g...@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 same
branch, or alternatively by setting tagOpt=--no-tags right after
cloning & deleting any existing tags:

    git clone --single-branch g...@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 g...@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 g...@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 
g...@github.com:git/git.git

Signed-off-by: Ævar Arnfjörð Bjarmason <ava...@gmail.com>
---

On Wed, Apr 19, 2017 at 3:38 AM, Junio C Hamano <gits...@pobox.com> wrote:
> Ævar Arnfjörð Bjarmason  <ava...@gmail.com> writes:
>
>> Add a --no-tags option to "git clone" to clone without tags. Currently
>> there's no easy way to clone a repository and end up with just a
>> "master" branch via --single-branch, or track all branches and no
>> tags. Now --no-tags can be added to "git clone" with or without
>> --single-branch to clone a repository without tags.
>
> Makes sense.
>
>> +--no-tags::
>> +     Don't clone any tags, and set `remote.origin.tagOpt=--no-tags`
>> +     in the config, ensuring that future `git pull` and `git fetch`
>> +     operations won't fetch any tags.
>
> OK.  Not just we ignore tags during the initial cloning, we set
> things up so that we do not _follow_ tags in subsequent fetches.
>
> s/won't fetch/won't follow/ is probably needed, as we still allow
> users to fetch tags by explicitly naming them on the command line.
> The only thing we are doing is to refrain from auto-following.
>
> As an end-user facing help, exact configuration name and value is
> much less helpful than telling them the effect of the setting in the
> words they understand, i.e. "make later fetches not to follow tags"
> or something.  

I reworded all of this to hopefully be more helpful.

> Hardcoded 'origin' in `remote.origin.tagOpt` is not correct anyway,
> so I'd suggest redoing this part of the doc.

Changed, FWIW various parts of the existing clone docs do the same
thing, so a follow-up change to that would make sense...

>> @@ -120,6 +121,8 @@ static struct option builtin_clone_options[] = {
>>                       N_("deepen history of shallow clone, excluding rev")),
>>       OPT_BOOL(0, "single-branch", &option_single_branch,
>>                   N_("clone only one branch, HEAD or --branch")),
>> +     OPT_BOOL_NONEG(0, "no-tags", &option_no_tags,
>> +                    N_("don't clone any tags, and set 
>> remote.<name>.tagOpt=--no-tags")),
>
> Likewise.  As an end-user facing help, exact configuration name and
> value is much less helpful than telling them the effect of the
> setting in the words they understand, i.e. "make later fetches not
> to follow tags" or something.

*Nod* changed.

>> +     if (option_no_tags) {
>> +             strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
>
> Good to use option_origin.
>
>> +             git_config_set(key.buf, "--no-tags");
>> +             strbuf_reset(&key);
>> +     }
>> +
>
> Thanks.

 Documentation/git-clone.txt | 14 ++++++++-
 builtin/clone.c             | 13 ++++++--
 t/t5612-clone-refspec.sh    | 73 +++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 30052cce49..57b3f478ed 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -13,7 +13,7 @@ SYNOPSIS
          [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
          [--dissociate] [--separate-git-dir <git dir>]
-         [--depth <depth>] [--[no-]single-branch]
+         [--depth <depth>] [--[no-]single-branch] [--no-tags]
          [--recurse-submodules] [--[no-]shallow-submodules]
          [--jobs <n>] [--] <repository> [<directory>]
 
@@ -215,6 +215,18 @@ objects from the source repository into a pack in the 
cloned repository.
        branch when `--single-branch` clone was made, no remote-tracking
        branch is created.
 
+--no-tags::
+       Don't clone any tags, and set
+       `remote.<remote>.tagOpt=--no-tags` in the config, ensuring
+       that future `git pull` and `git fetch` operations won't follow
+       any tags. Subsequent explicit tag fetches will still work,
+       (see linkgit:git-fetch[1]).
++
+Can be used in conjunction with `--single-branch` to clone & maintain
+a branch with no references other than a single cloned branch. This is
+useful e.g. to maintain minimal clones of the default branch of some
+repository for search indexing.
+
 --recurse-submodules[=<pathspec]::
        After the clone is created, initialize and clone submodules
        within based on the provided pathspec.  If no pathspec is
diff --git a/builtin/clone.c b/builtin/clone.c
index de85b85254..05f52d6f2b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
 
 static int option_no_checkout, option_bare, option_mirror, 
option_single_branch = -1;
 static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_no_tags;
 static int option_shallow_submodules;
 static int deepen;
 static char *option_template, *option_depth, *option_since;
@@ -120,6 +121,8 @@ static struct option builtin_clone_options[] = {
                        N_("deepen history of shallow clone, excluding rev")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
+       OPT_BOOL(0, "no-tags", &option_no_tags,
+                N_("don't clone any tags, and make later fetches not to follow 
them")),
        OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
                    N_("any cloned submodules will be shallow")),
        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,
        } else
                get_fetch_map(refs, refspec, &tail, 0);
 
-       if (!option_mirror && !option_single_branch)
+       if (!option_mirror && !option_single_branch && !option_no_tags)
                get_fetch_map(refs, tag_refspec, &tail, 0);
 
        return local_refs;
@@ -652,7 +655,7 @@ static void update_remote_refs(const struct ref *refs,
 
        if (refs) {
                write_remote_refs(mapped_refs);
-               if (option_single_branch)
+               if (option_single_branch && !option_no_tags)
                        write_followtags(refs, msg);
        }
 
@@ -1035,6 +1038,12 @@ int cmd_clone(int argc, const char **argv, const char 
*prefix)
        git_config_set(key.buf, repo);
        strbuf_reset(&key);
 
+       if (option_no_tags) {
+               strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+               git_config_set(key.buf, "--no-tags");
+               strbuf_reset(&key);
+       }
+
        if (option_required_reference.nr || option_optional_reference.nr)
                setup_reference();
 
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index 7ace2535c8..83317805a8 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -17,13 +17,20 @@ test_expect_success 'setup' '
        echo four >file &&
        git commit -a -m four &&
        git checkout master &&
+       git tag five &&
 
        # default clone
        git clone . dir_all &&
 
+       # default clone --no-tags
+       git clone --no-tags . dir_all_no_tags &&
+
        # default --single that follows HEAD=master
        git clone --single-branch . dir_master &&
 
+       # default --single that follows HEAD=master with no tags
+       git clone --single-branch --no-tags . dir_master_no_tags &&
+
        # default --single that follows HEAD=side
        git checkout side &&
        git clone --single-branch . dir_side &&
@@ -45,6 +52,9 @@ test_expect_success 'setup' '
        # explicit --single with tag
        git clone --single-branch --branch two . dir_tag &&
 
+       # explicit --single with tag and --no-tags
+       git clone --single-branch --no-tags --branch two . dir_tag_no_tags &&
+
        # advance both "master" and "side" branches
        git checkout side &&
        echo five >file &&
@@ -75,7 +85,17 @@ test_expect_success 'by default no tags will be kept 
updated' '
                git for-each-ref refs/tags >../actual
        ) &&
        git for-each-ref refs/tags >expect &&
-       test_must_fail test_cmp expect actual
+       test_must_fail test_cmp expect actual &&
+       test_line_count = 2 actual
+'
+
+test_expect_success 'clone with --no-tags' '
+       (
+               cd dir_all_no_tags && git fetch &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success '--single-branch while HEAD pointing at master' '
@@ -87,7 +107,46 @@ test_expect_success '--single-branch while HEAD pointing at 
master' '
        ) &&
        # only follow master
        git for-each-ref refs/heads/master >expect &&
-       test_cmp expect actual
+       # get & check latest tags
+       test_cmp expect actual &&
+       (
+               cd dir_master &&
+               git fetch --tags &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       git for-each-ref refs/tags >expect &&
+       test_cmp expect actual &&
+       test_line_count = 2 actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at master and 
--no-tags' '
+       (
+               cd dir_master_no_tags && git fetch &&
+               git for-each-ref refs/remotes/origin |
+               sed -e "/HEAD$/d" \
+                   -e "s|/remotes/origin/|/heads/|" >../actual
+       ) &&
+       # only follow master
+       git for-each-ref refs/heads/master >expect &&
+       test_cmp expect actual &&
+       # get tags (noop)
+       (
+               cd dir_master_no_tags &&
+               git fetch &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       >expect &&
+       test_cmp expect actual &&
+       test_line_count = 0 actual &&
+       # get tags with --tags overrides tagOpt
+       (
+               cd dir_master_no_tags &&
+               git fetch --tags &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       git for-each-ref refs/tags >expect &&
+       test_cmp expect actual &&
+       test_line_count = 2 actual
 '
 
 test_expect_success '--single-branch while HEAD pointing at side' '
@@ -123,6 +182,16 @@ test_expect_success '--single-branch with explicit 
--branch with tag fetches upd
        test_cmp expect actual
 '
 
+test_expect_success '--single-branch with explicit --branch with tag fetches 
updated tag despite --no-tags' '
+       (
+               cd dir_tag_no_tags && git fetch &&
+               git for-each-ref refs/tags >../actual
+       ) &&
+       git for-each-ref refs/tags/two >expect &&
+       test_cmp expect actual &&
+       test_line_count = 1 actual
+'
+
 test_expect_success '--single-branch with --mirror' '
        (
                cd dir_mirror && git fetch &&
-- 
2.11.0

Reply via email to