#include "trailer.h"
#include "wt-status.h"
#include "commit-slab.h"
+#include "commit-graph.h"
static struct ref_msg {
const char *gone;
char color[COLOR_MAXLEN];
struct align align;
struct {
- enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
+ enum {
+ RR_REF, RR_TRACK, RR_TRACKSHORT, RR_REMOTE_NAME, RR_REMOTE_REF
+ } option;
struct refname_atom refname;
- unsigned int nobracket : 1;
+ unsigned int nobracket : 1, push : 1, push_remote : 1;
} remote_ref;
struct {
enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
+ struct process_trailer_options trailer_opts;
unsigned int nlines;
} contents;
struct {
struct string_list params = STRING_LIST_INIT_DUP;
int i;
+ if (!strcmp(atom->name, "push") || starts_with(atom->name, "push:"))
+ atom->u.remote_ref.push = 1;
+
if (!arg) {
atom->u.remote_ref.option = RR_REF;
refname_atom_parser_internal(&atom->u.remote_ref.refname,
atom->u.remote_ref.option = RR_TRACKSHORT;
else if (!strcmp(s, "nobracket"))
atom->u.remote_ref.nobracket = 1;
- else {
+ else if (!strcmp(s, "remotename")) {
+ atom->u.remote_ref.option = RR_REMOTE_NAME;
+ atom->u.remote_ref.push_remote = 1;
+ } else if (!strcmp(s, "remoteref")) {
+ atom->u.remote_ref.option = RR_REMOTE_REF;
+ atom->u.remote_ref.push_remote = 1;
+ } else {
atom->u.remote_ref.option = RR_REF;
refname_atom_parser_internal(&atom->u.remote_ref.refname,
arg, atom->name);
static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
{
- if (arg)
- die(_("%%(trailers) does not take arguments"));
+ struct string_list params = STRING_LIST_INIT_DUP;
+ int i;
+
+ if (arg) {
+ string_list_split(¶ms, arg, ',', -1);
+ for (i = 0; i < params.nr; i++) {
+ const char *s = params.items[i].string;
+ if (!strcmp(s, "unfold"))
+ atom->u.contents.trailer_opts.unfold = 1;
+ else if (!strcmp(s, "only"))
+ atom->u.contents.trailer_opts.only_trailers = 1;
+ else
+ die(_("unknown %%(trailers) argument: %s"), s);
+ }
+ }
atom->u.contents.option = C_TRAILERS;
+ string_list_clear(¶ms, 0);
}
static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
atom->u.contents.option = C_SIG;
else if (!strcmp(arg, "subject"))
atom->u.contents.option = C_SUB;
- else if (!strcmp(arg, "trailers"))
- atom->u.contents.option = C_TRAILERS;
- else if (skip_prefix(arg, "lines=", &arg)) {
+ else if (skip_prefix(arg, "trailers", &arg)) {
+ skip_prefix(arg, ":", &arg);
+ trailers_atom_parser(format, atom, *arg ? arg : NULL);
+ } else if (skip_prefix(arg, "lines=", &arg)) {
atom->u.contents.option = C_LINES;
if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
die(_("positive value expected contents:lines=%s"), arg);
REALLOC_ARRAY(used_atom, used_atom_cnt);
used_atom[at].name = xmemdupz(atom, ep - atom);
used_atom[at].type = valid_atom[i].cmp_type;
- if (arg)
+ if (arg) {
arg = used_atom[at].name + (arg - atom) + 1;
+ if (!*arg) {
+ /*
+ * Treat empty sub-arguments list as NULL (i.e.,
+ * "%(atom:)" is equivalent to "%(atom)").
+ */
+ arg = NULL;
+ }
+ }
memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
if (valid_atom[i].parser)
valid_atom[i].parser(format, &used_atom[at], arg);
static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
{
- struct ref_formatting_stack *new;
+ struct ref_formatting_stack *new_stack;
push_stack_element(&state->stack);
- new = state->stack;
- new->at_end = end_align_handler;
- new->at_end_data = &atomv->atom->u.align;
+ new_stack = state->stack;
+ new_stack->at_end = end_align_handler;
+ new_stack->at_end_data = &atomv->atom->u.align;
}
static void if_then_else_handler(struct ref_formatting_stack **stack)
static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
{
- struct ref_formatting_stack *new;
+ struct ref_formatting_stack *new_stack;
struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);
if_then_else->str = atomv->atom->u.if_then_else.str;
if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
push_stack_element(&state->stack);
- new = state->stack;
- new->at_end = if_then_else_handler;
- new->at_end_data = if_then_else;
+ new_stack = state->stack;
+ new_stack->at_end = if_then_else_handler;
+ new_stack->at_end_data = if_then_else;
}
static int is_empty(const char *s)
if (deref)
name++;
if (!strcmp(name, "objecttype"))
- v->s = typename(obj->type);
+ v->s = type_name(obj->type);
else if (!strcmp(name, "objectsize")) {
v->value = sz;
v->s = xstrfmt("%lu", sz);
if (!strcmp(name, "tag"))
v->s = tag->tag;
else if (!strcmp(name, "type") && tag->tagged)
- v->s = typename(tag->tagged->type);
+ v->s = type_name(tag->tagged->type);
else if (!strcmp(name, "object") && tag->tagged)
v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
}
if (deref)
name++;
if (!strcmp(name, "tree")) {
- v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
+ v->s = xstrdup(oid_to_hex(get_commit_tree_oid(commit)));
}
else if (!strcmp(name, "numparent")) {
v->value = commit_list_count(commit->parents);
name++;
if (strcmp(name, "subject") &&
strcmp(name, "body") &&
- strcmp(name, "trailers") &&
+ !starts_with(name, "trailers") &&
!starts_with(name, "contents"))
continue;
if (!subpos)
append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
v->s = strbuf_detach(&s, NULL);
} else if (atom->u.contents.option == C_TRAILERS) {
- struct trailer_info info;
+ 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);
- /* Search for trailer info */
- trailer_info_get(&info, subpos);
- v->s = xmemdupz(info.trailer_start,
- info.trailer_end - info.trailer_start);
- trailer_info_release(&info);
+ v->s = strbuf_detach(&s, NULL);
} else if (atom->u.contents.option == C_BARE)
v->s = xstrdup(subpos);
}
*s = ">";
else
*s = "<>";
+ } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
+ int explicit;
+ const char *remote = atom->u.remote_ref.push ?
+ pushremote_for_branch(branch, &explicit) :
+ remote_for_branch(branch, &explicit);
+ if (explicit)
+ *s = xstrdup(remote);
+ else
+ *s = "";
+ } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
+ int explicit;
+ const char *merge;
+
+ merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
+ &explicit);
+ if (explicit)
+ *s = xstrdup(merge);
+ else
+ *s = "";
} else
die("BUG: unhandled RR_* enum");
}
if (refname)
fill_remote_ref_details(atom, refname, branch, &v->s);
continue;
- } else if (starts_with(name, "push")) {
+ } else if (atom->u.remote_ref.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;
+ if (atom->u.remote_ref.push_remote)
+ refname = NULL;
+ else {
+ refname = branch_get_push(branch, NULL);
+ if (!refname)
+ continue;
+ }
fill_remote_ref_details(atom, refname, branch, &v->s);
continue;
} else if (starts_with(name, "color:")) {
}
/*
- * Test whether the candidate or one of its parents is contained in the list.
+ * Test whether the candidate is contained in the list.
* Do not recurse to find out, though, but return -1 if inconclusive.
*/
static enum contains_result contains_test(struct commit *candidate,
const struct commit_list *want,
- struct contains_cache *cache)
+ struct contains_cache *cache,
+ uint32_t cutoff)
{
enum contains_result *cached = contains_cache_at(cache, candidate);
/* Otherwise, we don't know; prepare to recurse */
parse_commit_or_die(candidate);
+
+ if (candidate->generation < cutoff)
+ return CONTAINS_NO;
+
return CONTAINS_UNKNOWN;
}
struct contains_cache *cache)
{
struct contains_stack contains_stack = { 0, 0, NULL };
- enum contains_result result = contains_test(candidate, want, cache);
+ enum contains_result result;
+ uint32_t cutoff = GENERATION_NUMBER_INFINITY;
+ const struct commit_list *p;
+
+ for (p = want; p; p = p->next) {
+ struct commit *c = p->item;
+ load_commit_graph_info(c);
+ if (c->generation < cutoff)
+ cutoff = c->generation;
+ }
+ result = contains_test(candidate, want, cache, cutoff);
if (result != CONTAINS_UNKNOWN)
return result;
* If we just popped the stack, parents->item has been marked,
* therefore contains_test will return a meaningful yes/no.
*/
- else switch (contains_test(parents->item, want, cache)) {
+ else switch (contains_test(parents->item, want, cache, cutoff)) {
case CONTAINS_YES:
*contains_cache_at(cache, commit) = CONTAINS_YES;
contains_stack.nr--;
}
}
free(contains_stack.contains_stack);
- return contains_test(candidate, want, cache);
+ return contains_test(candidate, want, cache, cutoff);
}
static int commit_contains(struct ref_filter *filter, struct commit *commit,
free_array_item(item);
}
- for (i = 0; i < old_nr; i++)
- clear_commit_marks(to_clear[i], ALL_REV_FLAGS);
+ clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
free(to_clear);
}