Blame src/describe.c

Packit Service 20376f
/*
Packit Service 20376f
 * Copyright (C) the libgit2 contributors. All rights reserved.
Packit Service 20376f
 *
Packit Service 20376f
 * This file is part of libgit2, distributed under the GNU GPL v2 with
Packit Service 20376f
 * a Linking Exception. For full terms see the included COPYING file.
Packit Service 20376f
 */
Packit Service 20376f
#include "git2/describe.h"
Packit Service 20376f
#include "git2/strarray.h"
Packit Service 20376f
#include "git2/diff.h"
Packit Service 20376f
#include "git2/status.h"
Packit Service 20376f
Packit Service 20376f
#include "common.h"
Packit Service 20376f
#include "commit.h"
Packit Service 20376f
#include "commit_list.h"
Packit Service 20376f
#include "oidmap.h"
Packit Service 20376f
#include "refs.h"
Packit Service 20376f
#include "revwalk.h"
Packit Service 20376f
#include "tag.h"
Packit Service 20376f
#include "vector.h"
Packit Service 20376f
#include "repository.h"
Packit Service 20376f
Packit Service 20376f
/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
Packit Service 20376f
Packit Service 20376f
struct commit_name {
Packit Service 20376f
	git_tag *tag;
Packit Service 20376f
	unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
Packit Service 20376f
	unsigned name_checked:1;
Packit Service 20376f
	git_oid sha1;
Packit Service 20376f
	char *path;
Packit Service 20376f
Packit Service 20376f
	/* Khash workaround. They original key has to still be reachable */
Packit Service 20376f
	git_oid peeled;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
Packit Service 20376f
{
Packit Service 20376f
	khint_t pos = git_oidmap_lookup_index(map, key);
Packit Service 20376f
Packit Service 20376f
	if (!git_oidmap_valid_index(map, pos))
Packit Service 20376f
		return NULL;
Packit Service 20376f
Packit Service 20376f
	return git_oidmap_value_at(map, pos);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static struct commit_name *find_commit_name(
Packit Service 20376f
	git_oidmap *names,
Packit Service 20376f
	const git_oid *peeled)
Packit Service 20376f
{
Packit Service 20376f
	return (struct commit_name *)(oidmap_value_bykey(names, peeled));
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int replace_name(
Packit Service 20376f
	git_tag **tag,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	struct commit_name *e,
Packit Service 20376f
	unsigned int prio,
Packit Service 20376f
	const git_oid *sha1)
Packit Service 20376f
{
Packit Service 20376f
	git_time_t e_time = 0, t_time = 0;
Packit Service 20376f
Packit Service 20376f
	if (!e || e->prio < prio)
Packit Service 20376f
		return 1;
Packit Service 20376f
Packit Service 20376f
	if (e->prio == 2 && prio == 2) {
Packit Service 20376f
		/* Multiple annotated tags point to the same commit.
Packit Service 20376f
		 * Select one to keep based upon their tagger date.
Packit Service 20376f
		 */
Packit Service 20376f
		git_tag *t = NULL;
Packit Service 20376f
Packit Service 20376f
		if (!e->tag) {
Packit Service 20376f
			if (git_tag_lookup(&t, repo, &e->sha1) < 0)
Packit Service 20376f
				return 1;
Packit Service 20376f
			e->tag = t;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (git_tag_lookup(&t, repo, sha1) < 0)
Packit Service 20376f
			return 0;
Packit Service 20376f
Packit Service 20376f
		*tag = t;
Packit Service 20376f
Packit Service 20376f
		if (e->tag->tagger)
Packit Service 20376f
			e_time = e->tag->tagger->when.time;
Packit Service 20376f
Packit Service 20376f
		if (t->tagger)
Packit Service 20376f
			t_time = t->tagger->when.time;
Packit Service 20376f
Packit Service 20376f
		if (e_time < t_time)
Packit Service 20376f
			return 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int add_to_known_names(
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_oidmap *names,
Packit Service 20376f
	const char *path,
Packit Service 20376f
	const git_oid *peeled,
Packit Service 20376f
	unsigned int prio,
Packit Service 20376f
	const git_oid *sha1)
Packit Service 20376f
{
Packit Service 20376f
	struct commit_name *e = find_commit_name(names, peeled);
Packit Service 20376f
	bool found = (e != NULL);
Packit Service 20376f
Packit Service 20376f
	git_tag *tag = NULL;
Packit Service 20376f
	if (replace_name(&tag, repo, e, prio, sha1)) {
Packit Service 20376f
		if (!found) {
Packit Service 20376f
			e = git__malloc(sizeof(struct commit_name));
Packit Service 20376f
			GITERR_CHECK_ALLOC(e);
Packit Service 20376f
Packit Service 20376f
			e->path = NULL;
Packit Service 20376f
			e->tag = NULL;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (e->tag)
Packit Service 20376f
			git_tag_free(e->tag);
Packit Service 20376f
		e->tag = tag;
Packit Service 20376f
		e->prio = prio;
Packit Service 20376f
		e->name_checked = 0;
Packit Service 20376f
		git_oid_cpy(&e->sha1, sha1);
Packit Service 20376f
		git__free(e->path);
Packit Service 20376f
		e->path = git__strdup(path);
Packit Service 20376f
		git_oid_cpy(&e->peeled, peeled);
Packit Service 20376f
Packit Service 20376f
		if (!found) {
Packit Service 20376f
			int ret;
Packit Service 20376f
Packit Service 20376f
			git_oidmap_insert(names, &e->peeled, e, &ret;;
Packit Service 20376f
			if (ret < 0)
Packit Service 20376f
				return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	else
Packit Service 20376f
		git_tag_free(tag);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int retrieve_peeled_tag_or_object_oid(
Packit Service 20376f
	git_oid *peeled_out,
Packit Service 20376f
	git_oid *ref_target_out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const char *refname)
Packit Service 20376f
{
Packit Service 20376f
	git_reference *ref;
Packit Service 20376f
	git_object *peeled = NULL;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(ref_target_out, git_reference_target(ref));
Packit Service 20376f
	git_oid_cpy(peeled_out, git_object_id(peeled));
Packit Service 20376f
Packit Service 20376f
	if (git_oid_cmp(ref_target_out, peeled_out) != 0)
Packit Service 20376f
		error = 1; /* The reference was pointing to a annotated tag */
Packit Service 20376f
	else
Packit Service 20376f
		error = 0; /* Any other object */
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_reference_free(ref);
Packit Service 20376f
	git_object_free(peeled);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct git_describe_result {
Packit Service 20376f
	int dirty;
Packit Service 20376f
	int exact_match;
Packit Service 20376f
	int fallback_to_id;
Packit Service 20376f
	git_oid commit_id;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	struct commit_name *name;
Packit Service 20376f
	struct possible_tag *tag;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
struct get_name_data
Packit Service 20376f
{
Packit Service 20376f
	git_describe_options *opts;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	git_oidmap *names;
Packit Service 20376f
	git_describe_result *result;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int commit_name_dup(struct commit_name **out, struct commit_name *in)
Packit Service 20376f
{
Packit Service 20376f
	struct commit_name *name;
Packit Service 20376f
Packit Service 20376f
	name = git__malloc(sizeof(struct commit_name));
Packit Service 20376f
	GITERR_CHECK_ALLOC(name);
Packit Service 20376f
Packit Service 20376f
	memcpy(name, in,  sizeof(struct commit_name));
Packit Service 20376f
	name->tag = NULL;
Packit Service 20376f
	name->path = NULL;
Packit Service 20376f
Packit Service 20376f
	if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	name->path = git__strdup(in->path);
Packit Service 20376f
	GITERR_CHECK_ALLOC(name->path);
Packit Service 20376f
Packit Service 20376f
	*out = name;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int get_name(const char *refname, void *payload)
Packit Service 20376f
{
Packit Service 20376f
	struct get_name_data *data;
Packit Service 20376f
	bool is_tag, is_annotated, all;
Packit Service 20376f
	git_oid peeled, sha1;
Packit Service 20376f
	unsigned int prio;
Packit Service 20376f
	int error = 0;
Packit Service 20376f
Packit Service 20376f
	data = (struct get_name_data *)payload;
Packit Service 20376f
	is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
Packit Service 20376f
	all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
Packit Service 20376f
Packit Service 20376f
	/* Reject anything outside refs/tags/ unless --all */
Packit Service 20376f
	if (!all && !is_tag)
Packit Service 20376f
		return 0;
Packit Service 20376f
Packit Service 20376f
	/* Accept only tags that match the pattern, if given */
Packit Service 20376f
	if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern,
Packit Service 20376f
		refname + strlen(GIT_REFS_TAGS_DIR), 0)))
Packit Service 20376f
				return 0;
Packit Service 20376f
Packit Service 20376f
	/* Is it annotated? */
Packit Service 20376f
	if ((error = retrieve_peeled_tag_or_object_oid(
Packit Service 20376f
		&peeled, &sha1, data->repo, refname)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	is_annotated = error;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	 * By default, we only use annotated tags, but with --tags
Packit Service 20376f
	 * we fall back to lightweight ones (even without --tags,
Packit Service 20376f
	 * we still remember lightweight ones, only to give hints
Packit Service 20376f
	 * in an error message).  --all allows any refs to be used.
Packit Service 20376f
	 */
Packit Service 20376f
	if (is_annotated)
Packit Service 20376f
		prio = 2;
Packit Service 20376f
	else if (is_tag)
Packit Service 20376f
		prio = 1;
Packit Service 20376f
	else
Packit Service 20376f
		prio = 0;
Packit Service 20376f
Packit Service 20376f
	add_to_known_names(data->repo, data->names,
Packit Service 20376f
		all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
Packit Service 20376f
		&peeled, prio, &sha1;;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
struct possible_tag {
Packit Service 20376f
	struct commit_name *name;
Packit Service 20376f
	int depth;
Packit Service 20376f
	int found_order;
Packit Service 20376f
	unsigned flag_within;
Packit Service 20376f
};
Packit Service 20376f
Packit Service 20376f
static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
Packit Service 20376f
{
Packit Service 20376f
	struct possible_tag *tag;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	tag = git__malloc(sizeof(struct possible_tag));
Packit Service 20376f
	GITERR_CHECK_ALLOC(tag);
Packit Service 20376f
Packit Service 20376f
	memcpy(tag, in, sizeof(struct possible_tag));
Packit Service 20376f
	tag->name = NULL;
Packit Service 20376f
Packit Service 20376f
	if ((error = commit_name_dup(&tag->name, in->name)) < 0) {
Packit Service 20376f
		git__free(tag);
Packit Service 20376f
		*out = NULL;
Packit Service 20376f
		return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	*out = tag;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int compare_pt(const void *a_, const void *b_)
Packit Service 20376f
{
Packit Service 20376f
	struct possible_tag *a = (struct possible_tag *)a_;
Packit Service 20376f
	struct possible_tag *b = (struct possible_tag *)b_;
Packit Service 20376f
	if (a->depth != b->depth)
Packit Service 20376f
		return a->depth - b->depth;
Packit Service 20376f
	if (a->found_order != b->found_order)
Packit Service 20376f
		return a->found_order - b->found_order;
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
#define SEEN (1u << 0)
Packit Service 20376f
Packit Service 20376f
static unsigned long finish_depth_computation(
Packit Service 20376f
	git_pqueue *list,
Packit Service 20376f
	git_revwalk *walk,
Packit Service 20376f
	struct possible_tag *best)
Packit Service 20376f
{
Packit Service 20376f
	unsigned long seen_commits = 0;
Packit Service 20376f
	int error, i;
Packit Service 20376f
Packit Service 20376f
	while (git_pqueue_size(list) > 0) {
Packit Service 20376f
		git_commit_list_node *c = git_pqueue_pop(list);
Packit Service 20376f
		seen_commits++;
Packit Service 20376f
		if (c->flags & best->flag_within) {
Packit Service 20376f
			size_t index = 0;
Packit Service 20376f
			while (git_pqueue_size(list) > index) {
Packit Service 20376f
				git_commit_list_node *i = git_pqueue_get(list, index);
Packit Service 20376f
				if (!(i->flags & best->flag_within))
Packit Service 20376f
					break;
Packit Service 20376f
				index++;
Packit Service 20376f
			}
Packit Service 20376f
			if (index > git_pqueue_size(list))
Packit Service 20376f
				break;
Packit Service 20376f
		} else
Packit Service 20376f
			best->depth++;
Packit Service 20376f
		for (i = 0; i < c->out_degree; i++) {
Packit Service 20376f
			git_commit_list_node *p = c->parents[i];
Packit Service 20376f
			if ((error = git_commit_list_parse(walk, p)) < 0)
Packit Service 20376f
				return error;
Packit Service 20376f
			if (!(p->flags & SEEN))
Packit Service 20376f
				if ((error = git_pqueue_insert(list, p)) < 0)
Packit Service 20376f
					return error;
Packit Service 20376f
			p->flags |= c->flags;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	return seen_commits;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
Packit Service 20376f
{
Packit Service 20376f
	if (n->prio == 2 && !n->tag) {
Packit Service 20376f
		if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
Packit Service 20376f
			giterr_set(GITERR_TAG, "annotated tag '%s' not available", n->path);
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (n->tag && !n->name_checked) {
Packit Service 20376f
		if (!git_tag_name(n->tag)) {
Packit Service 20376f
			giterr_set(GITERR_TAG, "annotated tag '%s' has no embedded name", n->path);
Packit Service 20376f
			return -1;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* TODO: Cope with warnings
Packit Service 20376f
		if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
Packit Service 20376f
			warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
Packit Service 20376f
		*/
Packit Service 20376f
Packit Service 20376f
		n->name_checked = 1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (n->tag)
Packit Service 20376f
		git_buf_printf(buf, "%s", git_tag_name(n->tag));
Packit Service 20376f
	else
Packit Service 20376f
		git_buf_printf(buf, "%s", n->path);
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int find_unique_abbrev_size(
Packit Service 20376f
	int *out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_oid *oid_in,
Packit Service 20376f
	int abbreviated_size)
Packit Service 20376f
{
Packit Service 20376f
	size_t size = abbreviated_size;
Packit Service 20376f
	git_odb *odb;
Packit Service 20376f
	git_oid dummy;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	while (size < GIT_OID_HEXSZ) {
Packit Service 20376f
		if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
Packit Service 20376f
			*out = (int) size;
Packit Service 20376f
			return 0;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		/* If the error wasn't that it's not unique, then it's a proper error */
Packit Service 20376f
		if (error != GIT_EAMBIGUOUS)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		/* Try again with a larger size */
Packit Service 20376f
		size++;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If we didn't find any shorter prefix, we have to do the whole thing */
Packit Service 20376f
	*out = GIT_OID_HEXSZ;
Packit Service 20376f
	
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int show_suffix(
Packit Service 20376f
	git_buf *buf,
Packit Service 20376f
	int depth,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	const git_oid* id,
Packit Service 20376f
	size_t abbrev_size)
Packit Service 20376f
{
Packit Service 20376f
	int error, size = 0;
Packit Service 20376f
Packit Service 20376f
	char hex_oid[GIT_OID_HEXSZ];
Packit Service 20376f
Packit Service 20376f
	if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	git_oid_fmt(hex_oid, id);
Packit Service 20376f
Packit Service 20376f
	git_buf_printf(buf, "-%d-g", depth);
Packit Service 20376f
Packit Service 20376f
	git_buf_put(buf, hex_oid, size);
Packit Service 20376f
Packit Service 20376f
	return git_buf_oom(buf) ? -1 : 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
#define MAX_CANDIDATES_TAGS FLAG_BITS - 1
Packit Service 20376f
Packit Service 20376f
static int describe_not_found(const git_oid *oid, const char *message_format) {
Packit Service 20376f
	char oid_str[GIT_OID_HEXSZ + 1];
Packit Service 20376f
	git_oid_tostr(oid_str, sizeof(oid_str), oid);
Packit Service 20376f
Packit Service 20376f
	giterr_set(GITERR_DESCRIBE, message_format, oid_str);
Packit Service 20376f
	return GIT_ENOTFOUND;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int describe(
Packit Service 20376f
	struct get_name_data *data,
Packit Service 20376f
	git_commit *commit)
Packit Service 20376f
{
Packit Service 20376f
	struct commit_name *n;
Packit Service 20376f
	struct possible_tag *best;
Packit Service 20376f
	bool all, tags;
Packit Service 20376f
	git_revwalk *walk = NULL;
Packit Service 20376f
	git_pqueue list;
Packit Service 20376f
	git_commit_list_node *cmit, *gave_up_on = NULL;
Packit Service 20376f
	git_vector all_matches = GIT_VECTOR_INIT;
Packit Service 20376f
	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
Packit Service 20376f
	unsigned long seen_commits = 0;	/* TODO: Check long */
Packit Service 20376f
	unsigned int unannotated_cnt = 0;
Packit Service 20376f
	int error;
Packit Service 20376f
Packit Service 20376f
	if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
Packit Service 20376f
		return -1;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
Packit Service 20376f
	tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
Packit Service 20376f
Packit Service 20376f
	n = find_commit_name(data->names, git_commit_id(commit));
Packit Service 20376f
	if (n && (tags || all || n->prio == 2)) {
Packit Service 20376f
		/*
Packit Service 20376f
		 * Exact match to an existing ref.
Packit Service 20376f
		 */
Packit Service 20376f
		data->result->exact_match = 1;
Packit Service 20376f
		if ((error = commit_name_dup(&data->result->name, n)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!data->opts->max_candidates_tags) {
Packit Service 20376f
		error = describe_not_found(
Packit Service 20376f
			git_commit_id(commit),
Packit Service 20376f
			"cannot describe - no tag exactly matches '%s'");
Packit Service 20376f
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_commit_list_parse(walk, cmit)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	cmit->flags = SEEN;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_pqueue_insert(&list, cmit)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	while (git_pqueue_size(&list) > 0)
Packit Service 20376f
	{
Packit Service 20376f
		int i;
Packit Service 20376f
Packit Service 20376f
		git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
Packit Service 20376f
		seen_commits++;
Packit Service 20376f
Packit Service 20376f
		n = find_commit_name(data->names, &c->oid);
Packit Service 20376f
Packit Service 20376f
		if (n) {
Packit Service 20376f
			if (!tags && !all && n->prio < 2) {
Packit Service 20376f
				unannotated_cnt++;
Packit Service 20376f
			} else if (match_cnt < data->opts->max_candidates_tags) {
Packit Service 20376f
				struct possible_tag *t = git__malloc(sizeof(struct commit_name));
Packit Service 20376f
				GITERR_CHECK_ALLOC(t);
Packit Service 20376f
				if ((error = git_vector_insert(&all_matches, t)) < 0)
Packit Service 20376f
					goto cleanup;
Packit Service 20376f
Packit Service 20376f
				match_cnt++;
Packit Service 20376f
Packit Service 20376f
				t->name = n;
Packit Service 20376f
				t->depth = seen_commits - 1;
Packit Service 20376f
				t->flag_within = 1u << match_cnt;
Packit Service 20376f
				t->found_order = match_cnt;
Packit Service 20376f
				c->flags |= t->flag_within;
Packit Service 20376f
				if (n->prio == 2)
Packit Service 20376f
					annotated_cnt++;
Packit Service 20376f
			}
Packit Service 20376f
			else {
Packit Service 20376f
				gave_up_on = c;
Packit Service 20376f
				break;
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
Packit Service 20376f
			struct possible_tag *t = git_vector_get(&all_matches, cur_match);
Packit Service 20376f
			if (!(c->flags & t->flag_within))
Packit Service 20376f
				t->depth++;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
Packit Service 20376f
			/*
Packit Service 20376f
			if (debug) {
Packit Service 20376f
				char oid_str[GIT_OID_HEXSZ + 1];
Packit Service 20376f
				git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
Packit Service 20376f
Packit Service 20376f
				fprintf(stderr, "finished search at %s\n", oid_str);
Packit Service 20376f
			}
Packit Service 20376f
			*/
Packit Service 20376f
			break;
Packit Service 20376f
		}
Packit Service 20376f
		for (i = 0; i < c->out_degree; i++) {
Packit Service 20376f
			git_commit_list_node *p = c->parents[i];
Packit Service 20376f
			if ((error = git_commit_list_parse(walk, p)) < 0)
Packit Service 20376f
				goto cleanup;
Packit Service 20376f
			if (!(p->flags & SEEN))
Packit Service 20376f
				if ((error = git_pqueue_insert(&list, p)) < 0)
Packit Service 20376f
					goto cleanup;
Packit Service 20376f
			p->flags |= c->flags;
Packit Service 20376f
Packit Service 20376f
			if (data->opts->only_follow_first_parent)
Packit Service 20376f
				break;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (!match_cnt) {
Packit Service 20376f
		if (data->opts->show_commit_oid_as_fallback) {
Packit Service 20376f
			data->result->fallback_to_id = 1;
Packit Service 20376f
			git_oid_cpy(&data->result->commit_id, &cmit->oid);
Packit Service 20376f
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
		if (unannotated_cnt) {
Packit Service 20376f
			error = describe_not_found(git_commit_id(commit), 
Packit Service 20376f
				"cannot describe - "
Packit Service 20376f
				"no annotated tags can describe '%s'; "
Packit Service 20376f
			    "however, there were unannotated tags.");
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
		else {
Packit Service 20376f
			error = describe_not_found(git_commit_id(commit), 
Packit Service 20376f
				"cannot describe - "
Packit Service 20376f
				"no tags can describe '%s'.");
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git_vector_sort(&all_matches);
Packit Service 20376f
Packit Service 20376f
	best = (struct possible_tag *)git_vector_get(&all_matches, 0);
Packit Service 20376f
Packit Service 20376f
	if (gave_up_on) {
Packit Service 20376f
		if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
Packit Service 20376f
			goto cleanup;
Packit Service 20376f
		seen_commits--;
Packit Service 20376f
	}
Packit Service 20376f
	if ((error = finish_depth_computation(
Packit Service 20376f
		&list, walk, best)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	seen_commits += error;
Packit Service 20376f
	if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	/*
Packit Service 20376f
	{
Packit Service 20376f
		static const char *prio_names[] = {
Packit Service 20376f
			"head", "lightweight", "annotated",
Packit Service 20376f
		};
Packit Service 20376f
Packit Service 20376f
		char oid_str[GIT_OID_HEXSZ + 1];
Packit Service 20376f
Packit Service 20376f
		if (debug) {
Packit Service 20376f
			for (cur_match = 0; cur_match < match_cnt; cur_match++) {
Packit Service 20376f
				struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
Packit Service 20376f
				fprintf(stderr, " %-11s %8d %s\n",
Packit Service 20376f
					prio_names[t->name->prio],
Packit Service 20376f
					t->depth, t->name->path);
Packit Service 20376f
			}
Packit Service 20376f
			fprintf(stderr, "traversed %lu commits\n", seen_commits);
Packit Service 20376f
			if (gave_up_on) {
Packit Service 20376f
				git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
Packit Service 20376f
				fprintf(stderr,
Packit Service 20376f
					"more than %i tags found; listed %i most recent\n"
Packit Service 20376f
					"gave up search at %s\n",
Packit Service 20376f
					data->opts->max_candidates_tags, data->opts->max_candidates_tags,
Packit Service 20376f
					oid_str);
Packit Service 20376f
			}
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	*/
Packit Service 20376f
Packit Service 20376f
	git_oid_cpy(&data->result->commit_id, &cmit->oid);
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	{
Packit Service 20376f
		size_t i;
Packit Service 20376f
		struct possible_tag *match;
Packit Service 20376f
		git_vector_foreach(&all_matches, i, match) {
Packit Service 20376f
			git__free(match);
Packit Service 20376f
		}
Packit Service 20376f
	}
Packit Service 20376f
	git_vector_free(&all_matches);
Packit Service 20376f
	git_pqueue_free(&list);
Packit Service 20376f
	git_revwalk_free(walk);
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int normalize_options(
Packit Service 20376f
	git_describe_options *dst,
Packit Service 20376f
	const git_describe_options *src)
Packit Service 20376f
{
Packit Service 20376f
	git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
Packit Service 20376f
	if (!src) src = &default_options;
Packit Service 20376f
Packit Service 20376f
	*dst = *src;
Packit Service 20376f
Packit Service 20376f
	if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
Packit Service 20376f
		dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
Packit Service 20376f
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_describe_commit(
Packit Service 20376f
	git_describe_result **result,
Packit Service 20376f
	git_object *committish,
Packit Service 20376f
	git_describe_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	struct get_name_data data;
Packit Service 20376f
	struct commit_name *name;
Packit Service 20376f
	git_commit *commit;
Packit Service 20376f
	int error = -1;
Packit Service 20376f
	git_describe_options normalized;
Packit Service 20376f
Packit Service 20376f
	assert(committish);
Packit Service 20376f
Packit Service 20376f
	data.result = git__calloc(1, sizeof(git_describe_result));
Packit Service 20376f
	GITERR_CHECK_ALLOC(data.result);
Packit Service 20376f
	data.result->repo = git_object_owner(committish);
Packit Service 20376f
Packit Service 20376f
	data.repo = git_object_owner(committish);
Packit Service 20376f
Packit Service 20376f
	if ((error = normalize_options(&normalized, opts)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_VERSION(
Packit Service 20376f
		&normalized,
Packit Service 20376f
		GIT_DESCRIBE_OPTIONS_VERSION,
Packit Service 20376f
		"git_describe_options");
Packit Service 20376f
	data.opts = &normalized;
Packit Service 20376f
Packit Service 20376f
	data.names = git_oidmap_alloc();
Packit Service 20376f
	GITERR_CHECK_ALLOC(data.names);
Packit Service 20376f
Packit Service 20376f
	/** TODO: contains to be implemented */
Packit Service 20376f
Packit Service 20376f
	if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_foreach_name(
Packit Service 20376f
			git_object_owner(committish),
Packit Service 20376f
			get_name, &data)) < 0)
Packit Service 20376f
				goto cleanup;
Packit Service 20376f
Packit Service 20376f
	if (git_oidmap_size(data.names) == 0 && !opts->show_commit_oid_as_fallback) {
Packit Service 20376f
		giterr_set(GITERR_DESCRIBE, "cannot describe - "
Packit Service 20376f
			"no reference found, cannot describe anything.");
Packit Service 20376f
		error = -1;
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if ((error = describe(&data, commit)) < 0)
Packit Service 20376f
		goto cleanup;
Packit Service 20376f
Packit Service 20376f
cleanup:
Packit Service 20376f
	git_commit_free(commit);
Packit Service 20376f
Packit Service 20376f
	git_oidmap_foreach_value(data.names, name, {
Packit Service 20376f
		git_tag_free(name->tag);
Packit Service 20376f
		git__free(name->path);
Packit Service 20376f
		git__free(name);
Packit Service 20376f
	});
Packit Service 20376f
Packit Service 20376f
	git_oidmap_free(data.names);
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_describe_result_free(data.result);
Packit Service 20376f
	else
Packit Service 20376f
		*result = data.result;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_describe_workdir(
Packit Service 20376f
	git_describe_result **out,
Packit Service 20376f
	git_repository *repo,
Packit Service 20376f
	git_describe_options *opts)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_oid current_id;
Packit Service 20376f
	git_status_list *status = NULL;
Packit Service 20376f
	git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
Packit Service 20376f
	git_describe_result *result = NULL;
Packit Service 20376f
	git_object *commit;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_reference_name_to_id(&current_id, repo, GIT_HEAD_FILE)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_object_lookup(&commit, repo, &current_id, GIT_OBJ_COMMIT)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	/* The first step is to perform a describe of HEAD, so we can leverage this */
Packit Service 20376f
	if ((error = git_describe_commit(&result, commit, opts)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
	if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
Packit Service 20376f
		goto out;
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	if (git_status_list_entrycount(status) > 0)
Packit Service 20376f
		result->dirty = 1;
Packit Service 20376f
Packit Service 20376f
out:
Packit Service 20376f
	git_object_free(commit);
Packit Service 20376f
	git_status_list_free(status);
Packit Service 20376f
Packit Service 20376f
	if (error < 0)
Packit Service 20376f
		git_describe_result_free(result);
Packit Service 20376f
	else
Packit Service 20376f
		*out = result;
Packit Service 20376f
Packit Service 20376f
	return error;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
static int normalize_format_options(
Packit Service 20376f
	git_describe_format_options *dst,
Packit Service 20376f
	const git_describe_format_options *src)
Packit Service 20376f
{
Packit Service 20376f
	if (!src) {
Packit Service 20376f
		git_describe_init_format_options(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
Packit Service 20376f
		return 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	memcpy(dst, src, sizeof(git_describe_format_options));
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given)
Packit Service 20376f
{
Packit Service 20376f
	int error;
Packit Service 20376f
	git_repository *repo;
Packit Service 20376f
	struct commit_name *name;
Packit Service 20376f
	git_describe_format_options opts;
Packit Service 20376f
Packit Service 20376f
	assert(out && result);
Packit Service 20376f
Packit Service 20376f
	GITERR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
Packit Service 20376f
	normalize_format_options(&opts, given);
Packit Service 20376f
Packit Service 20376f
	git_buf_sanitize(out);
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	if (opts.always_use_long_format && opts.abbreviated_size == 0) {
Packit Service 20376f
		giterr_set(GITERR_DESCRIBE, "cannot describe - "
Packit Service 20376f
			"'always_use_long_format' is incompatible with a zero"
Packit Service 20376f
			"'abbreviated_size'");
Packit Service 20376f
		return -1;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
	repo = result->repo;
Packit Service 20376f
Packit Service 20376f
	/* If we did find an exact match, then it's the easier method */
Packit Service 20376f
	if (result->exact_match) {
Packit Service 20376f
		name = result->name;
Packit Service 20376f
		if ((error = display_name(out, repo, name)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
Packit Service 20376f
		if (opts.always_use_long_format) {
Packit Service 20376f
			const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
Packit Service 20376f
			if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
Packit Service 20376f
				return error;
Packit Service 20376f
		}
Packit Service 20376f
Packit Service 20376f
		if (result->dirty && opts.dirty_suffix)
Packit Service 20376f
			git_buf_puts(out, opts.dirty_suffix);
Packit Service 20376f
Packit Service 20376f
		return git_buf_oom(out) ? -1 : 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* If we didn't find *any* tags, we fall back to the commit's id */
Packit Service 20376f
	if (result->fallback_to_id) {
Packit Service 20376f
		char hex_oid[GIT_OID_HEXSZ + 1] = {0};
Packit Service 20376f
		int size = 0;
Packit Service 20376f
Packit Service 20376f
		if ((error = find_unique_abbrev_size(
Packit Service 20376f
			     &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
Packit Service 20376f
			return -1;
Packit Service 20376f
Packit Service 20376f
		git_oid_fmt(hex_oid, &result->commit_id);
Packit Service 20376f
		git_buf_put(out, hex_oid, size);
Packit Service 20376f
Packit Service 20376f
		if (result->dirty && opts.dirty_suffix)
Packit Service 20376f
			git_buf_puts(out, opts.dirty_suffix);
Packit Service 20376f
Packit Service 20376f
		return git_buf_oom(out) ? -1 : 0;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	/* Lastly, if we found a matching tag, we show that */
Packit Service 20376f
	name = result->tag->name;
Packit Service 20376f
Packit Service 20376f
	if ((error = display_name(out, repo, name)) < 0)
Packit Service 20376f
		return error;
Packit Service 20376f
Packit Service 20376f
	if (opts.abbreviated_size) {
Packit Service 20376f
		if ((error = show_suffix(out, result->tag->depth, repo,
Packit Service 20376f
			&result->commit_id, opts.abbreviated_size)) < 0)
Packit Service 20376f
			return error;
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (result->dirty && opts.dirty_suffix) {
Packit Service 20376f
		git_buf_puts(out, opts.dirty_suffix);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	return git_buf_oom(out) ? -1 : 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
void git_describe_result_free(git_describe_result *result)
Packit Service 20376f
{
Packit Service 20376f
	if (result == NULL)
Packit Service 20376f
		return;
Packit Service 20376f
Packit Service 20376f
	if (result->name) {
Packit Service 20376f
		git_tag_free(result->name->tag);
Packit Service 20376f
		git__free(result->name->path);
Packit Service 20376f
		git__free(result->name);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	if (result->tag) {
Packit Service 20376f
		git_tag_free(result->tag->name->tag);
Packit Service 20376f
		git__free(result->tag->name->path);
Packit Service 20376f
		git__free(result->tag->name);
Packit Service 20376f
		git__free(result->tag);
Packit Service 20376f
	}
Packit Service 20376f
Packit Service 20376f
	git__free(result);
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_describe_init_options(git_describe_options *opts, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}
Packit Service 20376f
Packit Service 20376f
int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
Packit Service 20376f
{
Packit Service 20376f
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
Packit Service 20376f
		opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
Packit Service 20376f
	return 0;
Packit Service 20376f
}