From: Junio C Hamano Date: Thu, 28 Dec 2017 22:08:50 +0000 (-0800) Subject: Merge branch 'sb/describe-blob' X-Git-Tag: v2.16.0-rc0~1 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/556de1a8e38ff03d31fd35751582447001f39d0c?ds=inline;hp=-c Merge branch 'sb/describe-blob' "git describe" was taught to dig trees deeper to find a : that refers to a given blob object. * sb/describe-blob: builtin/describe.c: describe a blob builtin/describe.c: factor out describe_commit builtin/describe.c: print debug statements earlier builtin/describe.c: rename `oid` to avoid variable shadowing revision.h: introduce blob/tree walking in order of the commits list-objects.c: factor out traverse_trees_and_blobs t6120: fix typo in test name --- 556de1a8e38ff03d31fd35751582447001f39d0c diff --combined Documentation/rev-list-options.txt index 8d8b7f492a,9066e0c777..22f5c9b43d --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@@ -686,6 -686,11 +686,11 @@@ ifdef::git-rev-list[ all object IDs which I need to download if I have the commit object _bar_ but not _foo_''. + --in-commit-order:: + Print tree and blob ids in order of the commits. The tree + and blob ids are printed after they are first referenced + by a commit. + --objects-edge:: Similar to `--objects`, but also print the IDs of excluded commits prefixed with a ``-'' character. This is used by @@@ -706,47 -711,6 +711,47 @@@ --unpacked:: Only useful with `--objects`; print the object IDs that are not in packs. + +--filter=:: + Only useful with one of the `--objects*`; omits objects (usually + blobs) from the list of printed objects. The '' + may be one of the following: ++ +The form '--filter=blob:none' omits all blobs. ++ +The form '--filter=blob:limit=[kmg]' omits blobs larger than n bytes +or units. n may be zero. The suffixes k, m, and g can be used to name +units in KiB, MiB, or GiB. For example, 'blob:limit=1k' is the same +as 'blob:limit=1024'. ++ +The form '--filter=sparse:oid=' uses a sparse-checkout +specification contained in the blob (or blob-expression) '' +to omit blobs that would not be not required for a sparse checkout on +the requested refs. ++ +The form '--filter=sparse:path=' similarly uses a sparse-checkout +specification contained in . + +--no-filter:: + Turn off any previous `--filter=` argument. + +--filter-print-omitted:: + Only useful with `--filter=`; prints a list of the objects omitted + by the filter. Object IDs are prefixed with a ``~'' character. + +--missing=:: + A debug option to help with future "partial clone" development. + This option specifies how missing objects are handled. ++ +The form '--missing=error' requests that rev-list stop with an error if +a missing object is encountered. This is the default action. ++ +The form '--missing=allow-any' will allow object traversal to continue +if a missing object is encountered. Missing objects will silently be +omitted from the results. ++ +The form '--missing=print' is like 'allow-any', but will also print a +list of the missing objects. Object IDs are prefixed with a ``?'' character. endif::git-rev-list[] --no-walk[=(sorted|unsorted)]:: diff --combined builtin/describe.c index e14e162ef6,5b4bfaba3f..3b0b204b1e --- a/builtin/describe.c +++ b/builtin/describe.c @@@ -3,15 -3,17 +3,18 @@@ #include "lockfile.h" #include "commit.h" #include "tag.h" + #include "blob.h" #include "refs.h" #include "builtin.h" #include "exec_cmd.h" #include "parse-options.h" +#include "revision.h" #include "diff.h" #include "hashmap.h" #include "argv-array.h" #include "run-command.h" + #include "revision.h" + #include "list-objects.h" #define MAX_TAGS (FLAG_BITS - 1) @@@ -181,7 -183,7 +184,7 @@@ static int get_name(const char *path, c } /* Is it annotated? */ - if (!peel_ref(path, peeled.hash)) { + if (!peel_ref(path, &peeled)) { is_annotated = !!oidcmp(oid, &peeled); } else { oidcpy(&peeled, oid); @@@ -256,7 -258,7 +259,7 @@@ static unsigned long finish_depth_compu return seen_commits; } - static void display_name(struct commit_name *n) + static void append_name(struct commit_name *n, struct strbuf *dst) { if (n->prio == 2 && !n->tag) { n->tag = lookup_tag(&n->oid); @@@ -272,19 -274,18 +275,18 @@@ } if (n->tag) - printf("%s", n->tag->tag); + strbuf_addstr(dst, n->tag->tag); else - printf("%s", n->path); + strbuf_addstr(dst, n->path); } - static void show_suffix(int depth, const struct object_id *oid) + static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst) { - printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev)); + strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev)); } - static void describe(const char *arg, int last_one) + static void describe_commit(struct object_id *oid, struct strbuf *dst) { - struct object_id oid; struct commit *cmit, *gave_up_on = NULL; struct commit_list *list; struct commit_name *n; @@@ -293,30 -294,25 +295,25 @@@ unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; - if (get_oid(arg, &oid)) - die(_("Not a valid object name %s"), arg); - cmit = lookup_commit_reference(&oid); - if (!cmit) - die(_("%s is not a valid '%s' object"), arg, commit_type); + cmit = lookup_commit_reference(oid); n = find_commit_name(&cmit->object.oid); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ - display_name(n); + append_name(n, dst); if (longformat) - show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid); + append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); return; } if (!max_candidates) die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid)); if (debug) - fprintf(stderr, _("searching to describe %s\n"), arg); + fprintf(stderr, _("No exact match on refs or tags, searching to describe\n")); if (!have_util) { struct hashmap_iter iter; @@@ -381,22 -377,21 +378,21 @@@ } if (!match_cnt) { - struct object_id *oid = &cmit->object.oid; + struct object_id *cmit_oid = &cmit->object.oid; if (always) { - printf("%s", find_unique_abbrev(oid->hash, abbrev)); + strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev)); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); return; } if (unannotated_cnt) die(_("No annotated tags can describe '%s'.\n" "However, there were unannotated tags: try --tags."), - oid_to_hex(oid)); + oid_to_hex(cmit_oid)); else die(_("No tags can describe '%s'.\n" "Try --always, or create some tags."), - oid_to_hex(oid)); + oid_to_hex(cmit_oid)); } QSORT(all_matches, match_cnt, compare_pt); @@@ -434,15 -429,86 +430,86 @@@ } } - display_name(all_matches[0].name); + append_name(all_matches[0].name, dst); if (abbrev) - show_suffix(all_matches[0].depth, &cmit->object.oid); + append_suffix(all_matches[0].depth, &cmit->object.oid, dst); if (suffix) - printf("%s", suffix); - printf("\n"); + strbuf_addstr(dst, suffix); + } + + struct process_commit_data { + struct object_id current_commit; + struct object_id looking_for; + struct strbuf *dst; + struct rev_info *revs; + }; + + static void process_commit(struct commit *commit, void *data) + { + struct process_commit_data *pcd = data; + pcd->current_commit = commit->object.oid; + } + + static void process_object(struct object *obj, const char *path, void *data) + { + struct process_commit_data *pcd = data; + + if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) { + reset_revision_walk(); + describe_commit(&pcd->current_commit, pcd->dst); + strbuf_addf(pcd->dst, ":%s", path); + free_commit_list(pcd->revs->commits); + pcd->revs->commits = NULL; + } + } + + static void describe_blob(struct object_id oid, struct strbuf *dst) + { + struct rev_info revs; + struct argv_array args = ARGV_ARRAY_INIT; + struct process_commit_data pcd = { null_oid, oid, dst, &revs}; + + argv_array_pushl(&args, "internal: The first arg is not parsed", + "--objects", "--in-commit-order", "--reverse", "HEAD", + NULL); + + init_revisions(&revs, NULL); + if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1) + BUG("setup_revisions could not handle all args?"); + + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + + traverse_commit_list(&revs, process_commit, process_object, &pcd); + reset_revision_walk(); + } + + static void describe(const char *arg, int last_one) + { + struct object_id oid; + struct commit *cmit; + struct strbuf sb = STRBUF_INIT; + + if (debug) + fprintf(stderr, _("describe %s\n"), arg); + + if (get_oid(arg, &oid)) + die(_("Not a valid object name %s"), arg); + cmit = lookup_commit_reference_gently(&oid, 1); + + if (cmit) + describe_commit(&oid, &sb); + else if (lookup_blob(&oid)) + describe_blob(oid, &sb); + else + die(_("%s is neither a commit nor blob"), arg); + + puts(sb.buf); if (!last_one) clear_commit_marks(cmit, -1); + + strbuf_release(&sb); } int cmd_describe(int argc, const char **argv, const char *prefix) @@@ -543,9 -609,7 +610,9 @@@ } } else if (dirty) { static struct lock_file index_lock; - int fd; + struct rev_info revs; + struct argv_array args = ARGV_ARRAY_INIT; + int fd, result; read_cache_preload(NULL); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, @@@ -554,13 -618,8 +621,13 @@@ if (0 <= fd) update_index_if_able(&the_index, &index_lock); - if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, - diff_index_args, prefix)) + init_revisions(&revs, prefix); + argv_array_pushv(&args, diff_index_args); + if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1) + BUG("malformed internal diff-index command line"); + result = run_diff_index(&revs, 0); + + if (!diff_result_code(&revs.diffopt, result)) suffix = NULL; else suffix = dirty; diff --combined list-objects.c index d9e83d05e1,4caa6fcb77..0966cdc9fa --- a/list-objects.c +++ b/list-objects.c @@@ -7,21 -7,16 +7,21 @@@ #include "tree-walk.h" #include "revision.h" #include "list-objects.h" +#include "list-objects-filter.h" +#include "list-objects-filter-options.h" static void process_blob(struct rev_info *revs, struct blob *blob, show_object_fn show, struct strbuf *path, const char *name, - void *cb_data) + void *cb_data, + filter_object_fn filter_fn, + void *filter_data) { struct object *obj = &blob->object; size_t pathlen; + enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW; if (!revs->blob_objects) return; @@@ -29,17 -24,11 +29,17 @@@ die("bad blob object"); if (obj->flags & (UNINTERESTING | SEEN)) return; - obj->flags |= SEEN; pathlen = path->len; strbuf_addstr(path, name); - show(obj, path->buf, cb_data); + if (filter_fn) + r = filter_fn(LOFS_BLOB, obj, + path->buf, &path->buf[pathlen], + filter_data); + if (r & LOFR_MARK_SEEN) + obj->flags |= SEEN; + if (r & LOFR_DO_SHOW) + show(obj, path->buf, cb_data); strbuf_setlen(path, pathlen); } @@@ -80,9 -69,7 +80,9 @@@ static void process_tree(struct rev_inf show_object_fn show, struct strbuf *base, const char *name, - void *cb_data) + void *cb_data, + filter_object_fn filter_fn, + void *filter_data) { struct object *obj = &tree->object; struct tree_desc desc; @@@ -90,7 -77,6 +90,7 @@@ enum interesting match = revs->diffopt.pathspec.nr == 0 ? all_entries_interesting: entry_not_interesting; int baselen = base->len; + enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW; if (!revs->tree_objects) return; @@@ -104,15 -90,9 +104,15 @@@ die("bad tree object %s", oid_to_hex(&obj->oid)); } - obj->flags |= SEEN; strbuf_addstr(base, name); - show(obj, base->buf, cb_data); + if (filter_fn) + r = filter_fn(LOFS_BEGIN_TREE, obj, + base->buf, &base->buf[baselen], + filter_data); + if (r & LOFR_MARK_SEEN) + obj->flags |= SEEN; + if (r & LOFR_DO_SHOW) + show(obj, base->buf, cb_data); if (base->len) strbuf_addch(base, '/'); @@@ -132,7 -112,7 +132,7 @@@ process_tree(revs, lookup_tree(entry.oid), show, base, entry.path, - cb_data); + cb_data, filter_fn, filter_data); else if (S_ISGITLINK(entry.mode)) process_gitlink(revs, entry.oid->hash, show, base, entry.path, @@@ -141,19 -121,8 +141,19 @@@ process_blob(revs, lookup_blob(entry.oid), show, base, entry.path, - cb_data); + cb_data, filter_fn, filter_data); } + + if (filter_fn) { + r = filter_fn(LOFS_END_TREE, obj, + base->buf, &base->buf[baselen], + filter_data); + if (r & LOFR_MARK_SEEN) + obj->flags |= SEEN; + if (r & LOFR_DO_SHOW) + show(obj, base->buf, cb_data); + } + strbuf_setlen(base, baselen); free_tree_buffer(tree); } @@@ -214,27 -183,15 +214,17 @@@ static void add_pending_tree(struct rev add_pending_object(revs, &tree->object, ""); } - static void do_traverse(struct rev_info *revs, - show_commit_fn show_commit, - show_object_fn show_object, - void *show_data, - filter_object_fn filter_fn, - void *filter_data) + static void traverse_trees_and_blobs(struct rev_info *revs, + struct strbuf *base, + show_object_fn show_object, - void *data) ++ void *show_data, ++ filter_object_fn filter_fn, ++ void *filter_data) { int i; - struct commit *commit; - struct strbuf base; - strbuf_init(&base, PATH_MAX); - while ((commit = get_revision(revs)) != NULL) { - /* - * an uninteresting boundary commit may not have its tree - * parsed yet, but we are not going to show them anyway - */ - if (commit->tree) - add_pending_tree(revs, commit->tree); - show_commit(commit, show_data); - } + assert(base->len == 0); + for (i = 0; i < revs->pending.nr; i++) { struct object_array_entry *pending = revs->pending.objects + i; struct object *obj = pending->item; @@@ -244,54 -201,54 +234,89 @@@ continue; if (obj->type == OBJ_TAG) { obj->flags |= SEEN; - show_object(obj, name, data); + show_object(obj, name, show_data); continue; } if (!path) path = ""; if (obj->type == OBJ_TREE) { process_tree(revs, (struct tree *)obj, show_object, - &base, path, show_data, - base, path, data); ++ base, path, show_data, + filter_fn, filter_data); continue; } if (obj->type == OBJ_BLOB) { process_blob(revs, (struct blob *)obj, show_object, - &base, path, show_data, - base, path, data); ++ base, path, show_data, + filter_fn, filter_data); continue; } die("unknown pending object %s (%s)", oid_to_hex(&obj->oid), name); } object_array_clear(&revs->pending); - strbuf_release(&base); + } + -void traverse_commit_list(struct rev_info *revs, - show_commit_fn show_commit, - show_object_fn show_object, - void *data) ++static void do_traverse(struct rev_info *revs, ++ show_commit_fn show_commit, ++ show_object_fn show_object, ++ void *show_data, ++ filter_object_fn filter_fn, ++ void *filter_data) + { + struct commit *commit; + struct strbuf csp; /* callee's scratch pad */ + strbuf_init(&csp, PATH_MAX); + + while ((commit = get_revision(revs)) != NULL) { + /* + * an uninteresting boundary commit may not have its tree + * parsed yet, but we are not going to show them anyway + */ + if (commit->tree) + add_pending_tree(revs, commit->tree); - show_commit(commit, data); ++ show_commit(commit, show_data); + + if (revs->tree_blobs_in_commit_order) + /* + * NEEDSWORK: Adding the tree and then flushing it here + * needs a reallocation for each commit. Can we pass the + * tree directory without allocation churn? + */ - traverse_trees_and_blobs(revs, &csp, show_object, data); ++ traverse_trees_and_blobs(revs, &csp, ++ show_object, show_data, ++ filter_fn, filter_data); + } - traverse_trees_and_blobs(revs, &csp, show_object, data); - ++ traverse_trees_and_blobs(revs, &csp, ++ show_object, show_data, ++ filter_fn, filter_data); + strbuf_release(&csp); } + +void traverse_commit_list(struct rev_info *revs, + show_commit_fn show_commit, + show_object_fn show_object, + void *show_data) +{ + do_traverse(revs, show_commit, show_object, show_data, NULL, NULL); +} + +void traverse_commit_list_filtered( + struct list_objects_filter_options *filter_options, + struct rev_info *revs, + show_commit_fn show_commit, + show_object_fn show_object, + void *show_data, + struct oidset *omitted) +{ + filter_object_fn filter_fn = NULL; + filter_free_fn filter_free_fn = NULL; + void *filter_data = NULL; + + filter_data = list_objects_filter__init(omitted, filter_options, + &filter_fn, &filter_free_fn); + do_traverse(revs, show_commit, show_object, show_data, + filter_fn, filter_data); + if (filter_data && filter_free_fn) + filter_free_fn(filter_data); +} diff --combined revision.c index f6a3da5cd9,9329d4ebbf..72f2b4572e --- a/revision.c +++ b/revision.c @@@ -395,16 -395,8 +395,16 @@@ static struct commit *one_relevant_pare * if the whole diff is removal of old data, and otherwise * REV_TREE_DIFFERENT (of course if the trees are the same we * want REV_TREE_SAME). - * That means that once we get to REV_TREE_DIFFERENT, we do not - * have to look any further. + * + * The only time we care about the distinction is when + * remove_empty_trees is in effect, in which case we care only about + * whether the whole change is REV_TREE_NEW, or if there's another type + * of change. Which means we can stop the diff early in either of these + * cases: + * + * 1. We're not using remove_empty_trees at all. + * + * 2. We saw anything except REV_TREE_NEW. */ static int tree_difference = REV_TREE_SAME; @@@ -415,11 -407,10 +415,11 @@@ static void file_add_remove(struct diff const char *fullpath, unsigned dirty_submodule) { int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; + struct rev_info *revs = options->change_fn_data; tree_difference |= diff; - if (tree_difference == REV_TREE_DIFFERENT) - DIFF_OPT_SET(options, HAS_CHANGES); + if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW) + options->flags.has_changes = 1; } static void file_change(struct diff_options *options, @@@ -431,7 -422,7 +431,7 @@@ unsigned old_dirty_submodule, unsigned new_dirty_submodule) { tree_difference = REV_TREE_DIFFERENT; - DIFF_OPT_SET(options, HAS_CHANGES); + options->flags.has_changes = 1; } static int rev_compare_tree(struct rev_info *revs, @@@ -464,7 -455,7 +464,7 @@@ } tree_difference = REV_TREE_SAME; - DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); + revs->pruning.flags.has_changes = 0; if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "", &revs->pruning) < 0) return REV_TREE_DIFFERENT; @@@ -480,7 -471,7 +480,7 @@@ static int rev_same_tree_as_empty(struc return 0; tree_difference = REV_TREE_SAME; - DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); + revs->pruning.flags.has_changes = 0; retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning); return retval >= 0 && (tree_difference == REV_TREE_SAME); @@@ -1412,11 -1403,10 +1412,11 @@@ void init_revisions(struct rev_info *re revs->abbrev = DEFAULT_ABBREV; revs->ignore_merges = 1; revs->simplify_history = 1; - DIFF_OPT_SET(&revs->pruning, RECURSIVE); - DIFF_OPT_SET(&revs->pruning, QUICK); + 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; @@@ -1832,7 -1822,7 +1832,7 @@@ static int handle_revision_opt(struct r revs->simplify_by_decoration = 1; revs->limited = 1; revs->prune = 1; - load_ref_decorations(DECORATE_SHORT_REFS); + 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; @@@ -1855,6 -1845,8 +1855,8 @@@ revs->dense = 0; } else if (!strcmp(arg, "--show-all")) { revs->show_all = 1; + } else if (!strcmp(arg, "--in-commit-order")) { + revs->tree_blobs_in_commit_order = 1; } else if (!strcmp(arg, "--remove-empty")) { revs->remove_empty_trees = 1; } else if (!strcmp(arg, "--merges")) { @@@ -1927,11 -1919,11 +1929,11 @@@ die("--unpacked= no longer supported."); } else if (!strcmp(arg, "-r")) { revs->diff = 1; - DIFF_OPT_SET(&revs->diffopt, RECURSIVE); + revs->diffopt.flags.recursive = 1; } else if (!strcmp(arg, "-t")) { revs->diff = 1; - DIFF_OPT_SET(&revs->diffopt, RECURSIVE); - DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE); + 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")) { @@@ -2076,7 -2068,7 +2078,7 @@@ revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE; } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) { revs->grep_filter.ignore_case = 1; - DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE); + revs->diffopt.flags.pickaxe_ignore_case = 1; } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) { revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED; } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) { @@@ -2409,7 -2401,7 +2411,7 @@@ int setup_revisions(int argc, const cha /* Pickaxe, diff-filter and rename following need diffs */ if (revs->diffopt.pickaxe || revs->diffopt.filter || - DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) + revs->diffopt.flags.follow_renames) revs->diff = 1; if (revs->topo_order) @@@ -2418,7 -2410,7 +2420,7 @@@ if (revs->prune_data.nr) { copy_pathspec(&revs->pruning.pathspec, &revs->prune_data); /* Can't prune commits with rename following: the paths change.. */ - if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) + if (!revs->diffopt.flags.follow_renames) revs->prune = 1; if (!revs->full_diff) copy_pathspec(&revs->diffopt.pathspec, diff --combined revision.h index 747bce8d8a,86985d68aa..19dc9bdddb --- a/revision.h +++ b/revision.h @@@ -4,7 -4,7 +4,7 @@@ #include "parse-options.h" #include "grep.h" #include "notes.h" -#include "commit.h" +#include "pretty.h" #include "diff.h" /* Remember to update object flag allocation in object.h */ @@@ -121,7 -121,8 +121,8 @@@ struct rev_info bisect:1, ancestry_path:1, first_parent_only:1, - line_level_traverse:1; + line_level_traverse:1, + tree_blobs_in_commit_order:1; /* Diff flags */ unsigned int diff:1,