Skip to content

Commit 24cb1bb

Browse files
committed
Speed up reflog pruning of unreachable commits
Instead of doing the (potentially very expensive) "in_merge_base()" check for each commit that might be pruned if it is unreachable, do a preparatory reachability graph of the commit space, so that the common case of being reachable can be tested directly. [ Cleaned up a bit and tweaked to actually work. - Linus ] Signed-off-by: Linus Torvalds <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9ffb15d commit 24cb1bb

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

builtin-reflog.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct collect_reflog_cb {
5252

5353
#define INCOMPLETE (1u<<10)
5454
#define STUDYING (1u<<11)
55+
#define REACHABLE (1u<<12)
5556

5657
static int tree_is_complete(const unsigned char *sha1)
5758
{
@@ -227,13 +228,52 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
227228
}
228229

229230
/* Reachable from the current ref? Don't prune. */
231+
if (commit->object.flags & REACHABLE)
232+
return 0;
230233
if (in_merge_bases(commit, &cb->ref_commit, 1))
231234
return 0;
232235

233236
/* We can't reach it - prune it. */
234237
return 1;
235238
}
236239

240+
static void mark_reachable(struct commit *commit, unsigned long expire_limit)
241+
{
242+
/*
243+
* We need to compute if commit on either side of an reflog
244+
* entry is reachable from the tip of the ref for all entries.
245+
* Mark commits that are reachable from the tip down to the
246+
* time threashold first; we know a commit marked thusly is
247+
* reachable from the tip without running in_merge_bases()
248+
* at all.
249+
*/
250+
struct commit_list *pending = NULL;
251+
252+
commit_list_insert(commit, &pending);
253+
while (pending) {
254+
struct commit_list *entry = pending;
255+
struct commit_list *parent;
256+
pending = entry->next;
257+
commit = entry->item;
258+
free(entry);
259+
if (commit->object.flags & REACHABLE)
260+
continue;
261+
if (parse_commit(commit))
262+
continue;
263+
commit->object.flags |= REACHABLE;
264+
if (commit->date < expire_limit)
265+
continue;
266+
parent = commit->parents;
267+
while (parent) {
268+
commit = parent->item;
269+
parent = parent->next;
270+
if (commit->object.flags & REACHABLE)
271+
continue;
272+
commit_list_insert(commit, &pending);
273+
}
274+
}
275+
}
276+
237277
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
238278
const char *email, unsigned long timestamp, int tz,
239279
const char *message, void *cb_data)
@@ -308,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
308348
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
309349
cb.ref = ref;
310350
cb.cmd = cmd;
351+
if (cb.ref_commit)
352+
mark_reachable(cb.ref_commit, cmd->expire_total);
311353
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
354+
if (cb.ref_commit)
355+
clear_commit_marks(cb.ref_commit, REACHABLE);
312356
finish:
313357
if (cb.newlog) {
314358
if (fclose(cb.newlog)) {

0 commit comments

Comments
 (0)