#include "bisect.h"
#include "packfile.h"
#include "worktree.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "commit-reach.h"
#include "commit-graph.h"
#include "prio-queue.h"
#include "hashmap.h"
+#include "utf8.h"
+#include "bloom.h"
+#include "json-writer.h"
volatile show_early_output_fn_t show_early_output;
implement_shared_commit_slab(revision_sources, char *);
+static inline int want_ancestry(const struct rev_info *revs);
+
void show_object_with_name(FILE *out, struct object *obj, const char *name)
{
const char *p;
};
static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
- const struct path_and_oids_entry *e1,
- const struct path_and_oids_entry *e2,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *keydata)
{
- return strcmp(e1->path, e2->path);
-}
+ const struct path_and_oids_entry *e1, *e2;
-static void paths_and_oids_init(struct hashmap *map)
-{
- hashmap_init(map, (hashmap_cmp_fn) path_and_oids_cmp, NULL, 0);
+ e1 = container_of(eptr, const struct path_and_oids_entry, ent);
+ e2 = container_of(entry_or_key, const struct path_and_oids_entry, ent);
+
+ return strcmp(e1->path, e2->path);
}
static void paths_and_oids_clear(struct hashmap *map)
{
struct hashmap_iter iter;
struct path_and_oids_entry *entry;
- hashmap_iter_init(map, &iter);
- while ((entry = (struct path_and_oids_entry *)hashmap_iter_next(&iter))) {
+ hashmap_for_each_entry(map, &iter, entry, ent /* member name */) {
oidset_clear(&entry->trees);
free(entry->path);
}
- hashmap_free(map, 1);
+ hashmap_clear_and_free(map, struct path_and_oids_entry, ent);
}
static void paths_and_oids_insert(struct hashmap *map,
struct path_and_oids_entry key;
struct path_and_oids_entry *entry;
- hashmap_entry_init(&key, hash);
+ hashmap_entry_init(&key.ent, hash);
/* use a shallow copy for the lookup */
key.path = (char *)path;
oidset_init(&key.trees, 0);
- if (!(entry = (struct path_and_oids_entry *)hashmap_get(map, &key, NULL))) {
+ entry = hashmap_get_entry(map, &key, ent, NULL);
+ if (!entry) {
entry = xcalloc(1, sizeof(struct path_and_oids_entry));
- hashmap_entry_init(entry, hash);
+ hashmap_entry_init(&entry->ent, hash);
entry->path = xstrdup(key.path);
oidset_init(&entry->trees, 16);
- hashmap_put(map, entry);
+ hashmap_put(map, &entry->ent);
}
oidset_insert(&entry->trees, oid);
struct oidset *trees)
{
unsigned has_interesting = 0, has_uninteresting = 0;
- struct hashmap map;
+ struct hashmap map = HASHMAP_INIT(path_and_oids_cmp, NULL);
struct hashmap_iter map_iter;
struct path_and_oids_entry *entry;
struct object_id *oid;
if (!has_uninteresting || !has_interesting)
return;
- paths_and_oids_init(&map);
-
oidset_iter_init(trees, &iter);
while ((oid = oidset_iter_next(&iter))) {
struct tree *tree = lookup_tree(r, oid);
add_children_by_path(r, tree, &map);
}
- hashmap_iter_init(&map, &map_iter);
- while ((entry = hashmap_iter_next(&map_iter)))
+ hashmap_for_each_entry(&map, &map_iter, entry, ent /* member name */)
mark_trees_uninteresting_sparse(r, &entry->trees);
paths_and_oids_clear(&map);
const char *name, unsigned mode,
const char *path)
{
+ struct interpret_branch_name_options options = { 0 };
if (!obj)
return;
if (revs->no_walk && (obj->flags & UNINTERESTING))
revs->no_walk = 0;
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
struct strbuf buf = STRBUF_INIT;
- int len = interpret_branch_name(name, 0, &buf, 0);
+ int len = interpret_branch_name(name, 0, &buf, &options);
if (0 < len && name[len] && buf.len)
strbuf_addstr(&buf, name + len);
struct tag *tag = (struct tag *) object;
if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
- if (!tag->tagged)
- die("bad tag");
- object = parse_object(revs->repo, &tag->tagged->oid);
+ object = parse_object(revs->repo, get_tagged_oid(tag));
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
if (object->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)object;
- if (parse_commit(commit) < 0)
+ if (repo_parse_commit(revs->repo, commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
options->flags.has_changes = 1;
}
+static int bloom_filter_atexit_registered;
+static unsigned int count_bloom_filter_maybe;
+static unsigned int count_bloom_filter_definitely_not;
+static unsigned int count_bloom_filter_false_positive;
+static unsigned int count_bloom_filter_not_present;
+
+static void trace2_bloom_filter_statistics_atexit(void)
+{
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ jw_object_intmax(&jw, "filter_not_present", count_bloom_filter_not_present);
+ jw_object_intmax(&jw, "maybe", count_bloom_filter_maybe);
+ jw_object_intmax(&jw, "definitely_not", count_bloom_filter_definitely_not);
+ jw_object_intmax(&jw, "false_positive", count_bloom_filter_false_positive);
+ jw_end(&jw);
+
+ trace2_data_json("bloom", the_repository, "statistics", &jw);
+
+ jw_release(&jw);
+}
+
+static int forbid_bloom_filters(struct pathspec *spec)
+{
+ if (spec->has_wildcard)
+ return 1;
+ if (spec->nr > 1)
+ return 1;
+ if (spec->magic & ~PATHSPEC_LITERAL)
+ return 1;
+ if (spec->nr && (spec->items[0].magic & ~PATHSPEC_LITERAL))
+ return 1;
+
+ return 0;
+}
+
+static void prepare_to_use_bloom_filter(struct rev_info *revs)
+{
+ struct pathspec_item *pi;
+ char *path_alloc = NULL;
+ const char *path, *p;
+ size_t len;
+ int path_component_nr = 1;
+
+ if (!revs->commits)
+ return;
+
+ if (forbid_bloom_filters(&revs->prune_data))
+ return;
+
+ repo_parse_commit(revs->repo, revs->commits->item);
+
+ revs->bloom_filter_settings = get_bloom_filter_settings(revs->repo);
+ if (!revs->bloom_filter_settings)
+ return;
+
+ if (!revs->pruning.pathspec.nr)
+ return;
+
+ pi = &revs->pruning.pathspec.items[0];
+
+ /* remove single trailing slash from path, if needed */
+ if (pi->len > 0 && pi->match[pi->len - 1] == '/') {
+ path_alloc = xmemdupz(pi->match, pi->len - 1);
+ path = path_alloc;
+ } else
+ path = pi->match;
+
+ len = strlen(path);
+ if (!len) {
+ revs->bloom_filter_settings = NULL;
+ free(path_alloc);
+ return;
+ }
+
+ p = path;
+ while (*p) {
+ /*
+ * At this point, the path is normalized to use Unix-style
+ * path separators. This is required due to how the
+ * changed-path Bloom filters store the paths.
+ */
+ if (*p == '/')
+ path_component_nr++;
+ p++;
+ }
+
+ revs->bloom_keys_nr = path_component_nr;
+ ALLOC_ARRAY(revs->bloom_keys, revs->bloom_keys_nr);
+
+ fill_bloom_key(path, len, &revs->bloom_keys[0],
+ revs->bloom_filter_settings);
+ path_component_nr = 1;
+
+ p = path + len - 1;
+ while (p > path) {
+ if (*p == '/')
+ fill_bloom_key(path, p - path,
+ &revs->bloom_keys[path_component_nr++],
+ revs->bloom_filter_settings);
+ p--;
+ }
+
+ if (trace2_is_enabled() && !bloom_filter_atexit_registered) {
+ atexit(trace2_bloom_filter_statistics_atexit);
+ bloom_filter_atexit_registered = 1;
+ }
+
+ free(path_alloc);
+}
+
+static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
+ struct commit *commit)
+{
+ struct bloom_filter *filter;
+ int result = 1, j;
+
+ if (!revs->repo->objects->commit_graph)
+ return -1;
+
+ if (commit_graph_generation(commit) == GENERATION_NUMBER_INFINITY)
+ return -1;
+
+ filter = get_bloom_filter(revs->repo, commit);
+
+ if (!filter) {
+ count_bloom_filter_not_present++;
+ return -1;
+ }
+
+ for (j = 0; result && j < revs->bloom_keys_nr; j++) {
+ result = bloom_filter_contains(filter,
+ &revs->bloom_keys[j],
+ revs->bloom_filter_settings);
+ }
+
+ if (result)
+ count_bloom_filter_maybe++;
+ else
+ count_bloom_filter_definitely_not++;
+
+ return result;
+}
+
static int rev_compare_tree(struct rev_info *revs,
- struct commit *parent, struct commit *commit)
+ struct commit *parent, struct commit *commit, int nth_parent)
{
struct tree *t1 = get_commit_tree(parent);
struct tree *t2 = get_commit_tree(commit);
+ int bloom_ret = 1;
if (!t1)
return REV_TREE_NEW;
return REV_TREE_SAME;
}
+ if (revs->bloom_keys_nr && !nth_parent) {
+ bloom_ret = check_maybe_different_in_bloom_filter(revs, commit);
+
+ if (bloom_ret == 0)
+ return REV_TREE_SAME;
+ }
+
tree_difference = REV_TREE_SAME;
revs->pruning.flags.has_changes = 0;
- if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
- &revs->pruning) < 0)
- return REV_TREE_DIFFERENT;
+ diff_tree_oid(&t1->object.oid, &t2->object.oid, "", &revs->pruning);
+
+ if (!nth_parent)
+ if (bloom_ret == 1 && tree_difference == REV_TREE_SAME)
+ count_bloom_filter_false_positive++;
+
return tree_difference;
}
static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
{
- int retval;
struct tree *t1 = get_commit_tree(commit);
if (!t1)
tree_difference = REV_TREE_SAME;
revs->pruning.flags.has_changes = 0;
- retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
+ diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
- return retval >= 0 && (tree_difference == REV_TREE_SAME);
+ return tree_difference == REV_TREE_SAME;
}
struct treesame_state {
ts->treesame[0] = 1;
}
}
- if (parse_commit(p) < 0)
+ if (repo_parse_commit(revs->repo, p) < 0)
die("cannot simplify commit %s (because of %s)",
oid_to_hex(&commit->object.oid),
oid_to_hex(&p->object.oid));
- switch (rev_compare_tree(revs, p, commit)) {
+ switch (rev_compare_tree(revs, p, commit, nth_parent)) {
case REV_TREE_SAME:
if (!revs->simplify_history || !relevant_commit(p)) {
/* Even if a merge with an uninteresting
}
parent->next = NULL;
commit->parents = parent;
- commit->object.flags |= TREESAME;
+
+ /*
+ * A merge commit is a "diversion" if it is not
+ * TREESAME to its first parent but is TREESAME
+ * to a later parent. In the simplified history,
+ * we "divert" the history walk to the later
+ * parent. These commits are shown when "show_pulls"
+ * is enabled, so do not mark the object as
+ * TREESAME here.
+ */
+ if (!revs->show_pulls || !nth_parent)
+ commit->object.flags |= TREESAME;
+
return;
case REV_TREE_NEW:
* IOW, we pretend this parent is a
* "root" commit.
*/
- if (parse_commit(p) < 0)
+ if (repo_parse_commit(revs->repo, p) < 0)
die("cannot simplify commit %s (invalid %s)",
oid_to_hex(&commit->object.oid),
oid_to_hex(&p->object.oid));
relevant_change = 1;
else
irrelevant_change = 1;
+
+ if (!nth_parent)
+ commit->object.flags |= PULL_MERGE;
+
continue;
}
die("bad tree compare for commit %s", oid_to_hex(&commit->object.oid));
parent = parent->next;
if (p)
p->object.flags |= UNINTERESTING;
- if (parse_commit_gently(p, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
continue;
if (p->parents)
mark_parents_uninteresting(p);
struct commit *p = parent->item;
int gently = revs->ignore_missing_links ||
revs->exclude_promisor_objects;
- if (parse_commit_gently(p, gently) < 0) {
+ if (repo_parse_commit_gently(revs->repo, p, gently) < 0) {
if (revs->exclude_promisor_objects &&
is_promisor_object(&p->object.oid)) {
if (revs->first_parent_only)
continue;
break;
}
- if (revs->min_age != -1 && (commit->date > revs->min_age))
+ if (revs->min_age != -1 && (commit->date > revs->min_age) &&
+ !revs->line_level_traverse)
continue;
date = commit->date;
p = &commit_list_insert(commit, p)->next;
{
struct worktree **worktrees, **p;
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
if (revs->single_worktree)
return;
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct index_state istate = { NULL };
revs->repo = r;
revs->abbrev = DEFAULT_ABBREV;
- revs->ignore_merges = 1;
+ revs->ignore_merges = -1;
revs->simplify_history = 1;
revs->pruning.repo = r;
revs->pruning.flags.recursive = 1;
revs->commit_format = CMIT_FMT_DEFAULT;
revs->expand_tabs_in_log_default = 8;
- init_grep_defaults(revs->repo);
grep_init(&revs->grep_filter, revs->repo, prefix);
revs->grep_filter.status_only = 1;
revs->diffopt.prefix_length = strlen(prefix);
}
- revs->notes_opt.use_default_notes = -1;
+ init_display_notes(&revs->notes_opt);
}
static void add_pending_commit_list(struct rev_info *revs,
return ret;
}
-int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
+static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
{
struct object_context oc;
char *mark;
return 0;
}
+int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt)
+{
+ int ret = handle_revision_arg_1(arg, revs, flags, revarg_opt);
+ if (!ret)
+ revs->rev_input_given = 1;
+ return ret;
+}
+
static void read_pathspec_from_stdin(struct strbuf *sb,
- struct argv_array *prune)
+ struct strvec *prune)
{
while (strbuf_getline(sb, stdin) != EOF)
- argv_array_push(prune, sb->buf);
+ strvec_push(prune, sb->buf);
}
static void read_revisions_from_stdin(struct rev_info *revs,
- struct argv_array *prune)
+ struct strvec *prune)
{
struct strbuf sb;
int seen_dashdash = 0;
revs->simplify_by_decoration = 1;
revs->limited = 1;
revs->prune = 1;
- load_ref_decorations(NULL, DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) {
revs->sort_order = REV_SORT_BY_COMMIT_DATE;
revs->topo_order = 1;
} else if (!strcmp(arg, "--unpacked")) {
revs->unpacked = 1;
} else if (starts_with(arg, "--unpacked=")) {
- die("--unpacked=<packfile> no longer supported.");
+ die(_("--unpacked=<packfile> no longer supported"));
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
revs->diffopt.flags.recursive = 1;
revs->diffopt.flags.recursive = 1;
revs->diffopt.flags.tree_in_recursive = 1;
} else if (!strcmp(arg, "-m")) {
+ /*
+ * To "diff-index", "-m" means "match missing", and to the "log"
+ * family of commands, it means "show full diff for merges". Set
+ * both fields appropriately.
+ */
revs->ignore_merges = 0;
+ revs->match_missing = 1;
+ } else if ((argcount = parse_long_opt("diff-merges", argv, &optarg))) {
+ if (!strcmp(optarg, "off")) {
+ revs->ignore_merges = 1;
+ } else {
+ die(_("unknown value for --diff-merges: %s"), optarg);
+ }
+ return argcount;
+ } else if (!strcmp(arg, "--no-diff-merges")) {
+ revs->ignore_merges = 1;
} else if (!strcmp(arg, "-c")) {
revs->diff = 1;
revs->dense_combined_merges = 0;
die("'%s': not a non-negative integer", arg);
revs->expand_tabs_in_log = val;
} else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
- revs->show_notes = 1;
+ enable_default_display_notes(&revs->notes_opt, &revs->show_notes);
revs->show_notes_given = 1;
- revs->notes_opt.use_default_notes = 1;
} else if (!strcmp(arg, "--show-signature")) {
revs->show_signature = 1;
} else if (!strcmp(arg, "--no-show-signature")) {
revs->track_first_time = 1;
} else if (skip_prefix(arg, "--show-notes=", &optarg) ||
skip_prefix(arg, "--notes=", &optarg)) {
- struct strbuf buf = STRBUF_INIT;
- revs->show_notes = 1;
- revs->show_notes_given = 1;
if (starts_with(arg, "--show-notes=") &&
revs->notes_opt.use_default_notes < 0)
revs->notes_opt.use_default_notes = 1;
- strbuf_addstr(&buf, optarg);
- expand_notes_ref(&buf);
- string_list_append(&revs->notes_opt.extra_notes_refs,
- strbuf_detach(&buf, NULL));
+ enable_ref_display_notes(&revs->notes_opt, &revs->show_notes, optarg);
+ revs->show_notes_given = 1;
} else if (!strcmp(arg, "--no-notes")) {
- revs->show_notes = 0;
+ disable_display_notes(&revs->notes_opt, &revs->show_notes);
revs->show_notes_given = 1;
- revs->notes_opt.use_default_notes = -1;
- /* we have been strdup'ing ourselves, so trick
- * string_list into free()ing strings */
- revs->notes_opt.extra_notes_refs.strdup_strings = 1;
- string_list_clear(&revs->notes_opt.extra_notes_refs, 0);
- revs->notes_opt.extra_notes_refs.strdup_strings = 0;
} else if (!strcmp(arg, "--standard-notes")) {
revs->show_notes_given = 1;
revs->notes_opt.use_default_notes = 1;
revs->topo_order = 1;
revs->rewrite_parents = 1;
revs->graph = graph_init(revs);
+ } else if (!strcmp(arg, "--encode-email-headers")) {
+ revs->encode_email_headers = 1;
+ } else if (!strcmp(arg, "--no-encode-email-headers")) {
+ revs->encode_email_headers = 0;
} else if (!strcmp(arg, "--root")) {
revs->show_root_diff = 1;
} else if (!strcmp(arg, "--no-commit-id")) {
} else if (!strcmp(arg, "--full-diff")) {
revs->diff = 1;
revs->full_diff = 1;
+ } else if (!strcmp(arg, "--show-pulls")) {
+ revs->show_pulls = 1;
} else if (!strcmp(arg, "--full-history")) {
revs->simplify_history = 0;
} else if (!strcmp(arg, "--relative-date")) {
}
static int handle_revision_pseudo_opt(const char *submodule,
- struct rev_info *revs,
- int argc, const char **argv, int *flags)
+ struct rev_info *revs,
+ const char **argv, int *flags)
{
const char *arg = argv[0];
const char *optarg;
*/
int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
{
- int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;
- struct argv_array prune_data = ARGV_ARRAY_INIT;
+ int i, flags, left, seen_dashdash, revarg_opt;
+ struct strvec prune_data = STRVEC_INIT;
const char *submodule = NULL;
+ int seen_end_of_options = 0;
if (opt)
submodule = opt->submodule;
argv[i] = NULL;
argc = i;
if (argv[i + 1])
- argv_array_pushv(&prune_data, argv + i + 1);
+ strvec_pushv(&prune_data, argv + i + 1);
seen_dashdash = 1;
break;
}
revarg_opt |= REVARG_CANNOT_BE_FILENAME;
for (left = i = 1; i < argc; i++) {
const char *arg = argv[i];
- if (*arg == '-') {
+ if (!seen_end_of_options && *arg == '-') {
int opts;
opts = handle_revision_pseudo_opt(submodule,
- revs, argc - i, argv + i,
+ revs, argv + i,
&flags);
if (opts > 0) {
i += opts - 1;
continue;
}
+ if (!strcmp(arg, "--end-of-options")) {
+ seen_end_of_options = 1;
+ continue;
+ }
+
opts = handle_revision_opt(revs, argc - i, argv + i,
&left, argv, opt);
if (opts > 0) {
for (j = i; j < argc; j++)
verify_filename(revs->prefix, argv[j], j == i);
- argv_array_pushv(&prune_data, argv + i);
+ strvec_pushv(&prune_data, argv + i);
break;
}
- else
- got_rev_arg = 1;
}
- if (prune_data.argc) {
+ if (prune_data.nr) {
/*
* If we need to introduce the magic "a lone ':' means no
* pathspec whatsoever", here is the place to do so.
* }
*/
parse_pathspec(&revs->prune_data, 0, 0,
- revs->prefix, prune_data.argv);
+ revs->prefix, prune_data.v);
}
- argv_array_clear(&prune_data);
+ strvec_clear(&prune_data);
if (revs->def == NULL)
revs->def = opt ? opt->def : NULL;
opt->tweak(revs, opt);
if (revs->show_merge)
prepare_show_merge(revs);
- if (revs->def && !revs->pending.nr && !revs->rev_input_given && !got_rev_arg) {
+ if (revs->def && !revs->pending.nr && !revs->rev_input_given) {
struct object_id oid;
struct object *object;
struct object_context oc;
if (revs->diffopt.objfind)
revs->simplify_history = 0;
+ if (revs->line_level_traverse) {
+ if (want_ancestry(revs))
+ revs->limited = 1;
+ revs->topo_order = 1;
+ }
+
if (revs->topo_order && !generation_numbers_enabled(the_repository))
revs->limited = 1;
copy_pathspec(&revs->diffopt.pathspec,
&revs->prune_data);
}
- if (revs->combine_merges)
+ if (revs->combine_merges && revs->ignore_merges < 0)
revs->ignore_merges = 0;
+ if (revs->ignore_merges < 0)
+ revs->ignore_merges = 1;
if (revs->combined_all_paths && !revs->combine_merges)
die("--combined-all-paths makes no sense without -c or --cc");
revs->diffopt.abbrev = revs->abbrev;
- if (revs->line_level_traverse) {
- revs->limited = 1;
- revs->topo_order = 1;
- }
-
diff_setup_done(&revs->diffopt);
grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
&revs->grep_filter);
+ if (!is_encoding_utf8(get_log_output_encoding()))
+ revs->grep_filter.ignore_locale = 1;
compile_grep_patterns(&revs->grep_filter);
if (revs->reverse && revs->reflog_info)
if (!revs->reflog_info && revs->grep_filter.use_reflog_filter)
die("cannot use --grep-reflog without --walk-reflogs");
- if (revs->first_parent_only && revs->bisect)
- die(_("--first-parent is incompatible with --bisect"));
-
if (revs->line_level_traverse &&
(revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
die(_("-L does not yet support diff formats besides -p and -s"));
if (!cnt ||
(commit->object.flags & UNINTERESTING) ||
!(commit->object.flags & TREESAME) ||
- (parent = one_relevant_parent(revs, commit->parents)) == NULL)
+ (parent = one_relevant_parent(revs, commit->parents)) == NULL ||
+ (revs->show_pulls && (commit->object.flags & PULL_MERGE)))
st->simplified = commit;
else {
pst = locate_simplify_state(revs, parent);
void reset_revision_walk(void)
{
- clear_object_flags(SEEN | ADDED | SHOWN);
+ clear_object_flags(SEEN | ADDED | SHOWN | TOPO_WALK_EXPLORED | TOPO_WALK_INDEGREE);
}
static int mark_uninteresting(const struct object_id *oid,
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
struct topo_walk_info *info = revs->topo_walk_info;
struct commit *c;
while ((c = prio_queue_peek(&info->explore_queue)) &&
- c->generation >= gen_cutoff)
+ commit_graph_generation(c) >= gen_cutoff)
explore_walk_step(revs);
}
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
- explore_to_depth(revs, c->generation);
+ explore_to_depth(revs, commit_graph_generation(c));
for (p = c->parents; p; p = p->next) {
struct commit *parent = p->item;
struct topo_walk_info *info = revs->topo_walk_info;
struct commit *c;
while ((c = prio_queue_peek(&info->indegree_queue)) &&
- c->generation >= gen_cutoff)
+ commit_graph_generation(c) >= gen_cutoff)
indegree_walk_step(revs);
}
+static void reset_topo_walk(struct rev_info *revs)
+{
+ struct topo_walk_info *info = revs->topo_walk_info;
+
+ clear_prio_queue(&info->explore_queue);
+ clear_prio_queue(&info->indegree_queue);
+ clear_prio_queue(&info->topo_queue);
+ clear_indegree_slab(&info->indegree);
+ clear_author_date_slab(&info->author_date);
+
+ FREE_AND_NULL(revs->topo_walk_info);
+}
+
static void init_topo_walk(struct rev_info *revs)
{
struct topo_walk_info *info;
struct commit_list *list;
+ if (revs->topo_walk_info)
+ reset_topo_walk(revs);
+
revs->topo_walk_info = xmalloc(sizeof(struct topo_walk_info));
info = revs->topo_walk_info;
memset(info, 0, sizeof(struct topo_walk_info));
info->min_generation = GENERATION_NUMBER_INFINITY;
for (list = revs->commits; list; list = list->next) {
struct commit *c = list->item;
+ uint32_t generation;
- if (parse_commit_gently(c, 1))
+ if (repo_parse_commit_gently(revs->repo, c, 1))
continue;
test_flag_and_insert(&info->explore_queue, c, TOPO_WALK_EXPLORED);
test_flag_and_insert(&info->indegree_queue, c, TOPO_WALK_INDEGREE);
- if (c->generation < info->min_generation)
- info->min_generation = c->generation;
+ generation = commit_graph_generation(c);
+ if (generation < info->min_generation)
+ info->min_generation = generation;
*(indegree_slab_at(&info->indegree, c)) = 1;
for (p = commit->parents; p; p = p->next) {
struct commit *parent = p->item;
int *pi;
+ uint32_t generation;
if (parent->object.flags & UNINTERESTING)
continue;
- if (parse_commit_gently(parent, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
continue;
- if (parent->generation < info->min_generation) {
- info->min_generation = parent->generation;
+ generation = commit_graph_generation(parent);
+ if (generation < info->min_generation) {
+ info->min_generation = generation;
compute_indegrees_to_depth(revs, info->min_generation);
}
FOR_EACH_OBJECT_PROMISOR_ONLY);
}
+ if (!revs->reflog_info)
+ prepare_to_use_bloom_filter(revs);
if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
sort_in_topological_order(&revs->commits, revs->sort_order);
} else if (revs->topo_order)
init_topo_walk(revs);
- if (revs->line_level_traverse)
+ if (revs->line_level_traverse && want_ancestry(revs))
+ /*
+ * At the moment we can only do line-level log with parent
+ * rewriting by performing this expensive pre-filtering step.
+ * If parent rewriting is not requested, then we rather
+ * perform the line-level log filtering during the regular
+ * history traversal.
+ */
line_log_filter(revs);
if (revs->simplify_merges)
simplify_merges(revs);
if (revs->children.name)
set_children(revs);
+
return 0;
}
return commit_ignore;
if (commit->object.flags & UNINTERESTING)
return commit_ignore;
+ if (revs->line_level_traverse && !want_ancestry(revs)) {
+ /*
+ * In case of line-level log with parent rewriting
+ * prepare_revision_walk() already took care of all line-level
+ * log filtering, and there is nothing left to do here.
+ *
+ * If parent rewriting was not requested, then this is the
+ * place to perform the line-level log filtering. Notably,
+ * this check, though expensive, must come before the other,
+ * cheaper filtering conditions, because the tracked line
+ * ranges must be adjusted even when the commit will end up
+ * being ignored based on other conditions.
+ */
+ if (!line_log_process_ranges_arbitrary_commit(revs, commit))
+ return commit_ignore;
+ }
if (revs->min_age != -1 &&
comparison_date(revs, commit) > revs->min_age)
return commit_ignore;
/* drop merges unless we want parenthood */
if (!want_ancestry(revs))
return commit_ignore;
+
+ if (revs->show_pulls && (commit->object.flags & PULL_MERGE))
+ return commit_show;
+
/*
* If we want ancestry, then need to keep any merges
* between relevant commits to tie together topology.
return c;
}
-char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
+const char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
{
if (commit->object.flags & BOUNDARY)
return "-";
void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
{
- char *mark = get_revision_mark(revs, commit);
+ const char *mark = get_revision_mark(revs, commit);
if (!strlen(mark))
return;
fputs(mark, stdout);