#include "ref-filter.h"
#include "revision.h"
#include "utf8.h"
+#include "git-compat-util.h"
+#include "version.h"
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
struct atom_value *v)
{
if (!strcmp(name, "objectname")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(sha1));
- v->s = s;
+ v->s = xstrdup(sha1_to_hex(sha1));
return 1;
}
if (!strcmp(name, "objectname:short")) {
if (!strcmp(name, "objecttype"))
v->s = typename(obj->type);
else if (!strcmp(name, "objectsize")) {
- char *s = xmalloc(40);
- sprintf(s, "%lu", sz);
v->ul = sz;
- v->s = s;
+ v->s = xstrfmt("%lu", sz);
}
else if (deref)
- grab_objectname(name, obj->sha1, v);
+ grab_objectname(name, obj->oid.hash, v);
}
}
v->s = tag->tag;
else if (!strcmp(name, "type") && tag->tagged)
v->s = typename(tag->tagged->type);
- else if (!strcmp(name, "object") && tag->tagged) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(tag->tagged->sha1));
- v->s = s;
- }
+ else if (!strcmp(name, "object") && tag->tagged)
+ v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
}
if (deref)
name++;
if (!strcmp(name, "tree")) {
- char *s = xmalloc(41);
- strcpy(s, sha1_to_hex(commit->tree->object.sha1));
- v->s = s;
+ v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
}
- if (!strcmp(name, "numparent")) {
- char *s = xmalloc(40);
+ else if (!strcmp(name, "numparent")) {
v->ul = commit_list_count(commit->parents);
- sprintf(s, "%lu", v->ul);
- v->s = s;
+ v->s = xstrfmt("%lu", v->ul);
}
else if (!strcmp(name, "parent")) {
- int num = commit_list_count(commit->parents);
- int i;
struct commit_list *parents;
- char *s = xmalloc(41 * num + 1);
- v->s = s;
- for (i = 0, parents = commit->parents;
- parents;
- parents = parents->next, i = i + 41) {
+ struct strbuf s = STRBUF_INIT;
+ for (parents = commit->parents; parents; parents = parents->next) {
struct commit *parent = parents->item;
- strcpy(s+i, sha1_to_hex(parent->object.sha1));
- if (parents->next)
- s[i+40] = ' ';
+ if (parents != commit->parents)
+ strbuf_addch(&s, ' ');
+ strbuf_addstr(&s, oid_to_hex(&parent->object.oid));
}
- if (!i)
- *s = '\0';
+ v->s = strbuf_detach(&s, NULL);
}
}
}
return dst;
}
+static const char *strip_ref_components(const char *refname, const char *nr_arg)
+{
+ char *end;
+ long nr = strtol(nr_arg, &end, 10);
+ long remaining = nr;
+ const char *start = refname;
+
+ if (nr < 1 || *end != '\0')
+ die(":strip= requires a positive integer argument");
+
+ while (remaining) {
+ switch (*start++) {
+ case '\0':
+ die("ref '%s' does not have %ld components to :strip",
+ refname, nr);
+ case '/':
+ remaining--;
+ break;
+ }
+ }
+ return start;
+}
+
/*
* Parse the object referred by ref, and grab needed value.
*/
formatp = strchr(name, ':');
if (formatp) {
int num_ours, num_theirs;
+ const char *arg;
formatp++;
if (!strcmp(formatp, "short"))
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
+ else if (skip_prefix(formatp, "strip=", &arg))
+ refname = strip_ref_components(refname, arg);
else if (!strcmp(formatp, "track") &&
(starts_with(name, "upstream") ||
starts_with(name, "push"))) {
- char buf[40];
if (stat_tracking_info(branch, &num_ours,
&num_theirs, NULL))
if (!num_ours && !num_theirs)
v->s = "";
- else if (!num_ours) {
- sprintf(buf, "[behind %d]", num_theirs);
- v->s = xstrdup(buf);
- } else if (!num_theirs) {
- sprintf(buf, "[ahead %d]", num_ours);
- v->s = xstrdup(buf);
- } else {
- sprintf(buf, "[ahead %d, behind %d]",
- num_ours, num_theirs);
- v->s = xstrdup(buf);
- }
+ else if (!num_ours)
+ v->s = xstrfmt("[behind %d]", num_theirs);
+ else if (!num_theirs)
+ v->s = xstrfmt("[ahead %d]", num_ours);
+ else
+ v->s = xstrfmt("[ahead %d, behind %d]",
+ num_ours, num_theirs);
continue;
} else if (!strcmp(formatp, "trackshort") &&
(starts_with(name, "upstream") ||
if (!deref)
v->s = refname;
- else {
- int len = strlen(refname);
- char *s = xmalloc(len + 4);
- sprintf(s, "%s^{}", refname);
- v->s = s;
- }
+ else
+ v->s = xstrfmt("%s^{}", refname);
}
for (i = 0; i < used_atom_cnt; i++) {
* 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;
+ tagged = ((struct tag *)obj)->tagged->oid.hash;
/*
* NEEDSWORK: This derefs tag only once, which
static int in_commit_list(const struct commit_list *want, struct commit *c)
{
for (; want; want = want->next)
- if (!hashcmp(want->item->object.sha1, c->object.sha1))
+ if (!oidcmp(&want->item->object.oid, &c->object.oid))
return 1;
return 0;
}
return is_descendant_of(commit, filter->with_commit);
}
+/*
+ * Return 1 if the refname matches one of the patterns, otherwise 0.
+ * A pattern can be a literal prefix (e.g. a refname "refs/heads/master"
+ * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
+ * matches "refs/heads/mas*", too).
+ */
+static int match_pattern(const char **patterns, const char *refname)
+{
+ /*
+ * When no '--format' option is given we need to skip the prefix
+ * for matching refs of tags and branches.
+ */
+ (void)(skip_prefix(refname, "refs/tags/", &refname) ||
+ skip_prefix(refname, "refs/heads/", &refname) ||
+ skip_prefix(refname, "refs/remotes/", &refname) ||
+ skip_prefix(refname, "refs/", &refname));
+
+ for (; *patterns; patterns++) {
+ if (!wildmatch(*patterns, refname, 0, NULL))
+ return 1;
+ }
+ return 0;
+}
+
/*
* 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).
+ * 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 char **pattern, const char *refname)
{
return 0;
}
+/* Return 1 if the refname matches one of the patterns, otherwise 0. */
+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->name_patterns, refname);
+ return match_pattern(filter->name_patterns, refname);
+}
+
/*
* Given a ref (sha1, refname), check if the ref belongs to the array
* of sha1s. If the given ref is a tag, check if the given tag points
if (!obj)
die(_("malformed object at '%s'"), refname);
if (obj->type == OBJ_TAG)
- tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+ tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash;
if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
return tagged_sha1;
return NULL;
if (!(kind & filter->kind))
return 0;
- if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
+ if (!filter_pattern_match(filter, refname))
return 0;
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
* obtain the commit using the 'oid' available and discard all
* non-commits early. The actual filtering is done later.
*/
- if (filter->merge_commit || filter->with_commit) {
+ if (filter->merge_commit || filter->with_commit || filter->verbose) {
commit = lookup_commit_reference_gently(oid->hash, 1);
if (!commit)
return 0;
get_ref_atom_value(a, s->atom, &va);
get_ref_atom_value(b, s->atom, &vb);
- switch (cmp_type) {
- case FIELD_STR:
+ if (s->version)
+ cmp = versioncmp(va->s, vb->s);
+ else if (cmp_type == FIELD_STR)
cmp = strcmp(va->s, vb->s);
- break;
- default:
+ else {
if (va->ul < vb->ul)
cmp = -1;
else if (va->ul == vb->ul)
- cmp = 0;
+ cmp = strcmp(a->refname, b->refname);
else
cmp = 1;
- break;
}
+
return (s->reverse) ? -cmp : cmp;
}
s->reverse = 1;
arg++;
}
+ if (skip_prefix(arg, "version:", &arg) ||
+ skip_prefix(arg, "v:", &arg))
+ s->version = 1;
len = strlen(arg);
s->atom = parse_ref_filter_atom(arg, arg+len);
return 0;