Commit f8758044 by Vicent Marti

Add loading and parsing of tag objects

Tag objects are now properly loaded from the revision pool.
New test t0801 checks for loading a parsing a series of tags, including
the tag of a tag.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent 364788e1
#ifndef INCLUDE_git_tag_h__
#define INCLUDE_git_tag_h__
#include "common.h"
#include "oid.h"
#include "tree.h"
/**
* @file git/tag.h
* @brief Git tag parsing routines
* @defgroup git_tag Git tag management
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/** Parsed representation of a tag object. */
typedef struct git_tag git_tag;
/**
* Locate a reference to a tag without loading it.
* The generated tag object is owned by the revision
* pool and shall not be freed by the user.
*
* @param pool the pool to use when locating the tag.
* @param id identity of the tag to locate.
* @return the tag; NULL if the tag could not be created
*/
GIT_EXTERN(git_tag *) git_tag_lookup(git_revpool *pool, const git_oid *id);
/**
* Locate a reference to a tag, and try to load and parse it it from
* the object cache or the object database.
* The generated tag object is owned by the revision
* pool and shall not be freed by the user.
*
* @param pool the pool to use when parsing/caching the tag.
* @param id identity of the tag to locate.
* @return the tag; NULL if the tag does not exist in the
* pool's git_odb, or if the tag is present but is
* too malformed to be parsed successfully.
*/
GIT_EXTERN(git_tag *) git_tag_parse(git_revpool *pool, const git_oid *id);
/**
* Get the id of a tag.
* @param tag a previously loaded tag.
* @return object identity for the tag.
*/
GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag);
/**
* Get the tagged object of a tag
* @param tag a previously loaded tag.
* @return reference to a repository object
*/
GIT_EXTERN(const git_repository_object *) git_tag_target(git_tag *t);
/**
* Get the type of a tag's tagged object
* @param tag a previously loaded tag.
* @return type of the tagged object
*/
GIT_EXTERN(git_otype) git_tag_type(git_tag *t);
/**
* Get the name of a tag
* @param tag a previously loaded tag.
* @return name of the tag
*/
GIT_EXTERN(const char *) git_tag_name(git_tag *t);
/**
* Get the tagger (author) of a tag
* @param tag a previously loaded tag.
* @return reference to the tag's author
*/
GIT_EXTERN(const git_person *) git_tag_tagger(git_tag *t);
/**
* Get the message of a tag
* @param tag a previously loaded tag.
* @return message of the tag
*/
GIT_EXTERN(const char *) git_tag_message(git_tag *t);
/** @} */
GIT_END_DECL
#endif
/*
* 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 "common.h"
#include "commit.h"
#include "tag.h"
#include "revwalk.h"
#include "git/odb.h"
void git_tag__free(git_tag *tag)
{
free(tag->message);
free(tag->tag_name);
free(tag->tagger);
free(tag);
}
const git_oid *git_tag_id(git_tag *t)
{
return &t->object.id;
}
const git_revpool_object *git_tag_target(git_tag *t)
{
if (t->target)
return t->target;
git_tag__parse(t);
return t->target;
}
git_otype git_tag_type(git_tag *t)
{
if (t->type)
return t->type;
git_tag__parse(t);
return t->type;
}
const char *git_tag_name(git_tag *t)
{
if (t->tag_name)
return t->tag_name;
git_tag__parse(t);
return t->tag_name;
}
const git_person *git_tag_tagger(git_tag *t)
{
if (t->tagger)
return t->tagger;
git_tag__parse(t);
return t->tagger;
}
const char *git_tag_message(git_tag *t)
{
if (t->message)
return t->message;
git_tag__parse(t);
return t->message;
}
static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
{
static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
};
git_oid target_oid;
unsigned int i, text_len;
char *search;
if (git__parse_oid(&target_oid, &buffer, buffer_end, "object ") < 0)
return GIT_EOBJCORRUPTED;
if (buffer + 5 >= buffer_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, "type ", 5) != 0)
return GIT_EOBJCORRUPTED;
buffer += 5;
tag->type = GIT_OBJ_BAD;
for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
size_t type_length = strlen(tag_types[i]);
if (buffer + type_length >= buffer_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, tag_types[i], type_length) == 0) {
tag->type = i;
buffer += type_length;
break;
}
}
if (tag->type == GIT_OBJ_BAD)
return GIT_EOBJCORRUPTED;
/* Lookup the tagged object once we know its type */
tag->target =
git_repository_lookup(tag->object.repo, &target_oid, tag->type);
/* FIXME: is the tag file really corrupted if the tagged object
* cannot be found on the database? */
/* if (tag->target == NULL)
return GIT_EOBJCORRUPTED; */
if (buffer + 4 >= buffer_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, "tag ", 4) != 0)
return GIT_EOBJCORRUPTED;
buffer += 4;
search = memchr(buffer, '\n', buffer_end - buffer);
if (search == NULL)
return GIT_EOBJCORRUPTED;
text_len = search - buffer;
if (tag->tag_name != NULL)
free(tag->tag_name);
tag->tag_name = git__malloc(text_len + 1);
memcpy(tag->tag_name, buffer, text_len);
tag->tag_name[text_len] = '\0';
buffer = search + 1;
if (tag->tagger != NULL)
free(tag->tagger);
tag->tagger = git__malloc(sizeof(git_person));
if (git__parse_person(tag->tagger, &buffer, buffer_end, "tagger ") != 0)
return GIT_EOBJCORRUPTED;
text_len = buffer_end - buffer;
if (tag->message != NULL)
free(tag->message);
tag->message = git__malloc(text_len + 1);
memcpy(tag->message, buffer, text_len);
tag->message[text_len] = '\0';
return 0;
}
int git_tag__parse(git_tag *tag)
{
int error = 0;
git_obj odb_object;
error = git_odb_read(&odb_object, tag->object.pool->db, &tag->object.id);
if (error < 0)
return error;
if (odb_object.type != GIT_OBJ_TAG) {
error = GIT_EOBJTYPE;
goto cleanup;
}
error = parse_tag_buffer(tag, odb_object.data, odb_object.data + odb_object.len);
cleanup:
git_obj_close(&odb_object);
return error;
}
git_tag *git_tag_lookup(git_revpool *pool, const git_oid *id)
{
git_tag *tag = NULL;
if (pool == NULL)
return NULL;
tag = (git_tag *)git_revpool_table_lookup(pool->objects, id);
if (tag != NULL)
return tag;
tag = git__malloc(sizeof(git_tag));
if (tag == NULL)
return NULL;
memset(tag, 0x0, sizeof(git_tag));
/* Initialize parent object */
git_oid_cpy(&tag->object.id, id);
tag->object.pool = pool;
tag->object.type = GIT_OBJ_TAG;
git_revpool_table_insert(pool->objects, (git_revpool_object *)tag);
return tag;
}
#ifndef INCLUDE_tag_h__
#define INCLUDE_tag_h__
#include "git/tag.h"
#include "revobject.h"
struct git_tag {
git_revpool_object object;
git_revpool_object *target;
git_otype type;
char *tag_name;
git_person *tagger;
char *message;
};
void git_tag__free(git_tag *tag);
int git_tag__parse(git_tag *tag);
#endif
#include "test_lib.h"
#include "test_helpers.h"
#include "commit.h"
#include <git/odb.h>
#include <git/commit.h>
#include <git/tag.h>
static const char *odb_dir = "../resources/pack-odb";
static const char *tag1_id = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1";
static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
BEGIN_TEST(readtag)
git_odb *db;
git_repository *repo;
git_tag *tag1, *tag2;
git_commit *commit;
git_oid id1, id2, id_commit;
must_pass(git_odb_open(&db, odb_dir));
repo = git_repository_alloc(db);
must_be_true(repo != NULL);
git_oid_mkstr(&id1, tag1_id);
git_oid_mkstr(&id2, tag2_id);
git_oid_mkstr(&id_commit, tagged_commit);
tag1 = git_tag_lookup(repo, &id1);
must_be_true(tag1 != NULL);
must_be_true(strcmp(git_tag_name(tag1), "test") == 0);
must_be_true(git_tag_type(tag1) == GIT_OBJ_TAG);
tag2 = (git_tag *)git_tag_target(tag1);
must_be_true(tag2 != NULL);
must_be_true(git_oid_cmp(&id2, git_tag_id(tag2)) == 0);
commit = (git_commit *)git_tag_target(tag2);
must_be_true(commit != NULL);
must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
git_repository_free(repo);
git_odb_close(db);
END_TEST
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment