Don't crash fast-import if the marks cannot be exported.
[gitweb.git] / builtin-reflog.c
index 1da7da0916af92eed0a3168036175376f0ac0c0a..b443ed9ef6936294e827a2700af80484f8a98506 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 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;
+       if (tree->object.flags & SEEN)
+               return 1;
+       if (tree->object.flags & INCOMPLETE)
                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);
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+       if (!desc.buf) {
+               char type[20];
+               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)) ||
+                   (new && !in_merge_bases(new, cb->ref_commit)))
+                       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;
 }
 
@@ -228,9 +263,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);