libgit2/src/commit.c
Vicent Marti b60488e1d7 Added sort method for commit lists.
Fixed bug when parsing time headers from commits.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
Signed-off-by: Andreas Ericsson <ae@op5.se>
2010-06-02 10:32:06 +02:00

348 lines
7.7 KiB
C

/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <time.h>
#include "common.h"
#include "commit.h"
#include "revwalk.h"
#include "git/odb.h"
const git_oid *git_commit_id(git_commit *c)
{
return &c->object.id;
}
void git_commit__mark_uninteresting(git_commit *commit)
{
if (commit == NULL)
return;
git_commit_node *parents = commit->parents.head;
commit->uninteresting = 1;
while (parents)
{
parents->commit->uninteresting = 1;
parents = parents->next;
}
}
git_commit *git_commit_parse(git_revpool *pool, const git_oid *id)
{
git_commit *commit = NULL;
if ((commit = git_commit_lookup(pool, id)) == NULL)
return NULL;
if (git_commit_parse_existing(commit) < 0)
goto error_cleanup;
return commit;
error_cleanup:
free(commit);
return NULL;
}
int git_commit_parse_existing(git_commit *commit)
{
git_obj commit_obj;
if (commit->parsed)
return 0;
if (git_odb_read(&commit_obj, commit->object.pool->db, &commit->object.id) < 0)
return -1;
if (commit_obj.type != GIT_OBJ_COMMIT)
goto error_cleanup;
if (git_commit__parse_buffer(commit, commit_obj.data, commit_obj.len) < 0)
goto error_cleanup;
git_obj_close(&commit_obj);
return 0;
error_cleanup:
git_obj_close(&commit_obj);
return -1;
}
git_commit *git_commit_lookup(git_revpool *pool, const git_oid *id)
{
git_commit *commit = NULL;
if (pool == NULL)
return NULL;
commit = (git_commit *)git_revpool_table_lookup(pool->commits, id);
if (commit != NULL)
return commit;
commit = git__malloc(sizeof(git_commit));
if (commit == NULL)
return NULL;
memset(commit, 0x0, sizeof(git_commit));
// Initialize parent object
git_oid_cpy(&commit->object.id, id);
commit->object.pool = pool;
git_revpool_table_insert(pool->commits, (git_revpool_object *)commit);
return commit;
}
int git_commit__parse_time(time_t *commit_time, char *buffer, const char *buffer_end)
{
if (memcmp(buffer, "author ", 7) != 0)
return -1;
buffer = memchr(buffer, '\n', buffer_end - buffer);
if (buffer == 0 || ++buffer >= buffer_end)
return -1;
if (memcmp(buffer, "committer ", 10) != 0)
return -1;
buffer = memchr(buffer, '\n', buffer_end - buffer);
if (buffer == 0 || ++buffer >= buffer_end)
return -1;
*commit_time = strtol(buffer, &buffer, 10);
return (buffer < buffer_end) ? 0 : -1;
}
int git_commit__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header)
{
const size_t sha_len = GIT_OID_HEXSZ;
const size_t header_len = strlen(header);
char *buffer = *buffer_out;
if (buffer + (header_len + sha_len + 1) > buffer_end)
return -1;
if (memcmp(buffer, header, header_len) != 0)
return -1;
if (buffer[header_len + sha_len] != '\n')
return -1;
if (git_oid_mkstr(oid, buffer + header_len) < 0)
return -1;
*buffer_out = buffer + (header_len + sha_len + 1);
return 0;
}
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
{
char *buffer = (char *)data;
const char *buffer_end = (char *)data + len;
git_oid oid;
if (commit->parsed)
return 0;
if (git_commit__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
return -1;
/*
* TODO: load tree into commit object
* TODO: commit grafts!
*/
while (git_commit__parse_oid(&oid, &buffer, buffer_end, "parent ") == 0) {
git_commit *parent;
if ((parent = git_commit_lookup(commit->object.pool, &oid)) == NULL)
return -1;
// Inherit uninteresting flag
if (commit->uninteresting)
parent->uninteresting = 1;
git_commit_list_append(&commit->parents, parent);
}
if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
return -1;
commit->parsed = 1;
return 0;
}
void git_commit_list_append(git_commit_list *list, git_commit *commit)
{
git_commit_node *node = NULL;
node = git__malloc(sizeof(git_commit_list));
if (node == NULL)
return;
node->commit = commit;
node->next = NULL;
node->prev = list->tail;
if (list->tail == NULL)
{
list->head = list->tail = node;
}
else
{
list->tail->next = node;
list->tail = node;
}
list->size++;
}
git_commit *git_commit_list_pop_back(git_commit_list *list)
{
git_commit_node *node;
git_commit *commit;
if (list->tail == NULL)
return NULL;
node = list->tail;
list->tail = list->tail->prev;
if (list->tail == NULL)
list->head = NULL;
commit = node->commit;
free(node);
list->size--;
return commit;
}
git_commit *git_commit_list_pop_front(git_commit_list *list)
{
git_commit_node *node;
git_commit *commit;
if (list->head == NULL)
return NULL;
node = list->head;
list->head = list->head->next;
if (list->head == NULL)
list->tail = NULL;
commit = node->commit;
free(node);
list->size--;
return commit;
}
void git_commit_list_clear(git_commit_list *list, int free_commits)
{
git_commit_node *node, *next_node;
node = list->head;
while (node)
{
if (free_commits)
free(node->commit);
next_node = node->next;
free(node);
node = next_node;
}
list->head = list->tail = NULL;
list->size = 0;
}
void git_commit_list_sort(git_commit_list *list)
{
git_commit_node *p, *q, *e, *tail;
int in_size, p_size, q_size, merge_count, i;
if (list->head == NULL)
return;
in_size = 1;
do
{
p = list->head;
tail = NULL;
merge_count = 0;
while (p != NULL)
{
merge_count++;
q = p;
p_size = 0;
q_size = in_size;
for (i = 0; i < in_size && q; ++i, q = q->next)
p_size++;
while (p_size > 0 || (q_size > 0 && q))
{
if (p_size == 0)
e = q, q = q->next, q_size--;
else if (q_size == 0 || q == NULL ||
p->commit->commit_time <= q->commit->commit_time)
e = p, p = p->next, p_size--;
else
e = q, q = q->next, q_size--;
if (tail != NULL)
tail->next = e;
else
list->head = e;
tail = e;
}
p = q;
}
tail->next = NULL;
in_size *= 2;
} while (merge_count > 1);
}