summaryrefslogtreecommitdiff
path: root/revision.c
diff options
context:
space:
mode:
Diffstat (limited to 'revision.c')
-rw-r--r--revision.c1148
1 files changed, 722 insertions, 426 deletions
diff --git a/revision.c b/revision.c
index 6aa7f4f..7e45f76 100644
--- a/revision.c
+++ b/revision.c
@@ -1,10 +1,18 @@
-#include "cache.h"
-#include "object-store.h"
+#include "git-compat-util.h"
+#include "config.h"
+#include "environment.h"
+#include "gettext.h"
+#include "hex.h"
+#include "object-name.h"
+#include "object-file.h"
+#include "object-store-ll.h"
+#include "oidset.h"
#include "tag.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
#include "diff.h"
+#include "diff-merges.h"
#include "refs.h"
#include "revision.h"
#include "repository.h"
@@ -13,17 +21,19 @@
#include "reflog-walk.h"
#include "patch-ids.h"
#include "decorate.h"
-#include "log-tree.h"
#include "string-list.h"
#include "line-log.h"
#include "mailmap.h"
#include "commit-slab.h"
-#include "dir.h"
#include "cache-tree.h"
#include "bisect.h"
#include "packfile.h"
#include "worktree.h"
-#include "argv-array.h"
+#include "read-cache.h"
+#include "setup.h"
+#include "sparse-index.h"
+#include "strvec.h"
+#include "trace2.h"
#include "commit-reach.h"
#include "commit-graph.h"
#include "prio-queue.h"
@@ -31,6 +41,10 @@
#include "utf8.h"
#include "bloom.h"
#include "json-writer.h"
+#include "list-objects-filter-options.h"
+#include "resolve-undo.h"
+#include "parse-options.h"
+#include "wildmatch.h"
volatile show_early_output_fn_t show_early_output;
@@ -43,10 +57,8 @@ 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;
-
fprintf(out, "%s ", oid_to_hex(&obj->oid));
- for (p = name; *p && *p != '\n'; p++)
+ for (const char *p = name; *p && *p != '\n'; p++)
fputc(*p, out);
fputc('\n', out);
}
@@ -69,7 +81,7 @@ static void mark_tree_contents_uninteresting(struct repository *r,
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
@@ -111,10 +123,10 @@ struct path_and_oids_entry {
struct oidset trees;
};
-static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
+static int path_and_oids_cmp(const void *hashmap_cmp_fn_data UNUSED,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *keydata)
+ const void *keydata UNUSED)
{
const struct path_and_oids_entry *e1, *e2;
@@ -124,11 +136,6 @@ static int path_and_oids_cmp(const void *hashmap_cmp_fn_data,
return strcmp(e1->path, e2->path);
}
-static void paths_and_oids_init(struct hashmap *map)
-{
- hashmap_init(map, path_and_oids_cmp, NULL, 0);
-}
-
static void paths_and_oids_clear(struct hashmap *map)
{
struct hashmap_iter iter;
@@ -139,7 +146,7 @@ static void paths_and_oids_clear(struct hashmap *map)
free(entry->path);
}
- hashmap_free_entries(map, struct path_and_oids_entry, ent);
+ hashmap_clear_and_free(map, struct path_and_oids_entry, ent);
}
static void paths_and_oids_insert(struct hashmap *map,
@@ -158,7 +165,7 @@ static void paths_and_oids_insert(struct hashmap *map,
entry = hashmap_get_entry(map, &key, ent, NULL);
if (!entry) {
- entry = xcalloc(1, sizeof(struct path_and_oids_entry));
+ CALLOC_ARRAY(entry, 1);
hashmap_entry_init(&entry->ent, hash);
entry->path = xstrdup(key.path);
oidset_init(&entry->trees, 16);
@@ -181,7 +188,7 @@ static void add_children_by_path(struct repository *r,
if (parse_tree_gently(tree, 1) < 0)
return;
- init_tree_desc(&desc, tree->buffer, tree->size);
+ init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
@@ -213,7 +220,7 @@ void mark_trees_uninteresting_sparse(struct repository *r,
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;
@@ -237,8 +244,6 @@ void mark_trees_uninteresting_sparse(struct repository *r,
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);
@@ -255,7 +260,7 @@ struct commit_stack {
struct commit **items;
size_t nr, alloc;
};
-#define COMMIT_STACK_INIT { NULL, 0, 0 }
+#define COMMIT_STACK_INIT { 0 }
static void commit_stack_push(struct commit_stack *stack, struct commit *commit)
{
@@ -274,7 +279,7 @@ static void commit_stack_clear(struct commit_stack *stack)
stack->nr = stack->alloc = 0;
}
-static void mark_one_parent_uninteresting(struct commit *commit,
+static void mark_one_parent_uninteresting(struct rev_info *revs, struct commit *commit,
struct commit_stack *pending)
{
struct commit_list *l;
@@ -291,20 +296,26 @@ static void mark_one_parent_uninteresting(struct commit *commit,
* wasn't uninteresting), in which case we need
* to mark its parents recursively too..
*/
- for (l = commit->parents; l; l = l->next)
+ for (l = commit->parents; l; l = l->next) {
commit_stack_push(pending, l->item);
+ if (revs && revs->exclude_first_parent_only)
+ break;
+ }
}
-void mark_parents_uninteresting(struct commit *commit)
+void mark_parents_uninteresting(struct rev_info *revs, struct commit *commit)
{
struct commit_stack pending = COMMIT_STACK_INIT;
struct commit_list *l;
- for (l = commit->parents; l; l = l->next)
- mark_one_parent_uninteresting(l->item, &pending);
+ for (l = commit->parents; l; l = l->next) {
+ mark_one_parent_uninteresting(revs, l->item, &pending);
+ if (revs && revs->exclude_first_parent_only)
+ break;
+ }
while (pending.nr > 0)
- mark_one_parent_uninteresting(commit_stack_pop(&pending),
+ mark_one_parent_uninteresting(revs, commit_stack_pop(&pending),
&pending);
commit_stack_clear(&pending);
@@ -315,15 +326,18 @@ static void add_pending_object_with_path(struct rev_info *revs,
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);
+ size_t namelen = strlen(name);
+ int len = repo_interpret_branch_name(the_repository, name,
+ namelen, &buf, &options);
- if (0 < len && name[len] && buf.len)
+ if (0 < len && len < namelen && buf.len)
strbuf_addstr(&buf, name + len);
add_reflog_for_walk(revs->reflog_info,
(struct commit *)obj,
@@ -351,7 +365,7 @@ void add_head_to_pending(struct rev_info *revs)
{
struct object_id oid;
struct object *obj;
- if (get_oid("HEAD", &oid))
+ if (repo_get_oid(the_repository, "HEAD", &oid))
return;
obj = parse_object(revs->repo, &oid);
if (!obj)
@@ -365,25 +379,20 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
{
struct object *object;
- /*
- * If the repository has commit graphs, repo_parse_commit() avoids
- * reading the object buffer, so use it whenever possible.
- */
- if (oid_object_info(revs->repo, oid, NULL) == OBJ_COMMIT) {
- struct commit *c = lookup_commit(revs->repo, oid);
- if (!repo_parse_commit(revs->repo, c))
- object = (struct object *) c;
- else
- object = NULL;
- } else {
- object = parse_object(revs->repo, oid);
- }
+ object = parse_object_with_flags(revs->repo, oid,
+ revs->verify_objects ? 0 :
+ PARSE_OBJECT_SKIP_HASH_CHECK |
+ PARSE_OBJECT_DISCARD_TREE);
if (!object) {
if (revs->ignore_missing)
- return object;
+ return NULL;
if (revs->exclude_promisor_objects && is_promisor_object(oid))
return NULL;
+ if (revs->do_not_die_on_missing_objects) {
+ oidset_insert(&revs->missing_commits, oid);
+ return NULL;
+ }
die("bad object %s", name);
}
object->flags |= flags;
@@ -411,15 +420,21 @@ static struct commit *handle_commit(struct rev_info *revs,
*/
while (object->type == OBJ_TAG) {
struct tag *tag = (struct tag *) object;
+ struct object_id *oid;
if (revs->tag_objects && !(flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
- object = parse_object(revs->repo, get_tagged_oid(tag));
+ oid = get_tagged_oid(tag);
+ object = parse_object(revs->repo, oid);
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
if (revs->exclude_promisor_objects &&
is_promisor_object(&tag->tagged->oid))
return NULL;
+ if (revs->do_not_die_on_missing_objects && oid) {
+ oidset_insert(&revs->missing_commits, oid);
+ return NULL;
+ }
die("bad object %s", oid_to_hex(&tag->tagged->oid));
}
object->flags |= flags;
@@ -439,10 +454,10 @@ static struct commit *handle_commit(struct rev_info *revs,
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);
+ mark_parents_uninteresting(revs, commit);
if (!revs->topo_order || !generation_numbers_enabled(the_repository))
revs->limited = 1;
@@ -600,13 +615,19 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
*
* 2. We saw anything except REV_TREE_NEW.
*/
+#define REV_TREE_SAME 0
+#define REV_TREE_NEW 1 /* Only new files */
+#define REV_TREE_OLD 2 /* Only files removed */
+#define REV_TREE_DIFFERENT 3 /* Mixed changes */
static int tree_difference = REV_TREE_SAME;
static void file_add_remove(struct diff_options *options,
- int addremove, unsigned mode,
- const struct object_id *oid,
- int oid_valid,
- const char *fullpath, unsigned dirty_submodule)
+ int addremove,
+ unsigned mode UNUSED,
+ const struct object_id *oid UNUSED,
+ int oid_valid UNUSED,
+ const char *fullpath UNUSED,
+ unsigned dirty_submodule UNUSED)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
struct rev_info *revs = options->change_fn_data;
@@ -617,12 +638,15 @@ static void file_add_remove(struct diff_options *options,
}
static void file_change(struct diff_options *options,
- unsigned old_mode, unsigned new_mode,
- const struct object_id *old_oid,
- const struct object_id *new_oid,
- int old_oid_valid, int new_oid_valid,
- const char *fullpath,
- unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+ unsigned old_mode UNUSED,
+ unsigned new_mode UNUSED,
+ const struct object_id *old_oid UNUSED,
+ const struct object_id *new_oid UNUSED,
+ int old_oid_valid UNUSED,
+ int new_oid_valid UNUSED,
+ const char *fullpath UNUSED,
+ unsigned old_dirty_submodule UNUSED,
+ unsigned new_dirty_submodule UNUSED)
{
tree_difference = REV_TREE_DIFFERENT;
options->flags.has_changes = 1;
@@ -633,7 +657,6 @@ 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 unsigned int count_bloom_filter_length_zero;
static void trace2_bloom_filter_statistics_atexit(void)
{
@@ -641,7 +664,6 @@ static void trace2_bloom_filter_statistics_atexit(void)
jw_object_begin(&jw, 0);
jw_object_intmax(&jw, "filter_not_present", count_bloom_filter_not_present);
- jw_object_intmax(&jw, "zero_length_filter", count_bloom_filter_length_zero);
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);
@@ -670,9 +692,9 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
{
struct pathspec_item *pi;
char *path_alloc = NULL;
- const char *path;
- int last_index;
- int len;
+ const char *path, *p;
+ size_t len;
+ int path_component_nr = 1;
if (!revs->commits)
return;
@@ -682,10 +704,7 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
repo_parse_commit(revs->repo, revs->commits->item);
- if (!revs->repo->objects->commit_graph)
- return;
-
- revs->bloom_filter_settings = revs->repo->objects->commit_graph->bloom_filter_settings;
+ revs->bloom_filter_settings = get_bloom_filter_settings(revs->repo);
if (!revs->bloom_filter_settings)
return;
@@ -693,20 +712,48 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs)
return;
pi = &revs->pruning.pathspec.items[0];
- last_index = pi->len - 1;
/* remove single trailing slash from path, if needed */
- if (pi->match[last_index] == '/') {
- path_alloc = xstrdup(pi->match);
- path_alloc[last_index] = '\0';
- path = path_alloc;
+ if (pi->len > 0 && pi->match[pi->len - 1] == '/') {
+ path_alloc = xmemdupz(pi->match, pi->len - 1);
+ path = path_alloc;
} else
- path = pi->match;
+ path = pi->match;
len = strlen(path);
+ if (!len) {
+ revs->bloom_filter_settings = NULL;
+ free(path_alloc);
+ return;
+ }
- revs->bloom_key = xmalloc(sizeof(struct bloom_key));
- fill_bloom_key(path, len, revs->bloom_key, revs->bloom_filter_settings);
+ 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);
@@ -720,7 +767,7 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
struct commit *commit)
{
struct bloom_filter *filter;
- int result;
+ int result = 1, j;
if (!revs->repo->objects->commit_graph)
return -1;
@@ -728,22 +775,19 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
if (commit_graph_generation(commit) == GENERATION_NUMBER_INFINITY)
return -1;
- filter = get_bloom_filter(revs->repo, commit, 0);
+ filter = get_bloom_filter(revs->repo, commit);
if (!filter) {
count_bloom_filter_not_present++;
return -1;
}
- if (!filter->len) {
- count_bloom_filter_length_zero++;
- 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);
}
- result = bloom_filter_contains(filter,
- revs->bloom_key,
- revs->bloom_filter_settings);
-
if (result)
count_bloom_filter_maybe++;
else
@@ -755,8 +799,8 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs,
static int rev_compare_tree(struct rev_info *revs,
struct commit *parent, struct commit *commit, int nth_parent)
{
- struct tree *t1 = get_commit_tree(parent);
- struct tree *t2 = get_commit_tree(commit);
+ struct tree *t1 = repo_get_commit_tree(the_repository, parent);
+ struct tree *t2 = repo_get_commit_tree(the_repository, commit);
int bloom_ret = 1;
if (!t1)
@@ -782,7 +826,7 @@ static int rev_compare_tree(struct rev_info *revs,
return REV_TREE_SAME;
}
- if (revs->bloom_key && !nth_parent) {
+ if (revs->bloom_keys_nr && !nth_parent) {
bloom_ret = check_maybe_different_in_bloom_filter(revs, commit);
if (bloom_ret == 0)
@@ -791,9 +835,7 @@ static int rev_compare_tree(struct rev_info *revs,
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)
@@ -804,17 +846,16 @@ static int rev_compare_tree(struct rev_info *revs,
static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
{
- int retval;
- struct tree *t1 = get_commit_tree(commit);
+ struct tree *t1 = repo_get_commit_tree(the_repository, commit);
if (!t1)
return 0;
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 {
@@ -943,7 +984,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
if (!revs->prune)
return;
- if (!get_commit_tree(commit))
+ if (!repo_get_commit_tree(the_repository, commit))
return;
if (!commit->parents) {
@@ -992,7 +1033,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
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));
@@ -1037,7 +1078,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
* 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));
@@ -1077,10 +1118,13 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
struct commit_list **list, struct prio_queue *queue)
{
struct commit_list *parent = commit->parents;
- unsigned left_flag;
+ unsigned pass_flags;
if (commit->object.flags & ADDED)
return 0;
+ if (revs->do_not_die_on_missing_objects &&
+ oidset_contains(&revs->missing_commits, &commit->object.oid))
+ return 0;
commit->object.flags |= ADDED;
if (revs->include_check &&
@@ -1105,17 +1149,19 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
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);
+ mark_parents_uninteresting(revs, p);
if (p->object.flags & SEEN)
continue;
- p->object.flags |= SEEN;
+ p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (list)
commit_list_insert_by_date(p, list);
if (queue)
prio_queue_put(queue, p);
+ if (revs->exclude_first_parent_only)
+ break;
}
return 0;
}
@@ -1130,20 +1176,25 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
if (revs->no_walk)
return 0;
- left_flag = (commit->object.flags & SYMMETRIC_LEFT);
+ pass_flags = (commit->object.flags & (SYMMETRIC_LEFT | ANCESTRY_PATH));
for (parent = commit->parents; parent; parent = parent->next) {
struct commit *p = parent->item;
int gently = revs->ignore_missing_links ||
- revs->exclude_promisor_objects;
- if (parse_commit_gently(p, gently) < 0) {
+ revs->exclude_promisor_objects ||
+ revs->do_not_die_on_missing_objects;
+ 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)
break;
continue;
}
- return -1;
+
+ if (revs->do_not_die_on_missing_objects)
+ oidset_insert(&revs->missing_commits, &p->object.oid);
+ else
+ return -1; /* corrupt repository */
}
if (revs->sources) {
char **slot = revision_sources_at(revs->sources, p);
@@ -1151,9 +1202,9 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
if (!*slot)
*slot = *revision_sources_at(revs->sources, commit);
}
- p->object.flags |= left_flag;
+ p->object.flags |= pass_flags;
if (!(p->object.flags & SEEN)) {
- p->object.flags |= SEEN;
+ p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (list)
commit_list_insert_by_date(p, list);
if (queue)
@@ -1230,12 +1281,14 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
/*
* Have we seen the same patch id?
*/
- id = has_commit_patch_id(commit, &ids);
+ id = patch_id_iter_first(commit, &ids);
if (!id)
continue;
commit->object.flags |= cherry_flag;
- id->commit->object.flags |= cherry_flag;
+ do {
+ id->commit->object.flags |= cherry_flag;
+ } while ((id = patch_id_iter_next(id, &ids)));
}
free_patch_ids(&ids);
@@ -1272,13 +1325,24 @@ static int still_interesting(struct commit_list *src, timestamp_t date, int slop
}
/*
- * "rev-list --ancestry-path A..B" computes commits that are ancestors
- * of B but not ancestors of A but further limits the result to those
- * that are descendants of A. This takes the list of bottom commits and
- * the result of "A..B" without --ancestry-path, and limits the latter
- * further to the ones that can reach one of the commits in "bottom".
+ * "rev-list --ancestry-path=C_0 [--ancestry-path=C_1 ...] A..B"
+ * computes commits that are ancestors of B but not ancestors of A but
+ * further limits the result to those that have any of C in their
+ * ancestry path (i.e. are either ancestors of any of C, descendants
+ * of any of C, or are any of C). If --ancestry-path is specified with
+ * no commit, we use all bottom commits for C.
+ *
+ * Before this function is called, ancestors of C will have already
+ * been marked with ANCESTRY_PATH previously.
+ *
+ * This takes the list of bottom commits and the result of "A..B"
+ * without --ancestry-path, and limits the latter further to the ones
+ * that have any of C in their ancestry path. Since the ancestors of C
+ * have already been marked (a prerequisite of this function), we just
+ * need to mark the descendants, then exclude any commit that does not
+ * have any of these marks.
*/
-static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+static void limit_to_ancestry(struct commit_list *bottoms, struct commit_list *list)
{
struct commit_list *p;
struct commit_list *rlist = NULL;
@@ -1291,7 +1355,7 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li
for (p = list; p; p = p->next)
commit_list_insert(p->item, &rlist);
- for (p = bottom; p; p = p->next)
+ for (p = bottoms; p; p = p->next)
p->item->object.flags |= TMP_MARK;
/*
@@ -1324,38 +1388,39 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li
*/
/*
- * The ones that are not marked with TMP_MARK are uninteresting
+ * The ones that are not marked with either TMP_MARK or
+ * ANCESTRY_PATH are uninteresting
*/
for (p = list; p; p = p->next) {
struct commit *c = p->item;
- if (c->object.flags & TMP_MARK)
+ if (c->object.flags & (TMP_MARK | ANCESTRY_PATH))
continue;
c->object.flags |= UNINTERESTING;
}
- /* We are done with the TMP_MARK */
+ /* We are done with TMP_MARK and ANCESTRY_PATH */
for (p = list; p; p = p->next)
- p->item->object.flags &= ~TMP_MARK;
- for (p = bottom; p; p = p->next)
- p->item->object.flags &= ~TMP_MARK;
+ p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
+ for (p = bottoms; p; p = p->next)
+ p->item->object.flags &= ~(TMP_MARK | ANCESTRY_PATH);
free_commit_list(rlist);
}
/*
- * Before walking the history, keep the set of "negative" refs the
- * caller has asked to exclude.
+ * Before walking the history, add the set of "negative" refs the
+ * caller has asked to exclude to the bottom list.
*
* This is used to compute "rev-list --ancestry-path A..B", as we need
* to filter the result of "A..B" further to the ones that can actually
* reach A.
*/
-static struct commit_list *collect_bottom_commits(struct commit_list *list)
+static void collect_bottom_commits(struct commit_list *list,
+ struct commit_list **bottom)
{
- struct commit_list *elem, *bottom = NULL;
+ struct commit_list *elem;
for (elem = list; elem; elem = elem->next)
if (elem->item->object.flags & BOTTOM)
- commit_list_insert(elem->item, &bottom);
- return bottom;
+ commit_list_insert(elem->item, bottom);
}
/* Assumes either left_only or right_only is set */
@@ -1379,20 +1444,20 @@ static int limit_list(struct rev_info *revs)
{
int slop = SLOP;
timestamp_t date = TIME_MAX;
- struct commit_list *list = revs->commits;
+ struct commit_list *original_list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
- struct commit_list *bottom = NULL;
struct commit *interesting_cache = NULL;
- if (revs->ancestry_path) {
- bottom = collect_bottom_commits(list);
- if (!bottom)
+ if (revs->ancestry_path_implicit_bottoms) {
+ collect_bottom_commits(original_list,
+ &revs->ancestry_path_bottoms);
+ if (!revs->ancestry_path_bottoms)
die("--ancestry-path given but there are no bottom commits");
}
- while (list) {
- struct commit *commit = pop_commit(&list);
+ while (original_list) {
+ struct commit *commit = pop_commit(&original_list);
struct object *obj = &commit->object;
show_early_output_fn_t show;
@@ -1401,11 +1466,11 @@ static int limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING;
- if (process_parents(revs, commit, &list, NULL) < 0)
+ if (process_parents(revs, commit, &original_list, NULL) < 0)
return -1;
if (obj->flags & UNINTERESTING) {
- mark_parents_uninteresting(commit);
- slop = still_interesting(list, date, slop, &interesting_cache);
+ mark_parents_uninteresting(revs, commit);
+ slop = still_interesting(original_list, date, slop, &interesting_cache);
if (slop)
continue;
break;
@@ -1413,6 +1478,9 @@ static int limit_list(struct rev_info *revs)
if (revs->min_age != -1 && (commit->date > revs->min_age) &&
!revs->line_level_traverse)
continue;
+ if (revs->max_age_as_filter != -1 &&
+ (commit->date < revs->max_age_as_filter) && !revs->line_level_traverse)
+ continue;
date = commit->date;
p = &commit_list_insert(commit, p)->next;
@@ -1429,23 +1497,24 @@ static int limit_list(struct rev_info *revs)
if (revs->left_only || revs->right_only)
limit_left_right(newlist, revs);
- if (bottom) {
- limit_to_ancestry(bottom, newlist);
- free_commit_list(bottom);
- }
+ if (revs->ancestry_path)
+ limit_to_ancestry(revs->ancestry_path_bottoms, newlist);
/*
* Check if any commits have become TREESAME by some of their parents
* becoming UNINTERESTING.
*/
- if (limiting_can_increase_treesame(revs))
+ if (limiting_can_increase_treesame(revs)) {
+ struct commit_list *list = NULL;
for (list = newlist; list; list = list->next) {
struct commit *c = list->item;
if (c->object.flags & (UNINTERESTING | TREESAME))
continue;
update_treesame(revs, c);
}
+ }
+ free_commit_list(original_list);
revs->commits = newlist;
return 0;
}
@@ -1484,39 +1553,93 @@ static void add_rev_cmdline_list(struct rev_info *revs,
}
}
-struct all_refs_cb {
- int all_flags;
- int warned_bad_reflog;
- struct rev_info *all_revs;
- const char *name_for_errormsg;
- struct worktree *wt;
-};
-
-int ref_excluded(struct string_list *ref_excludes, const char *path)
+int ref_excluded(const struct ref_exclusions *exclusions, const char *path)
{
+ const char *stripped_path = strip_namespace(path);
struct string_list_item *item;
- if (!ref_excludes)
- return 0;
- for_each_string_list_item(item, ref_excludes) {
+ for_each_string_list_item(item, &exclusions->excluded_refs) {
if (!wildmatch(item->string, path, 0))
return 1;
}
+
+ if (ref_is_hidden(stripped_path, path, &exclusions->hidden_refs))
+ return 1;
+
return 0;
}
+void init_ref_exclusions(struct ref_exclusions *exclusions)
+{
+ struct ref_exclusions blank = REF_EXCLUSIONS_INIT;
+ memcpy(exclusions, &blank, sizeof(*exclusions));
+}
+
+void clear_ref_exclusions(struct ref_exclusions *exclusions)
+{
+ string_list_clear(&exclusions->excluded_refs, 0);
+ strvec_clear(&exclusions->hidden_refs);
+ exclusions->hidden_refs_configured = 0;
+}
+
+void add_ref_exclusion(struct ref_exclusions *exclusions, const char *exclude)
+{
+ string_list_append(&exclusions->excluded_refs, exclude);
+}
+
+struct exclude_hidden_refs_cb {
+ struct ref_exclusions *exclusions;
+ const char *section;
+};
+
+static int hide_refs_config(const char *var, const char *value,
+ const struct config_context *ctx UNUSED,
+ void *cb_data)
+{
+ struct exclude_hidden_refs_cb *cb = cb_data;
+ cb->exclusions->hidden_refs_configured = 1;
+ return parse_hide_refs_config(var, value, cb->section,
+ &cb->exclusions->hidden_refs);
+}
+
+void exclude_hidden_refs(struct ref_exclusions *exclusions, const char *section)
+{
+ struct exclude_hidden_refs_cb cb;
+
+ if (strcmp(section, "fetch") && strcmp(section, "receive") &&
+ strcmp(section, "uploadpack"))
+ die(_("unsupported section for hidden refs: %s"), section);
+
+ if (exclusions->hidden_refs_configured)
+ die(_("--exclude-hidden= passed more than once"));
+
+ cb.exclusions = exclusions;
+ cb.section = section;
+
+ git_config(hide_refs_config, &cb);
+}
+
+struct all_refs_cb {
+ int all_flags;
+ int warned_bad_reflog;
+ struct rev_info *all_revs;
+ const char *name_for_errormsg;
+ struct worktree *wt;
+};
+
static int handle_one_ref(const char *path, const struct object_id *oid,
- int flag, void *cb_data)
+ int flag UNUSED,
+ void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct object *object;
- if (ref_excluded(cb->all_revs->ref_excludes, path))
+ if (ref_excluded(&cb->all_revs->ref_excludes, path))
return 0;
object = get_reference(cb->all_revs, path, oid, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_oid(cb->all_revs, path, oid, cb->all_flags);
+ add_pending_object(cb->all_revs, object, path);
return 0;
}
@@ -1529,24 +1652,6 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
cb->wt = NULL;
}
-void clear_ref_exclusion(struct string_list **ref_excludes_p)
-{
- if (*ref_excludes_p) {
- string_list_clear(*ref_excludes_p, 0);
- free(*ref_excludes_p);
- }
- *ref_excludes_p = NULL;
-}
-
-void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
-{
- if (!*ref_excludes_p) {
- *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));
- (*ref_excludes_p)->strdup_strings = 1;
- }
- string_list_append(*ref_excludes_p, exclude);
-}
-
static void handle_refs(struct ref_store *refs,
struct rev_info *revs, unsigned flags,
int (*for_each)(struct ref_store *, each_ref_fn, void *))
@@ -1581,17 +1686,18 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
}
static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, timestamp_t timestamp, int tz,
- const char *message, void *cb_data)
+ const char *email UNUSED,
+ timestamp_t timestamp UNUSED,
+ int tz UNUSED,
+ const char *message UNUSED,
+ void *cb_data)
{
handle_one_reflog_commit(ooid, cb_data);
handle_one_reflog_commit(noid, cb_data);
return 0;
}
-static int handle_one_reflog(const char *refname_in_wt,
- const struct object_id *oid,
- int flag, void *cb_data)
+static int handle_one_reflog(const char *refname_in_wt, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
struct strbuf refname = STRBUF_INIT;
@@ -1660,12 +1766,47 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
}
+static void add_resolve_undo_to_pending(struct index_state *istate, struct rev_info *revs)
+{
+ struct string_list_item *item;
+ struct string_list *resolve_undo = istate->resolve_undo;
+
+ if (!resolve_undo)
+ return;
+
+ for_each_string_list_item(item, resolve_undo) {
+ const char *path = item->string;
+ struct resolve_undo_info *ru = item->util;
+ int i;
+
+ if (!ru)
+ continue;
+ for (i = 0; i < 3; i++) {
+ struct blob *blob;
+
+ if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
+ continue;
+
+ blob = lookup_blob(revs->repo, &ru->oid[i]);
+ if (!blob) {
+ warning(_("resolve-undo records `%s` which is missing"),
+ oid_to_hex(&ru->oid[i]));
+ continue;
+ }
+ add_pending_object_with_path(revs, &blob->object, "",
+ ru->mode[i], path);
+ }
+ }
+}
+
static void do_add_index_objects_to_pending(struct rev_info *revs,
struct index_state *istate,
unsigned int flags)
{
int i;
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(istate);
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
struct blob *blob;
@@ -1686,6 +1827,8 @@ static void do_add_index_objects_to_pending(struct rev_info *revs,
add_cache_tree(istate->cache_tree, revs, &path, flags);
strbuf_release(&path);
}
+
+ add_resolve_undo_to_pending(istate, revs);
}
void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
@@ -1701,7 +1844,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
- struct index_state istate = { NULL };
+ struct index_state istate = INDEX_STATE_INIT(revs->repo);
if (wt->is_current)
continue; /* current index already taken care of */
@@ -1755,7 +1898,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
flags ^= UNINTERESTING | BOTTOM;
arg++;
}
- if (get_oid_committish(arg, &oid))
+ if (repo_get_oid_committish(the_repository, arg, &oid))
return 0;
while (1) {
it = get_reference(revs, arg, &oid, 0);
@@ -1791,33 +1934,17 @@ void repo_init_revisions(struct repository *r,
struct rev_info *revs,
const char *prefix)
{
- memset(revs, 0, sizeof(*revs));
+ struct rev_info blank = REV_INFO_INIT;
+ memcpy(revs, &blank, sizeof(*revs));
revs->repo = r;
- revs->abbrev = DEFAULT_ABBREV;
- revs->ignore_merges = 1;
- revs->simplify_history = 1;
revs->pruning.repo = r;
- revs->pruning.flags.recursive = 1;
- revs->pruning.flags.quick = 1;
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
revs->pruning.change_fn_data = revs;
- revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
- revs->dense = 1;
revs->prefix = prefix;
- revs->max_age = -1;
- revs->min_age = -1;
- revs->skip_count = -1;
- revs->max_count = -1;
- revs->max_parents = -1;
- revs->expand_tabs_in_log = -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);
+
+ grep_init(&revs->grep_filter, revs->repo);
revs->grep_filter.status_only = 1;
repo_diff_setup(revs->repo, &revs->diffopt);
@@ -1827,6 +1954,9 @@ void repo_init_revisions(struct repository *r,
}
init_display_notes(&revs->notes_opt);
+ list_objects_filter_init(&revs->filter);
+ init_ref_exclusions(&revs->ref_excludes);
+ oidset_init(&revs->missing_commits, 0);
}
static void add_pending_commit_list(struct rev_info *revs,
@@ -1841,24 +1971,44 @@ static void add_pending_commit_list(struct rev_info *revs,
}
}
+static const char *lookup_other_head(struct object_id *oid)
+{
+ int i;
+ static const char *const other_head[] = {
+ "MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "REBASE_HEAD"
+ };
+
+ for (i = 0; i < ARRAY_SIZE(other_head); i++)
+ if (!read_ref_full(other_head[i],
+ RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+ oid, NULL)) {
+ if (is_null_oid(oid))
+ die(_("%s exists but is a symbolic ref"), other_head[i]);
+ return other_head[i];
+ }
+
+ die(_("--merge requires one of the pseudorefs MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD or REBASE_HEAD"));
+}
+
static void prepare_show_merge(struct rev_info *revs)
{
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
struct commit *head, *other;
struct object_id oid;
+ const char *other_name;
const char **prune = NULL;
int i, prune_num = 1; /* counting terminating NULL */
struct index_state *istate = revs->repo->index;
- if (get_oid("HEAD", &oid))
+ if (repo_get_oid(the_repository, "HEAD", &oid))
die("--merge without HEAD?");
head = lookup_commit_or_die(&oid, "HEAD");
- if (get_oid("MERGE_HEAD", &oid))
- die("--merge without MERGE_HEAD?");
- other = lookup_commit_or_die(&oid, "MERGE_HEAD");
+ other_name = lookup_other_head(&oid);
+ other = lookup_commit_or_die(&oid, other_name);
add_pending_object(revs, &head->object, "HEAD");
- add_pending_object(revs, &other->object, "MERGE_HEAD");
- bases = get_merge_bases(head, other);
+ add_pending_object(revs, &other->object, other_name);
+ if (repo_get_merge_bases(the_repository, head, other, &bases) < 0)
+ exit(128);
add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
free_commit_list(bases);
@@ -1946,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
} else {
/* A...B -- find merge bases between the two */
struct commit *a, *b;
- struct commit_list *exclude;
+ struct commit_list *exclude = NULL;
a = lookup_commit_reference(revs->repo, &a_obj->oid);
b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric);
- exclude = get_merge_bases(a, b);
+ if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
+ free_commit_list(exclude);
+ return -1;
+ }
add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
flags_exclude);
add_pending_commit_list(revs, exclude, flags_exclude);
@@ -1997,7 +2150,7 @@ static int handle_dotdot(const char *arg,
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;
@@ -2039,9 +2192,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
int exclude_parent = 1;
if (mark[2]) {
- char *end;
- exclude_parent = strtoul(mark + 2, &end, 10);
- if (*end != '\0' || !exclude_parent)
+ if (strtol_i(mark + 2, 10, &exclude_parent) ||
+ exclude_parent < 1)
return -1;
}
@@ -2059,57 +2211,37 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
if (revarg_opt & REVARG_COMMITTISH)
get_sha1_flags |= GET_OID_COMMITTISH;
+ /*
+ * Even if revs->do_not_die_on_missing_objects is set, we
+ * should error out if we can't even get an oid, as
+ * `--missing=print` should be able to report missing oids.
+ */
if (get_oid_with_context(revs->repo, arg, get_sha1_flags, &oid, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, &oid, flags ^ local_flags);
if (!object)
- return revs->ignore_missing ? 0 : -1;
+ return (revs->ignore_missing || revs->do_not_die_on_missing_objects) ? 0 : -1;
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
free(oc.path);
return 0;
}
-static void read_pathspec_from_stdin(struct strbuf *sb,
- struct argv_array *prune)
+int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt)
{
- while (strbuf_getline(sb, stdin) != EOF)
- argv_array_push(prune, sb->buf);
+ int ret = handle_revision_arg_1(arg, revs, flags, revarg_opt);
+ if (!ret)
+ revs->rev_input_given = 1;
+ return ret;
}
-static void read_revisions_from_stdin(struct rev_info *revs,
- struct argv_array *prune)
+static void read_pathspec_from_stdin(struct strbuf *sb,
+ struct strvec *prune)
{
- struct strbuf sb;
- int seen_dashdash = 0;
- int save_warning;
-
- save_warning = warn_on_object_refname_ambiguity;
- warn_on_object_refname_ambiguity = 0;
-
- strbuf_init(&sb, 1000);
- while (strbuf_getline(&sb, stdin) != EOF) {
- int len = sb.len;
- if (!len)
- break;
- if (sb.buf[0] == '-') {
- if (len == 2 && sb.buf[1] == '-') {
- seen_dashdash = 1;
- break;
- }
- die("options not supported in --stdin mode");
- }
- if (handle_revision_arg(sb.buf, revs, 0,
- REVARG_CANNOT_BE_FILENAME))
- die("bad revision '%s'", sb.buf);
- }
- if (seen_dashdash)
- read_pathspec_from_stdin(&sb, prune);
-
- strbuf_release(&sb);
- warn_on_object_refname_ambiguity = save_warning;
+ while (strbuf_getline(sb, stdin) != EOF)
+ strvec_push(prune, sb->buf);
}
static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
@@ -2127,12 +2259,33 @@ static void add_message_grep(struct rev_info *revs, const char *pattern)
add_grep(revs, pattern, GREP_PATTERN_BODY);
}
+static int parse_count(const char *arg)
+{
+ int count;
+
+ if (strtol_i(arg, 10, &count) < 0)
+ die("'%s': not an integer", arg);
+ return count;
+}
+
+static timestamp_t parse_age(const char *arg)
+{
+ timestamp_t num;
+ char *p;
+
+ errno = 0;
+ num = parse_timestamp(arg, &p, 10);
+ if (errno || *p || p == arg)
+ die("'%s': not a number of seconds since epoch", arg);
+ return num;
+}
+
static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
int *unkc, const char **unkv,
const struct setup_revision_opt* opt)
{
const char *arg = argv[0];
- const char *optarg;
+ const char *optarg = NULL;
int argcount;
const unsigned hexsz = the_hash_algo->hexsz;
@@ -2144,7 +2297,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
!strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
!strcmp(arg, "--indexed-objects") ||
!strcmp(arg, "--alternate-refs") ||
- starts_with(arg, "--exclude=") ||
+ starts_with(arg, "--exclude=") || starts_with(arg, "--exclude-hidden=") ||
starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
{
@@ -2153,38 +2306,39 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
}
if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
- revs->max_count = atoi(optarg);
+ revs->max_count = parse_count(optarg);
revs->no_walk = 0;
return argcount;
} else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
- revs->skip_count = atoi(optarg);
+ revs->skip_count = parse_count(optarg);
return argcount;
} else if ((*arg == '-') && isdigit(arg[1])) {
/* accept -<digit>, like traditional "head" */
- if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
- revs->max_count < 0)
- die("'%s': not a non-negative integer", arg + 1);
+ revs->max_count = parse_count(arg + 1);
revs->no_walk = 0;
} else if (!strcmp(arg, "-n")) {
if (argc <= 1)
return error("-n requires an argument");
- revs->max_count = atoi(argv[1]);
+ revs->max_count = parse_count(argv[1]);
revs->no_walk = 0;
return 2;
} else if (skip_prefix(arg, "-n", &optarg)) {
- revs->max_count = atoi(optarg);
+ revs->max_count = parse_count(optarg);
revs->no_walk = 0;
} else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
- revs->max_age = atoi(optarg);
+ revs->max_age = parse_age(optarg);
return argcount;
} else if ((argcount = parse_long_opt("since", argv, &optarg))) {
revs->max_age = approxidate(optarg);
return argcount;
+ } else if ((argcount = parse_long_opt("since-as-filter", argv, &optarg))) {
+ revs->max_age_as_filter = approxidate(optarg);
+ return argcount;
} else if ((argcount = parse_long_opt("after", argv, &optarg))) {
revs->max_age = approxidate(optarg);
return argcount;
} else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
- revs->min_age = atoi(optarg);
+ revs->min_age = parse_age(optarg);
return argcount;
} else if ((argcount = parse_long_opt("before", argv, &optarg))) {
revs->min_age = approxidate(optarg);
@@ -2194,10 +2348,29 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
return argcount;
} else if (!strcmp(arg, "--first-parent")) {
revs->first_parent_only = 1;
+ } else if (!strcmp(arg, "--exclude-first-parent-only")) {
+ revs->exclude_first_parent_only = 1;
} else if (!strcmp(arg, "--ancestry-path")) {
revs->ancestry_path = 1;
revs->simplify_history = 0;
revs->limited = 1;
+ revs->ancestry_path_implicit_bottoms = 1;
+ } else if (skip_prefix(arg, "--ancestry-path=", &optarg)) {
+ struct commit *c;
+ struct object_id oid;
+ const char *msg = _("could not get commit for --ancestry-path argument %s");
+
+ revs->ancestry_path = 1;
+ revs->simplify_history = 0;
+ revs->limited = 1;
+
+ if (repo_get_oid_committish(revs->repo, optarg, &oid))
+ return error(msg, optarg);
+ get_reference(revs, optarg, &oid, ANCESTRY_PATH);
+ c = lookup_commit_reference(revs->repo, &oid);
+ if (!c)
+ return error(msg, optarg);
+ commit_list_insert(c, &revs->ancestry_path_bottoms);
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
init_reflog_walk(&revs->reflog_info);
} else if (!strcmp(arg, "--default")) {
@@ -2253,11 +2426,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--no-merges")) {
revs->max_parents = 1;
} else if (skip_prefix(arg, "--min-parents=", &optarg)) {
- revs->min_parents = atoi(optarg);
+ revs->min_parents = parse_count(optarg);
} else if (!strcmp(arg, "--no-min-parents")) {
revs->min_parents = 0;
} else if (skip_prefix(arg, "--max-parents=", &optarg)) {
- revs->max_parents = atoi(optarg);
+ revs->max_parents = parse_count(optarg);
} else if (!strcmp(arg, "--no-max-parents")) {
revs->max_parents = -1;
} else if (!strcmp(arg, "--boundary")) {
@@ -2266,16 +2439,16 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->left_right = 1;
} else if (!strcmp(arg, "--left-only")) {
if (revs->right_only)
- die("--left-only is incompatible with --right-only"
- " or --cherry");
+ die(_("options '%s' and '%s' cannot be used together"),
+ "--left-only", "--right-only/--cherry");
revs->left_only = 1;
} else if (!strcmp(arg, "--right-only")) {
if (revs->left_only)
- die("--right-only is incompatible with --left-only");
+ die(_("options '%s' and '%s' cannot be used together"), "--right-only", "--left-only");
revs->right_only = 1;
} else if (!strcmp(arg, "--cherry")) {
if (revs->left_only)
- die("--cherry is incompatible with --left-only");
+ die(_("options '%s' and '%s' cannot be used together"), "--cherry", "--left-only");
revs->cherry_mark = 1;
revs->right_only = 1;
revs->max_parents = 1;
@@ -2284,12 +2457,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->count = 1;
} else if (!strcmp(arg, "--cherry-mark")) {
if (revs->cherry_pick)
- die("--cherry-mark is incompatible with --cherry-pick");
+ die(_("options '%s' and '%s' cannot be used together"), "--cherry-mark", "--cherry-pick");
revs->cherry_mark = 1;
revs->limited = 1; /* needs limit_list() */
} else if (!strcmp(arg, "--cherry-pick")) {
if (revs->cherry_mark)
- die("--cherry-pick is incompatible with --cherry-mark");
+ die(_("options '%s' and '%s' cannot be used together"), "--cherry-pick", "--cherry-mark");
revs->cherry_pick = 1;
revs->limited = 1;
} else if (!strcmp(arg, "--objects")) {
@@ -2312,10 +2485,21 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->tree_objects = 1;
revs->blob_objects = 1;
revs->verify_objects = 1;
+ disable_commit_graph(revs->repo);
} 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, "--no-kept-objects")) {
+ revs->no_kept_objects = 1;
+ revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
+ revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS;
+ } else if (skip_prefix(arg, "--no-kept-objects=", &optarg)) {
+ revs->no_kept_objects = 1;
+ if (!strcmp(optarg, "in-core"))
+ revs->keep_pack_cache_flags |= IN_CORE_KEEP_PACKS;
+ if (!strcmp(optarg, "on-disk"))
+ revs->keep_pack_cache_flags |= ON_DISK_KEEP_PACKS;
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
revs->diffopt.flags.recursive = 1;
@@ -2323,19 +2507,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->diff = 1;
revs->diffopt.flags.recursive = 1;
revs->diffopt.flags.tree_in_recursive = 1;
- } else if (!strcmp(arg, "-m")) {
- revs->ignore_merges = 0;
- } else if (!strcmp(arg, "-c")) {
- revs->diff = 1;
- revs->dense_combined_merges = 0;
- revs->combine_merges = 1;
- } else if (!strcmp(arg, "--combined-all-paths")) {
- revs->diff = 1;
- revs->combined_all_paths = 1;
- } else if (!strcmp(arg, "--cc")) {
- revs->diff = 1;
- revs->dense_combined_merges = 1;
- revs->combine_merges = 1;
+ } else if ((argcount = diff_merges_parse_opts(revs, argv))) {
+ return argcount;
} else if (!strcmp(arg, "-v")) {
revs->verbose_header = 1;
} else if (!strcmp(arg, "--pretty")) {
@@ -2375,6 +2548,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->break_bar = xstrdup(optarg);
revs->track_linear = 1;
revs->track_first_time = 1;
+ } else if (!strcmp(arg, "--show-notes-by-default")) {
+ revs->show_notes_by_default = 1;
} else if (skip_prefix(arg, "--show-notes=", &optarg) ||
skip_prefix(arg, "--notes=", &optarg)) {
if (starts_with(arg, "--show-notes=") &&
@@ -2396,9 +2571,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->pretty_given = 1;
revs->abbrev_commit = 1;
} else if (!strcmp(arg, "--graph")) {
- revs->topo_order = 1;
- revs->rewrite_parents = 1;
+ graph_clear(revs->graph);
revs->graph = graph_init(revs);
+ } else if (!strcmp(arg, "--no-graph")) {
+ graph_clear(revs->graph);
+ revs->graph = NULL;
} else if (!strcmp(arg, "--encode-email-headers")) {
revs->encode_email_headers = 1;
} else if (!strcmp(arg, "--no-encode-email-headers")) {
@@ -2456,8 +2633,6 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
add_message_grep(revs, optarg);
return argcount;
- } else if (!strcmp(arg, "--grep-debug")) {
- revs->grep_filter.debug = 1;
} else if (!strcmp(arg, "--basic-regexp")) {
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_BRE;
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
@@ -2472,7 +2647,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--all-match")) {
revs->grep_filter.all_match = 1;
} else if (!strcmp(arg, "--invert-grep")) {
- revs->invert_grep = 1;
+ revs->grep_filter.no_body_match = 1;
} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
if (strcmp(optarg, "none"))
git_log_output_encoding = xstrdup(optarg);
@@ -2497,8 +2672,6 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
unkv[(*unkc)++] = arg;
return opts;
}
- if (revs->graph && revs->track_linear)
- die("--show-linear-break and --graph are incompatible");
return 1;
}
@@ -2517,13 +2690,24 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
ctx->argc -= n;
}
+void revision_opts_finish(struct rev_info *revs)
+{
+ if (revs->graph && revs->track_linear)
+ die(_("options '%s' and '%s' cannot be used together"), "--show-linear-break", "--graph");
+
+ if (revs->graph) {
+ revs->topo_order = 1;
+ revs->rewrite_parents = 1;
+ }
+}
+
static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
void *cb_data, const char *term)
{
struct strbuf bisect_refs = STRBUF_INIT;
int status;
strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
- status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
+ status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data);
strbuf_release(&bisect_refs);
return status;
}
@@ -2538,16 +2722,15 @@ static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void
return for_each_bisect_ref(refs, fn, cb_data, term_good);
}
-static int handle_revision_pseudo_opt(const char *submodule,
- struct rev_info *revs,
- int argc, const char **argv, int *flags)
+static int handle_revision_pseudo_opt(struct rev_info *revs,
+ const char **argv, int *flags)
{
const char *arg = argv[0];
const char *optarg;
struct ref_store *refs;
int argcount;
- if (submodule) {
+ if (revs->repo != the_repository) {
/*
* We need some something like get_submodule_worktrees()
* before we can go through all worktrees of a submodule,
@@ -2556,9 +2739,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
*/
if (!revs->single_worktree)
BUG("--single-worktree cannot be used together with submodule");
- refs = get_submodule_ref_store(submodule);
- } else
- refs = get_main_ref_store(revs->repo);
+ }
+ refs = get_main_ref_store(revs->repo);
/*
* NOTE!
@@ -2579,10 +2761,13 @@ static int handle_revision_pseudo_opt(const char *submodule,
init_all_refs_cb(&cb, revs, *flags);
other_head_refs(handle_one_ref, &cb);
}
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--branches")) {
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--branches");
handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--bisect")) {
read_bisect_terms(&term_bad, &term_good);
handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
@@ -2590,35 +2775,53 @@ static int handle_revision_pseudo_opt(const char *submodule,
for_each_good_bisect_ref);
revs->bisect = 1;
} else if (!strcmp(arg, "--tags")) {
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--tags");
handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--remotes")) {
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--remotes");
handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
struct all_refs_cb cb;
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref(handle_one_ref, optarg, &cb);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
return argcount;
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
add_ref_exclusion(&revs->ref_excludes, optarg);
return argcount;
+ } else if ((argcount = parse_long_opt("exclude-hidden", argv, &optarg))) {
+ exclude_hidden_refs(&revs->ref_excludes, optarg);
+ return argcount;
} else if (skip_prefix(arg, "--branches=", &optarg)) {
struct all_refs_cb cb;
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--branches");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/heads/", &cb);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--tags=", &optarg)) {
struct all_refs_cb cb;
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--tags");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/tags/", &cb);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (skip_prefix(arg, "--remotes=", &optarg)) {
struct all_refs_cb cb;
+ if (revs->ref_excludes.hidden_refs_configured)
+ return error(_("options '%s' and '%s' cannot be used together"),
+ "--exclude-hidden", "--remotes");
init_all_refs_cb(&cb, revs, *flags);
for_each_glob_ref_in(handle_one_ref, optarg, "refs/remotes/", &cb);
- clear_ref_exclusion(&revs->ref_excludes);
+ clear_ref_exclusions(&revs->ref_excludes);
} else if (!strcmp(arg, "--reflog")) {
add_reflogs_to_pending(revs, *flags);
} else if (!strcmp(arg, "--indexed-objects")) {
@@ -2628,22 +2831,27 @@ static int handle_revision_pseudo_opt(const char *submodule,
} else if (!strcmp(arg, "--not")) {
*flags ^= UNINTERESTING | BOTTOM;
} else if (!strcmp(arg, "--no-walk")) {
- revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ revs->no_walk = 1;
} else if (skip_prefix(arg, "--no-walk=", &optarg)) {
/*
* Detached form ("--no-walk X" as opposed to "--no-walk=X")
* not allowed, since the argument is optional.
*/
+ revs->no_walk = 1;
if (!strcmp(optarg, "sorted"))
- revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+ revs->unsorted_input = 0;
else if (!strcmp(optarg, "unsorted"))
- revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+ revs->unsorted_input = 1;
else
return error("invalid argument to --no-walk");
} else if (!strcmp(arg, "--do-walk")) {
revs->no_walk = 0;
} else if (!strcmp(arg, "--single-worktree")) {
revs->single_worktree = 1;
+ } else if (skip_prefix(arg, ("--filter="), &arg)) {
+ parse_list_objects_filter(&revs->filter, arg);
+ } else if (!strcmp(arg, ("--no-filter"))) {
+ list_objects_filter_set_no_filter(&revs->filter);
} else {
return 0;
}
@@ -2651,6 +2859,53 @@ static int handle_revision_pseudo_opt(const char *submodule,
return 1;
}
+static void read_revisions_from_stdin(struct rev_info *revs,
+ struct strvec *prune)
+{
+ struct strbuf sb;
+ int seen_dashdash = 0;
+ int seen_end_of_options = 0;
+ int save_warning;
+ int flags = 0;
+
+ save_warning = warn_on_object_refname_ambiguity;
+ warn_on_object_refname_ambiguity = 0;
+
+ strbuf_init(&sb, 1000);
+ while (strbuf_getline(&sb, stdin) != EOF) {
+ if (!sb.len)
+ break;
+
+ if (!strcmp(sb.buf, "--")) {
+ seen_dashdash = 1;
+ break;
+ }
+
+ if (!seen_end_of_options && sb.buf[0] == '-') {
+ const char *argv[] = { sb.buf, NULL };
+
+ if (!strcmp(sb.buf, "--end-of-options")) {
+ seen_end_of_options = 1;
+ continue;
+ }
+
+ if (handle_revision_pseudo_opt(revs, argv, &flags) > 0)
+ continue;
+
+ die(_("invalid option '%s' in --stdin mode"), sb.buf);
+ }
+
+ if (handle_revision_arg(sb.buf, revs, flags,
+ REVARG_CANNOT_BE_FILENAME))
+ die("bad revision '%s'", sb.buf);
+ }
+ if (seen_dashdash)
+ read_pathspec_from_stdin(&sb, prune);
+
+ strbuf_release(&sb);
+ warn_on_object_refname_ambiguity = save_warning;
+}
+
static void NORETURN diagnose_missing_default(const char *def)
{
int flags;
@@ -2674,14 +2929,10 @@ static void NORETURN diagnose_missing_default(const char *def)
*/
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;
- const char *submodule = NULL;
+ int i, flags, left, seen_dashdash, revarg_opt;
+ struct strvec prune_data = STRVEC_INIT;
int seen_end_of_options = 0;
- if (opt)
- submodule = opt->submodule;
-
/* First, search for "--" */
if (opt && opt->assume_dashdash) {
seen_dashdash = 1;
@@ -2691,10 +2942,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
const char *arg = argv[i];
if (strcmp(arg, "--"))
continue;
+ if (opt && opt->free_removed_argv_elements)
+ free((char *)argv[i]);
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;
}
@@ -2710,8 +2963,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (!seen_end_of_options && *arg == '-') {
int opts;
- opts = handle_revision_pseudo_opt(submodule,
- revs, argc - i, argv + i,
+ opts = handle_revision_pseudo_opt(
+ revs, argv + i,
&flags);
if (opts > 0) {
i += opts - 1;
@@ -2760,14 +3013,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
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;
}
+ revision_opts_finish(revs);
- 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.
@@ -2783,17 +3035,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
* }
*/
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)
+ if (!revs->def)
revs->def = opt ? opt->def : NULL;
if (opt && opt->tweak)
- opt->tweak(revs, opt);
+ opt->tweak(revs);
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;
@@ -2834,43 +3086,35 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
copy_pathspec(&revs->diffopt.pathspec,
&revs->prune_data);
}
- if (revs->combine_merges)
- revs->ignore_merges = 0;
- if (revs->combined_all_paths && !revs->combine_merges)
- die("--combined-all-paths makes no sense without -c or --cc");
+
+ diff_merges_setup_revs(revs);
revs->diffopt.abbrev = revs->abbrev;
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)
- die("cannot combine --reverse with --walk-reflogs");
if (revs->reflog_info && revs->limited)
die("cannot combine --walk-reflogs with history-limiting options");
if (revs->rewrite_parents && revs->children.name)
- die("cannot combine --parents and --children");
+ die(_("options '%s' and '%s' cannot be used together"), "--parents", "--children");
+ if (revs->filter.choice && !revs->blob_objects)
+ die(_("object filtering requires --objects"));
/*
* Limitations on the graph functionality
*/
- if (revs->reverse && revs->graph)
- die("cannot combine --reverse with --graph");
+ die_for_incompatible_opt3(!!revs->graph, "--graph",
+ !!revs->reverse, "--reverse",
+ !!revs->reflog_info, "--walk-reflogs");
- if (revs->reflog_info && revs->graph)
- die("cannot combine --walk-reflogs with --graph");
if (revs->no_walk && revs->graph)
- die("cannot combine --no-walk with --graph");
+ die(_("options '%s' and '%s' cannot be used together"), "--no-walk", "--graph");
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"));
+ die(_("the option '%s' requires '%s'"), "--grep-reflog", "--walk-reflogs");
if (revs->line_level_traverse &&
(revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
@@ -2879,9 +3123,62 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (revs->expand_tabs_in_log < 0)
revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
+ if (!revs->show_notes_given && revs->show_notes_by_default) {
+ enable_default_display_notes(&revs->notes_opt, &revs->show_notes);
+ revs->show_notes_given = 1;
+ }
+
return left;
}
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+ unsigned int i;
+
+ for (i = 0; i < cmdline->nr; i++)
+ free((char *)cmdline->rev[i].name);
+ free(cmdline->rev);
+}
+
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+ if (!mailmap)
+ return;
+ clear_mailmap(mailmap);
+ free(mailmap);
+}
+
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
+static void free_void_commit_list(void *list)
+{
+ free_commit_list(list);
+}
+
+void release_revisions(struct rev_info *revs)
+{
+ free_commit_list(revs->commits);
+ free_commit_list(revs->ancestry_path_bottoms);
+ object_array_clear(&revs->pending);
+ object_array_clear(&revs->boundary_commits);
+ release_revisions_cmdline(&revs->cmdline);
+ list_objects_filter_release(&revs->filter);
+ clear_pathspec(&revs->prune_data);
+ date_mode_release(&revs->date_mode);
+ release_revisions_mailmap(revs->mailmap);
+ free_grep_patterns(&revs->grep_filter);
+ graph_clear(revs->graph);
+ /* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
+ diff_free(&revs->pruning);
+ reflog_walk_info_release(revs->reflog_info);
+ release_revisions_topo_walk_info(revs->topo_walk_info);
+ clear_decoration(&revs->children, free_void_commit_list);
+ clear_decoration(&revs->merge_simplification, free);
+ clear_decoration(&revs->treesame, free);
+ line_log_free(revs);
+ oidset_clear(&revs->missing_commits);
+}
+
static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
{
struct commit_list *l = xcalloc(1, sizeof(*l));
@@ -2929,7 +3226,7 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs,
st = lookup_decoration(&revs->merge_simplification, &commit->object);
if (!st) {
- st = xcalloc(1, sizeof(*st));
+ CALLOC_ARRAY(st, 1);
add_decoration(&revs->merge_simplification, &commit->object, st);
}
return st;
@@ -3256,12 +3553,12 @@ void reset_revision_walk(void)
}
static int mark_uninteresting(const struct object_id *oid,
- struct packed_git *pack,
- uint32_t pos,
+ struct packed_git *pack UNUSED,
+ uint32_t pos UNUSED,
void *cb)
{
struct rev_info *revs = cb;
- struct object *o = parse_object(revs->repo, oid);
+ struct object *o = lookup_unknown_object(revs->repo, oid);
o->flags |= UNINTERESTING | SEEN;
return 0;
}
@@ -3270,7 +3567,7 @@ define_commit_slab(indegree_slab, int);
define_commit_slab(author_date_slab, timestamp_t);
struct topo_walk_info {
- uint32_t min_generation;
+ timestamp_t min_generation;
struct prio_queue explore_queue;
struct prio_queue indegree_queue;
struct prio_queue topo_queue;
@@ -3278,6 +3575,26 @@ struct topo_walk_info {
struct author_date_slab author_date;
};
+static int topo_walk_atexit_registered;
+static unsigned int count_explore_walked;
+static unsigned int count_indegree_walked;
+static unsigned int count_topo_walked;
+
+static void trace2_topo_walk_statistics_atexit(void)
+{
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ jw_object_intmax(&jw, "count_explore_walked", count_explore_walked);
+ jw_object_intmax(&jw, "count_indegree_walked", count_indegree_walked);
+ jw_object_intmax(&jw, "count_topo_walked", count_topo_walked);
+ jw_end(&jw);
+
+ trace2_data_json("topo_walk", the_repository, "statistics", &jw);
+
+ jw_release(&jw);
+}
+
static inline void test_flag_and_insert(struct prio_queue *q, struct commit *c, int flag)
{
if (c->object.flags & flag)
@@ -3296,9 +3613,11 @@ static void explore_walk_step(struct rev_info *revs)
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
+ count_explore_walked++;
+
if (revs->sort_order == REV_SORT_BY_AUTHOR_DATE)
record_author_date(&info->author_date, c);
@@ -3309,14 +3628,14 @@ static void explore_walk_step(struct rev_info *revs)
return;
if (c->object.flags & UNINTERESTING)
- mark_parents_uninteresting(c);
+ mark_parents_uninteresting(revs, c);
for (p = c->parents; p; p = p->next)
test_flag_and_insert(&info->explore_queue, p->item, TOPO_WALK_EXPLORED);
}
static void explore_to_depth(struct rev_info *revs,
- uint32_t gen_cutoff)
+ timestamp_t gen_cutoff)
{
struct topo_walk_info *info = revs->topo_walk_info;
struct commit *c;
@@ -3334,15 +3653,20 @@ static void indegree_walk_step(struct rev_info *revs)
if (!c)
return;
- if (parse_commit_gently(c, 1) < 0)
+ if (repo_parse_commit_gently(revs->repo, c, 1) < 0)
return;
+ count_indegree_walked++;
+
explore_to_depth(revs, commit_graph_generation(c));
for (p = c->parents; p; p = p->next) {
struct commit *parent = p->item;
int *pi = indegree_slab_at(&info->indegree, parent);
+ if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
+ return;
+
if (*pi)
(*pi)++;
else
@@ -3356,7 +3680,7 @@ static void indegree_walk_step(struct rev_info *revs)
}
static void compute_indegrees_to_depth(struct rev_info *revs,
- uint32_t gen_cutoff)
+ timestamp_t gen_cutoff)
{
struct topo_walk_info *info = revs->topo_walk_info;
struct commit *c;
@@ -3365,17 +3689,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
indegree_walk_step(revs);
}
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
{
- struct topo_walk_info *info = revs->topo_walk_info;
-
+ if (!info)
+ return;
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(info);
+}
- FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+ release_revisions_topo_walk_info(revs->topo_walk_info);
+ revs->topo_walk_info = NULL;
}
static void init_topo_walk(struct rev_info *revs)
@@ -3414,9 +3743,9 @@ static void init_topo_walk(struct rev_info *revs)
info->min_generation = GENERATION_NUMBER_INFINITY;
for (list = revs->commits; list; list = list->next) {
struct commit *c = list->item;
- uint32_t generation;
+ timestamp_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);
@@ -3446,6 +3775,11 @@ static void init_topo_walk(struct rev_info *revs)
*/
if (revs->sort_order == REV_SORT_IN_GRAPH_ORDER)
prio_queue_reverse(&info->topo_queue);
+
+ if (trace2_is_enabled() && !topo_walk_atexit_registered) {
+ atexit(trace2_topo_walk_statistics_atexit);
+ topo_walk_atexit_registered = 1;
+ }
}
static struct commit *next_topo_commit(struct rev_info *revs)
@@ -3472,15 +3806,17 @@ static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
oid_to_hex(&commit->object.oid));
}
+ count_topo_walked++;
+
for (p = commit->parents; p; p = p->next) {
struct commit *parent = p->item;
int *pi;
- uint32_t generation;
+ timestamp_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;
generation = commit_graph_generation(parent);
@@ -3534,7 +3870,7 @@ int prepare_revision_walk(struct rev_info *revs)
if (!revs->reflog_info)
prepare_to_use_bloom_filter(revs);
- if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
+ if (!revs->unsorted_input)
commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
return 0;
@@ -3577,7 +3913,7 @@ static enum rewrite_result rewrite_one_1(struct rev_info *revs,
return rewrite_one_ok;
if (!p->parents)
return rewrite_one_noparents;
- if ((p = one_relevant_parent(revs, p->parents)) == NULL)
+ if (!(p = one_relevant_parent(revs, p->parents)))
return rewrite_one_ok;
*pp = p;
}
@@ -3629,51 +3965,6 @@ int rewrite_parents(struct rev_info *revs, struct commit *commit,
return 0;
}
-static int commit_rewrite_person(struct strbuf *buf, const char *what, struct string_list *mailmap)
-{
- char *person, *endp;
- size_t len, namelen, maillen;
- const char *name;
- const char *mail;
- struct ident_split ident;
-
- person = strstr(buf->buf, what);
- if (!person)
- return 0;
-
- person += strlen(what);
- endp = strchr(person, '\n');
- if (!endp)
- return 0;
-
- len = endp - person;
-
- if (split_ident_line(&ident, person, len))
- return 0;
-
- mail = ident.mail_begin;
- maillen = ident.mail_end - ident.mail_begin;
- name = ident.name_begin;
- namelen = ident.name_end - ident.name_begin;
-
- if (map_user(mailmap, &mail, &maillen, &name, &namelen)) {
- struct strbuf namemail = STRBUF_INIT;
-
- strbuf_addf(&namemail, "%.*s <%.*s>",
- (int)namelen, name, (int)maillen, mail);
-
- strbuf_splice(buf, ident.name_begin - buf->buf,
- ident.mail_end - ident.name_begin + 1,
- namemail.buf, namemail.len);
-
- strbuf_release(&namemail);
-
- return 1;
- }
-
- return 0;
-}
-
static int commit_match(struct commit *commit, struct rev_info *opt)
{
int retval;
@@ -3699,18 +3990,19 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
* in it.
*/
encoding = get_log_output_encoding();
- message = logmsg_reencode(commit, NULL, encoding);
+ message = repo_logmsg_reencode(the_repository, commit, NULL, encoding);
/* Copy the commit to temporary if we are using "fake" headers */
if (buf.len)
strbuf_addstr(&buf, message);
if (opt->grep_filter.header_list && opt->mailmap) {
+ const char *commit_headers[] = { "author ", "committer ", NULL };
+
if (!buf.len)
strbuf_addstr(&buf, message);
- commit_rewrite_person(&buf, "\nauthor ", opt->mailmap);
- commit_rewrite_person(&buf, "\ncommitter ", opt->mailmap);
+ apply_mailmap_to_header(&buf, commit_headers, opt->mailmap);
}
/* Append "fake" message parts as needed */
@@ -3734,8 +4026,8 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
retval = grep_buffer(&opt->grep_filter,
(char *)message, strlen(message));
strbuf_release(&buf);
- unuse_commit_buffer(commit, message);
- return opt->invert_grep ? !retval : retval;
+ repo_unuse_commit_buffer(the_repository, commit, message);
+ return retval;
}
static inline int want_ancestry(const struct rev_info *revs)
@@ -3761,6 +4053,11 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
return commit_ignore;
if (revs->unpacked && has_object_pack(&commit->object.oid))
return commit_ignore;
+ if (revs->no_kept_objects) {
+ if (has_object_kept_pack(&commit->object.oid,
+ revs->keep_pack_cache_flags))
+ return commit_ignore;
+ }
if (commit->object.flags & UNINTERESTING)
return commit_ignore;
if (revs->line_level_traverse && !want_ancestry(revs)) {
@@ -3782,6 +4079,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
if (revs->min_age != -1 &&
comparison_date(revs, commit) > revs->min_age)
return commit_ignore;
+ if (revs->max_age_as_filter != -1 &&
+ comparison_date(revs, commit) < revs->max_age_as_filter)
+ return commit_ignore;
if (revs->min_parents || (revs->max_parents >= 0)) {
int n = commit_list_count(commit->parents);
if ((n < revs->min_parents) ||
@@ -3972,7 +4272,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
* Return true for entries that have not yet been shown. (This is an
* object_array_each_func_t.)
*/
-static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
+static int entry_unshown(struct object_array_entry *entry, void *cb_data UNUSED)
{
return !(entry->item->flags & SHOWN);
}
@@ -4000,10 +4300,8 @@ static void create_boundary_commit_list(struct rev_info *revs)
* boundary commits anyway. (This is what the code has always
* done.)
*/
- if (revs->commits) {
- free_commit_list(revs->commits);
- revs->commits = NULL;
- }
+ free_commit_list(revs->commits);
+ revs->commits = NULL;
/*
* Put all of the actual boundary commits from revs->boundary_commits
@@ -4140,10 +4438,8 @@ struct commit *get_revision(struct rev_info *revs)
graph_update(revs->graph, c);
if (!c) {
free_saved_parents(revs);
- if (revs->previous_parents) {
- free_commit_list(revs->previous_parents);
- revs->previous_parents = NULL;
- }
+ free_commit_list(revs->previous_parents);
+ revs->previous_parents = NULL;
}
return c;
}