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)) {