diff options
Diffstat (limited to 'builtin/reflog.c')
-rw-r--r-- | builtin/reflog.c | 69 |
1 files changed, 59 insertions, 10 deletions
diff --git a/builtin/reflog.c b/builtin/reflog.c index f73215686d..ebf610e64a 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -34,8 +34,11 @@ struct cmd_reflog_expire_cb { struct expire_reflog_cb { FILE *newlog; - const char *ref; - struct commit *ref_commit; + enum { + UE_NORMAL, + UE_ALWAYS, + UE_HEAD + } unreachable_expire_kind; struct commit_list *mark_list; unsigned long mark_limit; struct cmd_reflog_expire_cb *cmd; @@ -305,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, goto prune; if (timestamp < cb->cmd->expire_unreachable) { - if (!cb->ref_commit) + if (cb->unreachable_expire_kind == UE_ALWAYS) goto prune; if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1)) goto prune; @@ -332,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, return 0; } +static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data) +{ + struct commit_list **list = cb_data; + struct commit *tip_commit; + if (flags & REF_ISSYMREF) + return 0; + tip_commit = lookup_commit_reference_gently(sha1, 1); + if (!tip_commit) + return 0; + commit_list_insert(tip_commit, list); + return 0; +} + static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) { struct cmd_reflog_expire_cb *cmd = cb_data; struct expire_reflog_cb cb; struct ref_lock *lock; char *log_file, *newlog_path = NULL; + struct commit *tip_commit; + struct commit_list *tips; int status = 0; memset(&cb, 0, sizeof(cb)); @@ -357,18 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, cb.newlog = fopen(newlog_path, "w"); } - cb.ref_commit = lookup_commit_reference_gently(sha1, 1); - cb.ref = ref; cb.cmd = cmd; - if (cb.ref_commit) { - cb.mark_list = NULL; - commit_list_insert(cb.ref_commit, &cb.mark_list); + + if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) { + tip_commit = NULL; + cb.unreachable_expire_kind = UE_HEAD; + } else { + tip_commit = lookup_commit_reference_gently(sha1, 1); + if (!tip_commit) + cb.unreachable_expire_kind = UE_ALWAYS; + else + cb.unreachable_expire_kind = UE_NORMAL; + } + + if (cmd->expire_unreachable <= cmd->expire_total) + cb.unreachable_expire_kind = UE_ALWAYS; + + cb.mark_list = NULL; + tips = NULL; + if (cb.unreachable_expire_kind != UE_ALWAYS) { + if (cb.unreachable_expire_kind == UE_HEAD) { + struct commit_list *elem; + for_each_ref(push_tip_to_list, &tips); + for (elem = tips; elem; elem = elem->next) + commit_list_insert(elem->item, &cb.mark_list); + } else { + commit_list_insert(tip_commit, &cb.mark_list); + } cb.mark_limit = cmd->expire_total; mark_reachable(&cb); } + for_each_reflog_ent(ref, expire_reflog_ent, &cb); - if (cb.ref_commit) - clear_commit_marks(cb.ref_commit, REACHABLE); + + if (cb.unreachable_expire_kind != UE_ALWAYS) { + if (cb.unreachable_expire_kind == UE_HEAD) { + struct commit_list *elem; + for (elem = tips; elem; elem = elem->next) + clear_commit_marks(tip_commit, REACHABLE); + free_commit_list(tips); + } else { + clear_commit_marks(tip_commit, REACHABLE); + } + } finish: if (cb.newlog) { if (fclose(cb.newlog)) { |