Skip to content

Commit 079f970

Browse files
rscharfegitster
authored andcommitted
name-rev: sort tip names before applying
name_ref() is called for each ref and checks if its a better name for the referenced commit. If that's the case it remembers it and checks if a name based on it is better for its ancestors as well. This in done in the the order for_each_ref() imposes on us. That might not be optimal. If bad names happen to be encountered first (as defined by is_better_name()), names derived from them may spread to a lot of commits, only to be replaced by better names later. Setting better names first can avoid that. is_better_name() prefers tags, short distances and old references. The distance is a measure that we need to calculate for each candidate commit, but the other two properties are not dependent on the relationships of commits. Sorting the refs by them should yield better performance than the essentially random order we currently use. And applying older references first should also help to reduce rework due to the fact that older commits have less ancestors than newer ones. So add all details of names to the tip table first, then sort them to prefer tags and older references and then apply them in this order. Here's the performance as measures by hyperfine for the Linux repo before: Benchmark #1: ./git -C ../linux/ name-rev --all Time (mean ± σ): 851.1 ms ± 4.5 ms [User: 806.7 ms, System: 44.4 ms] Range (min … max): 845.9 ms … 859.5 ms 10 runs ... and with this patch: Benchmark #1: ./git -C ../linux/ name-rev --all Time (mean ± σ): 736.2 ms ± 8.7 ms [User: 688.4 ms, System: 47.5 ms] Range (min … max): 726.0 ms … 755.2 ms 10 runs Signed-off-by: René Scharfe <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2d53975 commit 079f970

File tree

1 file changed

+52
-8
lines changed

1 file changed

+52
-8
lines changed

builtin/name-rev.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,20 +247,29 @@ static struct tip_table {
247247
struct tip_table_entry {
248248
struct object_id oid;
249249
const char *refname;
250+
struct commit *commit;
251+
timestamp_t taggerdate;
252+
unsigned int from_tag:1;
253+
unsigned int deref:1;
250254
} *table;
251255
int nr;
252256
int alloc;
253257
int sorted;
254258
} tip_table;
255259

256260
static void add_to_tip_table(const struct object_id *oid, const char *refname,
257-
int shorten_unambiguous)
261+
int shorten_unambiguous, struct commit *commit,
262+
timestamp_t taggerdate, int from_tag, int deref)
258263
{
259264
refname = name_ref_abbrev(refname, shorten_unambiguous);
260265

261266
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
262267
oidcpy(&tip_table.table[tip_table.nr].oid, oid);
263268
tip_table.table[tip_table.nr].refname = xstrdup(refname);
269+
tip_table.table[tip_table.nr].commit = commit;
270+
tip_table.table[tip_table.nr].taggerdate = taggerdate;
271+
tip_table.table[tip_table.nr].from_tag = from_tag;
272+
tip_table.table[tip_table.nr].deref = deref;
264273
tip_table.nr++;
265274
tip_table.sorted = 0;
266275
}
@@ -271,12 +280,30 @@ static int tipcmp(const void *a_, const void *b_)
271280
return oidcmp(&a->oid, &b->oid);
272281
}
273282

283+
static int cmp_by_tag_and_age(const void *a_, const void *b_)
284+
{
285+
const struct tip_table_entry *a = a_, *b = b_;
286+
int cmp;
287+
288+
/* Prefer tags. */
289+
cmp = b->from_tag - a->from_tag;
290+
if (cmp)
291+
return cmp;
292+
293+
/* Older is better. */
294+
if (a->taggerdate < b->taggerdate)
295+
return -1;
296+
return a->taggerdate != b->taggerdate;
297+
}
298+
274299
static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
275300
{
276301
struct object *o = parse_object(the_repository, oid);
277302
struct name_ref_data *data = cb_data;
278303
int can_abbreviate_output = data->tags_only && data->name_only;
279304
int deref = 0;
305+
int from_tag = 0;
306+
struct commit *commit = NULL;
280307
timestamp_t taggerdate = TIME_MAX;
281308

282309
if (data->tags_only && !starts_with(path, "refs/tags/"))
@@ -325,8 +352,6 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
325352
return 0;
326353
}
327354

328-
add_to_tip_table(oid, path, can_abbreviate_output);
329-
330355
while (o && o->type == OBJ_TAG) {
331356
struct tag *t = (struct tag *) o;
332357
if (!t->tagged)
@@ -336,17 +361,35 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
336361
taggerdate = t->date;
337362
}
338363
if (o && o->type == OBJ_COMMIT) {
339-
struct commit *commit = (struct commit *)o;
340-
int from_tag = starts_with(path, "refs/tags/");
341-
364+
commit = (struct commit *)o;
365+
from_tag = starts_with(path, "refs/tags/");
342366
if (taggerdate == TIME_MAX)
343367
taggerdate = commit->date;
344-
path = name_ref_abbrev(path, can_abbreviate_output);
345-
name_rev(commit, path, taggerdate, from_tag, deref);
346368
}
369+
370+
add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
371+
from_tag, deref);
347372
return 0;
348373
}
349374

375+
static void name_tips(void)
376+
{
377+
int i;
378+
379+
/*
380+
* Try to set better names first, so that worse ones spread
381+
* less.
382+
*/
383+
QSORT(tip_table.table, tip_table.nr, cmp_by_tag_and_age);
384+
for (i = 0; i < tip_table.nr; i++) {
385+
struct tip_table_entry *e = &tip_table.table[i];
386+
if (e->commit) {
387+
name_rev(e->commit, e->refname, e->taggerdate,
388+
e->from_tag, e->deref);
389+
}
390+
}
391+
}
392+
350393
static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
351394
{
352395
struct tip_table_entry *table = table_;
@@ -559,6 +602,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
559602
cutoff = TIME_MIN;
560603
}
561604
for_each_ref(name_ref, &data);
605+
name_tips();
562606

563607
if (transform_stdin) {
564608
char buffer[2048];

0 commit comments

Comments
 (0)