@ -34,8 +34,11 @@ struct cmd_reflog_expire_cb {
struct expire_reflog_cb {
struct expire_reflog_cb {
FILE *newlog;
FILE *newlog;
const char *ref;
enum {
struct commit *ref_commit;
UE_NORMAL,
UE_ALWAYS,
UE_HEAD
} unreachable_expire_kind;
struct commit_list *mark_list;
struct commit_list *mark_list;
unsigned long mark_limit;
unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
struct cmd_reflog_expire_cb *cmd;
@ -305,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
goto prune;
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
if (timestamp < cb->cmd->expire_unreachable) {
if (!cb->ref_commit)
if (cb->unreachable_expire_kind == UE_ALWAYS)
goto prune;
goto prune;
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
goto prune;
goto prune;
@ -332,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
return 0;
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)
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 cmd_reflog_expire_cb *cmd = cb_data;
struct expire_reflog_cb cb;
struct expire_reflog_cb cb;
struct ref_lock *lock;
struct ref_lock *lock;
char *log_file, *newlog_path = NULL;
char *log_file, *newlog_path = NULL;
struct commit *tip_commit;
struct commit_list *tips;
int status = 0;
int status = 0;
memset(&cb, 0, sizeof(cb));
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.newlog = fopen(newlog_path, "w");
}
}
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
cb.ref = ref;
cb.cmd = cmd;
cb.cmd = cmd;
if (cb.ref_commit) {
cb.mark_list = NULL;
if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
commit_list_insert(cb.ref_commit, &cb.mark_list);
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;
cb.mark_limit = cmd->expire_total;
mark_reachable(&cb);
mark_reachable(&cb);
}
}
for_each_reflog_ent(ref, expire_reflog_ent, &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:
finish:
if (cb.newlog) {
if (cb.newlog) {
if (fclose(cb.newlog)) {
if (fclose(cb.newlog)) {