mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-12 10:05:57 +00:00

I accidentally wrote a separate priority queue implementation when I was working on file rename detection as part of the file hash signature calculation code. To simplify licensing terms, I just adapted that to a general purpose priority queue and replace the old priority queue implementation that was borrowed from elsewhere. This also removes parts of the COPYING document that no longer apply to libgit2.
201 lines
4.8 KiB
C
201 lines
4.8 KiB
C
/*
|
|
* Copyright (C) the libgit2 contributors. All rights reserved.
|
|
*
|
|
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
|
* a Linking Exception. For full terms see the included COPYING file.
|
|
*/
|
|
|
|
#include "commit_list.h"
|
|
#include "common.h"
|
|
#include "revwalk.h"
|
|
#include "pool.h"
|
|
#include "odb.h"
|
|
|
|
int git_commit_list_time_cmp(const void *a, const void *b)
|
|
{
|
|
const git_commit_list_node *commit_a = a;
|
|
const git_commit_list_node *commit_b = b;
|
|
|
|
return (commit_a->time < commit_b->time);
|
|
}
|
|
|
|
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
|
|
{
|
|
git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
|
|
if (new_list != NULL) {
|
|
new_list->item = item;
|
|
new_list->next = *list_p;
|
|
}
|
|
*list_p = new_list;
|
|
return new_list;
|
|
}
|
|
|
|
git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p)
|
|
{
|
|
git_commit_list **pp = list_p;
|
|
git_commit_list *p;
|
|
|
|
while ((p = *pp) != NULL) {
|
|
if (git_commit_list_time_cmp(p->item, item) > 0)
|
|
break;
|
|
|
|
pp = &p->next;
|
|
}
|
|
|
|
return git_commit_list_insert(item, pp);
|
|
}
|
|
|
|
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
|
|
{
|
|
return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
|
|
}
|
|
|
|
static int commit_error(git_commit_list_node *commit, const char *msg)
|
|
{
|
|
char commit_oid[GIT_OID_HEXSZ + 1];
|
|
git_oid_fmt(commit_oid, &commit->oid);
|
|
commit_oid[GIT_OID_HEXSZ] = '\0';
|
|
|
|
giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static git_commit_list_node **alloc_parents(
|
|
git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
|
|
{
|
|
if (n_parents <= PARENTS_PER_COMMIT)
|
|
return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
|
|
|
|
return (git_commit_list_node **)git_pool_malloc(
|
|
&walk->commit_pool, (uint32_t)(n_parents * sizeof(git_commit_list_node *)));
|
|
}
|
|
|
|
|
|
void git_commit_list_free(git_commit_list **list_p)
|
|
{
|
|
git_commit_list *list = *list_p;
|
|
|
|
if (list == NULL)
|
|
return;
|
|
|
|
while (list) {
|
|
git_commit_list *temp = list;
|
|
list = temp->next;
|
|
git__free(temp);
|
|
}
|
|
|
|
*list_p = NULL;
|
|
}
|
|
|
|
git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
|
|
{
|
|
git_commit_list *top = *stack;
|
|
git_commit_list_node *item = top ? top->item : NULL;
|
|
|
|
if (top) {
|
|
*stack = top->next;
|
|
git__free(top);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
static int commit_quick_parse(
|
|
git_revwalk *walk,
|
|
git_commit_list_node *commit,
|
|
const uint8_t *buffer,
|
|
size_t buffer_len)
|
|
{
|
|
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
|
|
const uint8_t *buffer_end = buffer + buffer_len;
|
|
const uint8_t *parents_start, *committer_start;
|
|
int i, parents = 0;
|
|
int commit_time;
|
|
|
|
buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
|
|
|
|
parents_start = buffer;
|
|
while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) {
|
|
parents++;
|
|
buffer += parent_len;
|
|
}
|
|
|
|
commit->parents = alloc_parents(walk, commit, parents);
|
|
GITERR_CHECK_ALLOC(commit->parents);
|
|
|
|
buffer = parents_start;
|
|
for (i = 0; i < parents; ++i) {
|
|
git_oid oid;
|
|
|
|
if (git_oid_fromstr(&oid, (const char *)buffer + strlen("parent ")) < 0)
|
|
return -1;
|
|
|
|
commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
|
|
if (commit->parents[i] == NULL)
|
|
return -1;
|
|
|
|
buffer += parent_len;
|
|
}
|
|
|
|
commit->out_degree = (unsigned short)parents;
|
|
|
|
if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
|
|
return commit_error(commit, "object is corrupted");
|
|
|
|
buffer++;
|
|
|
|
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
|
|
return commit_error(commit, "object is corrupted");
|
|
|
|
/* Skip trailing spaces */
|
|
while (buffer > committer_start && git__isspace(*buffer))
|
|
buffer--;
|
|
|
|
/* Seek for the begining of the pack of digits */
|
|
while (buffer > committer_start && git__isdigit(*buffer))
|
|
buffer--;
|
|
|
|
/* Skip potential timezone offset */
|
|
if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) {
|
|
buffer--;
|
|
|
|
while (buffer > committer_start && git__isspace(*buffer))
|
|
buffer--;
|
|
|
|
while (buffer > committer_start && git__isdigit(*buffer))
|
|
buffer--;
|
|
}
|
|
|
|
if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
|
|
return commit_error(commit, "cannot parse commit time");
|
|
|
|
commit->time = (time_t)commit_time;
|
|
commit->parsed = 1;
|
|
return 0;
|
|
}
|
|
|
|
int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
|
|
{
|
|
git_odb_object *obj;
|
|
int error;
|
|
|
|
if (commit->parsed)
|
|
return 0;
|
|
|
|
if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
|
|
return error;
|
|
|
|
if (obj->cached.type != GIT_OBJ_COMMIT) {
|
|
giterr_set(GITERR_INVALID, "Object is no commit object");
|
|
error = -1;
|
|
} else
|
|
error = commit_quick_parse(
|
|
walk, commit,
|
|
(const uint8_t *)git_odb_object_data(obj),
|
|
git_odb_object_size(obj));
|
|
|
|
git_odb_object_free(obj);
|
|
return error;
|
|
}
|
|
|