static int describe()

in src/describe.c [423:630]


static int describe(
	struct get_name_data *data,
	git_commit *commit)
{
	struct commit_name *n;
	struct possible_tag *best;
	bool all, tags;
	git_revwalk *walk = NULL;
	git_pqueue list;
	git_commit_list_node *cmit, *gave_up_on = NULL;
	git_vector all_matches = GIT_VECTOR_INIT;
	unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
	unsigned long seen_commits = 0;	/* TODO: Check long */
	unsigned int unannotated_cnt = 0;
	int error;

	if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
		return -1;

	if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
		goto cleanup;

	all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
	tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;

	git_oid_cpy(&data->result->commit_id, git_commit_id(commit));

	n = find_commit_name(data->names, git_commit_id(commit));
	if (n && (tags || all || n->prio == 2)) {
		/*
		 * Exact match to an existing ref.
		 */
		data->result->exact_match = 1;
		if ((error = commit_name_dup(&data->result->name, n)) < 0)
			goto cleanup;

		goto cleanup;
	}

	if (!data->opts->max_candidates_tags) {
		error = describe_not_found(
			git_commit_id(commit),
			"cannot describe - no tag exactly matches '%s'");

		goto cleanup;
	}

	if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
		goto cleanup;

	if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
		goto cleanup;

	if ((error = git_commit_list_parse(walk, cmit)) < 0)
		goto cleanup;

	cmit->flags = SEEN;

	if ((error = git_pqueue_insert(&list, cmit)) < 0)
		goto cleanup;

	while (git_pqueue_size(&list) > 0)
	{
		int i;

		git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
		seen_commits++;

		n = find_commit_name(data->names, &c->oid);

		if (n) {
			if (!tags && !all && n->prio < 2) {
				unannotated_cnt++;
			} else if (match_cnt < data->opts->max_candidates_tags) {
				struct possible_tag *t = git__malloc(sizeof(struct commit_name));
				GIT_ERROR_CHECK_ALLOC(t);
				if ((error = git_vector_insert(&all_matches, t)) < 0)
					goto cleanup;

				match_cnt++;

				t->name = n;
				t->depth = seen_commits - 1;
				t->flag_within = 1u << match_cnt;
				t->found_order = match_cnt;
				c->flags |= t->flag_within;
				if (n->prio == 2)
					annotated_cnt++;
			}
			else {
				gave_up_on = c;
				break;
			}
		}

		for (cur_match = 0; cur_match < match_cnt; cur_match++) {
			struct possible_tag *t = git_vector_get(&all_matches, cur_match);
			if (!(c->flags & t->flag_within))
				t->depth++;
		}

		if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
			/*
			if (debug) {
				char oid_str[GIT_OID_HEXSZ + 1];
				git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);

				fprintf(stderr, "finished search at %s\n", oid_str);
			}
			*/
			break;
		}
		for (i = 0; i < c->out_degree; i++) {
			git_commit_list_node *p = c->parents[i];
			if ((error = git_commit_list_parse(walk, p)) < 0)
				goto cleanup;
			if (!(p->flags & SEEN))
				if ((error = git_pqueue_insert(&list, p)) < 0)
					goto cleanup;
			p->flags |= c->flags;

			if (data->opts->only_follow_first_parent)
				break;
		}
	}

	if (!match_cnt) {
		if (data->opts->show_commit_oid_as_fallback) {
			data->result->fallback_to_id = 1;
			git_oid_cpy(&data->result->commit_id, &cmit->oid);

			goto cleanup;
		}
		if (unannotated_cnt) {
			error = describe_not_found(git_commit_id(commit),
				"cannot describe - "
				"no annotated tags can describe '%s'; "
			    "however, there were unannotated tags.");
			goto cleanup;
		}
		else {
			error = describe_not_found(git_commit_id(commit),
				"cannot describe - "
				"no tags can describe '%s'.");
			goto cleanup;
		}
	}

	git_vector_sort(&all_matches);

	best = (struct possible_tag *)git_vector_get(&all_matches, 0);

	if (gave_up_on) {
		if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
			goto cleanup;
		seen_commits--;
	}
	if ((error = finish_depth_computation(
		&list, walk, best)) < 0)
		goto cleanup;

	seen_commits += error;
	if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
		goto cleanup;

	/*
	{
		static const char *prio_names[] = {
			"head", "lightweight", "annotated",
		};

		char oid_str[GIT_OID_HEXSZ + 1];

		if (debug) {
			for (cur_match = 0; cur_match < match_cnt; cur_match++) {
				struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
				fprintf(stderr, " %-11s %8d %s\n",
					prio_names[t->name->prio],
					t->depth, t->name->path);
			}
			fprintf(stderr, "traversed %lu commits\n", seen_commits);
			if (gave_up_on) {
				git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
				fprintf(stderr,
					"more than %i tags found; listed %i most recent\n"
					"gave up search at %s\n",
					data->opts->max_candidates_tags, data->opts->max_candidates_tags,
					oid_str);
			}
		}
	}
	*/

	git_oid_cpy(&data->result->commit_id, &cmit->oid);

cleanup:
	{
		size_t i;
		struct possible_tag *match;
		git_vector_foreach(&all_matches, i, match) {
			git__free(match);
		}
	}
	git_vector_free(&all_matches);
	git_pqueue_free(&list);
	git_revwalk_free(walk);
	return error;
}