diff --git a/bisect.c b/bisect.c index a579b50884f8e6f8ce8390308d39b2664d050583..6de1abd407ba3f8b7698133d469389913bfd92c3 100644 --- a/bisect.c +++ b/bisect.c @@ -12,6 +12,7 @@ #include "bisect.h" #include "sha1-array.h" #include "argv-array.h" +#include "commit-slab.h" static struct oid_array good_revs; static struct oid_array skipped_revs; @@ -70,16 +71,19 @@ static void clear_distance(struct commit_list *list) } } +define_commit_slab(commit_weight, int *); +static struct commit_weight commit_weight; + #define DEBUG_BISECT 0 static inline int weight(struct commit_list *elem) { - return *((int*)(elem->item->util)); + return **commit_weight_at(&commit_weight, elem->item); } static inline void weight_set(struct commit_list *elem, int weight) { - *((int*)(elem->item->util)) = weight; + **commit_weight_at(&commit_weight, elem->item) = weight; } static int count_interesting_parents(struct commit *commit) @@ -265,7 +269,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list, struct commit *commit = p->item; unsigned flags = commit->object.flags; - p->item->util = &weights[n++]; + *commit_weight_at(&commit_weight, p->item) = &weights[n++]; switch (count_interesting_parents(commit)) { case 0: if (!(flags & TREESAME)) { @@ -372,6 +376,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches, int *weights; show_list("bisection 2 entry", 0, 0, *commit_list); + init_commit_weight(&commit_weight); /* * Count the number of total and tree-changing items on the @@ -412,6 +417,7 @@ void find_bisection(struct commit_list **commit_list, int *reaches, } free(weights); *commit_list = best; + clear_commit_weight(&commit_weight); } static int register_ref(const char *refname, const struct object_id *oid, diff --git a/blame.c b/blame.c index 14d0e0b5751c2abaad16dac81a71436ca29ddde4..ba1b33c7f4e92c432d4cbdcb43b8d061e436f027 100644 --- a/blame.c +++ b/blame.c @@ -6,6 +6,24 @@ #include "diffcore.h" #include "tag.h" #include "blame.h" +#include "commit-slab.h" + +define_commit_slab(blame_suspects, struct blame_origin *); +static struct blame_suspects blame_suspects; + +struct blame_origin *get_blame_suspects(struct commit *commit) +{ + struct blame_origin **result; + + result = blame_suspects_peek(&blame_suspects, commit); + + return result ? *result : NULL; +} + +static void set_blame_suspects(struct commit *commit, struct blame_origin *origin) +{ + *blame_suspects_at(&blame_suspects, commit) = origin; +} void blame_origin_decref(struct blame_origin *o) { @@ -15,12 +33,12 @@ void blame_origin_decref(struct blame_origin *o) blame_origin_decref(o->previous); free(o->file.ptr); /* Should be present exactly once in commit chain */ - for (p = o->commit->util; p; l = p, p = p->next) { + for (p = get_blame_suspects(o->commit); p; l = p, p = p->next) { if (p == o) { if (l) l->next = p->next; else - o->commit->util = p->next; + set_blame_suspects(o->commit, p->next); free(o); return; } @@ -41,8 +59,8 @@ static struct blame_origin *make_origin(struct commit *commit, const char *path) FLEX_ALLOC_STR(o, path, path); o->commit = commit; o->refcnt = 1; - o->next = commit->util; - commit->util = o; + o->next = get_blame_suspects(commit); + set_blame_suspects(commit, o); return o; } @@ -54,13 +72,13 @@ static struct blame_origin *get_origin(struct commit *commit, const char *path) { struct blame_origin *o, *l; - for (o = commit->util, l = NULL; o; l = o, o = o->next) { + for (o = get_blame_suspects(commit), l = NULL; o; l = o, o = o->next) { if (!strcmp(o->path, path)) { /* bump to front */ if (l) { l->next = o->next; - o->next = commit->util; - commit->util = o; + o->next = get_blame_suspects(commit); + set_blame_suspects(commit, o); } return blame_origin_incref(o); } @@ -478,7 +496,7 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig porigin->suspects = blame_merge(porigin->suspects, sorted); else { struct blame_origin *o; - for (o = porigin->commit->util; o; o = o->next) { + for (o = get_blame_suspects(porigin->commit); o; o = o->next) { if (o->suspects) { porigin->suspects = sorted; return; @@ -525,7 +543,7 @@ static struct blame_origin *find_origin(struct commit *parent, const char *paths[2]; /* First check any existing origins */ - for (porigin = parent->util; porigin; porigin = porigin->next) + for (porigin = get_blame_suspects(parent); porigin; porigin = porigin->next) if (!strcmp(porigin->path, origin->path)) { /* * The same path between origin and its parent @@ -1550,7 +1568,7 @@ void assign_blame(struct blame_scoreboard *sb, int opt) while (commit) { struct blame_entry *ent; - struct blame_origin *suspect = commit->util; + struct blame_origin *suspect = get_blame_suspects(commit); /* find one suspect to break down */ while (suspect && !suspect->suspects) @@ -1752,6 +1770,8 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam struct commit *final_commit = NULL; enum object_type type; + init_blame_suspects(&blame_suspects); + if (sb->reverse && sb->contents_from) die(_("--contents and --reverse do not blend well.")); @@ -1815,7 +1835,7 @@ void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blam } if (is_null_oid(&sb->final->object.oid)) { - o = sb->final->util; + o = get_blame_suspects(sb->final); sb->final_buf = xmemdupz(o->file.ptr, o->file.size); sb->final_buf_size = o->file.size; } diff --git a/blame.h b/blame.h index a6c915c277d99cfa39dd3fe6aee82c2f0f8320df..2092f9529c108d40100ec2a8175098edc8e8e5dd 100644 --- a/blame.h +++ b/blame.h @@ -172,4 +172,6 @@ extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, stru extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o); +extern struct blame_origin *get_blame_suspects(struct commit *commit); + #endif /* BLAME_H */ diff --git a/builtin/blame.c b/builtin/blame.c index 3295718841aab1051ee8bdde0b1fc0aa26ae5f00..5a0388aaef580e8d3592e1b5b767618fe638ca0b 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -543,7 +543,7 @@ static void output(struct blame_scoreboard *sb, int option) struct commit *commit = ent->suspect->commit; if (commit->object.flags & MORE_THAN_ONE_PATH) continue; - for (suspect = commit->util; suspect; suspect = suspect->next) { + for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) { if (suspect->guilty && count++) { commit->object.flags |= MORE_THAN_ONE_PATH; break; diff --git a/builtin/describe.c b/builtin/describe.c index cf1ae77d7c705cdfb34bdb5403bad8db4148eccf..bec2513b66cea8e5b3c7bae5de2c2e0bd31aecc9 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -15,9 +15,12 @@ #include "run-command.h" #include "revision.h" #include "list-objects.h" +#include "commit-slab.h" #define MAX_TAGS (FLAG_BITS - 1) +define_commit_slab(commit_names, struct commit_name *); + static const char * const describe_usage[] = { N_("git describe [] [...]"), N_("git describe [] --dirty"), @@ -37,6 +40,7 @@ static struct string_list patterns = STRING_LIST_INIT_NODUP; static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP; static int always; static const char *suffix, *dirty, *broken; +static struct commit_names commit_names; /* diff-index command arguments to check if working tree is dirty. */ static const char *diff_index_args[] = { @@ -321,11 +325,14 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) if (!have_util) { struct hashmap_iter iter; struct commit *c; - struct commit_name *n = hashmap_iter_first(&names, &iter); + struct commit_name *n; + + init_commit_names(&commit_names); + n = hashmap_iter_first(&names, &iter); for (; n; n = hashmap_iter_next(&iter)) { c = lookup_commit_reference_gently(&n->peeled, 1); if (c) - c->util = n; + *commit_names_at(&commit_names, c) = n; } have_util = 1; } @@ -336,8 +343,11 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) while (list) { struct commit *c = pop_commit(&list); struct commit_list *parents = c->parents; + struct commit_name **slot; + seen_commits++; - n = c->util; + slot = commit_names_peek(&commit_names, c); + n = slot ? *slot : NULL; if (n) { if (!tags && !all && n->prio < 2) { unannotated_cnt++; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 6c9768742fd4faecb3719e9c87997df3d490502a..73d12c1020c4c13ff00210ad4eafc028bd33b322 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -22,6 +22,7 @@ #include "quote.h" #include "remote.h" #include "blob.h" +#include "commit-slab.h" static const char *fast_export_usage[] = { N_("git fast-export [rev-list-opts]"), @@ -38,6 +39,7 @@ static int full_tree; static struct string_list extra_refs = STRING_LIST_INIT_NODUP; static struct refspec refspecs = REFSPEC_INIT_FETCH; static int anonymize; +static struct revision_sources revision_sources; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) @@ -589,7 +591,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) export_blob(&diff_queued_diff.queue[i]->two->oid); - refname = commit->util; + refname = *revision_sources_at(&revision_sources, commit); if (anonymize) { refname = anonymize_refname(refname); anonymize_ident_line(&committer, &committer_end); @@ -861,10 +863,11 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info) * This ref will not be updated through a commit, lets make * sure it gets properly updated eventually. */ - if (commit->util || commit->object.flags & SHOWN) + if (*revision_sources_at(&revision_sources, commit) || + commit->object.flags & SHOWN) string_list_append(&extra_refs, full_name)->util = commit; - if (!commit->util) - commit->util = full_name; + if (!*revision_sources_at(&revision_sources, commit)) + *revision_sources_at(&revision_sources, commit) = full_name; } } @@ -1028,8 +1031,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); init_revisions(&revs, prefix); + init_revision_sources(&revision_sources); revs.topo_order = 1; - revs.show_source = 1; + revs.sources = &revision_sources; revs.rewrite_parents = 1; argc = parse_options(argc, argv, prefix, options, fast_export_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); diff --git a/builtin/log.c b/builtin/log.c index 4686f68594829a85e9d73bcf0b6298c1e61b6852..0583f7f383a481f353909dc5b406b38745549c63 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -28,6 +28,7 @@ #include "mailmap.h" #include "gpg-interface.h" #include "progress.h" +#include "commit-slab.h" #define MAIL_DEFAULT_WRAP 72 @@ -148,6 +149,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; struct decoration_filter decoration_filter = {&decorate_refs_include, &decorate_refs_exclude}; + static struct revision_sources revision_sources; const struct option builtin_log_options[] = { OPT__QUIET(&quiet, N_("suppress diff output")), @@ -194,8 +196,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, rev->diffopt.filter || rev->diffopt.flags.follow_renames) rev->always_show_header = 0; - if (source) - rev->show_source = 1; + if (source) { + init_revision_sources(&revision_sources); + rev->sources = &revision_sources; + } if (mailmap) { rev->mailmap = xcalloc(1, sizeof(struct string_list)); @@ -1337,6 +1341,8 @@ static struct commit *get_base_commit(const char *base_commit, return base; } +define_commit_slab(commit_base, int); + static void prepare_bases(struct base_tree_info *bases, struct commit *base, struct commit **list, @@ -1345,11 +1351,13 @@ static void prepare_bases(struct base_tree_info *bases, struct commit *commit; struct rev_info revs; struct diff_options diffopt; + struct commit_base commit_base; int i; if (!base) return; + init_commit_base(&commit_base); diff_setup(&diffopt); diffopt.flags.recursive = 1; diff_setup_done(&diffopt); @@ -1362,7 +1370,7 @@ static void prepare_bases(struct base_tree_info *bases, for (i = 0; i < total; i++) { list[i]->object.flags &= ~UNINTERESTING; add_pending_object(&revs, &list[i]->object, "rev_list"); - list[i]->util = (void *)1; + *commit_base_at(&commit_base, list[i]) = 1; } base->object.flags |= UNINTERESTING; add_pending_object(&revs, &base->object, "base"); @@ -1376,7 +1384,7 @@ static void prepare_bases(struct base_tree_info *bases, while ((commit = get_revision(&revs)) != NULL) { struct object_id oid; struct object_id *patch_id; - if (commit->util) + if (*commit_base_at(&commit_base, commit)) continue; if (commit_patch_id(commit, &diffopt, &oid, 0)) die(_("cannot get patch id")); @@ -1385,6 +1393,7 @@ static void prepare_bases(struct base_tree_info *bases, oidcpy(patch_id, &oid); bases->nr_patch_id++; } + clear_commit_base(&commit_base); } static void print_bases(struct base_tree_info *bases, FILE *file) diff --git a/builtin/merge.c b/builtin/merge.c index b00d6f4821e7dc4067997bf515702edcfa413501..fb4faca2507fdf00a1bbd48817e2ff0bc9957fc3 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -445,6 +445,7 @@ static void merge_name(const char *remote, struct strbuf *msg) struct object_id branch_head; struct strbuf buf = STRBUF_INIT; struct strbuf bname = STRBUF_INIT; + struct merge_remote_desc *desc; const char *ptr; char *found_ref; int len, early; @@ -517,16 +518,13 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_release(&truname); } - if (remote_head->util) { - struct merge_remote_desc *desc; - desc = merge_remote_util(remote_head); - if (desc && desc->obj && desc->obj->type == OBJ_TAG) { - strbuf_addf(msg, "%s\t\t%s '%s'\n", - oid_to_hex(&desc->obj->oid), - type_name(desc->obj->type), - remote); - goto cleanup; - } + desc = merge_remote_util(remote_head); + if (desc && desc->obj && desc->obj->type == OBJ_TAG) { + strbuf_addf(msg, "%s\t\t%s '%s'\n", + oid_to_hex(&desc->obj->oid), + type_name(desc->obj->type), + remote); + goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", @@ -934,8 +932,11 @@ static void write_merge_heads(struct commit_list *remoteheads) for (j = remoteheads; j; j = j->next) { struct object_id *oid; struct commit *c = j->item; - if (c->util && merge_remote_util(c)->obj) { - oid = &merge_remote_util(c)->obj->oid; + struct merge_remote_desc *desc; + + desc = merge_remote_util(c); + if (desc && desc->obj) { + oid = &desc->obj->oid; } else { oid = &c->object.oid; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 387ddf85d21a443f060dbb212fe95a983d9e4f58..0eb440359dd40d51f2802979218bd20a2d3d732e 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -6,6 +6,7 @@ #include "refs.h" #include "parse-options.h" #include "sha1-lookup.h" +#include "commit-slab.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ @@ -17,11 +18,26 @@ typedef struct rev_name { int from_tag; } rev_name; +define_commit_slab(commit_rev_name, struct rev_name *); + static timestamp_t cutoff = TIME_MAX; +static struct commit_rev_name rev_names; /* How many generations are maximally preferred over _one_ merge traversal? */ #define MERGE_TRAVERSAL_WEIGHT 65535 +static struct rev_name *get_commit_rev_name(struct commit *commit) +{ + struct rev_name **slot = commit_rev_name_peek(&rev_names, commit); + + return slot ? *slot : NULL; +} + +static void set_commit_rev_name(struct commit *commit, struct rev_name *name) +{ + *commit_rev_name_at(&rev_names, commit) = name; +} + static int is_better_name(struct rev_name *name, const char *tip_name, timestamp_t taggerdate, @@ -65,7 +81,7 @@ static void name_rev(struct commit *commit, int generation, int distance, int from_tag, int deref) { - struct rev_name *name = (struct rev_name *)commit->util; + struct rev_name *name = get_commit_rev_name(commit); struct commit_list *parents; int parent_number = 1; char *to_free = NULL; @@ -84,7 +100,7 @@ static void name_rev(struct commit *commit, if (name == NULL) { name = xmalloc(sizeof(rev_name)); - commit->util = name; + set_commit_rev_name(commit, name); goto copy_data; } else if (is_better_name(name, tip_name, taggerdate, generation, distance, from_tag)) { @@ -296,7 +312,7 @@ static const char *get_rev_name(const struct object *o, struct strbuf *buf) if (o->type != OBJ_COMMIT) return get_exact_ref_match(o); c = (struct commit *) o; - n = c->util; + n = get_commit_rev_name(c); if (!n) return NULL; @@ -413,6 +429,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_END(), }; + init_commit_rev_name(&rev_names); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); if (all + transform_stdin + !!argc > 1) { diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 6c2148b71db593af1a4ef8d2d1bdbdfe16661851..f2e985c00abd2e6f7aa1eaa1c9368f4c3ada60ab 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -7,6 +7,7 @@ #include "argv-array.h" #include "parse-options.h" #include "dir.h" +#include "commit-slab.h" static const char* show_branch_usage[] = { N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" @@ -21,6 +22,11 @@ static int showbranch_use_color = -1; static struct argv_array default_args = ARGV_ARRAY_INIT; +/* + * TODO: convert this use of commit->object.flags to commit-slab + * instead to store a pointer to ref name directly. Then use the same + * UNINTERESTING definition from revision.h here. + */ #define UNINTERESTING 01 #define REV_SHIFT 2 @@ -59,15 +65,27 @@ struct commit_name { int generation; /* how many parents away from head_name */ }; +define_commit_slab(commit_name_slab, struct commit_name *); +static struct commit_name_slab name_slab; + +static struct commit_name *commit_to_name(struct commit *commit) +{ + return *commit_name_slab_at(&name_slab, commit); +} + + /* Name the commit as nth generation ancestor of head_name; * we count only the first-parent relationship for naming purposes. */ static void name_commit(struct commit *commit, const char *head_name, int nth) { struct commit_name *name; - if (!commit->util) - commit->util = xmalloc(sizeof(struct commit_name)); - name = commit->util; + + name = *commit_name_slab_at(&name_slab, commit); + if (!name) { + name = xmalloc(sizeof(*name)); + *commit_name_slab_at(&name_slab, commit) = name; + } name->head_name = head_name; name->generation = nth; } @@ -79,8 +97,8 @@ static void name_commit(struct commit *commit, const char *head_name, int nth) */ static void name_parent(struct commit *commit, struct commit *parent) { - struct commit_name *commit_name = commit->util; - struct commit_name *parent_name = parent->util; + struct commit_name *commit_name = commit_to_name(commit); + struct commit_name *parent_name = commit_to_name(parent); if (!commit_name) return; if (!parent_name || @@ -94,12 +112,12 @@ static int name_first_parent_chain(struct commit *c) int i = 0; while (c) { struct commit *p; - if (!c->util) + if (!commit_to_name(c)) break; if (!c->parents) break; p = c->parents->item; - if (!p->util) { + if (!commit_to_name(p)) { name_parent(c, p); i++; } @@ -122,7 +140,7 @@ static void name_commits(struct commit_list *list, /* First give names to the given heads */ for (cl = list; cl; cl = cl->next) { c = cl->item; - if (c->util) + if (commit_to_name(c)) continue; for (i = 0; i < num_rev; i++) { if (rev[i] == c) { @@ -148,9 +166,9 @@ static void name_commits(struct commit_list *list, struct commit_name *n; int nth; c = cl->item; - if (!c->util) + if (!commit_to_name(c)) continue; - n = c->util; + n = commit_to_name(c); parents = c->parents; nth = 0; while (parents) { @@ -158,7 +176,7 @@ static void name_commits(struct commit_list *list, struct strbuf newname = STRBUF_INIT; parents = parents->next; nth++; - if (p->util) + if (commit_to_name(p)) continue; switch (n->generation) { case 0: @@ -271,7 +289,7 @@ static void show_one_commit(struct commit *commit, int no_name) { struct strbuf pretty = STRBUF_INIT; const char *pretty_str = "(unavailable)"; - struct commit_name *name = commit->util; + struct commit_name *name = commit_to_name(commit); if (commit->object.parsed) { pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); @@ -660,6 +678,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) OPT_END() }; + init_commit_name_slab(&name_slab); + git_config(git_show_branch_config, NULL); /* If nothing is specified, try the default first */ diff --git a/commit-slab-decl.h b/commit-slab-decl.h new file mode 100644 index 0000000000000000000000000000000000000000..adc7b46c83b15f50ae2a85ce7e109fae4178f5fa --- /dev/null +++ b/commit-slab-decl.h @@ -0,0 +1,43 @@ +#ifndef COMMIT_SLAB_HDR_H +#define COMMIT_SLAB_HDR_H + +/* allocate ~512kB at once, allowing for malloc overhead */ +#ifndef COMMIT_SLAB_SIZE +#define COMMIT_SLAB_SIZE (512*1024-32) +#endif + +#define declare_commit_slab(slabname, elemtype) \ + \ +struct slabname { \ + unsigned slab_size; \ + unsigned stride; \ + unsigned slab_count; \ + elemtype **slab; \ +} + +/* + * Statically initialize a commit slab named "var". Note that this + * evaluates "stride" multiple times! Example: + * + * struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees); + * + */ +#define COMMIT_SLAB_INIT(stride, var) { \ + COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \ + (stride), 0, NULL \ +} + +#define declare_commit_slab_prototypes(slabname, elemtype) \ + \ +void init_ ##slabname## _with_stride(struct slabname *s, unsigned stride); \ +void init_ ##slabname(struct slabname *s); \ +void clear_ ##slabname(struct slabname *s); \ +elemtype *slabname## _at_peek(struct slabname *s, const struct commit *c, int add_if_missing); \ +elemtype *slabname## _at(struct slabname *s, const struct commit *c); \ +elemtype *slabname## _peek(struct slabname *s, const struct commit *c) + +#define define_shared_commit_slab(slabname, elemtype) \ + declare_commit_slab(slabname, elemtype); \ + declare_commit_slab_prototypes(slabname, elemtype) + +#endif /* COMMIT_SLAB_HDR_H */ diff --git a/commit-slab-impl.h b/commit-slab-impl.h new file mode 100644 index 0000000000000000000000000000000000000000..87a9cadfcca3f58045e2d7b2c3a143c8c4501f8d --- /dev/null +++ b/commit-slab-impl.h @@ -0,0 +1,97 @@ +#ifndef COMMIT_SLAB_IMPL_H +#define COMMIT_SLAB_IMPL_H + +#define MAYBE_UNUSED __attribute__((__unused__)) + +#define implement_static_commit_slab(slabname, elemtype) \ + implement_commit_slab(slabname, elemtype, static MAYBE_UNUSED) + +#define implement_shared_commit_slab(slabname, elemtype) \ + implement_commit_slab(slabname, elemtype, ) + +#define implement_commit_slab(slabname, elemtype, scope) \ + \ +static int stat_ ##slabname## realloc; \ + \ +scope void init_ ##slabname## _with_stride(struct slabname *s, \ + unsigned stride) \ +{ \ + unsigned int elem_size; \ + if (!stride) \ + stride = 1; \ + s->stride = stride; \ + elem_size = sizeof(elemtype) * stride; \ + s->slab_size = COMMIT_SLAB_SIZE / elem_size; \ + s->slab_count = 0; \ + s->slab = NULL; \ +} \ + \ +scope void init_ ##slabname(struct slabname *s) \ +{ \ + init_ ##slabname## _with_stride(s, 1); \ +} \ + \ +scope void clear_ ##slabname(struct slabname *s) \ +{ \ + unsigned int i; \ + for (i = 0; i < s->slab_count; i++) \ + free(s->slab[i]); \ + s->slab_count = 0; \ + FREE_AND_NULL(s->slab); \ +} \ + \ +scope elemtype *slabname## _at_peek(struct slabname *s, \ + const struct commit *c, \ + int add_if_missing) \ +{ \ + unsigned int nth_slab, nth_slot; \ + \ + nth_slab = c->index / s->slab_size; \ + nth_slot = c->index % s->slab_size; \ + \ + if (s->slab_count <= nth_slab) { \ + unsigned int i; \ + if (!add_if_missing) \ + return NULL; \ + REALLOC_ARRAY(s->slab, nth_slab + 1); \ + stat_ ##slabname## realloc++; \ + for (i = s->slab_count; i <= nth_slab; i++) \ + s->slab[i] = NULL; \ + s->slab_count = nth_slab + 1; \ + } \ + if (!s->slab[nth_slab]) { \ + if (!add_if_missing) \ + return NULL; \ + s->slab[nth_slab] = xcalloc(s->slab_size, \ + sizeof(**s->slab) * s->stride); \ + } \ + return &s->slab[nth_slab][nth_slot * s->stride]; \ +} \ + \ +scope elemtype *slabname## _at(struct slabname *s, \ + const struct commit *c) \ +{ \ + return slabname##_at_peek(s, c, 1); \ +} \ + \ +scope elemtype *slabname## _peek(struct slabname *s, \ + const struct commit *c) \ +{ \ + return slabname##_at_peek(s, c, 0); \ +} \ + \ +struct slabname + +/* + * Note that this redundant forward declaration is required + * to allow a terminating semicolon, which makes instantiations look + * like function declarations. I.e., the expansion of + * + * implement_commit_slab(indegree, int, static); + * + * ends in 'struct indegree;'. This would otherwise + * be a syntax error according (at least) to ISO C. It's hard to + * catch because GCC silently parses it by default. + */ + +#endif /* COMMIT_SLAB_IMPL_H */ diff --git a/commit-slab.h b/commit-slab.h index dcaab8ca0437b3adefb523cad531ac1f4b9264e0..69bf0c807c64b79216118f3485cfd5dac3b6a273 100644 --- a/commit-slab.h +++ b/commit-slab.h @@ -1,6 +1,9 @@ #ifndef COMMIT_SLAB_H #define COMMIT_SLAB_H +#include "commit-slab-decl.h" +#include "commit-slab-impl.h" + /* * define_commit_slab(slabname, elemtype) creates boilerplate code to define * a new struct (struct slabname) that is used to associate a piece of data @@ -41,114 +44,8 @@ * leaking memory. */ -/* allocate ~512kB at once, allowing for malloc overhead */ -#ifndef COMMIT_SLAB_SIZE -#define COMMIT_SLAB_SIZE (512*1024-32) -#endif - -#define MAYBE_UNUSED __attribute__((__unused__)) - -#define define_commit_slab(slabname, elemtype) \ - \ -struct slabname { \ - unsigned slab_size; \ - unsigned stride; \ - unsigned slab_count; \ - elemtype **slab; \ -}; \ -static int stat_ ##slabname## realloc; \ - \ -static MAYBE_UNUSED void init_ ##slabname## _with_stride(struct slabname *s, \ - unsigned stride) \ -{ \ - unsigned int elem_size; \ - if (!stride) \ - stride = 1; \ - s->stride = stride; \ - elem_size = sizeof(elemtype) * stride; \ - s->slab_size = COMMIT_SLAB_SIZE / elem_size; \ - s->slab_count = 0; \ - s->slab = NULL; \ -} \ - \ -static MAYBE_UNUSED void init_ ##slabname(struct slabname *s) \ -{ \ - init_ ##slabname## _with_stride(s, 1); \ -} \ - \ -static MAYBE_UNUSED void clear_ ##slabname(struct slabname *s) \ -{ \ - unsigned int i; \ - for (i = 0; i < s->slab_count; i++) \ - free(s->slab[i]); \ - s->slab_count = 0; \ - FREE_AND_NULL(s->slab); \ -} \ - \ -static MAYBE_UNUSED elemtype *slabname## _at_peek(struct slabname *s, \ - const struct commit *c, \ - int add_if_missing) \ -{ \ - unsigned int nth_slab, nth_slot; \ - \ - nth_slab = c->index / s->slab_size; \ - nth_slot = c->index % s->slab_size; \ - \ - if (s->slab_count <= nth_slab) { \ - unsigned int i; \ - if (!add_if_missing) \ - return NULL; \ - REALLOC_ARRAY(s->slab, nth_slab + 1); \ - stat_ ##slabname## realloc++; \ - for (i = s->slab_count; i <= nth_slab; i++) \ - s->slab[i] = NULL; \ - s->slab_count = nth_slab + 1; \ - } \ - if (!s->slab[nth_slab]) { \ - if (!add_if_missing) \ - return NULL; \ - s->slab[nth_slab] = xcalloc(s->slab_size, \ - sizeof(**s->slab) * s->stride); \ - } \ - return &s->slab[nth_slab][nth_slot * s->stride]; \ -} \ - \ -static MAYBE_UNUSED elemtype *slabname## _at(struct slabname *s, \ - const struct commit *c) \ -{ \ - return slabname##_at_peek(s, c, 1); \ -} \ - \ -static MAYBE_UNUSED elemtype *slabname## _peek(struct slabname *s, \ - const struct commit *c) \ -{ \ - return slabname##_at_peek(s, c, 0); \ -} \ - \ -struct slabname - -/* - * Note that this redundant forward declaration is required - * to allow a terminating semicolon, which makes instantiations look - * like function declarations. I.e., the expansion of - * - * define_commit_slab(indegree, int); - * - * ends in 'struct indegree;'. This would otherwise - * be a syntax error according (at least) to ISO C. It's hard to - * catch because GCC silently parses it by default. - */ - -/* - * Statically initialize a commit slab named "var". Note that this - * evaluates "stride" multiple times! Example: - * - * struct indegree indegrees = COMMIT_SLAB_INIT(1, indegrees); - * - */ -#define COMMIT_SLAB_INIT(stride, var) { \ - COMMIT_SLAB_SIZE / sizeof(**((var).slab)) / (stride), \ - (stride), 0, NULL \ -} +#define define_commit_slab(slabname, elemtype) \ + declare_commit_slab(slabname, elemtype); \ + implement_static_commit_slab(slabname, elemtype) #endif /* COMMIT_SLAB_H */ diff --git a/commit.c b/commit.c index 0030e79940ff8564b5c8c5ebab462eb1bd745174..2d4431a21fd16f55d873b6fe2ba1126722d3c4bf 100644 --- a/commit.c +++ b/commit.c @@ -1605,13 +1605,21 @@ int commit_tree_extended(const char *msg, size_t msg_len, return result; } +define_commit_slab(merge_desc_slab, struct merge_remote_desc *); +static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab); + +struct merge_remote_desc *merge_remote_util(struct commit *commit) +{ + return *merge_desc_slab_at(&merge_desc_slab, commit); +} + void set_merge_remote_desc(struct commit *commit, const char *name, struct object *obj) { struct merge_remote_desc *desc; FLEX_ALLOC_STR(desc, name, name); desc->obj = obj; - commit->util = desc; + *merge_desc_slab_at(&merge_desc_slab, commit) = desc; } struct commit *get_merge_parent(const char *name) @@ -1623,7 +1631,7 @@ struct commit *get_merge_parent(const char *name) return NULL; obj = parse_object(&oid); commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); - if (commit && !commit->util) + if (commit && !merge_remote_util(commit)) set_merge_remote_desc(commit, name, obj); return commit; } diff --git a/commit.h b/commit.h index c3af512f8b1b90b3963d0921f7ec0d0a43a2fcbe..e48f05dffcac2fc780386626701cdc64bd795076 100644 --- a/commit.h +++ b/commit.h @@ -16,9 +16,13 @@ struct commit_list { struct commit_list *next; }; +/* + * The size of this struct matters in full repo walk operations like + * 'git clone' or 'git gc'. Consider using commit-slab to attach data + * to a commit instead of adding new fields here. + */ struct commit { struct object object; - void *util; timestamp_t date; struct commit_list *parents; @@ -312,7 +316,7 @@ struct merge_remote_desc { struct object *obj; /* the named object, could be a tag */ char name[FLEX_ARRAY]; }; -#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util)) +extern struct merge_remote_desc *merge_remote_util(struct commit *); extern void set_merge_remote_desc(struct commit *commit, const char *name, struct object *obj); diff --git a/log-tree.c b/log-tree.c index 4aef85331e0b696d0373cf1bdb0743a7e6b58c36..0b97de5e879a9d90d8d62412d1743a37b7915fd3 100644 --- a/log-tree.c +++ b/log-tree.c @@ -295,8 +295,12 @@ void show_decorations(struct rev_info *opt, struct commit *commit) { struct strbuf sb = STRBUF_INIT; - if (opt->show_source && commit->util) - fprintf(opt->diffopt.file, "\t%s", (char *) commit->util); + if (opt->sources) { + char **slot = revision_sources_peek(opt->sources, commit); + + if (slot && *slot) + fprintf(opt->diffopt.file, "\t%s", *slot); + } if (!opt->show_decorations) return; format_decorations(&sb, commit, opt->diffopt.use_color); diff --git a/merge-recursive.c b/merge-recursive.c index f110e1c5ecbd6254b25785ecb49f29cb7bb75ed2..404f050cafe919023ec86d14230a0f6455714538 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -286,10 +286,12 @@ static void output(struct merge_options *o, int v, const char *fmt, ...) static void output_commit_title(struct merge_options *o, struct commit *commit) { + struct merge_remote_desc *desc; + strbuf_addchars(&o->obuf, ' ', o->call_depth * 2); - if (commit->util) - strbuf_addf(&o->obuf, "virtual %s\n", - merge_remote_util(commit)->name); + desc = merge_remote_util(commit); + if (desc) + strbuf_addf(&o->obuf, "virtual %s\n", desc->name); else { strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid, DEFAULT_ABBREV); diff --git a/object.h b/object.h index 5c13955000cdaec252f29a3b93599560c35deb8a..acc560b24e4ba57d820b61896ba07d4d3f640f33 100644 --- a/object.h +++ b/object.h @@ -42,6 +42,7 @@ struct object_array { * builtin/index-pack.c: 2021 * builtin/pack-objects.c: 20 * builtin/reflog.c: 10--12 + * builtin/show-branch.c: 0-------------------------------------------26 * builtin/unpack-objects.c: 2021 */ #define FLAG_BITS 27 diff --git a/revision.c b/revision.c index 40fd91ff2b16bffc725f8602f45117279e27ba19..0afae4744a12b9b1bfd484c12440db82e19c8c9f 100644 --- a/revision.c +++ b/revision.c @@ -29,6 +29,8 @@ volatile show_early_output_fn_t show_early_output; static const char *term_bad; static const char *term_good; +implement_shared_commit_slab(revision_sources, char *); + void show_object_with_name(FILE *out, struct object *obj, const char *name) { const char *p; @@ -265,14 +267,19 @@ 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) die("unable to parse commit %s", name); if (flags & UNINTERESTING) { mark_parents_uninteresting(commit); revs->limited = 1; } - if (revs->show_source && !commit->util) - commit->util = xstrdup(name); + if (revs->sources) { + char **slot = revision_sources_at(revs->sources, commit); + + if (!*slot) + *slot = xstrdup(name); + } return commit; } @@ -824,8 +831,12 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, } return -1; } - if (revs->show_source && !p->util) - p->util = commit->util; + if (revs->sources) { + char **slot = revision_sources_at(revs->sources, p); + + if (!*slot) + *slot = *revision_sources_at(revs->sources, commit); + } p->object.flags |= left_flag; if (!(p->object.flags & SEEN)) { p->object.flags |= SEEN; diff --git a/revision.h b/revision.h index b8c47b98e22562ef320197b4ddbc8f0c3ee40f98..bf2239f87689d9486c4b888bed5cba6affa21953 100644 --- a/revision.h +++ b/revision.h @@ -6,6 +6,7 @@ #include "notes.h" #include "pretty.h" #include "diff.h" +#include "commit-slab-decl.h" /* Remember to update object flag allocation in object.h */ #define SEEN (1u<<0) @@ -29,6 +30,7 @@ struct rev_info; struct log_info; struct string_list; struct saved_parents; +define_shared_commit_slab(revision_sources, char *); struct rev_cmdline_info { unsigned int nr; @@ -111,7 +113,6 @@ struct rev_info { right_only:1, rewrite_parents:1, print_parents:1, - show_source:1, show_decorations:1, reverse:1, reverse_output_stage:1, @@ -224,6 +225,8 @@ struct rev_info { struct commit_list *previous_parents; const char *break_bar; + + struct revision_sources *sources; }; extern int ref_excluded(struct string_list *, const char *path); diff --git a/sequencer.c b/sequencer.c index 4034c0461b5022dad01b25d824cdc4f47ee09d13..ee1f7b2df74572b1bff71436fa7961f47ac25378 100644 --- a/sequencer.c +++ b/sequencer.c @@ -27,6 +27,7 @@ #include "worktree.h" #include "oidmap.h" #include "oidset.h" +#include "commit-slab.h" #include "alias.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -4238,6 +4239,7 @@ static enum check_level get_missing_commit_check_level(void) return CHECK_IGNORE; } +define_commit_slab(commit_seen, unsigned char); /* * Check if the user dropped some commits by mistake * Behaviour determined by rebase.missingCommitsCheck. @@ -4251,6 +4253,9 @@ int check_todo_list(void) struct todo_list todo_list = TODO_LIST_INIT; struct strbuf missing = STRBUF_INIT; int advise_to_edit_todo = 0, res = 0, i; + struct commit_seen commit_seen; + + init_commit_seen(&commit_seen); strbuf_addstr(&todo_file, rebase_path_todo()); if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) { @@ -4267,7 +4272,7 @@ int check_todo_list(void) for (i = 0; i < todo_list.nr; i++) { struct commit *commit = todo_list.items[i].commit; if (commit) - commit->util = (void *)1; + *commit_seen_at(&commit_seen, commit) = 1; } todo_list_release(&todo_list); @@ -4283,11 +4288,11 @@ int check_todo_list(void) for (i = todo_list.nr - 1; i >= 0; i--) { struct todo_item *item = todo_list.items + i; struct commit *commit = item->commit; - if (commit && !commit->util) { + if (commit && !*commit_seen_at(&commit_seen, commit)) { strbuf_addf(&missing, " - %s %.*s\n", short_commit_name(commit), item->arg_len, item->arg); - commit->util = (void *)1; + *commit_seen_at(&commit_seen, commit) = 1; } } @@ -4313,6 +4318,7 @@ int check_todo_list(void) "The possible behaviours are: ignore, warn, error.\n\n")); leave_check: + clear_commit_seen(&commit_seen); strbuf_release(&todo_file); todo_list_release(&todo_list); @@ -4433,6 +4439,8 @@ static int subject2item_cmp(const void *fndata, return key ? strcmp(a->subject, key) : strcmp(a->subject, b->subject); } +define_commit_slab(commit_todo_item, struct todo_item *); + /* * Rearrange the todo list that has both "pick commit-id msg" and "pick * commit-id fixup!/squash! msg" in it so that the latter is put immediately @@ -4449,6 +4457,7 @@ int rearrange_squash(void) struct hashmap subject2item; int res = 0, rearranged = 0, *next, *tail, i; char **subjects; + struct commit_todo_item commit_todo; if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0) return -1; @@ -4457,6 +4466,7 @@ int rearrange_squash(void) return -1; } + init_commit_todo_item(&commit_todo); /* * The hashmap maps onelines to the respective todo list index. * @@ -4487,10 +4497,11 @@ int rearrange_squash(void) if (is_fixup(item->command)) { todo_list_release(&todo_list); + clear_commit_todo_item(&commit_todo); return error(_("the script was already rearranged.")); } - item->commit->util = item; + *commit_todo_item_at(&commit_todo, item->commit) = item; parse_commit(item->commit); commit_buffer = get_commit_buffer(item->commit, NULL); @@ -4517,9 +4528,9 @@ int rearrange_squash(void) else if (!strchr(p, ' ') && (commit2 = lookup_commit_reference_by_name(p)) && - commit2->util) + *commit_todo_item_at(&commit_todo, commit2)) /* found by commit name */ - i2 = (struct todo_item *)commit2->util + i2 = *commit_todo_item_at(&commit_todo, commit2) - todo_list.items; else { /* copy can be a prefix of the commit subject */ @@ -4596,5 +4607,6 @@ int rearrange_squash(void) hashmap_free(&subject2item, 1); todo_list_release(&todo_list); + clear_commit_todo_item(&commit_todo); return res; } diff --git a/shallow.c b/shallow.c index 9ff83cabcd3be2614f7ed770884ac237294b05e4..2abebeb8c857067315cf79b96dbba6b26a69a076 100644 --- a/shallow.c +++ b/shallow.c @@ -12,6 +12,7 @@ #include "commit-slab.h" #include "revision.h" #include "list-objects.h" +#include "commit-slab.h" static int is_shallow = -1; static struct stat_validity shallow_stat; @@ -74,6 +75,11 @@ int is_repository_shallow(void) return is_shallow; } +/* + * TODO: use "int" elemtype instead of "int *" when/if commit-slab + * supports a "valid" flag. + */ +define_commit_slab(commit_depth, int *); struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag) { @@ -82,25 +88,29 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, struct object_array stack = OBJECT_ARRAY_INIT; struct commit *commit = NULL; struct commit_graft *graft; + struct commit_depth depths; + init_commit_depth(&depths); while (commit || i < heads->nr || stack.nr) { struct commit_list *p; if (!commit) { if (i < heads->nr) { + int **depth_slot; commit = (struct commit *) deref_tag(heads->objects[i++].item, NULL, 0); if (!commit || commit->object.type != OBJ_COMMIT) { commit = NULL; continue; } - if (!commit->util) - commit->util = xmalloc(sizeof(int)); - *(int *)commit->util = 0; + depth_slot = commit_depth_at(&depths, commit); + if (!*depth_slot) + *depth_slot = xmalloc(sizeof(int)); + **depth_slot = 0; cur_depth = 0; } else { commit = (struct commit *) object_array_pop(&stack); - cur_depth = *(int *)commit->util; + cur_depth = **commit_depth_at(&depths, commit); } } parse_commit_or_die(commit); @@ -116,25 +126,31 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, } commit->object.flags |= not_shallow_flag; for (p = commit->parents, commit = NULL; p; p = p->next) { - if (!p->item->util) { - int *pointer = xmalloc(sizeof(int)); - p->item->util = pointer; - *pointer = cur_depth; + int **depth_slot = commit_depth_at(&depths, p->item); + if (!*depth_slot) { + *depth_slot = xmalloc(sizeof(int)); + **depth_slot = cur_depth; } else { - int *pointer = p->item->util; - if (cur_depth >= *pointer) + if (cur_depth >= **depth_slot) continue; - *pointer = cur_depth; + **depth_slot = cur_depth; } if (p->next) add_object_array(&p->item->object, NULL, &stack); else { commit = p->item; - cur_depth = *(int *)commit->util; + cur_depth = **commit_depth_at(&depths, commit); } } } + for (i = 0; i < depths.slab_count; i++) { + int j; + + for (j = 0; j < depths.slab_size; j++) + free(depths.slab[i][j]); + } + clear_commit_depth(&depths); return result; }