Code

Merge branch 'jc/diff-algo-cleanup'
[git.git] / builtin / reflog.c
1 #include "cache.h"
2 #include "builtin.h"
3 #include "commit.h"
4 #include "refs.h"
5 #include "dir.h"
6 #include "tree-walk.h"
7 #include "diff.h"
8 #include "revision.h"
9 #include "reachable.h"
11 /*
12  * reflog expire
13  */
15 static const char reflog_expire_usage[] =
16 "git reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
17 static const char reflog_delete_usage[] =
18 "git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
20 static unsigned long default_reflog_expire;
21 static unsigned long default_reflog_expire_unreachable;
23 struct cmd_reflog_expire_cb {
24         struct rev_info revs;
25         int dry_run;
26         int stalefix;
27         int rewrite;
28         int updateref;
29         int verbose;
30         unsigned long expire_total;
31         unsigned long expire_unreachable;
32         int recno;
33 };
35 struct expire_reflog_cb {
36         FILE *newlog;
37         enum {
38                 UE_NORMAL,
39                 UE_ALWAYS,
40                 UE_HEAD
41         } unreachable_expire_kind;
42         struct commit_list *mark_list;
43         unsigned long mark_limit;
44         struct cmd_reflog_expire_cb *cmd;
45         unsigned char last_kept_sha1[20];
46 };
48 struct collected_reflog {
49         unsigned char sha1[20];
50         char reflog[FLEX_ARRAY];
51 };
52 struct collect_reflog_cb {
53         struct collected_reflog **e;
54         int alloc;
55         int nr;
56 };
58 #define INCOMPLETE      (1u<<10)
59 #define STUDYING        (1u<<11)
60 #define REACHABLE       (1u<<12)
62 static int tree_is_complete(const unsigned char *sha1)
63 {
64         struct tree_desc desc;
65         struct name_entry entry;
66         int complete;
67         struct tree *tree;
69         tree = lookup_tree(sha1);
70         if (!tree)
71                 return 0;
72         if (tree->object.flags & SEEN)
73                 return 1;
74         if (tree->object.flags & INCOMPLETE)
75                 return 0;
77         if (!tree->buffer) {
78                 enum object_type type;
79                 unsigned long size;
80                 void *data = read_sha1_file(sha1, &type, &size);
81                 if (!data) {
82                         tree->object.flags |= INCOMPLETE;
83                         return 0;
84                 }
85                 tree->buffer = data;
86                 tree->size = size;
87         }
88         init_tree_desc(&desc, tree->buffer, tree->size);
89         complete = 1;
90         while (tree_entry(&desc, &entry)) {
91                 if (!has_sha1_file(entry.sha1) ||
92                     (S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
93                         tree->object.flags |= INCOMPLETE;
94                         complete = 0;
95                 }
96         }
97         free(tree->buffer);
98         tree->buffer = NULL;
100         if (complete)
101                 tree->object.flags |= SEEN;
102         return complete;
105 static int commit_is_complete(struct commit *commit)
107         struct object_array study;
108         struct object_array found;
109         int is_incomplete = 0;
110         int i;
112         /* early return */
113         if (commit->object.flags & SEEN)
114                 return 1;
115         if (commit->object.flags & INCOMPLETE)
116                 return 0;
117         /*
118          * Find all commits that are reachable and are not marked as
119          * SEEN.  Then make sure the trees and blobs contained are
120          * complete.  After that, mark these commits also as SEEN.
121          * If some of the objects that are needed to complete this
122          * commit are missing, mark this commit as INCOMPLETE.
123          */
124         memset(&study, 0, sizeof(study));
125         memset(&found, 0, sizeof(found));
126         add_object_array(&commit->object, NULL, &study);
127         add_object_array(&commit->object, NULL, &found);
128         commit->object.flags |= STUDYING;
129         while (study.nr) {
130                 struct commit *c;
131                 struct commit_list *parent;
133                 c = (struct commit *)study.objects[--study.nr].item;
134                 if (!c->object.parsed && !parse_object(c->object.sha1))
135                         c->object.flags |= INCOMPLETE;
137                 if (c->object.flags & INCOMPLETE) {
138                         is_incomplete = 1;
139                         break;
140                 }
141                 else if (c->object.flags & SEEN)
142                         continue;
143                 for (parent = c->parents; parent; parent = parent->next) {
144                         struct commit *p = parent->item;
145                         if (p->object.flags & STUDYING)
146                                 continue;
147                         p->object.flags |= STUDYING;
148                         add_object_array(&p->object, NULL, &study);
149                         add_object_array(&p->object, NULL, &found);
150                 }
151         }
152         if (!is_incomplete) {
153                 /*
154                  * make sure all commits in "found" array have all the
155                  * necessary objects.
156                  */
157                 for (i = 0; i < found.nr; i++) {
158                         struct commit *c =
159                                 (struct commit *)found.objects[i].item;
160                         if (!tree_is_complete(c->tree->object.sha1)) {
161                                 is_incomplete = 1;
162                                 c->object.flags |= INCOMPLETE;
163                         }
164                 }
165                 if (!is_incomplete) {
166                         /* mark all found commits as complete, iow SEEN */
167                         for (i = 0; i < found.nr; i++)
168                                 found.objects[i].item->flags |= SEEN;
169                 }
170         }
171         /* clear flags from the objects we traversed */
172         for (i = 0; i < found.nr; i++)
173                 found.objects[i].item->flags &= ~STUDYING;
174         if (is_incomplete)
175                 commit->object.flags |= INCOMPLETE;
176         else {
177                 /*
178                  * If we come here, we have (1) traversed the ancestry chain
179                  * from the "commit" until we reach SEEN commits (which are
180                  * known to be complete), and (2) made sure that the commits
181                  * encountered during the above traversal refer to trees that
182                  * are complete.  Which means that we know *all* the commits
183                  * we have seen during this process are complete.
184                  */
185                 for (i = 0; i < found.nr; i++)
186                         found.objects[i].item->flags |= SEEN;
187         }
188         /* free object arrays */
189         free(study.objects);
190         free(found.objects);
191         return !is_incomplete;
194 static int keep_entry(struct commit **it, unsigned char *sha1)
196         struct commit *commit;
198         if (is_null_sha1(sha1))
199                 return 1;
200         commit = lookup_commit_reference_gently(sha1, 1);
201         if (!commit)
202                 return 0;
204         /*
205          * Make sure everything in this commit exists.
206          *
207          * We have walked all the objects reachable from the refs
208          * and cache earlier.  The commits reachable by this commit
209          * must meet SEEN commits -- and then we should mark them as
210          * SEEN as well.
211          */
212         if (!commit_is_complete(commit))
213                 return 0;
214         *it = commit;
215         return 1;
218 /*
219  * Starting from commits in the cb->mark_list, mark commits that are
220  * reachable from them.  Stop the traversal at commits older than
221  * the expire_limit and queue them back, so that the caller can call
222  * us again to restart the traversal with longer expire_limit.
223  */
224 static void mark_reachable(struct expire_reflog_cb *cb)
226         struct commit *commit;
227         struct commit_list *pending;
228         unsigned long expire_limit = cb->mark_limit;
229         struct commit_list *leftover = NULL;
231         for (pending = cb->mark_list; pending; pending = pending->next)
232                 pending->item->object.flags &= ~REACHABLE;
234         pending = cb->mark_list;
235         while (pending) {
236                 struct commit_list *entry = pending;
237                 struct commit_list *parent;
238                 pending = entry->next;
239                 commit = entry->item;
240                 free(entry);
241                 if (commit->object.flags & REACHABLE)
242                         continue;
243                 if (parse_commit(commit))
244                         continue;
245                 commit->object.flags |= REACHABLE;
246                 if (commit->date < expire_limit) {
247                         commit_list_insert(commit, &leftover);
248                         continue;
249                 }
250                 commit->object.flags |= REACHABLE;
251                 parent = commit->parents;
252                 while (parent) {
253                         commit = parent->item;
254                         parent = parent->next;
255                         if (commit->object.flags & REACHABLE)
256                                 continue;
257                         commit_list_insert(commit, &pending);
258                 }
259         }
260         cb->mark_list = leftover;
263 static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
265         /*
266          * We may or may not have the commit yet - if not, look it
267          * up using the supplied sha1.
268          */
269         if (!commit) {
270                 if (is_null_sha1(sha1))
271                         return 0;
273                 commit = lookup_commit_reference_gently(sha1, 1);
275                 /* Not a commit -- keep it */
276                 if (!commit)
277                         return 0;
278         }
280         /* Reachable from the current ref?  Don't prune. */
281         if (commit->object.flags & REACHABLE)
282                 return 0;
284         if (cb->mark_list && cb->mark_limit) {
285                 cb->mark_limit = 0; /* dig down to the root */
286                 mark_reachable(cb);
287         }
289         return !(commit->object.flags & REACHABLE);
292 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
293                 const char *email, unsigned long timestamp, int tz,
294                 const char *message, void *cb_data)
296         struct expire_reflog_cb *cb = cb_data;
297         struct commit *old, *new;
299         if (timestamp < cb->cmd->expire_total)
300                 goto prune;
302         if (cb->cmd->rewrite)
303                 osha1 = cb->last_kept_sha1;
305         old = new = NULL;
306         if (cb->cmd->stalefix &&
307             (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
308                 goto prune;
310         if (timestamp < cb->cmd->expire_unreachable) {
311                 if (cb->unreachable_expire_kind == UE_ALWAYS)
312                         goto prune;
313                 if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
314                         goto prune;
315         }
317         if (cb->cmd->recno && --(cb->cmd->recno) == 0)
318                 goto prune;
320         if (cb->newlog) {
321                 char sign = (tz < 0) ? '-' : '+';
322                 int zone = (tz < 0) ? (-tz) : tz;
323                 fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
324                         sha1_to_hex(osha1), sha1_to_hex(nsha1),
325                         email, timestamp, sign, zone,
326                         message);
327                 hashcpy(cb->last_kept_sha1, nsha1);
328         }
329         if (cb->cmd->verbose)
330                 printf("keep %s", message);
331         return 0;
332  prune:
333         if (!cb->newlog || cb->cmd->verbose)
334                 printf("%sprune %s", cb->newlog ? "" : "would ", message);
335         return 0;
338 static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
340         struct commit_list **list = cb_data;
341         struct commit *tip_commit;
342         if (flags & REF_ISSYMREF)
343                 return 0;
344         tip_commit = lookup_commit_reference_gently(sha1, 1);
345         if (!tip_commit)
346                 return 0;
347         commit_list_insert(tip_commit, list);
348         return 0;
351 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
353         struct cmd_reflog_expire_cb *cmd = cb_data;
354         struct expire_reflog_cb cb;
355         struct ref_lock *lock;
356         char *log_file, *newlog_path = NULL;
357         struct commit *tip_commit;
358         struct commit_list *tips;
359         int status = 0;
361         memset(&cb, 0, sizeof(cb));
363         /*
364          * we take the lock for the ref itself to prevent it from
365          * getting updated.
366          */
367         lock = lock_any_ref_for_update(ref, sha1, 0);
368         if (!lock)
369                 return error("cannot lock ref '%s'", ref);
370         log_file = git_pathdup("logs/%s", ref);
371         if (!file_exists(log_file))
372                 goto finish;
373         if (!cmd->dry_run) {
374                 newlog_path = git_pathdup("logs/%s.lock", ref);
375                 cb.newlog = fopen(newlog_path, "w");
376         }
378         cb.cmd = cmd;
380         if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
381                 tip_commit = NULL;
382                 cb.unreachable_expire_kind = UE_HEAD;
383         } else {
384                 tip_commit = lookup_commit_reference_gently(sha1, 1);
385                 if (!tip_commit)
386                         cb.unreachable_expire_kind = UE_ALWAYS;
387                 else
388                         cb.unreachable_expire_kind = UE_NORMAL;
389         }
391         if (cmd->expire_unreachable <= cmd->expire_total)
392                 cb.unreachable_expire_kind = UE_ALWAYS;
394         cb.mark_list = NULL;
395         tips = NULL;
396         if (cb.unreachable_expire_kind != UE_ALWAYS) {
397                 if (cb.unreachable_expire_kind == UE_HEAD) {
398                         struct commit_list *elem;
399                         for_each_ref(push_tip_to_list, &tips);
400                         for (elem = tips; elem; elem = elem->next)
401                                 commit_list_insert(elem->item, &cb.mark_list);
402                 } else {
403                         commit_list_insert(tip_commit, &cb.mark_list);
404                 }
405                 cb.mark_limit = cmd->expire_total;
406                 mark_reachable(&cb);
407         }
409         for_each_reflog_ent(ref, expire_reflog_ent, &cb);
411         if (cb.unreachable_expire_kind != UE_ALWAYS) {
412                 if (cb.unreachable_expire_kind == UE_HEAD) {
413                         struct commit_list *elem;
414                         for (elem = tips; elem; elem = elem->next)
415                                 clear_commit_marks(tip_commit, REACHABLE);
416                         free_commit_list(tips);
417                 } else {
418                         clear_commit_marks(tip_commit, REACHABLE);
419                 }
420         }
421  finish:
422         if (cb.newlog) {
423                 if (fclose(cb.newlog)) {
424                         status |= error("%s: %s", strerror(errno),
425                                         newlog_path);
426                         unlink(newlog_path);
427                 } else if (cmd->updateref &&
428                         (write_in_full(lock->lock_fd,
429                                 sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
430                          write_str_in_full(lock->lock_fd, "\n") != 1 ||
431                          close_ref(lock) < 0)) {
432                         status |= error("Couldn't write %s",
433                                 lock->lk->filename);
434                         unlink(newlog_path);
435                 } else if (rename(newlog_path, log_file)) {
436                         status |= error("cannot rename %s to %s",
437                                         newlog_path, log_file);
438                         unlink(newlog_path);
439                 } else if (cmd->updateref && commit_ref(lock)) {
440                         status |= error("Couldn't set %s", lock->ref_name);
441                 } else {
442                         adjust_shared_perm(log_file);
443                 }
444         }
445         free(newlog_path);
446         free(log_file);
447         unlock_ref(lock);
448         return status;
451 static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
453         struct collected_reflog *e;
454         struct collect_reflog_cb *cb = cb_data;
455         size_t namelen = strlen(ref);
457         e = xmalloc(sizeof(*e) + namelen + 1);
458         hashcpy(e->sha1, sha1);
459         memcpy(e->reflog, ref, namelen + 1);
460         ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
461         cb->e[cb->nr++] = e;
462         return 0;
465 static struct reflog_expire_cfg {
466         struct reflog_expire_cfg *next;
467         unsigned long expire_total;
468         unsigned long expire_unreachable;
469         size_t len;
470         char pattern[FLEX_ARRAY];
471 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
473 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
475         struct reflog_expire_cfg *ent;
477         if (!reflog_expire_cfg_tail)
478                 reflog_expire_cfg_tail = &reflog_expire_cfg;
480         for (ent = reflog_expire_cfg; ent; ent = ent->next)
481                 if (ent->len == len &&
482                     !memcmp(ent->pattern, pattern, len))
483                         return ent;
485         ent = xcalloc(1, (sizeof(*ent) + len));
486         memcpy(ent->pattern, pattern, len);
487         ent->len = len;
488         *reflog_expire_cfg_tail = ent;
489         reflog_expire_cfg_tail = &(ent->next);
490         return ent;
493 static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
495         if (!value)
496                 return config_error_nonbool(var);
497         if (!strcmp(value, "never") || !strcmp(value, "false")) {
498                 *expire = 0;
499                 return 0;
500         }
501         *expire = approxidate(value);
502         return 0;
505 /* expiry timer slot */
506 #define EXPIRE_TOTAL   01
507 #define EXPIRE_UNREACH 02
509 static int reflog_expire_config(const char *var, const char *value, void *cb)
511         const char *lastdot = strrchr(var, '.');
512         unsigned long expire;
513         int slot;
514         struct reflog_expire_cfg *ent;
516         if (!lastdot || prefixcmp(var, "gc."))
517                 return git_default_config(var, value, cb);
519         if (!strcmp(lastdot, ".reflogexpire")) {
520                 slot = EXPIRE_TOTAL;
521                 if (parse_expire_cfg_value(var, value, &expire))
522                         return -1;
523         } else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
524                 slot = EXPIRE_UNREACH;
525                 if (parse_expire_cfg_value(var, value, &expire))
526                         return -1;
527         } else
528                 return git_default_config(var, value, cb);
530         if (lastdot == var + 2) {
531                 switch (slot) {
532                 case EXPIRE_TOTAL:
533                         default_reflog_expire = expire;
534                         break;
535                 case EXPIRE_UNREACH:
536                         default_reflog_expire_unreachable = expire;
537                         break;
538                 }
539                 return 0;
540         }
542         ent = find_cfg_ent(var + 3, lastdot - (var+3));
543         if (!ent)
544                 return -1;
545         switch (slot) {
546         case EXPIRE_TOTAL:
547                 ent->expire_total = expire;
548                 break;
549         case EXPIRE_UNREACH:
550                 ent->expire_unreachable = expire;
551                 break;
552         }
553         return 0;
556 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
558         struct reflog_expire_cfg *ent;
560         if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
561                 return; /* both given explicitly -- nothing to tweak */
563         for (ent = reflog_expire_cfg; ent; ent = ent->next) {
564                 if (!fnmatch(ent->pattern, ref, 0)) {
565                         if (!(slot & EXPIRE_TOTAL))
566                                 cb->expire_total = ent->expire_total;
567                         if (!(slot & EXPIRE_UNREACH))
568                                 cb->expire_unreachable = ent->expire_unreachable;
569                         return;
570                 }
571         }
573         /*
574          * If unconfigured, make stash never expire
575          */
576         if (!strcmp(ref, "refs/stash")) {
577                 if (!(slot & EXPIRE_TOTAL))
578                         cb->expire_total = 0;
579                 if (!(slot & EXPIRE_UNREACH))
580                         cb->expire_unreachable = 0;
581                 return;
582         }
584         /* Nothing matched -- use the default value */
585         if (!(slot & EXPIRE_TOTAL))
586                 cb->expire_total = default_reflog_expire;
587         if (!(slot & EXPIRE_UNREACH))
588                 cb->expire_unreachable = default_reflog_expire_unreachable;
591 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
593         struct cmd_reflog_expire_cb cb;
594         unsigned long now = time(NULL);
595         int i, status, do_all;
596         int explicit_expiry = 0;
598         default_reflog_expire_unreachable = now - 30 * 24 * 3600;
599         default_reflog_expire = now - 90 * 24 * 3600;
600         git_config(reflog_expire_config, NULL);
602         save_commit_buffer = 0;
603         do_all = status = 0;
604         memset(&cb, 0, sizeof(cb));
606         cb.expire_total = default_reflog_expire;
607         cb.expire_unreachable = default_reflog_expire_unreachable;
609         for (i = 1; i < argc; i++) {
610                 const char *arg = argv[i];
611                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
612                         cb.dry_run = 1;
613                 else if (!prefixcmp(arg, "--expire=")) {
614                         cb.expire_total = approxidate(arg + 9);
615                         explicit_expiry |= EXPIRE_TOTAL;
616                 }
617                 else if (!prefixcmp(arg, "--expire-unreachable=")) {
618                         cb.expire_unreachable = approxidate(arg + 21);
619                         explicit_expiry |= EXPIRE_UNREACH;
620                 }
621                 else if (!strcmp(arg, "--stale-fix"))
622                         cb.stalefix = 1;
623                 else if (!strcmp(arg, "--rewrite"))
624                         cb.rewrite = 1;
625                 else if (!strcmp(arg, "--updateref"))
626                         cb.updateref = 1;
627                 else if (!strcmp(arg, "--all"))
628                         do_all = 1;
629                 else if (!strcmp(arg, "--verbose"))
630                         cb.verbose = 1;
631                 else if (!strcmp(arg, "--")) {
632                         i++;
633                         break;
634                 }
635                 else if (arg[0] == '-')
636                         usage(reflog_expire_usage);
637                 else
638                         break;
639         }
641         /*
642          * We can trust the commits and objects reachable from refs
643          * even in older repository.  We cannot trust what's reachable
644          * from reflog if the repository was pruned with older git.
645          */
646         if (cb.stalefix) {
647                 init_revisions(&cb.revs, prefix);
648                 if (cb.verbose)
649                         printf("Marking reachable objects...");
650                 mark_reachable_objects(&cb.revs, 0, NULL);
651                 if (cb.verbose)
652                         putchar('\n');
653         }
655         if (do_all) {
656                 struct collect_reflog_cb collected;
657                 int i;
659                 memset(&collected, 0, sizeof(collected));
660                 for_each_reflog(collect_reflog, &collected);
661                 for (i = 0; i < collected.nr; i++) {
662                         struct collected_reflog *e = collected.e[i];
663                         set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
664                         status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
665                         free(e);
666                 }
667                 free(collected.e);
668         }
670         for (; i < argc; i++) {
671                 char *ref;
672                 unsigned char sha1[20];
673                 if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
674                         status |= error("%s points nowhere!", argv[i]);
675                         continue;
676                 }
677                 set_reflog_expiry_param(&cb, explicit_expiry, ref);
678                 status |= expire_reflog(ref, sha1, 0, &cb);
679         }
680         return status;
683 static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
684                 const char *email, unsigned long timestamp, int tz,
685                 const char *message, void *cb_data)
687         struct cmd_reflog_expire_cb *cb = cb_data;
688         if (!cb->expire_total || timestamp < cb->expire_total)
689                 cb->recno++;
690         return 0;
693 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
695         struct cmd_reflog_expire_cb cb;
696         int i, status = 0;
698         memset(&cb, 0, sizeof(cb));
700         for (i = 1; i < argc; i++) {
701                 const char *arg = argv[i];
702                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
703                         cb.dry_run = 1;
704                 else if (!strcmp(arg, "--rewrite"))
705                         cb.rewrite = 1;
706                 else if (!strcmp(arg, "--updateref"))
707                         cb.updateref = 1;
708                 else if (!strcmp(arg, "--verbose"))
709                         cb.verbose = 1;
710                 else if (!strcmp(arg, "--")) {
711                         i++;
712                         break;
713                 }
714                 else if (arg[0] == '-')
715                         usage(reflog_delete_usage);
716                 else
717                         break;
718         }
720         if (argc - i < 1)
721                 return error("Nothing to delete?");
723         for ( ; i < argc; i++) {
724                 const char *spec = strstr(argv[i], "@{");
725                 unsigned char sha1[20];
726                 char *ep, *ref;
727                 int recno;
729                 if (!spec) {
730                         status |= error("Not a reflog: %s", argv[i]);
731                         continue;
732                 }
734                 if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
735                         status |= error("no reflog for '%s'", argv[i]);
736                         continue;
737                 }
739                 recno = strtoul(spec + 2, &ep, 10);
740                 if (*ep == '}') {
741                         cb.recno = -recno;
742                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
743                 } else {
744                         cb.expire_total = approxidate(spec + 2);
745                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
746                         cb.expire_total = 0;
747                 }
749                 status |= expire_reflog(ref, sha1, 0, &cb);
750                 free(ref);
751         }
752         return status;
755 /*
756  * main "reflog"
757  */
759 static const char reflog_usage[] =
760 "git reflog [ show | expire | delete ]";
762 int cmd_reflog(int argc, const char **argv, const char *prefix)
764         if (argc > 1 && !strcmp(argv[1], "-h"))
765                 usage(reflog_usage);
767         /* With no command, we default to showing it. */
768         if (argc < 2 || *argv[1] == '-')
769                 return cmd_log_reflog(argc, argv, prefix);
771         if (!strcmp(argv[1], "show"))
772                 return cmd_log_reflog(argc - 1, argv + 1, prefix);
774         if (!strcmp(argv[1], "expire"))
775                 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
777         if (!strcmp(argv[1], "delete"))
778                 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
780         return cmd_log_reflog(argc, argv, prefix);