summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-10-09 22:24:40 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-10-09 22:24:40 +0200
commit46a2b8e855d5f6d8b60b81500a9f6779c7f63e63 (patch)
treeeec898cff4c27feb6e50e5265da9da7b5dd59ff4
parent324154a4538f9e821cc1065b72109033b6d5da03 (diff)
parenta3b9270dcfe61ce3429f82b514b302667d05bfc5 (diff)
downloadlibgit2-46a2b8e855d5f6d8b60b81500a9f6779c7f63e63.tar.gz
Merge pull request #2592 from libgit2/cmn/describe
Implement git-describe
-rw-r--r--include/git2.h1
-rw-r--r--include/git2/common.h2
-rw-r--r--include/git2/describe.h162
-rw-r--r--include/git2/errors.h1
-rw-r--r--src/commit_list.h2
-rw-r--r--src/describe.c871
-rw-r--r--src/oidmap.h16
-rw-r--r--tests/describe/describe.c50
-rw-r--r--tests/describe/describe_helpers.c42
-rw-r--r--tests/describe/describe_helpers.h15
-rw-r--r--tests/describe/t6120.c156
-rw-r--r--tests/resources/describe/.gitted/HEAD1
-rw-r--r--tests/resources/describe/.gitted/config8
-rw-r--r--tests/resources/describe/.gitted/indexbin0 -> 262 bytes
-rw-r--r--tests/resources/describe/.gitted/logs/HEAD14
-rw-r--r--tests/resources/describe/.gitted/logs/refs/heads/master14
-rw-r--r--tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04dbin0 -> 108 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32bin0 -> 125 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427adbin0 -> 127 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294fbin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cfbin0 -> 156 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1bin0 -> 87 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782bin0 -> 21 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4bbin0 -> 153 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583bin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0fbin0 -> 155 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525bin0 -> 18 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359bin0 -> 127 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1cbin0 -> 151 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9dbin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4ebin0 -> 77 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e62
-rw-r--r--tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1bin0 -> 154 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3cbin0 -> 77 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5abin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4cbin0 -> 152 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0ebin0 -> 186 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6abin0 -> 153 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729dbin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a476556912
-rw-r--r--tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8bbin0 -> 125 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a7053
-rw-r--r--tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8bin0 -> 187 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4bin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/refs/heads/master1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/A1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/B1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/D1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/R1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/c1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/e1
-rw-r--r--tests/resources/describe/another1
-rw-r--r--tests/resources/describe/file1
-rw-r--r--tests/resources/describe/side1
64 files changed, 1371 insertions, 1 deletions
diff --git a/include/git2.h b/include/git2.h
index d84353955..baa7fcaf8 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -19,6 +19,7 @@
#include "git2/commit.h"
#include "git2/common.h"
#include "git2/config.h"
+#include "git2/describe.h"
#include "git2/diff.h"
#include "git2/errors.h"
#include "git2/filter.h"
diff --git a/include/git2/common.h b/include/git2/common.h
index 32237efed..ceb27205a 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -83,6 +83,8 @@ GIT_BEGIN_DECL
*/
#define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000"
+#define FLAG_BITS 27
+
/**
* Return the version of the libgit2 library
* being currently used.
diff --git a/include/git2/describe.h b/include/git2/describe.h
new file mode 100644
index 000000000..66b88c4fa
--- /dev/null
+++ b/include/git2/describe.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+#ifndef INCLUDE_git_describe_h__
+#define INCLUDE_git_describe_h__
+
+#include "common.h"
+#include "types.h"
+#include "buffer.h"
+
+/**
+ * @file git2/describe.h
+ * @brief Git describing routines
+ * @defgroup git_describe Git describing routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Reference lookup strategy
+ *
+ * These behave like the --tags and --all optios to git-describe,
+ * namely they say to look for any reference in either refs/tags/ or
+ * refs/ respectively.
+ */
+typedef enum {
+ GIT_DESCRIBE_DEFAULT,
+ GIT_DESCRIBE_TAGS,
+ GIT_DESCRIBE_ALL,
+} git_describe_strategy_t;
+
+/**
+ * Describe options structure
+ *
+ * Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to correctly set
+ * the `version` field. E.g.
+ *
+ * git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ */
+typedef struct git_describe_options {
+ unsigned int version;
+
+ unsigned int max_candidates_tags; /** default: 10 */
+ unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */
+ const char *pattern;
+ /**
+ * When calculating the distance from the matching tag or
+ * reference, only walk down the first-parent ancestry.
+ */
+ int only_follow_first_parent;
+ /**
+ * If no matching tag or reference is found, the describe
+ * operation would normally fail. If this option is set, it
+ * will instead fall back to showing the full id of the
+ * commit.
+ */
+ int show_commit_oid_as_fallback;
+} git_describe_options;
+
+#define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10
+#define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7
+
+#define GIT_DESCRIBE_OPTIONS_VERSION 1
+#define GIT_DESCRIBE_OPTIONS_INIT { \
+ GIT_DESCRIBE_OPTIONS_VERSION, \
+ GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \
+}
+
+GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version);
+
+/**
+ * Options for formatting the describe string
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Size of the abbreviated commit id to use. This value is the
+ * lower bound for the length of the abbreviated string. The
+ * default is 7.
+ */
+ unsigned int abbreviated_size;
+
+ /**
+ * Set to use the long format even when a shorter name could be used.
+ */
+ int always_use_long_format;
+
+ /**
+ * If the workdir is dirty and this is set, this string will
+ * be appended to the description string.
+ */
+ char *dirty_suffix;
+} git_describe_format_options;
+
+#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1
+#define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \
+ GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \
+ GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \
+ }
+
+GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
+
+typedef struct git_describe_result git_describe_result;
+
+/**
+ * Describe a commit
+ *
+ * Perform the describe operation on the given committish object.
+ *
+ * @param result pointer to store the result. You must free this once
+ * you're done with it.
+ * @param committish a committish to describe
+ * @param opts the lookup options
+ */
+GIT_EXTERN(int) git_describe_commit(
+ git_describe_result **result,
+ git_object *committish,
+ git_describe_options *opts);
+
+/**
+ * Describe a commit
+ *
+ * Perform the describe operation on the current commit and the
+ * worktree. After peforming describe on HEAD, a status is run and the
+ * description is considered to be dirty if there are.
+ *
+ * @param result pointer to store the result. You must free this once
+ * you're done with it.
+ * @param repo the repository in which to perform the describe
+ * @param opts the lookup options
+ */
+GIT_EXTERN(int) git_describe_workdir(
+ git_describe_result **out,
+ git_repository *repo,
+ git_describe_options *opts);
+
+/**
+ * Print the describe result to a buffer
+ *
+ * @param result the result from `git_describe_commit()` or
+ * `git_describe_workdir()`.
+ * @param opt the formatting options
+ */
+GIT_EXTERN(int) git_describe_format(
+ git_buf *out,
+ const git_describe_result *result,
+ const git_describe_format_options *opts);
+
+/**
+ * Free the describe result.
+ */
+GIT_EXTERN(void) git_describe_result_free(git_describe_result *result);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 2ba9924f5..1e3ed3acb 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -89,6 +89,7 @@ typedef enum {
GITERR_REVERT,
GITERR_CALLBACK,
GITERR_CHERRYPICK,
+ GITERR_DESCRIBE,
} git_error_t;
/**
diff --git a/src/commit_list.h b/src/commit_list.h
index 490d841be..7cd3945ae 100644
--- a/src/commit_list.h
+++ b/src/commit_list.h
@@ -25,7 +25,7 @@ typedef struct git_commit_list_node {
uninteresting:1,
topo_delay:1,
parsed:1,
- flags : 4;
+ flags : FLAG_BITS;
unsigned short in_degree;
unsigned short out_degree;
diff --git a/src/describe.c b/src/describe.c
new file mode 100644
index 000000000..08c99a7d2
--- /dev/null
+++ b/src/describe.c
@@ -0,0 +1,871 @@
+/*
+ * 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 "git2/describe.h"
+#include "git2/strarray.h"
+#include "git2/diff.h"
+#include "git2/status.h"
+
+#include "common.h"
+#include "commit.h"
+#include "commit_list.h"
+#include "oidmap.h"
+#include "refs.h"
+#include "revwalk.h"
+#include "tag.h"
+#include "vector.h"
+#include "repository.h"
+
+/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
+
+struct commit_name {
+ git_tag *tag;
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
+ git_oid sha1;
+ char *path;
+
+ /* Khash workaround. They original key has to still be reachable */
+ git_oid peeled;
+};
+
+static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
+{
+ khint_t pos = git_oidmap_lookup_index(map, key);
+
+ if (!git_oidmap_valid_index(map, pos))
+ return NULL;
+
+ return git_oidmap_value_at(map, pos);
+}
+
+static struct commit_name *find_commit_name(
+ git_oidmap *names,
+ const git_oid *peeled)
+{
+ return (struct commit_name *)(oidmap_value_bykey(names, peeled));
+}
+
+static int replace_name(
+ git_tag **tag,
+ git_repository *repo,
+ struct commit_name *e,
+ unsigned int prio,
+ const git_oid *sha1)
+{
+ git_time_t e_time = 0, t_time = 0;
+
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ git_tag *t = NULL;
+
+ if (!e->tag) {
+ if (git_tag_lookup(&t, repo, &e->sha1) < 0)
+ return 1;
+ e->tag = t;
+ }
+
+ if (git_tag_lookup(&t, repo, sha1) < 0)
+ return 0;
+
+ *tag = t;
+
+ if (e->tag->tagger)
+ e_time = e->tag->tagger->when.time;
+
+ if (t->tagger)
+ t_time = t->tagger->when.time;
+
+ if (e_time < t_time)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int add_to_known_names(
+ git_repository *repo,
+ git_oidmap *names,
+ const char *path,
+ const git_oid *peeled,
+ unsigned int prio,
+ const git_oid *sha1)
+{
+ struct commit_name *e = find_commit_name(names, peeled);
+ bool found = (e != NULL);
+
+ git_tag *tag = NULL;
+ if (replace_name(&tag, repo, e, prio, sha1)) {
+ if (!found) {
+ e = git__malloc(sizeof(struct commit_name));
+ GITERR_CHECK_ALLOC(e);
+
+ e->path = NULL;
+ e->tag = NULL;
+ }
+
+ if (e->tag)
+ git_tag_free(e->tag);
+ e->tag = tag;
+ e->prio = prio;
+ e->name_checked = 0;
+ git_oid_cpy(&e->sha1, sha1);
+ git__free(e->path);
+ e->path = git__strdup(path);
+ git_oid_cpy(&e->peeled, peeled);
+
+ if (!found) {
+ int ret;
+
+ git_oidmap_insert(names, &e->peeled, e, ret);
+ if (ret < 0)
+ return -1;
+ }
+ }
+ else
+ git_tag_free(tag);
+
+ return 0;
+}
+
+static int retrieve_peeled_tag_or_object_oid(
+ git_oid *peeled_out,
+ git_oid *ref_target_out,
+ git_repository *repo,
+ const char *refname)
+{
+ git_reference *ref;
+ git_object *peeled = NULL;
+ int error;
+
+ if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
+ return error;
+
+ if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0)
+ goto cleanup;
+
+ git_oid_cpy(ref_target_out, git_reference_target(ref));
+ git_oid_cpy(peeled_out, git_object_id(peeled));
+
+ if (git_oid_cmp(ref_target_out, peeled_out) != 0)
+ error = 1; /* The reference was pointing to a annotated tag */
+ else
+ error = 0; /* Any other object */
+
+cleanup:
+ git_reference_free(ref);
+ git_object_free(peeled);
+ return error;
+}
+
+struct git_describe_result {
+ int dirty;
+ int exact_match;
+ int fallback_to_id;
+ git_oid commit_id;
+ git_repository *repo;
+ struct commit_name *name;
+ struct possible_tag *tag;
+};
+
+struct get_name_data
+{
+ git_describe_options *opts;
+ git_repository *repo;
+ git_oidmap *names;
+ git_describe_result *result;
+};
+
+static int commit_name_dup(struct commit_name **out, struct commit_name *in)
+{
+ struct commit_name *name;
+
+ name = git__malloc(sizeof(struct commit_name));
+ GITERR_CHECK_ALLOC(name);
+
+ memcpy(name, in, sizeof(struct commit_name));
+ name->tag = NULL;
+ name->path = NULL;
+
+ if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0)
+ return -1;
+
+ name->path = git__strdup(in->path);
+ GITERR_CHECK_ALLOC(name->path);
+
+ *out = name;
+ return 0;
+}
+
+static int get_name(const char *refname, void *payload)
+{
+ struct get_name_data *data;
+ bool is_tag, is_annotated, all;
+ git_oid peeled, sha1;
+ unsigned int prio;
+ int error = 0;
+
+ data = (struct get_name_data *)payload;
+ is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
+ all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
+
+ /* Reject anything outside refs/tags/ unless --all */
+ if (!all && !is_tag)
+ return 0;
+
+ /* Accept only tags that match the pattern, if given */
+ if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern,
+ refname + strlen(GIT_REFS_TAGS_DIR), 0)))
+ return 0;
+
+ /* Is it annotated? */
+ if ((error = retrieve_peeled_tag_or_object_oid(
+ &peeled, &sha1, data->repo, refname)) < 0)
+ return error;
+
+ is_annotated = error;
+
+ /*
+ * By default, we only use annotated tags, but with --tags
+ * we fall back to lightweight ones (even without --tags,
+ * we still remember lightweight ones, only to give hints
+ * in an error message). --all allows any refs to be used.
+ */
+ if (is_annotated)
+ prio = 2;
+ else if (is_tag)
+ prio = 1;
+ else
+ prio = 0;
+
+ add_to_known_names(data->repo, data->names,
+ all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
+ &peeled, prio, &sha1);
+ return 0;
+}
+
+struct possible_tag {
+ struct commit_name *name;
+ int depth;
+ int found_order;
+ unsigned flag_within;
+};
+
+static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
+{
+ struct possible_tag *tag;
+
+ tag = git__malloc(sizeof(struct possible_tag));
+ GITERR_CHECK_ALLOC(tag);
+
+ memcpy(tag, in, sizeof(struct possible_tag));
+ tag->name = NULL;
+
+ if (commit_name_dup(&tag->name, in->name) < 0)
+ return -1;
+
+ *out = tag;
+ return 0;
+}
+
+static int compare_pt(const void *a_, const void *b_)
+{
+ struct possible_tag *a = (struct possible_tag *)a_;
+ struct possible_tag *b = (struct possible_tag *)b_;
+ if (a->depth != b->depth)
+ return a->depth - b->depth;
+ if (a->found_order != b->found_order)
+ return a->found_order - b->found_order;
+ return 0;
+}
+
+#define SEEN (1u << 0)
+
+static unsigned long finish_depth_computation(
+ git_pqueue *list,
+ git_revwalk *walk,
+ struct possible_tag *best)
+{
+ unsigned long seen_commits = 0;
+ int error, i;
+
+ while (git_pqueue_size(list) > 0) {
+ git_commit_list_node *c = git_pqueue_pop(list);
+ seen_commits++;
+ if (c->flags & best->flag_within) {
+ size_t index = 0;
+ while (git_pqueue_size(list) > index) {
+ git_commit_list_node *i = git_pqueue_get(list, index);
+ if (!(i->flags & best->flag_within))
+ break;
+ index++;
+ }
+ if (index > git_pqueue_size(list))
+ break;
+ } else
+ best->depth++;
+ 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)
+ return error;
+ if (!(p->flags & SEEN))
+ if ((error = git_pqueue_insert(list, p)) < 0)
+ return error;
+ p->flags |= c->flags;
+ }
+ }
+ return seen_commits;
+}
+
+static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n)
+{
+ if (n->prio == 2 && !n->tag) {
+ if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
+ giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path);
+ return -1;
+ }
+ }
+
+ if (n->tag && !n->name_checked) {
+ if (!git_tag_name(n->tag)) {
+ giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path);
+ return -1;
+ }
+
+ /* TODO: Cope with warnings
+ if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
+ warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
+ */
+
+ n->name_checked = 1;
+ }
+
+ if (n->tag)
+ git_buf_printf(buf, "%s", git_tag_name(n->tag));
+ else
+ git_buf_printf(buf, "%s", n->path);
+
+ return 0;
+}
+
+static int find_unique_abbrev_size(
+ int *out,
+ git_repository *repo,
+ const git_oid *oid_in,
+ int abbreviated_size)
+{
+ size_t size = abbreviated_size;
+ git_odb *odb;
+ git_oid dummy;
+ int error;
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ return error;
+
+ while (size < GIT_OID_HEXSZ) {
+ if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
+ *out = (int) size;
+ return 0;
+ }
+
+ /* If the error wasn't that it's not unique, then it's a proper error */
+ if (error != GIT_EAMBIGUOUS)
+ return error;
+
+ /* Try again with a larger size */
+ size++;
+ }
+
+ /* If we didn't find any shorter prefix, we have to do the whole thing */
+ *out = GIT_OID_HEXSZ;
+
+ return 0;
+}
+
+static int show_suffix(
+ git_buf *buf,
+ int depth,
+ git_repository *repo,
+ const git_oid* id,
+ size_t abbrev_size)
+{
+ int error, size;
+
+ char hex_oid[GIT_OID_HEXSZ];
+
+ if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
+ return error;
+
+ git_oid_fmt(hex_oid, id);
+
+ git_buf_printf(buf, "-%d-g", depth);
+
+ git_buf_put(buf, hex_oid, size);
+
+ return git_buf_oom(buf) ? -1 : 0;
+}
+
+#define MAX_CANDIDATES_TAGS FLAG_BITS - 1
+
+static int describe_not_found(const git_oid *oid, const char *message_format) {
+ char oid_str[GIT_OID_HEXSZ + 1];
+ git_oid_tostr(oid_str, sizeof(oid_str), oid);
+
+ giterr_set(GITERR_DESCRIBE, message_format, oid_str);
+ return GIT_ENOTFOUND;
+}
+
+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));
+ GITERR_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;
+ }
+ }
+
+ best = (struct possible_tag *)git_vector_get(&all_matches, 0);
+
+ git_vector_sort(&all_matches);
+
+ best = (struct possible_tag *)git_vector_get(&all_matches, 0);
+
+ if (gave_up_on) {
+ git_pqueue_insert(&list, gave_up_on);
+ 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;
+}
+
+static int normalize_options(
+ git_describe_options *dst,
+ const git_describe_options *src)
+{
+ git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
+ if (!src) src = &default_options;
+
+ *dst = *src;
+
+ if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
+ dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
+
+ return 0;
+}
+
+int git_describe_commit(
+ git_describe_result **result,
+ git_object *committish,
+ git_describe_options *opts)
+{
+ struct get_name_data data;
+ struct commit_name *name;
+ git_commit *commit;
+ int error = -1;
+ git_describe_options normalized;
+
+ assert(committish);
+
+ data.result = git__calloc(1, sizeof(git_describe_result));
+ GITERR_CHECK_ALLOC(data.result);
+ data.result->repo = git_object_owner(committish);
+
+ data.opts = opts;
+ data.repo = git_object_owner(committish);
+
+ if ((error = normalize_options(&normalized, opts)) < 0)
+ return error;
+
+ GITERR_CHECK_VERSION(
+ &normalized,
+ GIT_DESCRIBE_OPTIONS_VERSION,
+ "git_describe_options");
+
+ data.names = git_oidmap_alloc();
+ GITERR_CHECK_ALLOC(data.names);
+
+ /** TODO: contains to be implemented */
+
+ if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0)
+ goto cleanup;
+
+ if (git_reference_foreach_name(
+ git_object_owner(committish),
+ get_name, &data) < 0)
+ goto cleanup;
+
+ if (git_oidmap_size(data.names) == 0) {
+ giterr_set(GITERR_DESCRIBE, "Cannot describe - "
+ "No reference found, cannot describe anything.");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = describe(&data, commit)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit_free(commit);
+
+ git_oidmap_foreach_value(data.names, name, {
+ git_tag_free(name->tag);
+ git__free(name->path);
+ git__free(name);
+ });
+
+ git_oidmap_free(data.names);
+
+ if (error < 0)
+ git_describe_result_free(data.result);
+ else
+ *result = data.result;
+
+ return error;
+}
+
+int git_describe_workdir(
+ git_describe_result **out,
+ git_repository *repo,
+ git_describe_options *opts)
+{
+ int error;
+ git_oid current_id;
+ git_status_list *status = NULL;
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_describe_result *result;
+ git_object *commit;
+
+ if ((error = git_reference_name_to_id(&current_id, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_object_lookup(&commit, repo, &current_id, GIT_OBJ_COMMIT)) < 0)
+ return error;
+
+ /* The first step is to perform a describe of HEAD, so we can leverage this */
+ if ((error = git_describe_commit(&result, commit, opts)) < 0)
+ goto out;
+
+ if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
+ goto out;
+
+
+ if (git_status_list_entrycount(status) > 0)
+ result->dirty = 1;
+
+out:
+ git_object_free(commit);
+ git_status_list_free(status);
+
+ if (error < 0)
+ git_describe_result_free(result);
+ else
+ *out = result;
+
+ return error;
+}
+
+int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts)
+{
+ int error;
+ git_repository *repo;
+ struct commit_name *name;
+
+ assert(out && result);
+
+ GITERR_CHECK_VERSION(opts, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
+ git_buf_sanitize(out);
+
+
+ if (opts->always_use_long_format && opts->abbreviated_size == 0) {
+ giterr_set(GITERR_DESCRIBE, "Cannot describe - "
+ "'always_use_long_format' is incompatible with a zero"
+ "'abbreviated_size'");
+ return -1;
+ }
+
+
+ repo = result->repo;
+
+ /* If we did find an exact match, then it's the easier method */
+ if (result->exact_match) {
+ name = result->name;
+ if ((error = display_name(out, repo, name)) < 0)
+ return error;
+
+ if (opts->always_use_long_format) {
+ const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
+ if ((error = show_suffix(out, 0, repo, id, opts->abbreviated_size)) < 0)
+ return error;
+ }
+
+ if (result->dirty && opts->dirty_suffix)
+ git_buf_puts(out, opts->dirty_suffix);
+
+ return git_buf_oom(out) ? -1 : 0;
+ }
+
+ /* If we didn't find *any* tags, we fall back to the commit's id */
+ if (result->fallback_to_id) {
+ char hex_oid[GIT_OID_HEXSZ + 1] = {0};
+ int size;
+ if ((error = find_unique_abbrev_size(
+ &size, repo, &result->commit_id, opts->abbreviated_size)) < 0)
+ return -1;
+
+ git_oid_fmt(hex_oid, &result->commit_id);
+ git_buf_put(out, hex_oid, size);
+
+ if (result->dirty && opts->dirty_suffix)
+ git_buf_puts(out, opts->dirty_suffix);
+
+ return git_buf_oom(out) ? -1 : 0;
+ }
+
+ /* Lastly, if we found a matching tag, we show that */
+ name = result->tag->name;
+
+ if ((error = display_name(out, repo, name)) < 0)
+ return error;
+
+ if (opts->abbreviated_size) {
+ if ((error = show_suffix(out, result->tag->depth, repo,
+ &result->commit_id, opts->abbreviated_size)) < 0)
+ return error;
+ }
+
+ if (result->dirty && opts->dirty_suffix) {
+ git_buf_puts(out, opts->dirty_suffix);
+ }
+
+ return git_buf_oom(out) ? -1 : 0;
+}
+
+void git_describe_result_free(git_describe_result *result)
+{
+ if (result == NULL)
+ return;
+
+ if (result->name) {
+ git_tag_free(result->name->tag);
+ git__free(result->name->path);
+ git__free(result->name);
+ }
+
+ if (result->tag) {
+ git_tag_free(result->tag->name->tag);
+ git__free(result->tag->name->path);
+ git__free(result->tag->name);
+ git__free(result->tag);
+ }
+
+ git__free(result);
+}
+
+int git_describe_init_options(git_describe_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
+ return 0;
+}
+
+int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
+ return 0;
+}
diff --git a/src/oidmap.h b/src/oidmap.h
index a29c7cd35..b871a7926 100644
--- a/src/oidmap.h
+++ b/src/oidmap.h
@@ -32,4 +32,20 @@ GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
#define git_oidmap_alloc() kh_init(oid)
#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
+#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k)
+#define git_oidmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_oidmap_value_at(h, idx) kh_val(h, idx)
+
+#define git_oidmap_insert(h, key, val, rval) do { \
+ khiter_t __pos = kh_put(oid, h, key, &rval); \
+ if (rval >= 0) { \
+ if (rval == 0) kh_key(h, __pos) = key; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_oidmap_foreach_value kh_foreach_value
+
+#define git_oidmap_size(h) kh_size(h)
+
#endif
diff --git a/tests/describe/describe.c b/tests/describe/describe.c
new file mode 100644
index 000000000..9a523a169
--- /dev/null
+++ b/tests/describe/describe.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+#include "describe_helpers.h"
+
+void test_describe_describe__can_describe_against_a_bare_repo(void)
+{
+ git_repository *repo;
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.show_commit_oid_as_fallback = 1;
+
+ assert_describe("be3563a*", "HEAD^", repo, &opts, &fmt_opts);
+
+ git_repository_free(repo);
+}
+
+static int delete_cb(git_reference *ref, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ return 0;
+}
+
+void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void)
+{
+ git_repository *repo;
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+ git_object *object;
+ git_describe_result *result = NULL;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_revparse_single(&object, repo, "HEAD"));
+
+ cl_git_pass(git_reference_foreach(repo, delete_cb, NULL));
+
+ cl_git_fail(git_describe_commit(&result, object, &opts));
+
+ git_describe_result_free(result);
+ git_object_free(object);
+ git_buf_free(&buf);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/describe/describe_helpers.c b/tests/describe/describe_helpers.c
new file mode 100644
index 000000000..7a6a73cb8
--- /dev/null
+++ b/tests/describe/describe_helpers.c
@@ -0,0 +1,42 @@
+#include "describe_helpers.h"
+
+void assert_describe(
+ const char *expected_output,
+ const char *revparse_spec,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts)
+{
+ git_object *object;
+ git_buf label = GIT_BUF_INIT;
+ git_describe_result *result;
+
+ cl_git_pass(git_revparse_single(&object, repo, revparse_spec));
+
+ cl_git_pass(git_describe_commit(&result, object, opts));
+ cl_git_pass(git_describe_format(&label, result, fmt_opts));
+
+ cl_git_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0));
+
+ git_describe_result_free(result);
+ git_object_free(object);
+ git_buf_free(&label);
+}
+
+void assert_describe_workdir(
+ const char *expected_output,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts)
+{
+ git_buf label = GIT_BUF_INIT;
+ git_describe_result *result;
+
+ cl_git_pass(git_describe_workdir(&result, repo, opts));
+ cl_git_pass(git_describe_format(&label, result, fmt_opts));
+
+ cl_git_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0));
+
+ git_describe_result_free(result);
+ git_buf_free(&label);
+}
diff --git a/tests/describe/describe_helpers.h b/tests/describe/describe_helpers.h
new file mode 100644
index 000000000..16a0638e3
--- /dev/null
+++ b/tests/describe/describe_helpers.h
@@ -0,0 +1,15 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+extern void assert_describe(
+ const char *expected_output,
+ const char *revparse_spec,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts);
+
+extern void assert_describe_workdir(
+ const char *expected_output,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts);
diff --git a/tests/describe/t6120.c b/tests/describe/t6120.c
new file mode 100644
index 000000000..2377335a5
--- /dev/null
+++ b/tests/describe/t6120.c
@@ -0,0 +1,156 @@
+#include "clar_libgit2.h"
+#include "describe_helpers.h"
+#include "repository.h"
+
+// Ported from https://github.com/git/git/blob/adfc1857bdb090786fd9d22c1acec39371c76048/t/t6120-describe.sh
+
+static git_repository *repo;
+
+void test_describe_t6120__initialize(void)
+{
+ repo = cl_git_sandbox_init("describe");
+}
+
+void test_describe_t6120__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_describe_t6120__default(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ assert_describe("A-*", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("A-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("R-*", "HEAD^^", repo, &opts, &fmt_opts);
+ assert_describe("A-*", "HEAD^^2", repo, &opts, &fmt_opts);
+ assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("R-*", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__tags(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+
+ assert_describe("c-*", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("c-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("e-*", "HEAD^^", repo, &opts, &fmt_opts);
+ assert_describe("c-*", "HEAD^^2", repo, &opts, &fmt_opts);
+ assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__all(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_ALL;
+
+ assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("tags/c-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__longformat(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ fmt_opts.always_use_long_format = 1;
+
+ assert_describe("B-0-*", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("A-3-*", "HEAD^^2", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__firstparent(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+
+ assert_describe("c-7-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.only_follow_first_parent = 1;
+ assert_describe("e-3-*", "HEAD", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__workdir(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ assert_describe_workdir("A-*[0-9a-f]", repo, &opts, &fmt_opts);
+ cl_git_mkfile("describe/file", "something different");
+
+ fmt_opts.dirty_suffix = "-dirty";
+ assert_describe_workdir("A-*[0-9a-f]-dirty", repo, &opts, &fmt_opts);
+ fmt_opts.dirty_suffix = ".mod";
+ assert_describe_workdir("A-*[0-9a-f].mod", repo, &opts, &fmt_opts);
+}
+
+static void commit_and_tag(
+ git_time_t *time,
+ const char *commit_msg,
+ const char *tag_name)
+{
+ git_index *index;
+ git_oid commit_id;
+ git_reference *ref;
+
+ cl_git_pass(git_repository_index__weakptr(&index, repo));
+
+ cl_git_append2file("describe/file", "\n");
+
+ git_index_add_bypath(index, "describe/file");
+ git_index_write(index);
+
+ *time += 10;
+ cl_repo_commit_from_index(&commit_id, repo, NULL, *time, commit_msg);
+
+ if (tag_name == NULL)
+ return;
+
+ cl_git_pass(git_reference_create(&ref, repo, tag_name, &commit_id, 0, NULL, NULL));
+ git_reference_free(ref);
+}
+
+void test_describe_t6120__pattern(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ git_oid tag_id;
+ git_object *head;
+ git_signature *tagger;
+ git_time_t time;
+
+ /* set-up matching pattern tests */
+ cl_git_pass(git_revparse_single(&head, repo, "HEAD"));
+
+ time = 1380553019;
+ cl_git_pass(git_signature_new(&tagger, "tagger", "tagger@libgit2.org", time, 0));
+ cl_git_pass(git_tag_create(&tag_id, repo, "test-annotated", head, tagger, "test-annotated", 0));
+ git_signature_free(tagger);
+ git_object_free(head);
+
+ commit_and_tag(&time, "one more", "refs/tags/test1-lightweight");
+ commit_and_tag(&time, "yet another", "refs/tags/test2-lightweight");
+ commit_and_tag(&time, "even more", NULL);
+
+
+ /* Exercize */
+ opts.pattern = "test-*";
+ assert_describe("test-annotated-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+ opts.pattern = "test1-*";
+ assert_describe("test1-lightweight-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.pattern = "test2-*";
+ assert_describe("test2-lightweight-*", "HEAD", repo, &opts, &fmt_opts);
+
+ fmt_opts.always_use_long_format = 1;
+ assert_describe("test2-lightweight-*", "HEAD^", repo, &opts, &fmt_opts);
+}
diff --git a/tests/resources/describe/.gitted/HEAD b/tests/resources/describe/.gitted/HEAD
new file mode 100644
index 000000000..cb4380516
--- /dev/null
+++ b/tests/resources/describe/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/describe/.gitted/config b/tests/resources/describe/.gitted/config
new file mode 100644
index 000000000..454e576b9
--- /dev/null
+++ b/tests/resources/describe/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/describe/.gitted/index b/tests/resources/describe/.gitted/index
new file mode 100644
index 000000000..f5f35e26b
--- /dev/null
+++ b/tests/resources/describe/.gitted/index
Binary files differ
diff --git a/tests/resources/describe/.gitted/logs/HEAD b/tests/resources/describe/.gitted/logs/HEAD
new file mode 100644
index 000000000..fc49c6fa3
--- /dev/null
+++ b/tests/resources/describe/.gitted/logs/HEAD
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 108b485d8268ea595df8ffea74f0f4b186577d32 nulltoken <emeric.fermas@gmail.com> 1380209394 +0200 commit (initial): initial
+108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209404 +0200 commit: second
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken <emeric.fermas@gmail.com> 1380209414 +0200 commit: third
+b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken <emeric.fermas@gmail.com> 1380209425 +0200 commit: A
+81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken <emeric.fermas@gmail.com> 1380209445 +0200 commit: c
+6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken <emeric.fermas@gmail.com> 1380209465 +0200 commit: B
+31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 merge c: Merge made by the 'recursive' strategy.
+ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken <emeric.fermas@gmail.com> 1380209496 +0200 commit: D
+6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken <emeric.fermas@gmail.com> 1380209527 +0200 commit: another
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken <emeric.fermas@gmail.com> 1380209547 +0200 commit: yet another
+a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken <emeric.fermas@gmail.com> 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy.
+949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken <emeric.fermas@gmail.com> 1380209567 +0200 commit: x
diff --git a/tests/resources/describe/.gitted/logs/refs/heads/master b/tests/resources/describe/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..fc49c6fa3
--- /dev/null
+++ b/tests/resources/describe/.gitted/logs/refs/heads/master
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 108b485d8268ea595df8ffea74f0f4b186577d32 nulltoken <emeric.fermas@gmail.com> 1380209394 +0200 commit (initial): initial
+108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209404 +0200 commit: second
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken <emeric.fermas@gmail.com> 1380209414 +0200 commit: third
+b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken <emeric.fermas@gmail.com> 1380209425 +0200 commit: A
+81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken <emeric.fermas@gmail.com> 1380209445 +0200 commit: c
+6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken <emeric.fermas@gmail.com> 1380209465 +0200 commit: B
+31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 merge c: Merge made by the 'recursive' strategy.
+ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken <emeric.fermas@gmail.com> 1380209496 +0200 commit: D
+6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken <emeric.fermas@gmail.com> 1380209527 +0200 commit: another
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken <emeric.fermas@gmail.com> 1380209547 +0200 commit: yet another
+a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken <emeric.fermas@gmail.com> 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy.
+949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken <emeric.fermas@gmail.com> 1380209567 +0200 commit: x
diff --git a/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d b/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d
new file mode 100644
index 000000000..4b98de8fb
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32
new file mode 100644
index 000000000..0d6187b10
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad
new file mode 100644
index 000000000..3540cfae0
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f
new file mode 100644
index 000000000..f2eaf83e5
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6
new file mode 100644
index 000000000..e44246b17
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf
new file mode 100644
index 000000000..a8769814b
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78
new file mode 100644
index 000000000..faf1fbe7c
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1
new file mode 100644
index 000000000..3353bf9ea
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
new file mode 100644
index 000000000..d0398e6e3
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b
new file mode 100644
index 000000000..7752a9558
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583
new file mode 100644
index 000000000..f27d552fd
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f
new file mode 100644
index 000000000..311ee2fad
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525
new file mode 100644
index 000000000..f04379f35
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359
new file mode 100644
index 000000000..5deb7ec06
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
new file mode 100644
index 000000000..4d5447467
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c
new file mode 100644
index 000000000..e71707f4b
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d
new file mode 100644
index 000000000..734f7dc42
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e
new file mode 100644
index 000000000..ef9f53a39
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 b/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6
new file mode 100644
index 000000000..36f198686
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6
@@ -0,0 +1,2 @@
+xK @]sٛ4|c<Co0S .yy:e&H4; #rl#ٳ8\-aUtc3Kmx
+OJ[Ti$W68k!Ԣ~U<)a \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495 b/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495
new file mode 100644
index 000000000..80a08fc3a
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1
new file mode 100644
index 000000000..9c773e4b5
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c
new file mode 100644
index 000000000..59fc7095c
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a
new file mode 100644
index 000000000..a2016f45f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c
new file mode 100644
index 000000000..e47f3fb85
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4
new file mode 100644
index 000000000..432b2c193
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e
new file mode 100644
index 000000000..2c7aafab5
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411
new file mode 100644
index 000000000..5fff6fa1f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a
new file mode 100644
index 000000000..eb9ab148a
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d
new file mode 100644
index 000000000..ee45b7650
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691 b/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691
new file mode 100644
index 000000000..320161a55
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691
@@ -0,0 +1,2 @@
+xA
+  A&1PJgcf3ta֤!uiqdF/촍zbFLBEwxsѤғ%0{cb$J)irFcm-U羷!oȨ[jyۇP]jrʨeDdßI>J[QhK% \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54
new file mode 100644
index 000000000..4cbaff192
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b
new file mode 100644
index 000000000..651ec782e
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 b/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705
new file mode 100644
index 000000000..fe86e7c7c
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705
@@ -0,0 +1,3 @@
+xM
+  AͧQ(WJc,ܿ7 j-+EnerY9 Xg* 8df
+Ad预[NB yEfqho^ѫ>՗}޹\P‘$~ ش9GG \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 b/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8
new file mode 100644
index 000000000..408c5da33
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4
new file mode 100644
index 000000000..4512d16d6
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20
new file mode 100644
index 000000000..a36463115
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8
new file mode 100644
index 000000000..2e15b4fb0
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
new file mode 100644
index 000000000..b2d51d93b
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
Binary files differ
diff --git a/tests/resources/describe/.gitted/refs/heads/master b/tests/resources/describe/.gitted/refs/heads/master
new file mode 100644
index 000000000..0b2a54130
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a6095f816e81f64651595d488badc42399837d6a
diff --git a/tests/resources/describe/.gitted/refs/tags/A b/tests/resources/describe/.gitted/refs/tags/A
new file mode 100644
index 000000000..aced4fd0f
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/A
@@ -0,0 +1 @@
+aaddd4f14847e0e323924ec262c2343249a84f8b
diff --git a/tests/resources/describe/.gitted/refs/tags/B b/tests/resources/describe/.gitted/refs/tags/B
new file mode 100644
index 000000000..ab1a5e69a
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/B
@@ -0,0 +1 @@
+52912fbab0715dec53d43053966e78ad213ba359
diff --git a/tests/resources/describe/.gitted/refs/tags/D b/tests/resources/describe/.gitted/refs/tags/D
new file mode 100644
index 000000000..90f420854
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/D
@@ -0,0 +1 @@
+10bd08b099ecb79184c60183f5c94ca915f427ad
diff --git a/tests/resources/describe/.gitted/refs/tags/R b/tests/resources/describe/.gitted/refs/tags/R
new file mode 100644
index 000000000..ef04b7c9f
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/R
@@ -0,0 +1 @@
+680166b6cd31f76354fee2572618e6b0142d05e6
diff --git a/tests/resources/describe/.gitted/refs/tags/c b/tests/resources/describe/.gitted/refs/tags/c
new file mode 100644
index 000000000..650d82fdb
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/c
@@ -0,0 +1 @@
+6126a5f9c57ebc81e64370ec3095184ad92dab1c
diff --git a/tests/resources/describe/.gitted/refs/tags/e b/tests/resources/describe/.gitted/refs/tags/e
new file mode 100644
index 000000000..5e88d6f13
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/e
@@ -0,0 +1 @@
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf
diff --git a/tests/resources/describe/another b/tests/resources/describe/another
new file mode 100644
index 000000000..a3d00fa8a
--- /dev/null
+++ b/tests/resources/describe/another
@@ -0,0 +1 @@
+DDD
diff --git a/tests/resources/describe/file b/tests/resources/describe/file
new file mode 100644
index 000000000..fd66be08d
--- /dev/null
+++ b/tests/resources/describe/file
@@ -0,0 +1 @@
+X
diff --git a/tests/resources/describe/side b/tests/resources/describe/side
new file mode 100644
index 000000000..fd66be08d
--- /dev/null
+++ b/tests/resources/describe/side
@@ -0,0 +1 @@
+X