Skip to content

Commit

Permalink
fetch: honor the user-provided refspecs when pruning refs
Browse files Browse the repository at this point in the history
If the user gave us refspecs on the command line, we should use those
when deciding whether to prune a ref instead of relying on the
refspecs in the config.

Previously, running

    git fetch --prune origin refs/heads/master:refs/remotes/origin/master

would delete every other ref under the origin namespace because we
were using the refspec to filter the available refs but using the
configured refspec to figure out if a ref had been deleted on the
remote. This is clearly the wrong thing to do.

Change prune_refs and get_stale_heads to simply accept a list of
references and a list of refspecs. The caller of either function needs
to decide what refspecs should be used to decide whether a ref is
stale.

Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Carlos Martín Nieto authored and Junio C Hamano committed Oct 16, 2011
1 parent c500352 commit ed43de6
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 20 deletions.
12 changes: 8 additions & 4 deletions builtin/fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,10 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
return ret;
}

static int prune_refs(struct transport *transport, struct ref *ref_map)
static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
{
int result = 0;
struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
const char *dangling_msg = dry_run
? _(" (%s will become dangling)\n")
: _(" (%s has become dangling)\n");
Expand Down Expand Up @@ -734,8 +734,12 @@ static int do_fetch(struct transport *transport,
free_refs(ref_map);
return 1;
}
if (prune)
prune_refs(transport, ref_map);
if (prune) {
if (ref_count)
prune_refs(refs, ref_count, ref_map);
else
prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
}
free_refs(ref_map);

/* if neither --no-tags nor --tags was specified, do automated tag
Expand Down
3 changes: 2 additions & 1 deletion builtin/remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
else
string_list_append(&states->tracked, abbrev_branch(ref->name));
}
stale_refs = get_stale_heads(states->remote, fetch_map);
stale_refs = get_stale_heads(states->remote->fetch,
states->remote->fetch_refspec_nr, fetch_map);
for (ref = stale_refs; ref; ref = ref->next) {
struct string_list_item *item =
string_list_append(&states->stale, abbrev_branch(ref->name));
Expand Down
35 changes: 23 additions & 12 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -1678,36 +1678,47 @@ struct ref *guess_remote_head(const struct ref *head,
}

struct stale_heads_info {
struct remote *remote;
struct string_list *ref_names;
struct ref **stale_refs_tail;
struct refspec *refs;
int ref_count;
};

static int get_stale_heads_cb(const char *refname,
const unsigned char *sha1, int flags, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct refspec refspec;
memset(&refspec, 0, sizeof(refspec));
refspec.dst = (char *)refname;
if (!remote_find_tracking(info->remote, &refspec)) {
if (!((flags & REF_ISSYMREF) ||
string_list_has_string(info->ref_names, refspec.src))) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
hashcpy(ref->new_sha1, sha1);
}
struct refspec query;
memset(&query, 0, sizeof(struct refspec));
query.dst = (char *)refname;

if (query_refspecs(info->refs, info->ref_count, &query))
return 0; /* No matches */

/*
* If we did find a suitable refspec and it's not a symref and
* it's not in the list of refs that currently exist in that
* remote we consider it to be stale.
*/
if (!((flags & REF_ISSYMREF) ||
string_list_has_string(info->ref_names, query.src))) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
hashcpy(ref->new_sha1, sha1);
}

free(query.src);
return 0;
}

struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
{
struct ref *ref, *stale_refs = NULL;
struct string_list ref_names = STRING_LIST_INIT_NODUP;
struct stale_heads_info info;
info.remote = remote;
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
info.refs = refs;
info.ref_count = ref_count;
for (ref = fetch_map; ref; ref = ref->next)
string_list_append(&ref_names, ref->name);
sort_string_list(&ref_names);
Expand Down
2 changes: 1 addition & 1 deletion remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,6 @@ struct ref *guess_remote_head(const struct ref *head,
int all);

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

#endif
4 changes: 2 additions & 2 deletions t/t5510-fetch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ test_expect_success 'fetch --prune on its own works as expected' '
test_must_fail git rev-parse origin/extrabranch
'

test_expect_failure 'fetch --prune with a branch name keeps branches' '
test_expect_success 'fetch --prune with a branch name keeps branches' '
cd "$D" &&
git clone . prune-branch &&
cd prune-branch &&
Expand All @@ -96,7 +96,7 @@ test_expect_failure 'fetch --prune with a branch name keeps branches' '
git rev-parse origin/extrabranch
'

test_expect_failure 'fetch --prune with a namespace keeps other namespaces' '
test_expect_success 'fetch --prune with a namespace keeps other namespaces' '
cd "$D" &&
git clone . prune-namespace &&
cd prune-namespace &&
Expand Down

0 comments on commit ed43de6

Please sign in to comment.