unsigned reverse : 1;
};
-struct refinfo {
- char *refname;
+struct ref_array_item {
unsigned char objectname[20];
int flag;
const char *symref;
struct atom_value *value;
+ char *refname;
+};
+
+struct ref_array {
+ int nr, alloc;
+ struct ref_array_item **items;
+};
+
+struct ref_filter {
+ const char **name_patterns;
+};
+
+struct ref_filter_cbdata {
+ struct ref_array array;
+ struct ref_filter filter;
};
static struct {
{ "contents:body" },
{ "contents:signature" },
{ "upstream" },
+ { "push" },
{ "symref" },
{ "flag" },
{ "HEAD" },
* a "*" to denote deref_tag().
*
* We parse given format string and sort specifiers, and make a list
- * of properties that we need to extract out of objects. refinfo
+ * of properties that we need to extract out of objects. ref_array_item
* structure will hold an array of values extracted that can be
* indexed with the "atom number", which is an index into this
* array.
/*
* Used to parse format string and sort specifiers
*/
-static int parse_atom(const char *atom, const char *ep)
+int parse_ref_filter_atom(const char *atom, const char *ep)
{
const char *sp;
int i, at;
* Make sure the format string is well formed, and parse out
* the used atoms.
*/
-static int verify_format(const char *format)
+int verify_ref_format(const char *format)
{
const char *cp, *sp;
if (!ep)
return error("malformed format string %s", sp);
/* sp points at "%(" and ep points at the closing ")" */
- at = parse_atom(sp + 2, ep);
+ at = parse_ref_filter_atom(sp + 2, ep);
cp = ep + 1;
if (skip_prefix(used_atom[at], "color:", &color))
/*
* We got here because atomname ends in "date" or "date<something>";
* it's not possible that <something> is not ":<format>" because
- * parse_atom() wouldn't have allowed it, so we can assume that no
+ * parse_ref_filter_atom() wouldn't have allowed it, so we can assume that no
* ":" means no format is specified, and use the default.
*/
formatp = strchr(atomname, ':');
/*
* Parse the object referred by ref, and grab needed value.
*/
-static void populate_value(struct refinfo *ref)
+static void populate_value(struct ref_array_item *ref)
{
void *buf;
struct object *obj;
else if (starts_with(name, "symref"))
refname = ref->symref ? ref->symref : "";
else if (starts_with(name, "upstream")) {
+ const char *branch_name;
/* only local branches may have an upstream */
- if (!starts_with(ref->refname, "refs/heads/"))
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
continue;
- branch = branch_get(ref->refname + 11);
+ branch = branch_get(branch_name);
- if (!branch || !branch->merge || !branch->merge[0] ||
- !branch->merge[0]->dst)
+ refname = branch_get_upstream(branch, NULL);
+ if (!refname)
+ continue;
+ } else if (starts_with(name, "push")) {
+ const char *branch_name;
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
+ continue;
+ branch = branch_get(branch_name);
+
+ refname = branch_get_push(branch, NULL);
+ if (!refname)
continue;
- refname = branch->merge[0]->dst;
} else if (starts_with(name, "color:")) {
char color[COLOR_MAXLEN] = "";
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
else if (!strcmp(formatp, "track") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
char buf[40];
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
}
continue;
} else if (!strcmp(formatp, "trackshort") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
assert(branch);
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
* Given a ref, return the value for the atom. This lazily gets value
* out of the object by calling populate value.
*/
-static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
+static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom_value **v)
{
if (!ref->value) {
populate_value(ref);
*v = &ref->value[atom];
}
-struct grab_ref_cbdata {
- struct refinfo **grab_array;
- const char **grab_pattern;
- int grab_cnt;
-};
+/*
+ * Return 1 if the refname matches one of the patterns, otherwise 0.
+ * A pattern can be path prefix (e.g. a refname "refs/heads/master"
+ * matches a pattern "refs/heads/") or a wildcard (e.g. the same ref
+ * matches "refs/heads/m*",too).
+ */
+static int match_name_as_path(const char **pattern, const char *refname)
+{
+ int namelen = strlen(refname);
+ for (; *pattern; pattern++) {
+ const char *p = *pattern;
+ int plen = strlen(p);
+
+ if ((plen <= namelen) &&
+ !strncmp(refname, p, plen) &&
+ (refname[plen] == '\0' ||
+ refname[plen] == '/' ||
+ p[plen-1] == '/'))
+ return 1;
+ if (!wildmatch(p, refname, WM_PATHNAME, NULL))
+ return 1;
+ }
+ return 0;
+}
+
+/* Allocate space for a new ref_array_item and copy the objectname and flag to it */
+static struct ref_array_item *new_ref_array_item(const char *refname,
+ const unsigned char *objectname,
+ int flag)
+{
+ struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item));
+ ref->refname = xstrdup(refname);
+ hashcpy(ref->objectname, objectname);
+ ref->flag = flag;
+
+ return ref;
+}
/*
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
-static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+int ref_filter_handler(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- struct grab_ref_cbdata *cb = cb_data;
- struct refinfo *ref;
- int cnt;
+ struct ref_filter_cbdata *ref_cbdata = cb_data;
+ struct ref_filter *filter = &ref_cbdata->filter;
+ struct ref_array_item *ref;
if (flag & REF_BAD_NAME) {
warning("ignoring ref with broken name %s", refname);
return 0;
}
- if (*cb->grab_pattern) {
- const char **pattern;
- int namelen = strlen(refname);
- for (pattern = cb->grab_pattern; *pattern; pattern++) {
- const char *p = *pattern;
- int plen = strlen(p);
-
- if ((plen <= namelen) &&
- !strncmp(refname, p, plen) &&
- (refname[plen] == '\0' ||
- refname[plen] == '/' ||
- p[plen-1] == '/'))
- break;
- if (!wildmatch(p, refname, WM_PATHNAME, NULL))
- break;
- }
- if (!*pattern)
- return 0;
- }
+ if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
+ return 0;
/*
* We do not open the object yet; sort may only need refname
* to do its job and the resulting list may yet to be pruned
* by maxcount logic.
*/
- ref = xcalloc(1, sizeof(*ref));
- ref->refname = xstrdup(refname);
- hashcpy(ref->objectname, sha1);
- ref->flag = flag;
+ ref = new_ref_array_item(refname, oid->hash, flag);
- cnt = cb->grab_cnt;
- REALLOC_ARRAY(cb->grab_array, cnt + 1);
- cb->grab_array[cnt++] = ref;
- cb->grab_cnt = cnt;
+ REALLOC_ARRAY(ref_cbdata->array.items, ref_cbdata->array.nr + 1);
+ ref_cbdata->array.items[ref_cbdata->array.nr++] = ref;
return 0;
}
-static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
+/* Free memory allocated for a ref_array_item */
+static void free_array_item(struct ref_array_item *item)
+{
+ free((char *)item->symref);
+ free(item->refname);
+ free(item);
+}
+
+/* Free all memory allocated for ref_array */
+void ref_array_clear(struct ref_array *array)
+{
+ int i;
+
+ for (i = 0; i < array->nr; i++)
+ free_array_item(array->items[i]);
+ free(array->items);
+ array->items = NULL;
+ array->nr = array->alloc = 0;
+}
+
+static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
{
struct atom_value *va, *vb;
int cmp;
cmp_type cmp_type = used_atom_type[s->atom];
- get_value(a, s->atom, &va);
- get_value(b, s->atom, &vb);
+ get_ref_atom_value(a, s->atom, &va);
+ get_ref_atom_value(b, s->atom, &vb);
switch (cmp_type) {
case FIELD_STR:
cmp = strcmp(va->s, vb->s);
return (s->reverse) ? -cmp : cmp;
}
-static struct ref_sort *ref_sort;
+static struct ref_sorting *ref_sorting;
static int compare_refs(const void *a_, const void *b_)
{
- struct refinfo *a = *((struct refinfo **)a_);
- struct refinfo *b = *((struct refinfo **)b_);
- struct ref_sort *s;
+ struct ref_array_item *a = *((struct ref_array_item **)a_);
+ struct ref_array_item *b = *((struct ref_array_item **)b_);
+ struct ref_sorting *s;
- for (s = ref_sort; s; s = s->next) {
- int cmp = cmp_ref_sort(s, a, b);
+ for (s = ref_sorting; s; s = s->next) {
+ int cmp = cmp_ref_sorting(s, a, b);
if (cmp)
return cmp;
}
return 0;
}
-static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+void ref_array_sort(struct ref_sorting *sort, struct ref_array *array)
{
- ref_sort = sort;
- qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
+ ref_sorting = sort;
+ qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
}
static void print_value(struct atom_value *v, int quote_style)
}
}
-static void show_ref(struct refinfo *info, const char *format, int quote_style)
+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
{
const char *cp, *sp, *ep;
ep = strchr(sp, ')');
if (cp < sp)
emit(cp, sp);
- get_value(info, parse_atom(sp + 2, ep), &atomv);
+ get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
print_value(atomv, quote_style);
}
if (*cp) {
putchar('\n');
}
-static struct ref_sort *default_sort(void)
+/* If no sorting option is given, use refname to sort as default */
+struct ref_sorting *ref_default_sorting(void)
{
static const char cstr_name[] = "refname";
- struct ref_sort *sort = xcalloc(1, sizeof(*sort));
+ struct ref_sorting *sort = xcalloc(1, sizeof(*sort));
sort->next = NULL;
- sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
+ sort->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));
return sort;
}
-static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
{
- struct ref_sort **sort_tail = opt->value;
- struct ref_sort *s;
+ struct ref_sorting **sort_tail = opt->value;
+ struct ref_sorting *s;
int len;
if (!arg) /* should --no-sort void the list ? */
arg++;
}
len = strlen(arg);
- s->atom = parse_atom(arg, arg+len);
+ s->atom = parse_ref_filter_atom(arg, arg+len);
return 0;
}
static char const * const for_each_ref_usage[] = {
- N_("git for-each-ref [options] [<pattern>]"),
+ N_("git for-each-ref [<options>] [<pattern>]"),
NULL
};
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
- int i, num_refs;
+ int i;
const char *format = "%(objectname) %(objecttype)\t%(refname)";
- struct ref_sort *sort = NULL, **sort_tail = &sort;
+ struct ref_sorting *sort = NULL, **sort_tail = &sort;
int maxcount = 0, quote_style = 0;
- struct refinfo **refs;
- struct grab_ref_cbdata cbdata;
+ struct ref_filter_cbdata ref_cbdata;
struct option opts[] = {
OPT_BIT('s', "shell", "e_style,
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
OPT_CALLBACK(0 , "sort", sort_tail, N_("key"),
- N_("field name to sort on"), &opt_parse_sort),
+ N_("field name to sort on"), &parse_opt_ref_sorting),
OPT_END(),
};
error("more than one quoting style?");
usage_with_options(for_each_ref_usage, opts);
}
- if (verify_format(format))
+ if (verify_ref_format(format))
usage_with_options(for_each_ref_usage, opts);
if (!sort)
- sort = default_sort();
+ sort = ref_default_sorting();
/* for warn_ambiguous_refs */
git_config(git_default_config, NULL);
- memset(&cbdata, 0, sizeof(cbdata));
- cbdata.grab_pattern = argv;
- for_each_rawref(grab_single_ref, &cbdata);
- refs = cbdata.grab_array;
- num_refs = cbdata.grab_cnt;
+ memset(&ref_cbdata, 0, sizeof(ref_cbdata));
+ ref_cbdata.filter.name_patterns = argv;
+ for_each_rawref(ref_filter_handler, &ref_cbdata);
- sort_refs(sort, refs, num_refs);
+ ref_array_sort(sort, &ref_cbdata.array);
- if (!maxcount || num_refs < maxcount)
- maxcount = num_refs;
+ if (!maxcount || ref_cbdata.array.nr < maxcount)
+ maxcount = ref_cbdata.array.nr;
for (i = 0; i < maxcount; i++)
- show_ref(refs[i], format, quote_style);
+ show_ref_array_item(ref_cbdata.array.items[i], format, quote_style);
+ ref_array_clear(&ref_cbdata.array);
return 0;
}