remote: create tags if we have them Together with include-tag, this make us behave more like git. After a fetch, try to create any tags the remote told us about for which we have objects locally.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
diff --git a/src/remote.c b/src/remote.c
index 2eb2fd1..aef5004 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -473,25 +473,36 @@ int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats
int git_remote_update_tips(git_remote *remote)
{
- int error = 0;
+ int error = 0, autotag;
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
git_oid old;
+ git_pkt *pkt;
+ git_odb *odb;
git_vector *refs;
git_remote_head *head;
git_reference *ref;
struct git_refspec *spec;
+ char *tagstr = "refs/tags/*:refs/tags/*";
+ git_refspec tagspec;
assert(remote);
- refs = &remote->refs;
+ refs = &remote->transport->refs;
spec = &remote->fetch;
if (refs->length == 0)
return 0;
+ if (git_repository_odb(&odb, remote->repo) < 0)
+ return -1;
+
+ if (git_refspec__parse(&tagspec, tagstr, true) < 0)
+ return -1;
+
/* HEAD is only allowed to be the first in the list */
- head = refs->contents[0];
+ pkt = refs->contents[0];
+ head = &((git_pkt_ref *)pkt)->head;
if (!strcmp(head->name, GIT_HEAD_FILE)) {
if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
return -1;
@@ -501,10 +512,36 @@ int git_remote_update_tips(git_remote *remote)
}
for (; i < refs->length; ++i) {
- head = refs->contents[i];
+ autotag = 0;
+ git_pkt *pkt = refs->contents[i];
- if (git_refspec_transform_r(&refname, spec, head->name) < 0)
- goto on_error;
+ if (pkt->type == GIT_PKT_REF)
+ head = &((git_pkt_ref *)pkt)->head;
+ else
+ continue;
+
+ /* Ignore malformed ref names (which also saves us from tag^{} */
+ if (!git_reference_is_valid_name(head->name))
+ continue;
+
+ if (git_refspec_src_matches(spec, head->name)) {
+ if (git_refspec_transform_r(&refname, spec, head->name) < 0)
+ goto on_error;
+ } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
+ autotag = 1;
+
+ if (!git_refspec_src_matches(&tagspec, head->name))
+ continue;
+
+ git_buf_clear(&refname);
+ if (git_buf_puts(&refname, head->name) < 0)
+ goto on_error;
+ } else {
+ continue;
+ }
+
+ if (autotag && !git_odb_exists(odb, &head->oid))
+ continue;
error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND)
@@ -516,7 +553,9 @@ int git_remote_update_tips(git_remote *remote)
if (!git_oid_cmp(&old, &head->oid))
continue;
- if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
+ /* In autotag mode, don't overwrite any locally-existing tags */
+ error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, !autotag);
+ if (error < 0 && error != GIT_EEXISTS)
goto on_error;
git_reference_free(ref);
@@ -527,10 +566,12 @@ int git_remote_update_tips(git_remote *remote)
}
}
+ git_refspec__free(&tagspec);
git_buf_free(&refname);
return 0;
on_error:
+ git_refspec__free(&tagspec);
git_buf_free(&refname);
return -1;