On Thu, Apr 27, 2017 at 7:54 PM, Stefan Beller <sbel...@google.com> wrote:
> On Wed, Apr 26, 2017 at 4:12 PM, Ævar Arnfjörð Bjarmason
> <ava...@gmail.com> wrote:
>> 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 = 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 main
>> --single-branch would have retrieved, 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>
>
> I like the option, though I dislike the implementation, specifically as you
> brought up e.g. "[PATCH] various: disallow --no-no-OPT for --no-opt options".
>
> Can we have an option "--tags" instead, which is on by default
> and then you can negate it to --no-tags, without having to worry
> about the no-no case.
>
> The problem with tags is that they are in a shared name-space
> and not part of the remote refspec. If they were, the documentation
> would be way easier, too going this way.

I don't mind doing it this way, but this really should be part of an
unrelated topic to eliminate --no-OPT in favor of just --OPT which is
supplied by default, if we want to go that route.

Right now it makes more sense to have a --no-tags, because this along
with e.g. --no-hardlinks and numerous other options[1] is for an
obscure use-case most users won't care about, and then we have &
document --no-BEHAVIOR.

Perhaps we shouldn't have that at all, go through all these --no-OPT
and replace them with --OPT in both the docs & code & get rid of the
--no-no-OPT & some confusion that way.

1. git grep -- '\[--no-' Documentation/git-*txt

>> +--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 and
>> +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.
>
> In the future we may want to have '--depth' also imply --no-tags,
> just as it implies --single-branch.

Are there some cases where we'd like to also somehow import & rewrite
tags covering the given --depth?

>> @@ -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)
>
> I am debating if this needs to be an || instead of &&, as you would not
> want to write the tags with "--no-single-branch --no-tags" ?

This hunk is confusing out of context, but I believe it's doing the
right thing now. This write_followtags() codepath is only taken (and
is only meaningful) with --single-branch, there's an earlier change
just before that:

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

Which ensures that with --no-single-branch --no-tags that tags aren't
written, and there's tests for that.

Reply via email to