Explicit refspec for a tag is ignored with GIT_REMOTE_DOWNLOAD_TAGS_AUTO (refs/tags/v1:refs/custom/x never updated) · Issue #7438 · libgit2/libgit2 · GitHub
Skip to content

Explicit refspec for a tag is ignored with GIT_REMOTE_DOWNLOAD_TAGS_AUTO (refs/tags/v1:refs/custom/x never updated) #7438

Open

Description

@odrobnik

An explicit fetch refspec for a tag is ignored under the default GIT_REMOTE_DOWNLOAD_TAGS_AUTO. git_remote_fetch(remote, ["refs/tags/v1:refs/custom/x"], …) never creates or updates refs/custom/x, while git fetch origin refs/tags/v1:refs/custom/x does.

update_one_tip first checks the tag auto-follow refspec. For any refs/tags/* head in AUTO mode it sets autotag = true and refname = head->name, and the refspec being applied is then only consulted if (!autotag && …). So the explicit mapping never gets a look:

  • the head is handled as an auto-followed tag instead;
  • that only ever creates refs/tags/v1 itself (git_reference_create(…, !autotag), never forced), and does nothing if the tag exists.

In git, an explicit refspec takes the ref wherever it says: get_fetch_map puts refs/custom/x in the ref map, and tag following only adds tags on top of that. With GIT_REMOTE_DOWNLOAD_TAGS_NONE, libgit2 does honour the refspec. The same applies to +refs/tags/*:refs/tags/* given explicitly: it should update tags with force, but AUTO mode treats them as followed and never overwrites them.

Reproduction steps

fetchrepro.c from #7435/#7436 (git_remote_fetch with the given refspecs), and:

#!/bin/sh
# An explicit refspec mapping a tag to another namespace:
# `git fetch origin refs/tags/v1:refs/custom/x` vs
# git_remote_fetch(remote, ["refs/tags/v1:refs/custom/x"]) (./fetchrepro).
set -e
FETCH=${FETCH:-./fetchrepro}
export GIT_CONFIG_GLOBAL=/dev/null GIT_AUTHOR_NAME=a GIT_AUTHOR_EMAIL=a@b \
       GIT_COMMITTER_NAME=a GIT_COMMITTER_EMAIL=a@b
rm -rf src git libgit2

git init -q -b main src
git -C src commit -q --allow-empty -m one
git -C src tag -a -m one v1
git clone -q src git
git clone -q src libgit2           # both now have the tag v1 locally

git -C git fetch -q origin refs/tags/v1:refs/custom/x
$FETCH libgit2 refs/tags/v1:refs/custom/x
for r in git libgit2; do
    echo "--- $r: refs/custom"
    git -C $r for-each-ref --format='%(refname)' refs/custom
done

Expected behavior

As with git 2.54, refs/custom/x is created, pointing at the tag v1.

Actual behavior

--- git: refs/custom
refs/custom/x
--- libgit2: refs/custom

refs/custom/x is never created. If the clone didn't have the tag yet, libgit2 creates refs/tags/v1 instead.

Version of libgit2 (release number or SHA1)

v1.9.4. update_one_tip is unchanged in v1.9.7 and on main (42a89d7).

Operating system(s) tested

macOS 27 (arm64)


A possible fix is to apply the tag auto-follow shortcut only when the refspec being processed doesn't itself map the tag. That means checking git_refspec_src_matches(spec, head->name) with a destination before treating the head as an auto-followed tag. GitKit works around it that way in its remote.c shim.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions