Skip to content

Commit 84047e0

Browse files
committed
Merge branch 'lt/maint-reflog-expire' into maint
* lt/maint-reflog-expire: Speed up reflog pruning of unreachable commits Clean up reflog unreachability pruning decision
2 parents 5027acc + 24cb1bb commit 84047e0

File tree

1 file changed

+70
-6
lines changed

1 file changed

+70
-6
lines changed

builtin-reflog.c

Lines changed: 70 additions & 6 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
{
@@ -209,6 +210,70 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
209210
return 1;
210211
}
211212

213+
static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
214+
{
215+
/*
216+
* We may or may not have the commit yet - if not, look it
217+
* up using the supplied sha1.
218+
*/
219+
if (!commit) {
220+
if (is_null_sha1(sha1))
221+
return 0;
222+
223+
commit = lookup_commit_reference_gently(sha1, 1);
224+
225+
/* Not a commit -- keep it */
226+
if (!commit)
227+
return 0;
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+
236+
/* We can't reach it - prune it. */
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+
212277
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
213278
const char *email, unsigned long timestamp, int tz,
214279
const char *message, void *cb_data)
@@ -230,12 +295,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
230295
if (timestamp < cb->cmd->expire_unreachable) {
231296
if (!cb->ref_commit)
232297
goto prune;
233-
if (!old && !is_null_sha1(osha1))
234-
old = lookup_commit_reference_gently(osha1, 1);
235-
if (!new && !is_null_sha1(nsha1))
236-
new = lookup_commit_reference_gently(nsha1, 1);
237-
if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
238-
(new && !in_merge_bases(new, &cb->ref_commit, 1)))
298+
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
239299
goto prune;
240300
}
241301

@@ -288,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
288348
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
289349
cb.ref = ref;
290350
cb.cmd = cmd;
351+
if (cb.ref_commit)
352+
mark_reachable(cb.ref_commit, cmd->expire_total);
291353
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
354+
if (cb.ref_commit)
355+
clear_commit_marks(cb.ref_commit, REACHABLE);
292356
finish:
293357
if (cb.newlog) {
294358
if (fclose(cb.newlog)) {

0 commit comments

Comments
 (0)