Tags are auto-followed when no refspec stores anything (git fetch origin main) · Issue #7436 · libgit2/libgit2 · GitHub
Skip to content

Tags are auto-followed when no refspec stores anything (git fetch origin main) #7436

Open

Description

@odrobnik

git follows tags only when the fetch stores something. get_ref_map enables tag auto-following only if one of the refspecs (given, or configured) has a destination. So git fetch origin main writes FETCH_HEAD and creates no tags, and neither does a fetch of a URL's HEAD.

With the default GIT_REMOTE_DOWNLOAD_TAGS_AUTO, libgit2 follows tags whatever the refspecs are. git_remote_fetch(remote, ["main"], …) creates every tag that points into the fetched history as a local tag.

This is the opposite case from #3870 (tags git follows that libgit2 misses).

Reproduction steps

fetchrepro.c is the program from #7435. With refspec arguments it calls git_remote_fetch(remote, &refspecs, &opts, NULL).

fetchrepro.c
/*
 * Fetch from "origin" as `git fetch [--prune] origin [<refspec>...]`
 * would: git_remote_fetch() with the given refspecs, or with NULL (the
 * remote's configured refspecs) when there are none.
 *
 * usage: fetchrepro <repository> [--prune] [<refspec>...]
 */
#include <stdio.h>
#include <string.h>
#include <git2.h>

static int check(int error, const char *what)
{
	if (error < 0) {
		const git_error *e = git_error_last();
		fprintf(stderr, "%s: %s\n", what, e ? e->message : "unknown error");
		return 1;
	}
	return 0;
}

int main(int argc, char **argv)
{
	git_repository *repo = NULL;
	git_remote *remote = NULL;
	git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
	git_strarray refspecs = { NULL, 0 };
	int first = 2, failed;

	if (argc < 2) {
		fprintf(stderr, "usage: %s <repository> [--prune] [<refspec>...]\n", argv[0]);
		return 2;
	}
	if (argc > 2 && !strcmp(argv[2], "--prune")) {
		opts.prune = GIT_FETCH_PRUNE;
		first = 3;
	}
	refspecs.strings = argv + first;
	refspecs.count = (size_t)(argc - first);

	git_libgit2_init();
	failed = check(git_repository_open(&repo, argv[1]), "open") ||
		check(git_remote_lookup(&remote, repo, "origin"), "lookup") ||
		check(git_remote_fetch(remote, refspecs.count ? &refspecs : NULL, &opts, NULL), "fetch");

	git_remote_free(remote);
	git_repository_free(repo);
	git_libgit2_shutdown();
	return failed;
}
#!/bin/sh
# Tag auto-following in libgit2: `git fetch origin main` vs
# git_remote_fetch(remote, ["main"]) (./fetchrepro) on identical clones.
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 clone -q src git
git clone -q src libgit2
git -C src commit -q --allow-empty -m two
git -C src tag v1                 # points at the commit being fetched

git -C git fetch -q origin main   # FETCH_HEAD only: no refspec stores anything
$FETCH libgit2 main
for r in git libgit2; do
    echo "--- $r: tags"
    git -C $r tag -l
done

Expected behavior

As with git 2.54, no tags are created: the fetch only updates FETCH_HEAD.

Actual behavior

--- git: tags
--- libgit2: tags
v1

libgit2 creates refs/tags/v1.

Version of libgit2 (release number or SHA1)

v1.9.4. The tag handling (update_one_tip's autotag path, download_tags) is unchanged in v1.9.7 and on main (42a89d7).

Operating system(s) tested

macOS 27 (arm64)


For reference, GitKit works around this (Cocoanetics/GitKit#46). When the remote's tag mode is AUTO and none of the fetch's refspecs (given, or else configured) has a destination, it sets download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE. A remote with tagOpt = --tags keeps fetching all tags, as in git.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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