diff options
Diffstat (limited to 'ref-filter.c')
| -rw-r--r-- | ref-filter.c | 341 |
1 files changed, 251 insertions, 90 deletions
diff --git a/ref-filter.c b/ref-filter.c index 0ac3fca67d..f7fb0c7e0e 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -895,7 +895,9 @@ static int head_atom_parser(struct ref_format *format UNUSED, { if (arg) return err_no_arg(err, "HEAD"); - atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL); + atom->u.head = refs_resolve_refdup(get_main_ref_store(the_repository), + "HEAD", RESOLVE_REF_READING, NULL, + NULL); return 0; } @@ -1611,6 +1613,12 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam if (formatp) { formatp++; parse_date_format(formatp, &date_mode); + + /* + * If this is a sort field and a format was specified, we'll + * want to compare formatted date by string value. + */ + v->atom->type = FIELD_STR; } if (!eoemail) @@ -1621,7 +1629,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam tz = strtol(zone, NULL, 10); if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) goto bad; - v->s = xstrdup(show_date(timestamp, tz, &date_mode)); + v->s = xstrdup(show_date(timestamp, tz, date_mode)); v->value = timestamp; date_mode_release(&date_mode); return; @@ -1985,7 +1993,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp struct strbuf s = STRBUF_INIT; /* Format the trailer info according to the trailer_opts given */ - format_trailers_from_commit(&s, subpos, &atom->u.contents.trailer_opts); + format_trailers_from_commit(&atom->u.contents.trailer_opts, subpos, &s); v->s = strbuf_detach(&s, NULL); } else if (atom->u.contents.option == C_BARE) @@ -2129,7 +2137,9 @@ static const char *rstrip_ref_components(const char *refname, int len) static const char *show_ref(struct refname_atom *atom, const char *refname) { if (atom->option == R_SHORT) - return shorten_unambiguous_ref(refname, warn_ambiguous_refs); + return refs_shorten_unambiguous_ref(get_main_ref_store(the_repository), + refname, + warn_ambiguous_refs); else if (atom->option == R_LSTRIP) return lstrip_ref_components(refname, atom->lstrip); else if (atom->option == R_RSTRIP) @@ -2332,8 +2342,10 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) CALLOC_ARRAY(ref->value, used_atom_cnt); if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) { - ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING, - NULL, NULL); + ref->symref = refs_resolve_refdup(get_main_ref_store(the_repository), + ref->refname, + RESOLVE_REF_READING, + NULL, NULL); if (!ref->symref) ref->symref = xstrdup(""); } @@ -2505,17 +2517,12 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) return 0; /* - * If it is a tag object, see if we use a value that derefs - * the object, and if we do grab the object it refers to. + * If it is a tag object, see if we use the peeled value. If we do, + * grab the peeled OID. */ - oi_deref.oid = *get_tagged_oid((struct tag *)obj); + if (need_tagged && peel_iterated_oid(the_repository, &obj->oid, &oi_deref.oid)) + die("bad tag"); - /* - * NEEDSWORK: This derefs tag only once, which - * is good to deal with chains of trust, but - * is not consistent with what deref_tag() does - * which peels the onion to the core. - */ return get_object(ref, 1, &obj, &oi_deref, err); } @@ -2627,13 +2634,20 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, each_ref_fn cb, void *cb_data) { + if (filter->kind & FILTER_REFS_ROOT_REFS) { + /* In this case, we want to print all refs including root refs. */ + return refs_for_each_include_root_refs(get_main_ref_store(the_repository), + cb, cb_data); + } + if (!filter->match_as_path) { /* * in this case, the patterns are applied after * prefixes like "refs/heads/" etc. are stripped off, * so 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), + "", NULL, cb, cb_data); } if (filter->ignore_case) { @@ -2642,7 +2656,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * so just return everything and let the caller * sort it out. */ - return for_each_fullref_in("", cb, cb_data); + return refs_for_each_fullref_in(get_main_ref_store(the_repository), + "", NULL, cb, cb_data); } if (!filter->name_patterns[0]) { @@ -2713,15 +2728,18 @@ static struct ref_array_item *new_ref_array_item(const char *refname, return ref; } +static void ref_array_append(struct ref_array *array, struct ref_array_item *ref) +{ + ALLOC_GROW(array->items, array->nr + 1, array->alloc); + array->items[array->nr++] = ref; +} + struct ref_array_item *ref_array_push(struct ref_array *array, const char *refname, const struct object_id *oid) { struct ref_array_item *ref = new_ref_array_item(refname, oid); - - ALLOC_GROW(array->items, array->nr + 1, array->alloc); - array->items[array->nr++] = ref; - + ref_array_append(array, ref); return ref; } @@ -2746,6 +2764,11 @@ static int ref_kind_from_refname(const char *refname) return ref_kind[i].kind; } + if (is_pseudo_ref(refname)) + return FILTER_REFS_PSEUDOREFS; + if (is_root_ref(refname)) + return FILTER_REFS_ROOT_REFS; + return FILTER_REFS_OTHERS; } @@ -2758,48 +2781,45 @@ static int filter_ref_kind(struct ref_filter *filter, const char *refname) return ref_kind_from_refname(refname); } -struct ref_filter_cbdata { - struct ref_array *array; - struct ref_filter *filter; - struct contains_cache contains_cache; - struct contains_cache no_contains_cache; -}; - -/* - * A call-back given to for_each_ref(). Filter refs and keep them for - * later object processing. - */ -static int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data) +static struct ref_array_item *apply_ref_filter(const char *refname, const struct object_id *oid, + int flag, struct ref_filter *filter) { - struct ref_filter_cbdata *ref_cbdata = cb_data; - struct ref_filter *filter = ref_cbdata->filter; struct ref_array_item *ref; struct commit *commit = NULL; unsigned int kind; if (flag & REF_BAD_NAME) { warning(_("ignoring ref with broken name %s"), refname); - return 0; + return NULL; } if (flag & REF_ISBROKEN) { warning(_("ignoring broken ref %s"), refname); - return 0; + return NULL; } /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */ kind = filter_ref_kind(filter, refname); - if (!(kind & filter->kind)) - return 0; + + /* + * Generally HEAD refs are printed with special description denoting a rebase, + * detached state and so forth. This is useful when only printing the HEAD ref + * But when it is being printed along with other root refs, it makes sense to + * keep the formatting consistent. So we mask the type to act like a root ref. + */ + if (filter->kind & FILTER_REFS_ROOT_REFS && kind == FILTER_REFS_DETACHED_HEAD) + kind = FILTER_REFS_ROOT_REFS; + else if (!(kind & filter->kind)) + return NULL; if (!filter_pattern_match(filter, refname)) - return 0; + return NULL; if (filter_exclude_match(filter, refname)) - return 0; + return NULL; if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname)) - return 0; + return NULL; /* * A merge filter is applied on refs pointing to commits. Hence @@ -2810,15 +2830,15 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, filter->with_commit || filter->no_commit || filter->verbose) { commit = lookup_commit_reference_gently(the_repository, oid, 1); if (!commit) - return 0; + return NULL; /* We perform the filtering for the '--contains' option... */ if (filter->with_commit && - !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache)) - return 0; + !commit_contains(filter, commit, filter->with_commit, &filter->internal.contains_cache)) + return NULL; /* ...or for the `--no-contains' option */ if (filter->no_commit && - commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache)) - return 0; + commit_contains(filter, commit, filter->no_commit, &filter->internal.no_contains_cache)) + return NULL; } /* @@ -2826,11 +2846,32 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, * to do its job and the resulting list may yet to be pruned * by maxcount logic. */ - ref = ref_array_push(ref_cbdata->array, refname, oid); + ref = new_ref_array_item(refname, oid); ref->commit = commit; ref->flag = flag; ref->kind = kind; + return ref; +} + +struct ref_filter_cbdata { + struct ref_array *array; + struct ref_filter *filter; +}; + +/* + * A call-back given to for_each_ref(). Filter refs and keep them for + * later object processing. + */ +static int filter_one(const char *refname, const struct object_id *oid, int flag, void *cb_data) +{ + struct ref_filter_cbdata *ref_cbdata = cb_data; + struct ref_array_item *ref; + + ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter); + if (ref) + ref_array_append(ref_cbdata->array, ref); + return 0; } @@ -2848,6 +2889,49 @@ static void free_array_item(struct ref_array_item *item) free(item); } +struct ref_filter_and_format_cbdata { + struct ref_filter *filter; + struct ref_format *format; + + struct ref_filter_and_format_internal { + int count; + } internal; +}; + +static int filter_and_format_one(const char *refname, const struct object_id *oid, int flag, void *cb_data) +{ + struct ref_filter_and_format_cbdata *ref_cbdata = cb_data; + struct ref_array_item *ref; + struct strbuf output = STRBUF_INIT, err = STRBUF_INIT; + + ref = apply_ref_filter(refname, oid, flag, ref_cbdata->filter); + if (!ref) + return 0; + + if (format_ref_array_item(ref, ref_cbdata->format, &output, &err)) + die("%s", err.buf); + + if (output.len || !ref_cbdata->format->array_opts.omit_empty) { + fwrite(output.buf, 1, output.len, stdout); + putchar('\n'); + } + + strbuf_release(&output); + strbuf_release(&err); + free_array_item(ref); + + /* + * Increment the running count of refs that match the filter. If + * max_count is set and we've reached the max, stop the ref + * iteration by returning a nonzero value. + */ + if (ref_cbdata->format->array_opts.max_count && + ++ref_cbdata->internal.count >= ref_cbdata->format->array_opts.max_count) + return 1; + + return 0; +} + /* Free all memory allocated for ref_array */ void ref_array_clear(struct ref_array *array) { @@ -2966,28 +3050,14 @@ void filter_ahead_behind(struct repository *r, free(commits); } -/* - * API for filtering a set of refs. Based on the type of refs the user - * has requested, we iterate through those refs and apply filters - * as per the given ref_filter structure and finally store the - * filtered refs in the ref_array structure. - */ -int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type) +static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data) { - struct ref_filter_cbdata ref_cbdata; - int save_commit_buffer_orig; int ret = 0; - ref_cbdata.array = array; - ref_cbdata.filter = filter; - filter->kind = type & FILTER_REFS_KIND_MASK; - save_commit_buffer_orig = save_commit_buffer; - save_commit_buffer = 0; - - init_contains_cache(&ref_cbdata.contains_cache); - init_contains_cache(&ref_cbdata.no_contains_cache); + init_contains_cache(&filter->internal.contains_cache); + init_contains_cache(&filter->internal.no_contains_cache); /* Simple per-ref filtering */ if (!filter->kind) @@ -3000,19 +3070,55 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int * of filter_ref_kind(). */ if (filter->kind == FILTER_REFS_BRANCHES) - ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata); + ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/heads/", NULL, + fn, cb_data); else if (filter->kind == FILTER_REFS_REMOTES) - ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata); + ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/remotes/", NULL, + fn, cb_data); else if (filter->kind == FILTER_REFS_TAGS) - ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata); - else if (filter->kind & FILTER_REFS_ALL) - ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata); - if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) - head_ref(ref_filter_handler, &ref_cbdata); + ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), + "refs/tags/", NULL, fn, + cb_data); + else if (filter->kind & FILTER_REFS_REGULAR) + ret = for_each_fullref_in_pattern(filter, fn, cb_data); + + /* + * When printing all ref types, HEAD is already included, + * so we don't want to print HEAD again. + */ + if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) && + (filter->kind & FILTER_REFS_DETACHED_HEAD)) + refs_head_ref(get_main_ref_store(the_repository), fn, + cb_data); } - clear_contains_cache(&ref_cbdata.contains_cache); - clear_contains_cache(&ref_cbdata.no_contains_cache); + clear_contains_cache(&filter->internal.contains_cache); + clear_contains_cache(&filter->internal.no_contains_cache); + + return ret; +} + +/* + * API for filtering a set of refs. Based on the type of refs the user + * has requested, we iterate through those refs and apply filters + * as per the given ref_filter structure and finally store the + * filtered refs in the ref_array structure. + */ +int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type) +{ + struct ref_filter_cbdata ref_cbdata; + int save_commit_buffer_orig; + int ret = 0; + + ref_cbdata.array = array; + ref_cbdata.filter = filter; + + save_commit_buffer_orig = save_commit_buffer; + save_commit_buffer = 0; + + ret = do_filter_refs(filter, type, filter_one, &ref_cbdata); /* Filters that need revision walking */ reach_filter(array, &filter->reachable_from, INCLUDE_REACHED); @@ -3022,6 +3128,51 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int return ret; } +static inline int can_do_iterative_format(struct ref_filter *filter, + struct ref_sorting *sorting, + struct ref_format *format) +{ + /* + * Filtering & formatting results within a single ref iteration + * callback is not compatible with options that require + * post-processing a filtered ref_array. These include: + * - filtering on reachability + * - sorting the filtered results + * - including ahead-behind information in the formatted output + */ + return !(filter->reachable_from || + filter->unreachable_from || + sorting || + format->bases.nr); +} + +void filter_and_format_refs(struct ref_filter *filter, unsigned int type, + struct ref_sorting *sorting, + struct ref_format *format) +{ + if (can_do_iterative_format(filter, sorting, format)) { + int save_commit_buffer_orig; + struct ref_filter_and_format_cbdata ref_cbdata = { + .filter = filter, + .format = format, + }; + + save_commit_buffer_orig = save_commit_buffer; + save_commit_buffer = 0; + + do_filter_refs(filter, type, filter_and_format_one, &ref_cbdata); + + save_commit_buffer = save_commit_buffer_orig; + } else { + struct ref_array array = { 0 }; + filter_refs(&array, filter, type); + filter_ahead_behind(the_repository, format, &array); + ref_array_sort(sorting, &array); + print_formatted_ref_array(&array, format); + ref_array_clear(&array); + } +} + static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b) { if (!(a->kind ^ b->kind)) @@ -3139,7 +3290,8 @@ void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array) { - QSORT_S(array->items, array->nr, compare_refs, sorting); + if (sorting) + QSORT_S(array->items, array->nr, compare_refs, sorting); } static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state) @@ -3210,6 +3362,29 @@ int format_ref_array_item(struct ref_array_item *info, return 0; } +void print_formatted_ref_array(struct ref_array *array, struct ref_format *format) +{ + int total; + struct strbuf output = STRBUF_INIT, err = STRBUF_INIT; + + total = format->array_opts.max_count; + if (!total || array->nr < total) + total = array->nr; + for (int i = 0; i < total; i++) { + strbuf_reset(&err); + strbuf_reset(&output); + if (format_ref_array_item(array->items[i], format, &output, &err)) + die("%s", err.buf); + if (output.len || !format->array_opts.omit_empty) { + fwrite(output.buf, 1, output.len, stdout); + putchar('\n'); + } + } + + strbuf_release(&err); + strbuf_release(&output); +} + void pretty_print_ref(const char *name, const struct object_id *oid, struct ref_format *format) { @@ -3245,18 +3420,6 @@ static int parse_sorting_atom(const char *atom) return res; } -/* If no sorting option is given, use refname to sort as default */ -static struct ref_sorting *ref_default_sorting(void) -{ - static const char cstr_name[] = "refname"; - - struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting)); - - sorting->next = NULL; - sorting->atom = parse_sorting_atom(cstr_name); - return sorting; -} - static void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg) { struct ref_sorting *s; @@ -3280,9 +3443,7 @@ struct ref_sorting *ref_sorting_options(struct string_list *options) struct string_list_item *item; struct ref_sorting *sorting = NULL, **tail = &sorting; - if (!options->nr) { - sorting = ref_default_sorting(); - } else { + if (options->nr) { for_each_string_list_item(item, options) parse_ref_sorting(tail, item->string); } |
