diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-10-09 22:24:40 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-10-09 22:24:40 +0200 |
commit | 46a2b8e855d5f6d8b60b81500a9f6779c7f63e63 (patch) | |
tree | eec898cff4c27feb6e50e5265da9da7b5dd59ff4 | |
parent | 324154a4538f9e821cc1065b72109033b6d5da03 (diff) | |
parent | a3b9270dcfe61ce3429f82b514b302667d05bfc5 (diff) | |
download | libgit2-46a2b8e855d5f6d8b60b81500a9f6779c7f63e63.tar.gz |
Merge pull request #2592 from libgit2/cmn/describe
Implement git-describe
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(¤t_id, repo, GIT_HEAD_FILE)) < 0) + return error; + + if ((error = git_object_lookup(&commit, repo, ¤t_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 Binary files differnew file mode 100644 index 000000000..f5f35e26b --- /dev/null +++ b/tests/resources/describe/.gitted/index 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 Binary files differnew file mode 100644 index 000000000..4b98de8fb --- /dev/null +++ b/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d diff --git a/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 Binary files differnew file mode 100644 index 000000000..0d6187b10 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 diff --git a/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad Binary files differnew file mode 100644 index 000000000..3540cfae0 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad diff --git a/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f Binary files differnew file mode 100644 index 000000000..f2eaf83e5 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f diff --git a/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 Binary files differnew file mode 100644 index 000000000..e44246b17 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 diff --git a/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf Binary files differnew file mode 100644 index 000000000..a8769814b --- /dev/null +++ b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf diff --git a/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 Binary files differnew file mode 100644 index 000000000..faf1fbe7c --- /dev/null +++ b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 diff --git a/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 Binary files differnew file mode 100644 index 000000000..3353bf9ea --- /dev/null +++ b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 diff --git a/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 Binary files differnew file mode 100644 index 000000000..d0398e6e3 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 diff --git a/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b Binary files differnew file mode 100644 index 000000000..7752a9558 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b diff --git a/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 Binary files differnew file mode 100644 index 000000000..f27d552fd --- /dev/null +++ b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 diff --git a/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f Binary files differnew file mode 100644 index 000000000..311ee2fad --- /dev/null +++ b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f diff --git a/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 Binary files differnew file mode 100644 index 000000000..f04379f35 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 diff --git a/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 Binary files differnew file mode 100644 index 000000000..5deb7ec06 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 diff --git a/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 Binary files differnew file mode 100644 index 000000000..4d5447467 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 diff --git a/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c Binary files differnew file mode 100644 index 000000000..e71707f4b --- /dev/null +++ b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c diff --git a/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d Binary files differnew file mode 100644 index 000000000..734f7dc42 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d diff --git a/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e Binary files differnew file mode 100644 index 000000000..ef9f53a39 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e 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 Binary files differnew file mode 100644 index 000000000..80a08fc3a --- /dev/null +++ b/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495 diff --git a/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 Binary files differnew file mode 100644 index 000000000..9c773e4b5 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 diff --git a/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c Binary files differnew file mode 100644 index 000000000..59fc7095c --- /dev/null +++ b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c diff --git a/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a Binary files differnew file mode 100644 index 000000000..a2016f45f --- /dev/null +++ b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a diff --git a/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c Binary files differnew file mode 100644 index 000000000..e47f3fb85 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c diff --git a/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 Binary files differnew file mode 100644 index 000000000..432b2c193 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 diff --git a/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e Binary files differnew file mode 100644 index 000000000..2c7aafab5 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e diff --git a/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 Binary files differnew file mode 100644 index 000000000..5fff6fa1f --- /dev/null +++ b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 diff --git a/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a Binary files differnew file mode 100644 index 000000000..eb9ab148a --- /dev/null +++ b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a diff --git a/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d Binary files differnew file mode 100644 index 000000000..ee45b7650 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d 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 Binary files differnew file mode 100644 index 000000000..4cbaff192 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 diff --git a/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b Binary files differnew file mode 100644 index 000000000..651ec782e --- /dev/null +++ b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b 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,ܿ7j-+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 Binary files differnew file mode 100644 index 000000000..408c5da33 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 diff --git a/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 Binary files differnew file mode 100644 index 000000000..4512d16d6 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 diff --git a/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 Binary files differnew file mode 100644 index 000000000..a36463115 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 diff --git a/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 Binary files differnew file mode 100644 index 000000000..2e15b4fb0 --- /dev/null +++ b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 diff --git a/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 Binary files differnew file mode 100644 index 000000000..b2d51d93b --- /dev/null +++ b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 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
|