Skip to content

Commit 9c0c09f

Browse files
committed
Merge branch 'cn/fetch-prune'
* cn/fetch-prune: fetch: treat --tags like refs/tags/*:refs/tags/* when pruning fetch: honor the user-provided refspecs when pruning refs remote: separate out the remote_find_tracking logic into query_refspecs t5510: add tests for fetch --prune fetch: free all the additional refspecs Conflicts: remote.c
2 parents f384a2e + e8c1e6c commit 9c0c09f

File tree

5 files changed

+137
-55
lines changed

5 files changed

+137
-55
lines changed

builtin/fetch.c

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,10 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
510510
return ret;
511511
}
512512

513-
static int prune_refs(struct transport *transport, struct ref *ref_map)
513+
static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
514514
{
515515
int result = 0;
516-
struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
516+
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
517517
const char *dangling_msg = dry_run
518518
? _(" (%s will become dangling)\n")
519519
: _(" (%s has become dangling)\n");
@@ -704,8 +704,31 @@ static int do_fetch(struct transport *transport,
704704
free_refs(ref_map);
705705
return 1;
706706
}
707-
if (prune)
708-
prune_refs(transport, ref_map);
707+
if (prune) {
708+
/* If --tags was specified, pretend the user gave us the canonical tags refspec */
709+
if (tags == TAGS_SET) {
710+
const char *tags_str = "refs/tags/*:refs/tags/*";
711+
struct refspec *tags_refspec, *refspec;
712+
713+
/* Copy the refspec and add the tags to it */
714+
refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
715+
tags_refspec = parse_fetch_refspec(1, &tags_str);
716+
memcpy(refspec, refs, ref_count * sizeof(struct refspec));
717+
memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
718+
ref_count++;
719+
720+
prune_refs(refspec, ref_count, ref_map);
721+
722+
ref_count--;
723+
/* The rest of the strings belong to fetch_one */
724+
free_refspec(1, tags_refspec);
725+
free(refspec);
726+
} else if (ref_count) {
727+
prune_refs(refs, ref_count, ref_map);
728+
} else {
729+
prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
730+
}
731+
}
709732
free_refs(ref_map);
710733

711734
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -888,7 +911,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
888911
atexit(unlock_pack);
889912
refspec = parse_fetch_refspec(ref_nr, refs);
890913
exit_code = do_fetch(transport, refspec, ref_nr);
891-
free(refspec);
914+
free_refspec(ref_nr, refspec);
892915
transport_disconnect(transport);
893916
transport = NULL;
894917
return exit_code;

builtin/remote.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
349349
else
350350
string_list_append(&states->tracked, abbrev_branch(ref->name));
351351
}
352-
stale_refs = get_stale_heads(states->remote, fetch_map);
352+
stale_refs = get_stale_heads(states->remote->fetch,
353+
states->remote->fetch_refspec_nr, fetch_map);
353354
for (ref = stale_refs; ref; ref = ref->next) {
354355
struct string_list_item *item =
355356
string_list_append(&states->stale, abbrev_branch(ref->name));

remote.c

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -803,59 +803,56 @@ static int match_name_with_pattern(const char *key, const char *name,
803803
return ret;
804804
}
805805

806-
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
807-
const char *name)
806+
static int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
808807
{
809808
int i;
810-
char *ret = NULL;
811-
for (i = 0; i < nr_refspec; i++) {
812-
struct refspec *refspec = refspecs + i;
813-
if (refspec->pattern) {
814-
if (match_name_with_pattern(refspec->src, name,
815-
refspec->dst, &ret))
816-
return ret;
817-
} else if (!strcmp(refspec->src, name))
818-
return xstrdup(refspec->dst);
819-
}
820-
return NULL;
821-
}
809+
int find_src = !query->src;
822810

