@matheusbsbs / matheus-bagliano / commits / 494fbfe87a
Fork 0

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 <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio Hamano committed Mar 30, 2009 at 21:34 UTC 494fbfe87ade4658cb5c3a061a5be5d6f6496607
1 file changed +44
builtin-reflog.c
+44
@@ -52,6 +52,7 @@ struct collect_reflog_cb {
52
53 #define INCOMPLETE (1u<<10)
54 #define STUDYING (1u<<11)
55 +#define REACHABLE (1u<<12)
56
57 static int tree_is_complete(const unsigned char *sha1)
58 {
@@ -227,6 +228,8 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
228 }
229
230 /* Reachable from the current ref? Don't prune. */
231 + if (commit->object.flags & REACHABLE)
232 + return 0;
233 if (in_merge_bases(commit, &cb->ref_commit, 1))
234 return 0;
235
@@ -234,6 +237,43 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
237 return 1;
238 }
239
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 +
277 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
278 const char *email, unsigned long timestamp, int tz,
279 const char *message, void *cb_data)
@@ -308,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
348 cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
349 cb.ref = ref;
350 cb.cmd = cmd;
351 + if (cb.ref_commit)
352 + mark_reachable(cb.ref_commit, cmd->expire_total);
353 for_each_reflog_ent(ref, expire_reflog_ent, &cb);
354 + if (cb.ref_commit)
355 + clear_commit_marks(cb.ref_commit, REACHABLE);
356 finish:
357 if (cb.newlog) {
358 if (fclose(cb.newlog)) {