Merge branch 'jp/fetch-cull-many-refs'
* jp/fetch-cull-many-refs: remote: fix use-after-free error detected by glibc in ref_remove_duplicates fetch: Speed up fetch of large numbers of refs remote: Make ref_remove_duplicates faster for large numbers of refs
This commit is contained in:
@ -489,7 +489,8 @@ static int add_existing(const char *refname, const unsigned char *sha1,
|
|||||||
int flag, void *cbdata)
|
int flag, void *cbdata)
|
||||||
{
|
{
|
||||||
struct string_list *list = (struct string_list *)cbdata;
|
struct string_list *list = (struct string_list *)cbdata;
|
||||||
string_list_insert(refname, list);
|
struct string_list_item *item = string_list_insert(refname, list);
|
||||||
|
item->util = (void *)sha1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,9 +616,14 @@ static void check_not_current_branch(struct ref *ref_map)
|
|||||||
static int do_fetch(struct transport *transport,
|
static int do_fetch(struct transport *transport,
|
||||||
struct refspec *refs, int ref_count)
|
struct refspec *refs, int ref_count)
|
||||||
{
|
{
|
||||||
|
struct string_list existing_refs = { NULL, 0, 0, 0 };
|
||||||
|
struct string_list_item *peer_item = NULL;
|
||||||
struct ref *ref_map;
|
struct ref *ref_map;
|
||||||
struct ref *rm;
|
struct ref *rm;
|
||||||
int autotags = (transport->remote->fetch_tags == 1);
|
int autotags = (transport->remote->fetch_tags == 1);
|
||||||
|
|
||||||
|
for_each_ref(add_existing, &existing_refs);
|
||||||
|
|
||||||
if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
|
if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
|
||||||
tags = TAGS_SET;
|
tags = TAGS_SET;
|
||||||
if (transport->remote->fetch_tags == -1)
|
if (transport->remote->fetch_tags == -1)
|
||||||
@ -640,8 +646,13 @@ static int do_fetch(struct transport *transport,
|
|||||||
check_not_current_branch(ref_map);
|
check_not_current_branch(ref_map);
|
||||||
|
|
||||||
for (rm = ref_map; rm; rm = rm->next) {
|
for (rm = ref_map; rm; rm = rm->next) {
|
||||||
if (rm->peer_ref)
|
if (rm->peer_ref) {
|
||||||
read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
|
peer_item = string_list_lookup(rm->peer_ref->name,
|
||||||
|
&existing_refs);
|
||||||
|
if (peer_item)
|
||||||
|
hashcpy(rm->peer_ref->old_sha1,
|
||||||
|
peer_item->util);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tags == TAGS_DEFAULT && autotags)
|
if (tags == TAGS_DEFAULT && autotags)
|
||||||
|
39
remote.c
39
remote.c
@ -6,6 +6,7 @@
|
|||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "dir.h"
|
#include "dir.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
#include "string-list.h"
|
||||||
|
|
||||||
static struct refspec s_tag_refspec = {
|
static struct refspec s_tag_refspec = {
|
||||||
0,
|
0,
|
||||||
@ -734,29 +735,33 @@ int for_each_remote(each_remote_fn fn, void *priv)
|
|||||||
|
|
||||||
void ref_remove_duplicates(struct ref *ref_map)
|
void ref_remove_duplicates(struct ref *ref_map)
|
||||||
{
|
{
|
||||||
struct ref **posn;
|
struct string_list refs = { NULL, 0, 0, 0 };
|
||||||
struct ref *next;
|
struct string_list_item *item = NULL;
|
||||||
for (; ref_map; ref_map = ref_map->next) {
|
struct ref *prev = NULL, *next = NULL;
|
||||||
|
for (; ref_map; prev = ref_map, ref_map = next) {
|
||||||
|
next = ref_map->next;
|
||||||
if (!ref_map->peer_ref)
|
if (!ref_map->peer_ref)
|
||||||
continue;
|
continue;
|
||||||
posn = &ref_map->next;
|
|
||||||
while (*posn) {
|
item = string_list_lookup(ref_map->peer_ref->name, &refs);
|
||||||
if ((*posn)->peer_ref &&
|
if (item) {
|
||||||
!strcmp((*posn)->peer_ref->name,
|
if (strcmp(((struct ref *)item->util)->name,
|
||||||
ref_map->peer_ref->name)) {
|
ref_map->name))
|
||||||
if (strcmp((*posn)->name, ref_map->name))
|
|
||||||
die("%s tracks both %s and %s",
|
die("%s tracks both %s and %s",
|
||||||
ref_map->peer_ref->name,
|
ref_map->peer_ref->name,
|
||||||
(*posn)->name, ref_map->name);
|
((struct ref *)item->util)->name,
|
||||||
next = (*posn)->next;
|
ref_map->name);
|
||||||
free((*posn)->peer_ref);
|
prev->next = ref_map->next;
|
||||||
free(*posn);
|
free(ref_map->peer_ref);
|
||||||
*posn = next;
|
free(ref_map);
|
||||||
} else {
|
ref_map = prev; /* skip this; we freed it */
|
||||||
posn = &(*posn)->next;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item = string_list_insert(ref_map->peer_ref->name, &refs);
|
||||||
|
item->util = ref_map;
|
||||||
}
|
}
|
||||||
|
string_list_clear(&refs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int remote_has_url(struct remote *remote, const char *url)
|
int remote_has_url(struct remote *remote, const char *url)
|
||||||
|
@ -341,4 +341,15 @@ test_expect_success 'fetch into the current branch with --update-head-ok' '
|
|||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success "should be able to fetch with duplicate refspecs" '
|
||||||
|
mkdir dups &&
|
||||||
|
cd dups &&
|
||||||
|
git init &&
|
||||||
|
git config branch.master.remote three &&
|
||||||
|
git config remote.three.url ../three/.git &&
|
||||||
|
git config remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
|
||||||
|
git config --add remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
|
||||||
|
git fetch three
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Reference in New Issue
Block a user