823-
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
824-
{
825-
int find_src = refspec->src == NULL;
826-
char *needle, **result;
827-
int i;
811+
if (find_src && !query->dst)
812+
return error("query_refspecs: need either src or dst");
828813

829-
if (find_src) {
830-
if (!refspec->dst)
831-
return error("find_tracking: need either src or dst");
832-
needle = refspec->dst;
833-
result = &refspec->src;
834-
} else {
835-
needle = refspec->src;
836-
result = &refspec->dst;
837-
}
814+
for (i = 0; i < ref_count; i++) {
815+
struct refspec *refspec = &refs[i];
816+
const char *key = find_src ? refspec->dst : refspec->src;
817+
const char *value = find_src ? refspec->src : refspec->dst;
818+
const char *needle = find_src ? query->dst : query->src;
819+
char **result = find_src ? &query->src : &query->dst;
838820

839-
for (i = 0; i < remote->fetch_refspec_nr; i++) {
840-
struct refspec *fetch = &remote->fetch[i];
841-
const char *key = find_src ? fetch->dst : fetch->src;
842-
const char *value = find_src ? fetch->src : fetch->dst;
843-
if (!fetch->dst)
821+
if (!refspec->dst)
844822
continue;
845-
if (fetch->pattern) {
823+
if (refspec->pattern) {
846824
if (match_name_with_pattern(key, needle, value, result)) {
847-
refspec->force = fetch->force;
825+
query->force = refspec->force;
848826
return 0;
849827
}
850828
} else if (!strcmp(needle, key)) {
851829
*result = xstrdup(value);
852-
refspec->force = fetch->force;
830+
query->force = refspec->force;
853831
return 0;
854832
}
855833
}
856834
return -1;
857835
}
858836

837+
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
838+
const char *name)
839+
{
840+
struct refspec query;
841+
842+
memset(&query, 0, sizeof(struct refspec));
843+
query.src = (char *)name;
844+
845+
if (query_refspecs(refspecs, nr_refspec, &query))
846+
return NULL;
847+
848+
return query.dst;
849+
}
850+
851+
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
852+
{
853+
return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
854+
}
855+
859856
static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
860857
const char *name)
861858
{
@@ -1659,36 +1656,47 @@ struct ref *guess_remote_head(const struct ref *head,
16591656
}
16601657

16611658
struct stale_heads_info {
1662-
struct remote *remote;
16631659
struct string_list *ref_names;
16641660
struct ref **stale_refs_tail;
1661+
struct refspec *refs;
1662+
int ref_count;
16651663
};
16661664

16671665
static int get_stale_heads_cb(const char *refname,
16681666
const unsigned char *sha1, int flags, void *cb_data)
16691667
{
16701668
struct stale_heads_info *info = cb_data;
1671-
struct refspec refspec;
1672-
memset(&refspec, 0, sizeof(refspec));
1673-
refspec.dst = (char *)refname;
1674-
if (!remote_find_tracking(info->remote, &refspec)) {
1675-
if (!((flags & REF_ISSYMREF) ||
1676-
string_list_has_string(info->ref_names, refspec.src))) {
1677-
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
1678-
hashcpy(ref->new_sha1, sha1);
1679-
}
1669+
struct refspec query;
1670+
memset(&query, 0, sizeof(struct refspec));
1671+
query.dst = (char *)refname;
1672+
1673+
if (query_refspecs(info->refs, info->ref_count, &query))
1674+
return 0; /* No matches */
1675+
1676+
/*
1677+
* If we did find a suitable refspec and it's not a symref and
1678+
* it's not in the list of refs that currently exist in that
1679+
* remote we consider it to be stale.
1680+
*/
1681+
if (!((flags & REF_ISSYMREF) ||
1682+
string_list_has_string(info->ref_names, query.src))) {
1683+
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
1684+
hashcpy(ref->new_sha1, sha1);
16801685
}
1686+
1687+
free(query.src);
16811688
return 0;
16821689
}
16831690

1684-
struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
1691+
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
16851692
{
16861693
struct ref *ref, *stale_refs = NULL;
16871694
struct string_list ref_names = STRING_LIST_INIT_NODUP;
16881695
struct stale_heads_info info;
1689-
info.remote = remote;
16901696
info.ref_names = &ref_names;
16911697
info.stale_refs_tail = &stale_refs;
1698+
info.refs = refs;
1699+
info.ref_count = ref_count;
16921700
for (ref = fetch_map; ref; ref = ref->next)
16931701
string_list_append(&ref_names, ref->name);
16941702
sort_string_list(&ref_names);

remote.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,6 @@ struct ref *guess_remote_head(const struct ref *head,
164164
int all);
165165

166166
/* Return refs which no longer exist on remote */
167-
struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
167+
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);
168168

169169
#endif

t/t5510-fetch.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,56 @@ test_expect_success "fetch test for-merge" '
7676
cut -f -2 .git/FETCH_HEAD >actual &&
7777
test_cmp expected actual'
7878

79+
test_expect_success 'fetch --prune on its own works as expected' '
80+
cd "$D" &&
81+
git clone . prune &&
82+
cd prune &&
83+
git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
84+
85+
git fetch --prune origin &&
86+
test_must_fail git rev-parse origin/extrabranch
87+
'
88+
89+
test_expect_success 'fetch --prune with a branch name keeps branches' '
90+
cd "$D" &&
91+
git clone . prune-branch &&
92+
cd prune-branch &&
93+
git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
94+
95+
git fetch --prune origin master &&
96+
git rev-parse origin/extrabranch
97+
'
98+
99+
test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
100+
cd "$D" &&
101+
git clone . prune-namespace &&
102+
cd prune-namespace &&
103+
104+
git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* &&
105+
git rev-parse origin/master
106+
'
107+
108+
test_expect_success 'fetch --prune --tags does not delete the remote-tracking branches' '
109+
cd "$D" &&
110+
git clone . prune-tags &&
111+
cd prune-tags &&
112+
git fetch origin refs/heads/master:refs/tags/sometag &&
113+
114+
git fetch --prune --tags origin &&
115+
git rev-parse origin/master &&
116+
test_must_fail git rev-parse somebranch
117+
'
118+
119+
test_expect_success 'fetch --prune --tags with branch does not delete other remote-tracking branches' '
120+
cd "$D" &&
121+
git clone . prune-tags-branch &&
122+
cd prune-tags-branch &&
123+
git fetch origin refs/heads/master:refs/remotes/origin/extrabranch &&
124+
125+
git fetch --prune --tags origin master &&
126+
git rev-parse origin/extrabranch
127+
'
128+
79129
test_expect_success 'fetch tags when there is no tags' '
80130
81131
cd "$D" &&

0 commit comments

Comments
 (0)