#include "blob.h"
#include "quote.h"
#include "parse-options.h"
+#include "remote.h"
/* Quoting styles */
#define QUOTE_NONE 0
struct refinfo {
char *refname;
unsigned char objectname[20];
+ int flag;
+ const char *symref;
struct atom_value *value;
};
{ "subject" },
{ "body" },
{ "contents" },
+ { "upstream" },
+ { "symref" },
+ { "flag" },
};
/*
*/
static const char **used_atom;
static cmp_type *used_atom_type;
-static int used_atom_cnt, sort_atom_limit, need_tagged;
+static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref;
/*
* Used to parse format string and sort specifiers
(sizeof(*used_atom_type) * used_atom_cnt));
used_atom[at] = xmemdupz(atom, ep - atom);
used_atom_type[at] = valid_atom[i].cmp_type;
+ if (*atom == '*')
+ need_tagged = 1;
+ if (!strcmp(used_atom[at], "symref"))
+ need_symref = 1;
return at;
}
{
while (*cp) {
if (*cp == '%') {
- /* %( is the start of an atom;
+ /*
+ * %( is the start of an atom;
* %% is a quoted per-cent.
*/
if (cp[1] == '(')
static const char *copy_email(const char *buf)
{
const char *email = strchr(buf, '<');
- const char *eoemail = strchr(email, '>');
- if (!email || !eoemail)
+ const char *eoemail;
+ if (!email)
+ return "";
+ eoemail = strchr(email, '>');
+ if (!eoemail)
return "";
return xmemdupz(email, eoemail + 1 - email);
}
grab_date(wholine, v, name);
}
- /* For a tag or a commit object, if "creator" or "creatordate" is
+ /*
+ * For a tag or a commit object, if "creator" or "creatordate" is
* requested, do something special.
*/
if (strcmp(who, "tagger") && strcmp(who, "committer"))
}
}
-/* We want to have empty print-string for field requests
+/*
+ * We want to have empty print-string for field requests
* that do not apply (e.g. "authordate" for a tag object)
*/
static void fill_missing_values(struct atom_value *val)
}
}
-/*
- * generate a format suitable for scanf from a ref_rev_parse_rules
- * rule, that is replace the "%.*s" spec with a "%s" spec
- */
-static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
+static inline char *copy_advance(char *dst, const char *src)
{
- char *spec;
-
- spec = strstr(rule, "%.*s");
- if (!spec || strstr(spec + 4, "%.*s"))
- die("invalid rule in ref_rev_parse_rules: %s", rule);
-
- /* copy all until spec */
- strncpy(scanf_fmt, rule, spec - rule);
- scanf_fmt[spec - rule] = '\0';
- /* copy new spec */
- strcat(scanf_fmt, "%s");
- /* copy remaining rule */
- strcat(scanf_fmt, spec + 4);
-
- return;
-}
-
-/*
- * Shorten the refname to an non-ambiguous form
- */
-static char *get_short_ref(struct refinfo *ref)
-{
- int i;
- static char **scanf_fmts;
- static int nr_rules;
- char *short_name;
-
- /* pre generate scanf formats from ref_rev_parse_rules[] */
- if (!nr_rules) {
- size_t total_len = 0;
-
- /* the rule list is NULL terminated, count them first */
- for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
- /* no +1 because strlen("%s") < strlen("%.*s") */
- total_len += strlen(ref_rev_parse_rules[nr_rules]);
-
- scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
-
- total_len = 0;
- for (i = 0; i < nr_rules; i++) {
- scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
- + total_len;
- gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
- total_len += strlen(ref_rev_parse_rules[i]);
- }
- }
-
- /* bail out if there are no rules */
- if (!nr_rules)
- return ref->refname;
-
- /* buffer for scanf result, at most ref->refname must fit */
- short_name = xstrdup(ref->refname);
-
- /* skip first rule, it will always match */
- for (i = nr_rules - 1; i > 0 ; --i) {
- int j;
- int short_name_len;
-
- if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
- continue;
-
- short_name_len = strlen(short_name);
-
- /*
- * check if the short name resolves to a valid ref,
- * but use only rules prior to the matched one
- */
- for (j = 0; j < i; j++) {
- const char *rule = ref_rev_parse_rules[j];
- unsigned char short_objectname[20];
- char refname[PATH_MAX];
-
- /*
- * the short name is ambiguous, if it resolves
- * (with this previous rule) to a valid ref
- * read_ref() returns 0 on success
- */
- mksnpath(refname, sizeof(refname),
- rule, short_name_len, short_name);
- if (!read_ref(refname, short_objectname))
- break;
- }
-
- /*
- * short name is non-ambiguous if all previous rules
- * haven't resolved to a valid ref
- */
- if (j == i)
- return short_name;
- }
-
- free(short_name);
- return ref->refname;
+ while (*src)
+ *dst++ = *src++;
+ return dst;
}
-
/*
* Parse the object referred by ref, and grab needed value.
*/
ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
- buf = get_obj(ref->objectname, &obj, &size, &eaten);
- if (!buf)
- die("missing object %s for %s",
- sha1_to_hex(ref->objectname), ref->refname);
- if (!obj)
- die("parse_object_buffer failed on %s for %s",
- sha1_to_hex(ref->objectname), ref->refname);
+ if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
+ unsigned char unused1[20];
+ const char *symref;
+ symref = resolve_ref(ref->refname, unused1, 1, NULL);
+ if (symref)
+ ref->symref = xstrdup(symref);
+ else
+ ref->symref = "";
+ }
/* Fill in specials first */
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i];
struct atom_value *v = &ref->value[i];
int deref = 0;
+ const char *refname;
+ const char *formatp;
+
if (*name == '*') {
deref = 1;
name++;
}
- if (!prefixcmp(name, "refname")) {
- const char *formatp = strchr(name, ':');
- const char *refname = ref->refname;
-
- /* look for "short" refname format */
- if (formatp) {
- formatp++;
- if (!strcmp(formatp, "short"))
- refname = get_short_ref(ref);
- else
- die("unknown refname format %s",
- formatp);
- }
- if (!deref)
- v->s = refname;
+ if (!prefixcmp(name, "refname"))
+ refname = ref->refname;
+ else if (!prefixcmp(name, "symref"))
+ refname = ref->symref ? ref->symref : "";
+ else if (!prefixcmp(name, "upstream")) {
+ struct branch *branch;
+ /* only local branches may have an upstream */
+ if (prefixcmp(ref->refname, "refs/heads/"))
+ continue;
+ branch = branch_get(ref->refname + 11);
+
+ if (!branch || !branch->merge || !branch->merge[0] ||
+ !branch->merge[0]->dst)
+ continue;
+ refname = branch->merge[0]->dst;
+ }
+ else if (!strcmp(name, "flag")) {
+ char buf[256], *cp = buf;
+ if (ref->flag & REF_ISSYMREF)
+ cp = copy_advance(cp, ",symref");
+ if (ref->flag & REF_ISPACKED)
+ cp = copy_advance(cp, ",packed");
+ if (cp == buf)
+ v->s = "";
else {
- int len = strlen(refname);
- char *s = xmalloc(len + 4);
- sprintf(s, "%s^{}", refname);
- v->s = s;
+ *cp = '\0';
+ v->s = xstrdup(buf + 1);
}
+ continue;
+ }
+ else
+ continue;
+
+ formatp = strchr(name, ':');
+ /* look for "short" refname format */
+ if (formatp) {
+ formatp++;
+ if (!strcmp(formatp, "short"))
+ refname = shorten_unambiguous_ref(refname,
+ warn_ambiguous_refs);
+ else
+ die("unknown %.*s format %s",
+ (int)(formatp - name), name, formatp);
+ }
+
+ if (!deref)
+ v->s = refname;
+ else {
+ int len = strlen(refname);
+ char *s = xmalloc(len + 4);
+ sprintf(s, "%s^{}", refname);
+ v->s = s;
}
}
+ for (i = 0; i < used_atom_cnt; i++) {
+ struct atom_value *v = &ref->value[i];
+ if (v->s == NULL)
+ goto need_obj;
+ }
+ return;
+
+ need_obj:
+ buf = get_obj(ref->objectname, &obj, &size, &eaten);
+ if (!buf)
+ die("missing object %s for %s",
+ sha1_to_hex(ref->objectname), ref->refname);
+ if (!obj)
+ die("parse_object_buffer failed on %s for %s",
+ sha1_to_hex(ref->objectname), ref->refname);
+
grab_values(ref->value, 0, obj, buf, size);
if (!eaten)
free(buf);
- /* If there is no atom that wants to know about tagged
+ /*
+ * If there is no atom that wants to know about tagged
* object, we are done.
*/
if (!need_tagged || (obj->type != OBJ_TAG))
return;
- /* If it is a tag object, see if we use a value that derefs
+ /*
+ * 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.
*/
tagged = ((struct tag *)obj)->tagged->sha1;
- /* NEEDSWORK: This derefs tag only once, which
+ /*
+ * 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.
};
/*
- * A call-back given to for_each_ref(). It is unfortunate that we
- * need to use global variables to pass extra information to this
- * function.
+ * 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)
{
return 0;
}
- /* We do not open the object yet; sort may only need refname
+ /*
+ * 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;
cnt = cb->grab_cnt;
cb->grab_array = xrealloc(cb->grab_array,
return -1;
*sort_tail = s = xcalloc(1, sizeof(*s));
- sort_tail = &s->next;
if (*arg == '-') {
s->reverse = 1;
OPT_END(),
};
- parse_options(argc, argv, opts, for_each_ref_usage, 0);
+ parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
if (maxcount < 0) {
error("invalid --count argument: `%d'", maxcount);
usage_with_options(for_each_ref_usage, opts);
sort = default_sort();
sort_atom_limit = used_atom_cnt;
+ /* for warn_ambiguous_refs */
+ git_config(git_default_config, NULL);
+
memset(&cbdata, 0, sizeof(cbdata));
cbdata.grab_pattern = argv;
- for_each_ref(grab_single_ref, &cbdata);
+ for_each_rawref(grab_single_ref, &cbdata);
refs = cbdata.grab_array;
num_refs = cbdata.grab_cnt;
- for (i = 0; i < used_atom_cnt; i++) {
- if (used_atom[i][0] == '*') {
- need_tagged = 1;
- break;
- }
- }
-
sort_refs(sort, refs, num_refs);
if (!maxcount || num_refs < maxcount)