Merge branch 'js/apply-recount-allow-noop'
[git] / builtin / reflog.c
1 #include "builtin.h"
2 #include "config.h"
3 #include "lockfile.h"
4 #include "object-store.h"
5 #include "repository.h"
6 #include "commit.h"
7 #include "refs.h"
8 #include "dir.h"
9 #include "tree-walk.h"
10 #include "diff.h"
11 #include "revision.h"
12 #include "reachable.h"
13 #include "worktree.h"
14
15 /* NEEDSWORK: switch to using parse_options */
16 static const char reflog_expire_usage[] =
17 "git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all] <refs>...";
18 static const char reflog_delete_usage[] =
19 "git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <refs>...";
20 static const char reflog_exists_usage[] =
21 "git reflog exists <ref>";
22
23 static timestamp_t default_reflog_expire;
24 static timestamp_t default_reflog_expire_unreachable;
25
26 struct cmd_reflog_expire_cb {
27         struct rev_info revs;
28         int stalefix;
29         timestamp_t expire_total;
30         timestamp_t expire_unreachable;
31         int recno;
32 };
33
34 struct expire_reflog_policy_cb {
35         enum {
36                 UE_NORMAL,
37                 UE_ALWAYS,
38                 UE_HEAD
39         } unreachable_expire_kind;
40         struct commit_list *mark_list;
41         unsigned long mark_limit;
42         struct cmd_reflog_expire_cb cmd;
43         struct commit *tip_commit;
44         struct commit_list *tips;
45 };
46
47 struct collected_reflog {
48         struct object_id oid;
49         char reflog[FLEX_ARRAY];
50 };
51
52 struct collect_reflog_cb {
53         struct collected_reflog **e;
54         int alloc;
55         int nr;
56         struct worktree *wt;
57 };
58
59 /* Remember to update object flag allocation in object.h */
60 #define INCOMPLETE      (1u<<10)
61 #define STUDYING        (1u<<11)
62 #define REACHABLE       (1u<<12)
63
64 static int tree_is_complete(const struct object_id *oid)
65 {
66         struct tree_desc desc;
67         struct name_entry entry;
68         int complete;
69         struct tree *tree;
70
71         tree = lookup_tree(the_repository, oid);
72         if (!tree)
73                 return 0;
74         if (tree->object.flags & SEEN)
75                 return 1;
76         if (tree->object.flags & INCOMPLETE)
77                 return 0;
78
79         if (!tree->buffer) {
80                 enum object_type type;
81                 unsigned long size;
82                 void *data = read_object_file(oid, &type, &size);
83                 if (!data) {
84                         tree->object.flags |= INCOMPLETE;
85                         return 0;
86                 }
87                 tree->buffer = data;
88                 tree->size = size;
89         }
90         init_tree_desc(&desc, tree->buffer, tree->size);
91         complete = 1;
92         while (tree_entry(&desc, &entry)) {
93                 if (!has_sha1_file(entry.oid->hash) ||
94                     (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) {
95                         tree->object.flags |= INCOMPLETE;
96                         complete = 0;
97                 }
98         }
99         free_tree_buffer(tree);
100
101         if (complete)
102                 tree->object.flags |= SEEN;
103         return complete;
104 }
105
106 static int commit_is_complete(struct commit *commit)
107 {
108         struct object_array study;
109         struct object_array found;
110         int is_incomplete = 0;
111         int i;
112
113         /* early return */
114         if (commit->object.flags & SEEN)
115                 return 1;
116         if (commit->object.flags & INCOMPLETE)
117                 return 0;
118         /*
119          * Find all commits that are reachable and are not marked as
120          * SEEN.  Then make sure the trees and blobs contained are
121          * complete.  After that, mark these commits also as SEEN.
122          * If some of the objects that are needed to complete this
123          * commit are missing, mark this commit as INCOMPLETE.
124          */
125         memset(&study, 0, sizeof(study));
126         memset(&found, 0, sizeof(found));
127         add_object_array(&commit->object, NULL, &study);
128         add_object_array(&commit->object, NULL, &found);
129         commit->object.flags |= STUDYING;
130         while (study.nr) {
131                 struct commit *c;
132                 struct commit_list *parent;
133
134                 c = (struct commit *)object_array_pop(&study);
135                 if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
136                         c->object.flags |= INCOMPLETE;
137
138                 if (c->object.flags & INCOMPLETE) {
139                         is_incomplete = 1;
140                         break;
141                 }
142                 else if (c->object.flags & SEEN)
143                         continue;
144                 for (parent = c->parents; parent; parent = parent->next) {
145                         struct commit *p = parent->item;
146                         if (p->object.flags & STUDYING)
147                                 continue;
148                         p->object.flags |= STUDYING;
149                         add_object_array(&p->object, NULL, &study);
150                         add_object_array(&p->object, NULL, &found);
151                 }
152         }
153         if (!is_incomplete) {
154                 /*
155                  * make sure all commits in "found" array have all the
156                  * necessary objects.
157                  */
158                 for (i = 0; i < found.nr; i++) {
159                         struct commit *c =
160                                 (struct commit *)found.objects[i].item;
161                         if (!tree_is_complete(get_commit_tree_oid(c))) {
162                                 is_incomplete = 1;
163                                 c->object.flags |= INCOMPLETE;
164                         }
165                 }
166                 if (!is_incomplete) {
167                         /* mark all found commits as complete, iow SEEN */
168                         for (i = 0; i < found.nr; i++)
169                                 found.objects[i].item->flags |= SEEN;
170                 }
171         }
172         /* clear flags from the objects we traversed */
173         for (i = 0; i < found.nr; i++)
174                 found.objects[i].item->flags &= ~STUDYING;
175         if (is_incomplete)
176                 commit->object.flags |= INCOMPLETE;
177         else {
178                 /*
179                  * If we come here, we have (1) traversed the ancestry chain
180                  * from the "commit" until we reach SEEN commits (which are
181                  * known to be complete), and (2) made sure that the commits
182                  * encountered during the above traversal refer to trees that
183                  * are complete.  Which means that we know *all* the commits
184                  * we have seen during this process are complete.
185                  */
186                 for (i = 0; i < found.nr; i++)
187                         found.objects[i].item->flags |= SEEN;
188         }
189         /* free object arrays */
190         object_array_clear(&study);
191         object_array_clear(&found);
192         return !is_incomplete;
193 }
194
195 static int keep_entry(struct commit **it, struct object_id *oid)
196 {
197         struct commit *commit;
198
199         if (is_null_oid(oid))
200                 return 1;
201         commit = lookup_commit_reference_gently(the_repository, oid, 1);
202         if (!commit)
203                 return 0;
204
205         /*
206          * Make sure everything in this commit exists.
207          *
208          * We have walked all the objects reachable from the refs
209          * and cache earlier.  The commits reachable by this commit
210          * must meet SEEN commits -- and then we should mark them as
211          * SEEN as well.
212          */
213         if (!commit_is_complete(commit))
214                 return 0;
215         *it = commit;
216         return 1;
217 }
218
219 /*
220  * Starting from commits in the cb->mark_list, mark commits that are
221  * reachable from them.  Stop the traversal at commits older than
222  * the expire_limit and queue them back, so that the caller can call
223  * us again to restart the traversal with longer expire_limit.
224  */
225 static void mark_reachable(struct expire_reflog_policy_cb *cb)
226 {
227         struct commit_list *pending;
228         timestamp_t expire_limit = cb->mark_limit;
229         struct commit_list *leftover = NULL;
230
231         for (pending = cb->mark_list; pending; pending = pending->next)
232                 pending->item->object.flags &= ~REACHABLE;
233
234         pending = cb->mark_list;
235         while (pending) {
236                 struct commit_list *parent;
237                 struct commit *commit = pop_commit(&pending);
238                 if (commit->object.flags & REACHABLE)
239                         continue;
240                 if (parse_commit(commit))
241                         continue;
242                 commit->object.flags |= REACHABLE;
243                 if (commit->date < expire_limit) {
244                         commit_list_insert(commit, &leftover);
245                         continue;
246                 }
247                 commit->object.flags |= REACHABLE;
248                 parent = commit->parents;
249                 while (parent) {
250                         commit = parent->item;
251                         parent = parent->next;
252                         if (commit->object.flags & REACHABLE)
253                                 continue;
254                         commit_list_insert(commit, &pending);
255                 }
256         }
257         cb->mark_list = leftover;
258 }
259
260 static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
261 {
262         /*
263          * We may or may not have the commit yet - if not, look it
264          * up using the supplied sha1.
265          */
266         if (!commit) {
267                 if (is_null_oid(oid))
268                         return 0;
269
270                 commit = lookup_commit_reference_gently(the_repository, oid,
271                                                         1);
272
273                 /* Not a commit -- keep it */
274                 if (!commit)
275                         return 0;
276         }
277
278         /* Reachable from the current ref?  Don't prune. */
279         if (commit->object.flags & REACHABLE)
280                 return 0;
281
282         if (cb->mark_list && cb->mark_limit) {
283                 cb->mark_limit = 0; /* dig down to the root */
284                 mark_reachable(cb);
285         }
286
287         return !(commit->object.flags & REACHABLE);
288 }
289
290 /*
291  * Return true iff the specified reflog entry should be expired.
292  */
293 static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
294                                     const char *email, timestamp_t timestamp, int tz,
295                                     const char *message, void *cb_data)
296 {
297         struct expire_reflog_policy_cb *cb = cb_data;
298         struct commit *old_commit, *new_commit;
299
300         if (timestamp < cb->cmd.expire_total)
301                 return 1;
302
303         old_commit = new_commit = NULL;
304         if (cb->cmd.stalefix &&
305             (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
306                 return 1;
307
308         if (timestamp < cb->cmd.expire_unreachable) {
309                 if (cb->unreachable_expire_kind == UE_ALWAYS)
310                         return 1;
311                 if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
312                         return 1;
313         }
314
315         if (cb->cmd.recno && --(cb->cmd.recno) == 0)
316                 return 1;
317
318         return 0;
319 }
320
321 static int push_tip_to_list(const char *refname, const struct object_id *oid,
322                             int flags, void *cb_data)
323 {
324         struct commit_list **list = cb_data;
325         struct commit *tip_commit;
326         if (flags & REF_ISSYMREF)
327                 return 0;
328         tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
329         if (!tip_commit)
330                 return 0;
331         commit_list_insert(tip_commit, list);
332         return 0;
333 }
334
335 static int is_head(const char *refname)
336 {
337         switch (ref_type(refname)) {
338         case REF_TYPE_OTHER_PSEUDOREF:
339         case REF_TYPE_MAIN_PSEUDOREF:
340                 if (parse_worktree_ref(refname, NULL, NULL, &refname))
341                         BUG("not a worktree ref: %s", refname);
342                 break;
343         default:
344                 break;
345         }
346         return !strcmp(refname, "HEAD");
347 }
348
349 static void reflog_expiry_prepare(const char *refname,
350                                   const struct object_id *oid,
351                                   void *cb_data)
352 {
353         struct expire_reflog_policy_cb *cb = cb_data;
354
355         if (!cb->cmd.expire_unreachable || is_head(refname)) {
356                 cb->tip_commit = NULL;
357                 cb->unreachable_expire_kind = UE_HEAD;
358         } else {
359                 cb->tip_commit = lookup_commit_reference_gently(the_repository,
360                                                                 oid, 1);
361                 if (!cb->tip_commit)
362                         cb->unreachable_expire_kind = UE_ALWAYS;
363                 else
364                         cb->unreachable_expire_kind = UE_NORMAL;
365         }
366
367         if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
368                 cb->unreachable_expire_kind = UE_ALWAYS;
369
370         cb->mark_list = NULL;
371         cb->tips = NULL;
372         if (cb->unreachable_expire_kind != UE_ALWAYS) {
373                 if (cb->unreachable_expire_kind == UE_HEAD) {
374                         struct commit_list *elem;
375
376                         for_each_ref(push_tip_to_list, &cb->tips);
377                         for (elem = cb->tips; elem; elem = elem->next)
378                                 commit_list_insert(elem->item, &cb->mark_list);
379                 } else {
380                         commit_list_insert(cb->tip_commit, &cb->mark_list);
381                 }
382                 cb->mark_limit = cb->cmd.expire_total;
383                 mark_reachable(cb);
384         }
385 }
386
387 static void reflog_expiry_cleanup(void *cb_data)
388 {
389         struct expire_reflog_policy_cb *cb = cb_data;
390
391         if (cb->unreachable_expire_kind != UE_ALWAYS) {
392                 if (cb->unreachable_expire_kind == UE_HEAD) {
393                         struct commit_list *elem;
394                         for (elem = cb->tips; elem; elem = elem->next)
395                                 clear_commit_marks(elem->item, REACHABLE);
396                         free_commit_list(cb->tips);
397                 } else {
398                         clear_commit_marks(cb->tip_commit, REACHABLE);
399                 }
400         }
401 }
402
403 static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
404 {
405         struct collected_reflog *e;
406         struct collect_reflog_cb *cb = cb_data;
407         struct strbuf newref = STRBUF_INIT;
408
409         /*
410          * Avoid collecting the same shared ref multiple times because
411          * they are available via all worktrees.
412          */
413         if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
414                 return 0;
415
416         strbuf_worktree_ref(cb->wt, &newref, ref);
417         FLEX_ALLOC_STR(e, reflog, newref.buf);
418         strbuf_release(&newref);
419
420         oidcpy(&e->oid, oid);
421         ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
422         cb->e[cb->nr++] = e;
423         return 0;
424 }
425
426 static struct reflog_expire_cfg {
427         struct reflog_expire_cfg *next;
428         timestamp_t expire_total;
429         timestamp_t expire_unreachable;
430         char pattern[FLEX_ARRAY];
431 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
432
433 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
434 {
435         struct reflog_expire_cfg *ent;
436
437         if (!reflog_expire_cfg_tail)
438                 reflog_expire_cfg_tail = &reflog_expire_cfg;
439
440         for (ent = reflog_expire_cfg; ent; ent = ent->next)
441                 if (!strncmp(ent->pattern, pattern, len) &&
442                     ent->pattern[len] == '\0')
443                         return ent;
444
445         FLEX_ALLOC_MEM(ent, pattern, pattern, len);
446         *reflog_expire_cfg_tail = ent;
447         reflog_expire_cfg_tail = &(ent->next);
448         return ent;
449 }
450
451 /* expiry timer slot */
452 #define EXPIRE_TOTAL   01
453 #define EXPIRE_UNREACH 02
454
455 static int reflog_expire_config(const char *var, const char *value, void *cb)
456 {
457         const char *pattern, *key;
458         int pattern_len;
459         timestamp_t expire;
460         int slot;
461         struct reflog_expire_cfg *ent;
462
463         if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
464                 return git_default_config(var, value, cb);
465
466         if (!strcmp(key, "reflogexpire")) {
467                 slot = EXPIRE_TOTAL;
468                 if (git_config_expiry_date(&expire, var, value))
469                         return -1;
470         } else if (!strcmp(key, "reflogexpireunreachable")) {
471                 slot = EXPIRE_UNREACH;
472                 if (git_config_expiry_date(&expire, var, value))
473                         return -1;
474         } else
475                 return git_default_config(var, value, cb);
476
477         if (!pattern) {
478                 switch (slot) {
479                 case EXPIRE_TOTAL:
480                         default_reflog_expire = expire;
481                         break;
482                 case EXPIRE_UNREACH:
483                         default_reflog_expire_unreachable = expire;
484                         break;
485                 }
486                 return 0;
487         }
488
489         ent = find_cfg_ent(pattern, pattern_len);
490         if (!ent)
491                 return -1;
492         switch (slot) {
493         case EXPIRE_TOTAL:
494                 ent->expire_total = expire;
495                 break;
496         case EXPIRE_UNREACH:
497                 ent->expire_unreachable = expire;
498                 break;
499         }
500         return 0;
501 }
502
503 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
504 {
505         struct reflog_expire_cfg *ent;
506
507         if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
508                 return; /* both given explicitly -- nothing to tweak */
509
510         for (ent = reflog_expire_cfg; ent; ent = ent->next) {
511                 if (!wildmatch(ent->pattern, ref, 0)) {
512                         if (!(slot & EXPIRE_TOTAL))
513                                 cb->expire_total = ent->expire_total;
514                         if (!(slot & EXPIRE_UNREACH))
515                                 cb->expire_unreachable = ent->expire_unreachable;
516                         return;
517                 }
518         }
519
520         /*
521          * If unconfigured, make stash never expire
522          */
523         if (!strcmp(ref, "refs/stash")) {
524                 if (!(slot & EXPIRE_TOTAL))
525                         cb->expire_total = 0;
526                 if (!(slot & EXPIRE_UNREACH))
527                         cb->expire_unreachable = 0;
528                 return;
529         }
530
531         /* Nothing matched -- use the default value */
532         if (!(slot & EXPIRE_TOTAL))
533                 cb->expire_total = default_reflog_expire;
534         if (!(slot & EXPIRE_UNREACH))
535                 cb->expire_unreachable = default_reflog_expire_unreachable;
536 }
537
538 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
539 {
540         struct expire_reflog_policy_cb cb;
541         timestamp_t now = time(NULL);
542         int i, status, do_all, all_worktrees = 1;
543         int explicit_expiry = 0;
544         unsigned int flags = 0;
545
546         default_reflog_expire_unreachable = now - 30 * 24 * 3600;
547         default_reflog_expire = now - 90 * 24 * 3600;
548         git_config(reflog_expire_config, NULL);
549
550         save_commit_buffer = 0;
551         do_all = status = 0;
552         memset(&cb, 0, sizeof(cb));
553
554         cb.cmd.expire_total = default_reflog_expire;
555         cb.cmd.expire_unreachable = default_reflog_expire_unreachable;
556
557         for (i = 1; i < argc; i++) {
558                 const char *arg = argv[i];
559                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
560                         flags |= EXPIRE_REFLOGS_DRY_RUN;
561                 else if (starts_with(arg, "--expire=")) {
562                         if (parse_expiry_date(arg + 9, &cb.cmd.expire_total))
563                                 die(_("'%s' is not a valid timestamp"), arg);
564                         explicit_expiry |= EXPIRE_TOTAL;
565                 }
566                 else if (starts_with(arg, "--expire-unreachable=")) {
567                         if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable))
568                                 die(_("'%s' is not a valid timestamp"), arg);
569                         explicit_expiry |= EXPIRE_UNREACH;
570                 }
571                 else if (!strcmp(arg, "--stale-fix"))
572                         cb.cmd.stalefix = 1;
573                 else if (!strcmp(arg, "--rewrite"))
574                         flags |= EXPIRE_REFLOGS_REWRITE;
575                 else if (!strcmp(arg, "--updateref"))
576                         flags |= EXPIRE_REFLOGS_UPDATE_REF;
577                 else if (!strcmp(arg, "--all"))
578                         do_all = 1;
579                 else if (!strcmp(arg, "--single-worktree"))
580                         all_worktrees = 0;
581                 else if (!strcmp(arg, "--verbose"))
582                         flags |= EXPIRE_REFLOGS_VERBOSE;
583                 else if (!strcmp(arg, "--")) {
584                         i++;
585                         break;
586                 }
587                 else if (arg[0] == '-')
588                         usage(reflog_expire_usage);
589                 else
590                         break;
591         }
592
593         /*
594          * We can trust the commits and objects reachable from refs
595          * even in older repository.  We cannot trust what's reachable
596          * from reflog if the repository was pruned with older git.
597          */
598         if (cb.cmd.stalefix) {
599                 repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
600                 if (flags & EXPIRE_REFLOGS_VERBOSE)
601                         printf("Marking reachable objects...");
602                 mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
603                 if (flags & EXPIRE_REFLOGS_VERBOSE)
604                         putchar('\n');
605         }
606
607         if (do_all) {
608                 struct collect_reflog_cb collected;
609                 struct worktree **worktrees, **p;
610                 int i;
611
612                 memset(&collected, 0, sizeof(collected));
613                 worktrees = get_worktrees(0);
614                 for (p = worktrees; *p; p++) {
615                         if (!all_worktrees && !(*p)->is_current)
616                                 continue;
617                         collected.wt = *p;
618                         refs_for_each_reflog(get_worktree_ref_store(*p),
619                                              collect_reflog, &collected);
620                 }
621                 free_worktrees(worktrees);
622                 for (i = 0; i < collected.nr; i++) {
623                         struct collected_reflog *e = collected.e[i];
624                         set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
625                         status |= reflog_expire(e->reflog, &e->oid, flags,
626                                                 reflog_expiry_prepare,
627                                                 should_expire_reflog_ent,
628                                                 reflog_expiry_cleanup,
629                                                 &cb);
630                         free(e);
631                 }
632                 free(collected.e);
633         }
634
635         for (; i < argc; i++) {
636                 char *ref;
637                 struct object_id oid;
638                 if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
639                         status |= error("%s points nowhere!", argv[i]);
640                         continue;
641                 }
642                 set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
643                 status |= reflog_expire(ref, &oid, flags,
644                                         reflog_expiry_prepare,
645                                         should_expire_reflog_ent,
646                                         reflog_expiry_cleanup,
647                                         &cb);
648         }
649         return status;
650 }
651
652 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
653                 const char *email, timestamp_t timestamp, int tz,
654                 const char *message, void *cb_data)
655 {
656         struct expire_reflog_policy_cb *cb = cb_data;
657         if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total)
658                 cb->cmd.recno++;
659         return 0;
660 }
661
662 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
663 {
664         struct expire_reflog_policy_cb cb;
665         int i, status = 0;
666         unsigned int flags = 0;
667
668         memset(&cb, 0, sizeof(cb));
669
670         for (i = 1; i < argc; i++) {
671                 const char *arg = argv[i];
672                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
673                         flags |= EXPIRE_REFLOGS_DRY_RUN;
674                 else if (!strcmp(arg, "--rewrite"))
675                         flags |= EXPIRE_REFLOGS_REWRITE;
676                 else if (!strcmp(arg, "--updateref"))
677                         flags |= EXPIRE_REFLOGS_UPDATE_REF;
678                 else if (!strcmp(arg, "--verbose"))
679                         flags |= EXPIRE_REFLOGS_VERBOSE;
680                 else if (!strcmp(arg, "--")) {
681                         i++;
682                         break;
683                 }
684                 else if (arg[0] == '-')
685                         usage(reflog_delete_usage);
686                 else
687                         break;
688         }
689
690         if (argc - i < 1)
691                 return error("Nothing to delete?");
692
693         for ( ; i < argc; i++) {
694                 const char *spec = strstr(argv[i], "@{");
695                 struct object_id oid;
696                 char *ep, *ref;
697                 int recno;
698
699                 if (!spec) {
700                         status |= error("Not a reflog: %s", argv[i]);
701                         continue;
702                 }
703
704                 if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
705                         status |= error("no reflog for '%s'", argv[i]);
706                         continue;
707                 }
708
709                 recno = strtoul(spec + 2, &ep, 10);
710                 if (*ep == '}') {
711                         cb.cmd.recno = -recno;
712                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
713                 } else {
714                         cb.cmd.expire_total = approxidate(spec + 2);
715                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
716                         cb.cmd.expire_total = 0;
717                 }
718
719                 status |= reflog_expire(ref, &oid, flags,
720                                         reflog_expiry_prepare,
721                                         should_expire_reflog_ent,
722                                         reflog_expiry_cleanup,
723                                         &cb);
724                 free(ref);
725         }
726         return status;
727 }
728
729 static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
730 {
731         int i, start = 0;
732
733         for (i = 1; i < argc; i++) {
734                 const char *arg = argv[i];
735                 if (!strcmp(arg, "--")) {
736                         i++;
737                         break;
738                 }
739                 else if (arg[0] == '-')
740                         usage(reflog_exists_usage);
741                 else
742                         break;
743         }
744
745         start = i;
746
747         if (argc - start != 1)
748                 usage(reflog_exists_usage);
749
750         if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL))
751                 die("invalid ref format: %s", argv[start]);
752         return !reflog_exists(argv[start]);
753 }
754
755 /*
756  * main "reflog"
757  */
758
759 static const char reflog_usage[] =
760 "git reflog [ show | expire | delete | exists ]";
761
762 int cmd_reflog(int argc, const char **argv, const char *prefix)
763 {
764         if (argc > 1 && !strcmp(argv[1], "-h"))
765                 usage(reflog_usage);
766
767         /* With no command, we default to showing it. */
768         if (argc < 2 || *argv[1] == '-')
769                 return cmd_log_reflog(argc, argv, prefix);
770
771         if (!strcmp(argv[1], "show"))
772                 return cmd_log_reflog(argc - 1, argv + 1, prefix);
773
774         if (!strcmp(argv[1], "expire"))
775                 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
776
777         if (!strcmp(argv[1], "delete"))
778                 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
779
780         if (!strcmp(argv[1], "exists"))
781                 return cmd_reflog_exists(argc - 1, argv + 1, prefix);
782
783         return cmd_log_reflog(argc, argv, prefix);
784 }