Skip to content

Commit 03cb91b

Browse files
committed
reflog --expire-unreachable: special case entries in "HEAD" reflog
"git reflog expire" (and "git gc") examines the reflog entries and discards old/stale ones using two criteria. The entries that are older than "reflogexpire" (defaults to 90 days) are unconditionally removed, and the entries that are older than "reflogexpireunreachable" (defaults to 30 days) are removed if the entry point at commits that are not reachable from the value of the ref. This is reasonable for local branches, remote tracking branches and tags. You (or other people) may have failed experiments that have been made and then later discarded by resetting the tip of the branch back, and setting the value of "reflogexpireunreachable" shorter than that of "reflogexpire" will prune the entries that describe these failed experiments earlier than the entries that describe the steps that led to the current history. It however doesn't make much sense for "HEAD" reflog. When you switch between branches, it is normal that the tip of the branch you were on is not an ancestor of the branch you have switched to. The moral equivalent of expiring failed experiments in per-branch reflog for "HEAD" reflog is to expire entries that talk about commits that cannot be reached from any ref. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 713c79e commit 03cb91b

File tree

1 file changed

+59
-10
lines changed

1 file changed

+59
-10
lines changed

builtin-reflog.c

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ struct cmd_reflog_expire_cb {
3434

3535
struct expire_reflog_cb {
3636
FILE *newlog;
37-
const char *ref;
38-
struct commit *ref_commit;
37+
enum {
38+
UE_NORMAL,
39+
UE_ALWAYS,
40+
UE_HEAD
41+
} unreachable_expire_kind;
3942
struct commit_list *mark_list;
4043
unsigned long mark_limit;
4144
struct cmd_reflog_expire_cb *cmd;
@@ -305,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
305308
goto prune;
306309

307310
if (timestamp < cb->cmd->expire_unreachable) {
308-
if (!cb->ref_commit)
311+
if (cb->unreachable_expire_kind == UE_ALWAYS)
309312
goto prune;
310313
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
311314
goto prune;
@@ -332,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
332335
return 0;
333336
}
334337

338+
static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
339+
{
340+
struct commit_list **list = cb_data;
341+
struct commit *tip_commit;
342+
if (flags & REF_ISSYMREF)
343+
return 0;
344+
tip_commit = lookup_commit_reference_gently(sha1, 1);
345+
if (!tip_commit)
346+
return 0;
347+
commit_list_insert(tip_commit, list);
348+
return 0;
349+
}
350+
335351
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
336352
{
337353
struct cmd_reflog_expire_cb *cmd = cb_data;
338354
struct expire_reflog_cb cb;
339355
struct ref_lock *lock;
340356
char *log_file, *newlog_path = NULL;
357+
struct commit *tip_commit;
358+
struct commit_list *tips;
341359
int status = 0;
342360

343361
memset(&cb, 0, sizeof(cb));
@@ -357,18 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
357375
cb.newlog = fopen(newlog_path, "w");
358376
}
359377

360-
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
361-
cb.ref = ref;
362378
cb.cmd = cmd;
363-
if (cb.ref_commit) {
364-
cb.mark_list = NULL;
365-
commit_list_insert(cb.ref_commit, &cb.mark_list);
379+
380+
if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
381+
tip_commit = NULL;
382+
cb.unreachable_expire_kind = UE_HEAD;
383+
} else {
384+
tip_commit = lookup_commit_reference_gently(sha1, 1);
385+
if (!tip_commit)
386+
cb.unreachable_expire_kind = UE_ALWAYS;
387+
else
388+
cb.unreachable_expire_kind = UE_NORMAL;
389+
}
390+
391+
if (cmd->expire_unreachable <= cmd->expire_total)
392+
cb.unreachable_expire_kind = UE_ALWAYS;
393+
394+
cb.mark_list = NULL;
395+
tips = NULL;
396+
if (cb.unreachable_expire_kind != UE_ALWAYS) {
397+
if (cb.unreachable_expire_kind == UE_HEAD) {
398+
struct commit_list *elem;
399+
for_each_ref(push_tip_to_list, &tips);
400+
for (elem = tips; elem; elem = elem->next)
401+
commit_list_insert(elem->item, &cb.mark_list);
402+
} else {
403+
commit_list_insert(tip_commit, &cb.mark_list);
404+
}
366405
cb.mark_limit = cmd->expire_total;
367406
mark_reachable(&cb);
368407
}
408+
369409
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
370-
if (cb.ref_commit)
371-
clear_commit_marks(cb.ref_commit, REACHABLE);
410+
411+
if (cb.unreachable_expire_kind != UE_ALWAYS) {
412+
if (cb.unreachable_expire_kind == UE_HEAD) {
413+
struct commit_list *elem;
414+
for (elem = tips; elem; elem = elem->next)
415+
clear_commit_marks(tip_commit, REACHABLE);
416+
free_commit_list(tips);
417+
} else {
418+
clear_commit_marks(tip_commit, REACHABLE);
419+
}
420+
}
372421
finish:
373422
if (cb.newlog) {
374423
if (fclose(cb.newlog)) {

0 commit comments

Comments
 (0)