@matheusbsbs / matheus-bagliano / commits / 079f970971
Fork 0

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 <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Feb 5, 2020 at 18:50 UTC 079f970971e8e66bdf5b67d924fc2a5177277ffd
1 file changed +52 -8
builtin/name-rev.c
+52 -8
@@ -247,6 +247,10 @@ static struct tip_table {
247 struct tip_table_entry {
248 struct object_id oid;
249 const char *refname;
250 + struct commit *commit;
251 + timestamp_t taggerdate;
252 + unsigned int from_tag:1;
253 + unsigned int deref:1;
254 } *table;
255 int nr;
256 int alloc;
@@ -254,13 +258,18 @@ static struct tip_table {
258 } tip_table;
259
260 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)
263 {
264 refname = name_ref_abbrev(refname, shorten_unambiguous);
265
266 ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
267 oidcpy(&tip_table.table[tip_table.nr].oid, oid);
268 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;
273 tip_table.nr++;
274 tip_table.sorted = 0;
275 }
@@ -271,12 +280,30 @@ static int tipcmp(const void *a_, const void *b_)
280 return oidcmp(&a->oid, &b->oid);
281 }
282
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 +
299 static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
300 {
301 struct object *o = parse_object(the_repository, oid);
302 struct name_ref_data *data = cb_data;
303 int can_abbreviate_output = data->tags_only && data->name_only;
304 int deref = 0;
305 + int from_tag = 0;
306 + struct commit *commit = NULL;
307 timestamp_t taggerdate = TIME_MAX;
308
309 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
352 return 0;
353 }
354
328 - add_to_tip_table(oid, path, can_abbreviate_output);
329 -
355 while (o && o->type == OBJ_TAG) {
356 struct tag *t = (struct tag *) o;
357 if (!t->tagged)
@@ -336,17 +361,35 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
361 taggerdate = t->date;
362 }
363 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/");
366 if (taggerdate == TIME_MAX)
367 taggerdate = commit->date;
344 - path = name_ref_abbrev(path, can_abbreviate_output);
345 - name_rev(commit, path, taggerdate, from_tag, deref);
368 }
369 +
370 + add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate,
371 + from_tag, deref);
372 return 0;
373 }
374
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 +
393 static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
394 {
395 struct tip_table_entry *table = table_;
@@ -559,6 +602,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
602 cutoff = TIME_MIN;
603 }
604 for_each_ref(name_ref, &data);
605 + name_tips();
606
607 if (transform_stdin) {
608 char buffer[2048];