diff options
Diffstat (limited to 'ref-filter.c')
| -rw-r--r-- | ref-filter.c | 488 |
1 files changed, 444 insertions, 44 deletions
diff --git a/ref-filter.c b/ref-filter.c index 4991cd4f7a..fae9f4b8ed 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1,14 +1,15 @@ #include "git-compat-util.h" -#include "alloc.h" #include "environment.h" #include "gettext.h" +#include "config.h" #include "gpg-interface.h" #include "hex.h" #include "parse-options.h" +#include "run-command.h" #include "refs.h" #include "wildmatch.h" #include "object-name.h" -#include "object-store.h" +#include "object-store-ll.h" #include "oid-array.h" #include "repository.h" #include "commit.h" @@ -146,10 +147,12 @@ enum atom_type { ATOM_TAGGERDATE, ATOM_CREATOR, ATOM_CREATORDATE, + ATOM_DESCRIBE, ATOM_SUBJECT, ATOM_BODY, ATOM_TRAILERS, ATOM_CONTENTS, + ATOM_SIGNATURE, ATOM_RAW, ATOM_UPSTREAM, ATOM_PUSH, @@ -215,6 +218,11 @@ static struct used_atom { struct email_option { enum { EO_RAW, EO_TRIM, EO_LOCALPART } option; } email_option; + struct { + enum { S_BARE, S_GRADE, S_SIGNER, S_KEY, + S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option; + } signature; + const char **describe_args; struct refname_atom refname; char *head; } u; @@ -251,6 +259,110 @@ static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg) return -1; } +/* + * Parse option of name "candidate" in the option string "to_parse" of + * the form + * + * "candidate1[=val1],candidate2[=val2],candidate3[=val3],..." + * + * The remaining part of "to_parse" is stored in "end" (if we are + * parsing the last candidate, then this is NULL) and the value of + * the candidate is stored in "valuestart" and its length in "valuelen", + * that is the portion after "=". Since it is possible for a "candidate" + * to not have a value, in such cases, "valuestart" is set to point to + * NULL and "valuelen" to 0. + * + * The function returns 1 on success. It returns 0 if we don't find + * "candidate" in "to_parse" or we find "candidate" but it is followed + * by more chars (for example, "candidatefoo"), that is, we don't find + * an exact match. + * + * This function only does the above for one "candidate" at a time. So + * it has to be called each time trying to parse a "candidate" in the + * option string "to_parse". + */ +static int match_atom_arg_value(const char *to_parse, const char *candidate, + const char **end, const char **valuestart, + size_t *valuelen) +{ + const char *atom; + + if (!skip_prefix(to_parse, candidate, &atom)) + return 0; /* definitely not "candidate" */ + + if (*atom == '=') { + /* we just saw "candidate=" */ + *valuestart = atom + 1; + atom = strchrnul(*valuestart, ','); + *valuelen = atom - *valuestart; + } else if (*atom != ',' && *atom != '\0') { + /* key begins with "candidate" but has more chars */ + return 0; + } else { + /* just "candidate" without "=val" */ + *valuestart = NULL; + *valuelen = 0; + } + + /* atom points at either the ',' or NUL after this key[=val] */ + if (*atom == ',') + atom++; + else if (*atom) + BUG("Why is *atom not NULL yet?"); + + *end = atom; + return 1; +} + +/* + * Parse boolean option of name "candidate" in the option list "to_parse" + * of the form + * + * "candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..." + * + * The remaining part of "to_parse" is stored in "end" (if we are parsing + * the last candidate, then this is NULL) and the value (if given) is + * parsed and stored in "val", so "val" always points to either 0 or 1. + * If the value is not given, then "val" is set to point to 1. + * + * The boolean value is parsed using "git_parse_maybe_bool()", so the + * accepted values are + * + * to set true - "1", "yes", "true" + * to set false - "0", "no", "false" + * + * This function returns 1 on success. It returns 0 when we don't find + * an exact match for "candidate" or when the boolean value given is + * not valid. + */ +static int match_atom_bool_arg(const char *to_parse, const char *candidate, + const char **end, int *val) +{ + const char *argval; + char *strval; + size_t arglen; + int v; + + if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen)) + return 0; + + if (!argval) { + *val = 1; + return 1; + } + + strval = xstrndup(argval, arglen); + v = git_parse_maybe_bool(strval); + free(strval); + + if (v == -1) + return 0; + + *val = v; + + return 1; +} + static int color_atom_parser(struct ref_format *format, struct used_atom *atom, const char *color_value, struct strbuf *err) { @@ -407,6 +519,36 @@ static int subject_atom_parser(struct ref_format *format UNUSED, return 0; } +static int parse_signature_option(const char *arg) +{ + if (!arg) + return S_BARE; + else if (!strcmp(arg, "signer")) + return S_SIGNER; + else if (!strcmp(arg, "grade")) + return S_GRADE; + else if (!strcmp(arg, "key")) + return S_KEY; + else if (!strcmp(arg, "fingerprint")) + return S_FINGERPRINT; + else if (!strcmp(arg, "primarykeyfingerprint")) + return S_PRI_KEY_FP; + else if (!strcmp(arg, "trustlevel")) + return S_TRUST_LEVEL; + return -1; +} + +static int signature_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + int opt = parse_signature_option(arg); + if (opt < 0) + return err_bad_arg(err, "signature", arg); + atom->u.signature.option = opt; + return 0; +} + static int trailers_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom, const char *arg, struct strbuf *err) @@ -441,9 +583,10 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato atom->u.contents.option = C_BARE; else if (!strcmp(arg, "body")) atom->u.contents.option = C_BODY; - else if (!strcmp(arg, "size")) + else if (!strcmp(arg, "size")) { + atom->type = FIELD_ULONG; atom->u.contents.option = C_LENGTH; - else if (!strcmp(arg, "signature")) + } else if (!strcmp(arg, "signature")) atom->u.contents.option = C_SIG; else if (!strcmp(arg, "subject")) atom->u.contents.option = C_SUB; @@ -462,15 +605,97 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato return 0; } +static int describe_atom_option_parser(struct strvec *args, const char **arg, + struct strbuf *err) +{ + const char *argval; + size_t arglen = 0; + int optval = 0; + + if (match_atom_bool_arg(*arg, "tags", arg, &optval)) { + if (!optval) + strvec_push(args, "--no-tags"); + else + strvec_push(args, "--tags"); + return 1; + } + + if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) { + char *endptr; + + if (!arglen) + return strbuf_addf_ret(err, -1, + _("argument expected for %s"), + "describe:abbrev"); + if (strtol(argval, &endptr, 10) < 0) + return strbuf_addf_ret(err, -1, + _("positive value expected %s=%s"), + "describe:abbrev", argval); + if (endptr - argval != arglen) + return strbuf_addf_ret(err, -1, + _("cannot fully parse %s=%s"), + "describe:abbrev", argval); + + strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:match"); + + strvec_pushf(args, "--match=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:exclude"); + + strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval); + return 1; + } + + return 0; +} + +static int describe_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + struct strvec args = STRVEC_INIT; + + for (;;) { + int found = 0; + const char *bad_arg = arg; + + if (!arg || !*arg) + break; + + found = describe_atom_option_parser(&args, &arg, err); + if (found < 0) + return found; + if (!found) + return err_bad_arg(err, "describe", bad_arg); + } + atom->u.describe_args = strvec_detach(&args); + return 0; +} + static int raw_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom, const char *arg, struct strbuf *err) { if (!arg) atom->u.raw_data.option = RAW_BARE; - else if (!strcmp(arg, "size")) + else if (!strcmp(arg, "size")) { + atom->type = FIELD_ULONG; atom->u.raw_data.option = RAW_LENGTH; - else + } else return err_bad_arg(err, "raw", arg); return 0; } @@ -597,7 +822,7 @@ static int if_atom_parser(struct ref_format *format UNUSED, return 0; } -static int rest_atom_parser(struct ref_format *format, +static int rest_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom UNUSED, const char *arg, struct strbuf *err) { @@ -606,7 +831,8 @@ static int rest_atom_parser(struct ref_format *format, return 0; } -static int ahead_behind_atom_parser(struct ref_format *format, struct used_atom *atom, +static int ahead_behind_atom_parser(struct ref_format *format, + struct used_atom *atom UNUSED, const char *arg, struct strbuf *err) { struct string_list_item *item; @@ -664,10 +890,12 @@ static struct { [ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME }, [ATOM_CREATOR] = { "creator", SOURCE_OBJ }, [ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME }, + [ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser }, [ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser }, [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser }, [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser }, [ATOM_CONTENTS] = { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser }, + [ATOM_SIGNATURE] = { "signature", SOURCE_OBJ, FIELD_STR, signature_atom_parser }, [ATOM_RAW] = { "raw", SOURCE_OBJ, FIELD_STR, raw_atom_parser }, [ATOM_UPSTREAM] = { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser }, [ATOM_PUSH] = { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser }, @@ -1405,6 +1633,92 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void } } +static void grab_signature(struct atom_value *val, int deref, struct object *obj) +{ + int i; + struct commit *commit = (struct commit *) obj; + struct signature_check sigc = { 0 }; + int signature_checked = 0; + + for (i = 0; i < used_atom_cnt; i++) { + struct used_atom *atom = &used_atom[i]; + const char *name = atom->name; + struct atom_value *v = &val[i]; + int opt; + + if (!!deref != (*name == '*')) + continue; + if (deref) + name++; + + if (!skip_prefix(name, "signature", &name) || + (*name && *name != ':')) + continue; + if (!*name) + name = NULL; + else + name++; + + opt = parse_signature_option(name); + if (opt < 0) + continue; + + if (!signature_checked) { + check_commit_signature(commit, &sigc); + signature_checked = 1; + } + + switch (opt) { + case S_BARE: + v->s = xstrdup(sigc.output ? sigc.output: ""); + break; + case S_SIGNER: + v->s = xstrdup(sigc.signer ? sigc.signer : ""); + break; + case S_GRADE: + switch (sigc.result) { + case 'G': + switch (sigc.trust_level) { + case TRUST_UNDEFINED: + case TRUST_NEVER: + v->s = xstrfmt("%c", (char)'U'); + break; + default: + v->s = xstrfmt("%c", (char)'G'); + break; + } + break; + case 'B': + case 'E': + case 'N': + case 'X': + case 'Y': + case 'R': + v->s = xstrfmt("%c", (char)sigc.result); + break; + } + break; + case S_KEY: + v->s = xstrdup(sigc.key ? sigc.key : ""); + break; + case S_FINGERPRINT: + v->s = xstrdup(sigc.fingerprint ? + sigc.fingerprint : ""); + break; + case S_PRI_KEY_FP: + v->s = xstrdup(sigc.primary_key_fingerprint ? + sigc.primary_key_fingerprint : ""); + break; + case S_TRUST_LEVEL: + v->s = xstrdup(gpg_trust_level_to_str(sigc.trust_level)); + break; + } + } + + if (signature_checked) + signature_check_clear(&sigc); +} + static void find_subpos(const char *buf, const char **sub, size_t *sublen, const char **body, size_t *bodylen, @@ -1483,6 +1797,44 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size } } +static void grab_describe_values(struct atom_value *val, int deref, + struct object *obj) +{ + struct commit *commit = (struct commit *)obj; + int i; + + for (i = 0; i < used_atom_cnt; i++) { + struct used_atom *atom = &used_atom[i]; + enum atom_type type = atom->atom_type; + const char *name = atom->name; + struct atom_value *v = &val[i]; + + struct child_process cmd = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + + if (type != ATOM_DESCRIBE) + continue; + + if (!!deref != (*name == '*')) + continue; + + cmd.git_cmd = 1; + strvec_push(&cmd.args, "describe"); + strvec_pushv(&cmd.args, atom->u.describe_args); + strvec_push(&cmd.args, oid_to_hex(&commit->object.oid)); + if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) { + error(_("failed to run 'describe'")); + v->s = xstrdup(""); + continue; + } + strbuf_rtrim(&out); + v->s = strbuf_detach(&out, NULL); + + strbuf_release(&err); + } +} + /* See grab_values */ static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data) { @@ -1509,7 +1861,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp v->s = xmemdupz(buf, buf_size); v->s_size = buf_size; } else if (atom->u.raw_data.option == RAW_LENGTH) { - v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size); + v->value = buf_size; + v->s = xstrfmt("%"PRIuMAX, v->value); } continue; } @@ -1535,9 +1888,10 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp v->s = strbuf_detach(&sb, NULL); } else if (atom->u.contents.option == C_BODY_DEP) v->s = xmemdupz(bodypos, bodylen); - else if (atom->u.contents.option == C_LENGTH) - v->s = xstrfmt("%"PRIuMAX, (uintmax_t)strlen(subpos)); - else if (atom->u.contents.option == C_BODY) + else if (atom->u.contents.option == C_LENGTH) { + v->value = strlen(subpos); + v->s = xstrfmt("%"PRIuMAX, v->value); + } else if (atom->u.contents.option == C_BODY) v->s = xmemdupz(bodypos, nonsiglen); else if (atom->u.contents.option == C_SIG) v->s = xmemdupz(sigpos, siglen); @@ -1592,12 +1946,15 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s grab_tag_values(val, deref, obj); grab_sub_body_contents(val, deref, data); grab_person("tagger", val, deref, buf); + grab_describe_values(val, deref, obj); break; case OBJ_COMMIT: grab_commit_values(val, deref, obj); grab_sub_body_contents(val, deref, data); grab_person("author", val, deref, buf); grab_person("committer", val, deref, buf); + grab_signature(val, deref, obj); + grab_describe_values(val, deref, obj); break; case OBJ_TREE: /* grab_tree_values(val, deref, obj, buf, sz); */ @@ -1914,6 +2271,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) v->s_size = ATOM_SIZE_UNSPECIFIED; v->handler = append_atom; + v->value = 0; v->atom = atom; if (*name == '*') { @@ -2104,12 +2462,12 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom, * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref * matches "refs/heads/mas*", too). */ -static int match_pattern(const struct ref_filter *filter, const char *refname) +static int match_pattern(const char **patterns, const char *refname, + int ignore_case) { - const char **patterns = filter->name_patterns; unsigned flags = 0; - if (filter->ignore_case) + if (ignore_case) flags |= WM_CASEFOLD; /* @@ -2134,13 +2492,13 @@ static int match_pattern(const struct ref_filter *filter, const char *refname) * matches a pattern "refs/heads/" but not "refs/heads/m") or a * wildcard (e.g. the same ref matches "refs/heads/m*", too). */ -static int match_name_as_path(const struct ref_filter *filter, const char *refname) +static int match_name_as_path(const char **pattern, const char *refname, + int ignore_case) { - const char **pattern = filter->name_patterns; int namelen = strlen(refname); unsigned flags = WM_PATHNAME; - if (filter->ignore_case) + if (ignore_case) flags |= WM_CASEFOLD; for (; *pattern; pattern++) { @@ -2165,8 +2523,20 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname) if (!*filter->name_patterns) return 1; /* No pattern always matches */ if (filter->match_as_path) - return match_name_as_path(filter, refname); - return match_pattern(filter, refname); + return match_name_as_path(filter->name_patterns, refname, + filter->ignore_case); + return match_pattern(filter->name_patterns, refname, + filter->ignore_case); +} + +static int filter_exclude_match(struct ref_filter *filter, const char *refname) +{ + if (!filter->exclude.nr) + return 0; + if (filter->match_as_path) + return match_name_as_path(filter->exclude.v, refname, + filter->ignore_case); + return match_pattern(filter->exclude.v, refname, filter->ignore_case); } /* @@ -2198,43 +2568,53 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, if (!filter->name_patterns[0]) { /* no patterns; we have to look at everything */ - return for_each_fullref_in("", cb, cb_data); + return refs_for_each_fullref_in(get_main_ref_store(the_repository), + "", filter->exclude.v, cb, cb_data); } return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository), NULL, filter->name_patterns, + filter->exclude.v, cb, cb_data); } /* * Given a ref (oid, refname), check if the ref belongs to the array * of oids. If the given ref is a tag, check if the given tag points - * at one of the oids in the given oid array. + * at one of the oids in the given oid array. Returns non-zero if a + * match is found. + * * NEEDSWORK: - * 1. Only a single level of indirection is obtained, we might want to - * change this to account for multiple levels (e.g. annotated tags - * pointing to annotated tags pointing to a commit.) - * 2. As the refs are cached we might know what refname peels to without + * As the refs are cached we might know what refname peels to without * the need to parse the object via parse_object(). peel_ref() might be a * more efficient alternative to obtain the pointee. */ -static const struct object_id *match_points_at(struct oid_array *points_at, - const struct object_id *oid, - const char *refname) +static int match_points_at(struct oid_array *points_at, + const struct object_id *oid, + const char *refname) { - const struct object_id *tagged_oid = NULL; struct object *obj; if (oid_array_lookup(points_at, oid) >= 0) - return oid; - obj = parse_object(the_repository, oid); + return 1; + obj = parse_object_with_flags(the_repository, oid, + PARSE_OBJECT_SKIP_HASH_CHECK); + while (obj && obj->type == OBJ_TAG) { + struct tag *tag = (struct tag *)obj; + + if (parse_tag(tag) < 0) { + obj = NULL; + break; + } + + if (oid_array_lookup(points_at, get_tagged_oid(tag)) >= 0) + return 1; + + obj = tag->tagged; + } if (!obj) die(_("malformed object at '%s'"), refname); - if (obj->type == OBJ_TAG) - tagged_oid = get_tagged_oid((struct tag *)obj); - if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0) - return tagged_oid; - return NULL; + return 0; } /* @@ -2336,6 +2716,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, if (!filter_pattern_match(filter, refname)) return 0; + if (filter_exclude_match(filter, refname)) + return 0; + if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname)) return 0; @@ -2418,13 +2801,13 @@ void ref_array_clear(struct ref_array *array) #define EXCLUDE_REACHED 0 #define INCLUDE_REACHED 1 static void reach_filter(struct ref_array *array, - struct commit_list *check_reachable, + struct commit_list **check_reachable, int include_reached) { int i, old_nr; struct commit **to_clear; - if (!check_reachable) + if (!*check_reachable) return; CALLOC_ARRAY(to_clear, array->nr); @@ -2434,7 +2817,7 @@ static void reach_filter(struct ref_array *array, } tips_reachable_from_bases(the_repository, - check_reachable, + *check_reachable, to_clear, array->nr, UNINTERESTING); @@ -2455,8 +2838,8 @@ static void reach_filter(struct ref_array *array, clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS); - while (check_reachable) { - struct commit *merge_commit = pop_commit(&check_reachable); + while (*check_reachable) { + struct commit *merge_commit = pop_commit(check_reachable); clear_commit_marks(merge_commit, ALL_REV_FLAGS); } @@ -2553,8 +2936,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int clear_contains_cache(&ref_cbdata.no_contains_cache); /* Filters that need revision walking */ - reach_filter(array, filter->reachable_from, INCLUDE_REACHED); - reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED); + reach_filter(array, &filter->reachable_from, INCLUDE_REACHED); + reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED); save_commit_buffer = save_commit_buffer_orig; return ret; @@ -2866,3 +3249,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) return 0; } + +void ref_filter_init(struct ref_filter *filter) +{ + struct ref_filter blank = REF_FILTER_INIT; + memcpy(filter, &blank, sizeof(blank)); +} + +void ref_filter_clear(struct ref_filter *filter) +{ + strvec_clear(&filter->exclude); + oid_array_clear(&filter->points_at); + free_commit_list(filter->with_commit); + free_commit_list(filter->no_commit); + free_commit_list(filter->reachable_from); + free_commit_list(filter->unreachable_from); + ref_filter_init(filter); +} |
