revision traversal: retire BOUNDARY_SHOW
[gitweb.git] / builtin-reflog.c
index 1da7da0916af92eed0a3168036175376f0ac0c0a..186aabce042a1d6e5d83141495a854db09223bf8 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 static const char reflog_expire_usage[] =
-"git-reflog expire [--verbose] [--dry-run] [--fix-stale] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+"git-reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
 
 static unsigned long default_reflog_expire;
 static unsigned long default_reflog_expire_unreachable;
@@ -34,35 +34,51 @@ struct expire_reflog_cb {
        struct cmd_reflog_expire_cb *cmd;
 };
 
+#define INCOMPLETE     (1u<<10)
+#define STUDYING       (1u<<11)
+
 static int tree_is_complete(const unsigned char *sha1)
 {
        struct tree_desc desc;
-       void *buf;
-       char type[20];
+       struct name_entry entry;
+       int complete;
+       struct tree *tree;
 
-       buf = read_sha1_file(sha1, type, &desc.size);
-       if (!buf)
+       tree = lookup_tree(sha1);
+       if (!tree)
                return 0;
-       desc.buf = buf;
-       while (desc.size) {
-               const unsigned char *elem;
-               const char *name;
-               unsigned mode;
-
-               elem = tree_entry_extract(&desc, &name, &mode);
-               if (!has_sha1_file(elem) ||
-                   (S_ISDIR(mode) && !tree_is_complete(elem))) {
-                       free(buf);
+       if (tree->object.flags & SEEN)
+               return 1;
+       if (tree->object.flags & INCOMPLETE)
+               return 0;
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+       if (!desc.buf) {
+               enum object_type type;
+               void *data = read_sha1_file(sha1, &type, &desc.size);
+               if (!data) {
+                       tree->object.flags |= INCOMPLETE;
                        return 0;
                }
-               update_tree_entry(&desc);
+               desc.buf = data;
+               tree->buffer = data;
        }
-       free(buf);
-       return 1;
-}
+       complete = 1;
+       while (tree_entry(&desc, &entry)) {
+               if (!has_sha1_file(entry.sha1) ||
+                   (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
+                       tree->object.flags |= INCOMPLETE;
+                       complete = 0;
+               }
+       }
+       free(tree->buffer);
+       tree->buffer = NULL;
 
-#define INCOMPLETE     (1u<<10)
-#define STUDYING       (1u<<11)
+       if (complete)
+               tree->object.flags |= SEEN;
+       return complete;
+}
 
 static int commit_is_complete(struct commit *commit)
 {
@@ -112,14 +128,17 @@ static int commit_is_complete(struct commit *commit)
                }
        }
        if (!is_incomplete) {
-               /* make sure all commits in found have all the
+               /*
+                * make sure all commits in "found" array have all the
                 * necessary objects.
                 */
-               for (i = 0; !is_incomplete && i < found.nr; i++) {
+               for (i = 0; i < found.nr; i++) {
                        struct commit *c =
                                (struct commit *)found.objects[i].item;
-                       if (!tree_is_complete(c->tree->object.sha1))
+                       if (!tree_is_complete(c->tree->object.sha1)) {
                                is_incomplete = 1;
+                               c->object.flags |= INCOMPLETE;
+                       }
                }
                if (!is_incomplete) {
                        /* mark all found commits as complete, iow SEEN */
@@ -132,6 +151,18 @@ static int commit_is_complete(struct commit *commit)
                found.objects[i].item->flags &= ~STUDYING;
        if (is_incomplete)
                commit->object.flags |= INCOMPLETE;
+       else {
+               /*
+                * If we come here, we have (1) traversed the ancestry chain
+                * from the "commit" until we reach SEEN commits (which are
+                * known to be complete), and (2) made sure that the commits
+                * encountered during the above traversal refer to trees that
+                * are complete.  Which means that we know *all* the commits
+                * we have seen during this process are complete.
+                */
+               for (i = 0; i < found.nr; i++)
+                       found.objects[i].item->flags |= SEEN;
+       }
        /* free object arrays */
        free(study.objects);
        free(found.objects);
@@ -142,7 +173,6 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 {
        struct commit *commit;
 
-       *it = NULL;
        if (is_null_sha1(sha1))
                return 1;
        commit = lookup_commit_reference_gently(sha1, 1);
@@ -164,41 +194,46 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
 }
 
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-                            char *data, void *cb_data)
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
 {
        struct expire_reflog_cb *cb = cb_data;
-       unsigned long timestamp;
-       char *cp, *ep;
        struct commit *old, *new;
 
-       cp = strchr(data, '>');
-       if (!cp || *++cp != ' ')
-               goto prune;
-       timestamp = strtoul(cp, &ep, 10);
-       if (*ep != ' ')
-               goto prune;
        if (timestamp < cb->cmd->expire_total)
                goto prune;
 
+       old = new = NULL;
        if (cb->cmd->stalefix &&
            (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
                goto prune;
 
-       if ((timestamp < cb->cmd->expire_unreachable) &&
-           (!cb->ref_commit ||
-            (old && !in_merge_bases(old, cb->ref_commit)) ||
-            (new && !in_merge_bases(new, cb->ref_commit))))
-               goto prune;
+       if (timestamp < cb->cmd->expire_unreachable) {
+               if (!cb->ref_commit)
+                       goto prune;
+               if (!old && !is_null_sha1(osha1))
+                       old = lookup_commit_reference_gently(osha1, 1);
+               if (!new && !is_null_sha1(nsha1))
+                       new = lookup_commit_reference_gently(nsha1, 1);
+               if ((old && !in_merge_bases(old, &cb->ref_commit, 1)) ||
+                   (new && !in_merge_bases(new, &cb->ref_commit, 1)))
+                       goto prune;
+       }
 
-       if (cb->newlog)
-               fprintf(cb->newlog, "%s %s %s",
-                       sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
+       if (cb->newlog) {
+               char sign = (tz < 0) ? '-' : '+';
+               int zone = (tz < 0) ? (-tz) : tz;
+               fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
+                       sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                       email, timestamp, sign, zone,
+                       message);
+       }
        if (cb->cmd->verbose)
-               printf("keep %s", data);
+               printf("keep %s", message);
        return 0;
  prune:
        if (!cb->newlog || cb->cmd->verbose)
-               printf("%sprune %s", cb->newlog ? "" : "would ", data);
+               printf("%sprune %s", cb->newlog ? "" : "would ", message);
        return 0;
 }
 
@@ -207,20 +242,18 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        struct cmd_reflog_expire_cb *cmd = cb_data;
        struct expire_reflog_cb cb;
        struct ref_lock *lock;
-       char *newlog_path = NULL;
+       char *log_file, *newlog_path = NULL;
        int status = 0;
 
-       if (strncmp(ref, "refs/", 5))
-               return error("not a ref '%s'", ref);
-
        memset(&cb, 0, sizeof(cb));
        /* we take the lock for the ref itself to prevent it from
         * getting updated.
         */
-       lock = lock_ref_sha1(ref + 5, sha1);
+       lock = lock_any_ref_for_update(ref, sha1);
        if (!lock)
                return error("cannot lock ref '%s'", ref);
-       if (!file_exists(lock->log_file))
+       log_file = xstrdup(git_path("logs/%s", ref));
+       if (!file_exists(log_file))
                goto finish;
        if (!cmd->dry_run) {
                newlog_path = xstrdup(git_path("logs/%s.lock", ref));
@@ -228,9 +261,6 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        }
 
        cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
-       if (!cb.ref_commit)
-               fprintf(stderr,
-                       "warning: ref '%s' does not point at a commit\n", ref);
        cb.ref = ref;
        cb.cmd = cmd;
        for_each_reflog_ent(ref, expire_reflog_ent, &cb);
@@ -239,13 +269,14 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
                if (fclose(cb.newlog))
                        status |= error("%s: %s", strerror(errno),
                                        newlog_path);
-               if (rename(newlog_path, lock->log_file)) {
+               if (rename(newlog_path, log_file)) {
                        status |= error("cannot rename %s to %s",
-                                       newlog_path, lock->log_file);
+                                       newlog_path, log_file);
                        unlink(newlog_path);
                }
        }
        free(newlog_path);
+       free(log_file);
        unlock_ref(lock);
        return status;
 }
@@ -290,9 +321,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        cb.dry_run = 1;
-               else if (!strncmp(arg, "--expire=", 9))
+               else if (!prefixcmp(arg, "--expire="))
                        cb.expire_total = approxidate(arg + 9);
-               else if (!strncmp(arg, "--expire-unreachable=", 21))
+               else if (!prefixcmp(arg, "--expire-unreachable="))
                        cb.expire_unreachable = approxidate(arg + 21);
                else if (!strcmp(arg, "--stale-fix"))
                        cb.stalefix = 1;
@@ -319,7 +350,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        }
 
        if (do_all)
-               status |= for_each_ref(expire_reflog, &cb);
+               status |= for_each_reflog(expire_reflog, &cb);
        while (i < argc) {
                const char *ref = argv[i++];
                unsigned char sha1[20];
@@ -341,10 +372,16 @@ static const char reflog_usage[] =
 
 int cmd_reflog(int argc, const char **argv, const char *prefix)
 {
-       if (argc < 2)
-               usage(reflog_usage);
-       else if (!strcmp(argv[1], "expire"))
+       /* With no command, we default to showing it. */
+       if (argc < 2 || *argv[1] == '-')
+               return cmd_log_reflog(argc, argv, prefix);
+
+       if (!strcmp(argv[1], "show"))
+               return cmd_log_reflog(argc - 1, argv + 1, prefix);
+
+       if (!strcmp(argv[1], "expire"))
                return cmd_reflog_expire(argc - 1, argv + 1, prefix);
-       else
-               usage(reflog_usage);
+
+       /* Not a recognized reflog command..*/
+       usage(reflog_usage);
 }