Improve the safety check used in fetch.c
The recent safety check to trust only the commits we have made
things impossibly slow and turn out to waste a lot of memory.
This commit fixes it with the following improvements:
- mark already scanned objects and avoid rescanning the same
object again;
- free the tree entries when we have scanned the tree entries;
this is the same as b0d8923ec0
which reduced memory usage by rev-list;
- plug memory leak from the object_list dequeuing code;
- use the process_queue not just for fetching but for scanning,
to make things tail recursive to avoid deep recursion; the
deep recursion was especially prominent when we cloned a big
pack.
- avoid has_sha1_file() call when we already know we do not have
that object.
Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
76
fetch.c
76
fetch.c
@ -33,36 +33,33 @@ static void report_missing(const char *what, const unsigned char *missing)
|
|||||||
what, missing_hex, sha1_to_hex(current_commit_sha1));
|
what, missing_hex, sha1_to_hex(current_commit_sha1));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int make_sure_we_have_it(const char *what, unsigned char *sha1)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
if (!has_sha1_file(sha1)) {
|
|
||||||
status = fetch(sha1);
|
|
||||||
if (status && what)
|
|
||||||
report_missing(what, sha1);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process(unsigned char *sha1, const char *type);
|
static int process(unsigned char *sha1, const char *type);
|
||||||
|
|
||||||
static int process_tree(struct tree *tree)
|
static int process_tree(struct tree *tree)
|
||||||
{
|
{
|
||||||
struct tree_entry_list *entries;
|
struct tree_entry_list *entry;
|
||||||
|
|
||||||
if (parse_tree(tree))
|
if (parse_tree(tree))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (entries = tree->entries; entries; entries = entries->next) {
|
entry = tree->entries;
|
||||||
if (process(entries->item.any->sha1,
|
tree->entries = NULL;
|
||||||
entries->directory ? tree_type : blob_type))
|
while (entry) {
|
||||||
|
struct tree_entry_list *next = entry->next;
|
||||||
|
if (process(entry->item.any->sha1,
|
||||||
|
entry->directory ? tree_type : blob_type))
|
||||||
return -1;
|
return -1;
|
||||||
|
free(entry);
|
||||||
|
entry = next;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COMPLETE 1U
|
#define COMPLETE 1U
|
||||||
|
#define TO_FETCH 2U
|
||||||
|
#define TO_SCAN 4U
|
||||||
|
#define SCANNED 8U
|
||||||
|
|
||||||
static struct commit_list *complete = NULL;
|
static struct commit_list *complete = NULL;
|
||||||
|
|
||||||
static int process_commit(struct commit *commit)
|
static int process_commit(struct commit *commit)
|
||||||
@ -73,13 +70,14 @@ static int process_commit(struct commit *commit)
|
|||||||
while (complete && complete->item->date >= commit->date) {
|
while (complete && complete->item->date >= commit->date) {
|
||||||
pop_most_recent_commit(&complete, COMPLETE);
|
pop_most_recent_commit(&complete, COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (commit->object.flags & COMPLETE)
|
if (commit->object.flags & COMPLETE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memcpy(current_commit_sha1, commit->object.sha1, 20);
|
memcpy(current_commit_sha1, commit->object.sha1, 20);
|
||||||
|
|
||||||
|
pull_say("walk %s\n", sha1_to_hex(commit->object.sha1));
|
||||||
|
|
||||||
if (get_tree) {
|
if (get_tree) {
|
||||||
if (process(commit->tree->object.sha1, tree_type))
|
if (process(commit->tree->object.sha1, tree_type))
|
||||||
return -1;
|
return -1;
|
||||||
@ -89,8 +87,7 @@ static int process_commit(struct commit *commit)
|
|||||||
if (get_history) {
|
if (get_history) {
|
||||||
struct commit_list *parents = commit->parents;
|
struct commit_list *parents = commit->parents;
|
||||||
for (; parents; parents = parents->next) {
|
for (; parents; parents = parents->next) {
|
||||||
if (process(parents->item->object.sha1,
|
if (process(parents->item->object.sha1, commit_type))
|
||||||
commit_type))
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +106,10 @@ static struct object_list **process_queue_end = &process_queue;
|
|||||||
|
|
||||||
static int process_object(struct object *obj)
|
static int process_object(struct object *obj)
|
||||||
{
|
{
|
||||||
|
if (obj->flags & SCANNED)
|
||||||
|
return 0;
|
||||||
|
obj->flags |= SCANNED;
|
||||||
|
|
||||||
if (obj->type == commit_type) {
|
if (obj->type == commit_type) {
|
||||||
if (process_commit((struct commit *)obj))
|
if (process_commit((struct commit *)obj))
|
||||||
return -1;
|
return -1;
|
||||||
@ -139,14 +140,19 @@ static int process(unsigned char *sha1, const char *type)
|
|||||||
if (has_sha1_file(sha1)) {
|
if (has_sha1_file(sha1)) {
|
||||||
parse_object(sha1);
|
parse_object(sha1);
|
||||||
/* We already have it, so we should scan it now. */
|
/* We already have it, so we should scan it now. */
|
||||||
return process_object(obj);
|
if (obj->flags & (SCANNED | TO_SCAN))
|
||||||
|
return 0;
|
||||||
|
object_list_insert(obj, process_queue_end);
|
||||||
|
process_queue_end = &(*process_queue_end)->next;
|
||||||
|
obj->flags |= TO_SCAN;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
if (object_list_contains(process_queue, obj))
|
if (obj->flags & (COMPLETE | TO_FETCH))
|
||||||
return 0;
|
return 0;
|
||||||
object_list_insert(obj, process_queue_end);
|
object_list_insert(obj, process_queue_end);
|
||||||
process_queue_end = &(*process_queue_end)->next;
|
process_queue_end = &(*process_queue_end)->next;
|
||||||
|
obj->flags |= TO_FETCH;
|
||||||
|
|
||||||
//fprintf(stderr, "prefetch %s\n", sha1_to_hex(sha1));
|
|
||||||
prefetch(sha1);
|
prefetch(sha1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -154,21 +160,27 @@ static int process(unsigned char *sha1, const char *type)
|
|||||||
|
|
||||||
static int loop(void)
|
static int loop(void)
|
||||||
{
|
{
|
||||||
|
struct object_list *elem;
|
||||||
|
|
||||||
while (process_queue) {
|
while (process_queue) {
|
||||||
struct object *obj = process_queue->item;
|
struct object *obj = process_queue->item;
|
||||||
/*
|
elem = process_queue;
|
||||||
fprintf(stderr, "%d objects to pull\n",
|
process_queue = elem->next;
|
||||||
object_list_length(process_queue));
|
free(elem);
|
||||||
*/
|
|
||||||
process_queue = process_queue->next;
|
|
||||||
if (!process_queue)
|
if (!process_queue)
|
||||||
process_queue_end = &process_queue;
|
process_queue_end = &process_queue;
|
||||||
|
|
||||||
//fprintf(stderr, "fetch %s\n", sha1_to_hex(obj->sha1));
|
/* If we are not scanning this object, we placed it in
|
||||||
|
* the queue because we needed to fetch it first.
|
||||||
if (make_sure_we_have_it(obj->type ? obj->type : "object",
|
*/
|
||||||
obj->sha1))
|
if (! (obj->flags & TO_SCAN)) {
|
||||||
return -1;
|
if (fetch(obj->sha1)) {
|
||||||
|
report_missing(obj->type
|
||||||
|
? obj->type
|
||||||
|
: "object", obj->sha1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!obj->type)
|
if (!obj->type)
|
||||||
parse_object(obj->sha1);
|
parse_object(obj->sha1);
|
||||||
if (process_object(obj))
|
if (process_object(obj))
|
||||||
|
Reference in New Issue
Block a user