Code

Merge branch 'jc/maint-no-reflog-expire-unreach-for-head'
[git.git] / builtin / reflog.c
index f73215686d15a266c2d20b007fe80ff4beabbb33..ebf610e64a267c5ca769d70203b282fb8f96a434 100644 (file)
@@ -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)) {