Merge branch 'nd/fetch-into-shallow'
[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"
10
11 /*
12  * reflog expire
13  */
14
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>...";
19
20 static unsigned long default_reflog_expire;
21 static unsigned long default_reflog_expire_unreachable;
22
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 };
34
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 };
47
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 };
57
58 #define INCOMPLETE      (1u<<10)
59 #define STUDYING        (1u<<11)
60 #define REACHABLE       (1u<<12)
61
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;
68
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;
76
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(tree);
98
99         if (complete)
100                 tree->object.flags |= SEEN;
101         return complete;
102 }
103
104 static int commit_is_complete(struct commit *commit)
105 {
106         struct object_array study;
107         struct object_array found;
108         int is_incomplete = 0;
109         int i;
110
111         /* early return */
112         if (commit->object.flags & SEEN)
113                 return 1;
114         if (commit->object.flags & INCOMPLETE)
115                 return 0;
116         /*
117          * Find all commits that are reachable and are not marked as
118          * SEEN.  Then make sure the trees and blobs contained are
119          * complete.  After that, mark these commits also as SEEN.
120          * If some of the objects that are needed to complete this
121          * commit are missing, mark this commit as INCOMPLETE.
122          */
123         memset(&study, 0, sizeof(study));
124         memset(&found, 0, sizeof(found));
125         add_object_array(&commit->object, NULL, &study);
126         add_object_array(&commit->object, NULL, &found);
127         commit->object.flags |= STUDYING;
128         while (study.nr) {
129                 struct commit *c;
130                 struct commit_list *parent;
131
132                 c = (struct commit *)study.objects[--study.nr].item;
133                 if (!c->object.parsed && !parse_object(c->object.sha1))
134                         c->object.flags |= INCOMPLETE;
135
136                 if (c->object.flags & INCOMPLETE) {
137                         is_incomplete = 1;
138                         break;
139                 }
140                 else if (c->object.flags & SEEN)
141                         continue;
142                 for (parent = c->parents; parent; parent = parent->next) {
143                         struct commit *p = parent->item;
144                         if (p->object.flags & STUDYING)
145                                 continue;
146                         p->object.flags |= STUDYING;
147                         add_object_array(&p->object, NULL, &study);
148                         add_object_array(&p->object, NULL, &found);
149                 }
150         }
151         if (!is_incomplete) {
152                 /*
153                  * make sure all commits in "found" array have all the
154                  * necessary objects.
155                  */
156                 for (i = 0; i < found.nr; i++) {
157                         struct commit *c =
158                                 (struct commit *)found.objects[i].item;
159                         if (!tree_is_complete(c->tree->object.sha1)) {
160                                 is_incomplete = 1;
161                                 c->object.flags |= INCOMPLETE;
162                         }
163                 }
164                 if (!is_incomplete) {
165                         /* mark all found commits as complete, iow SEEN */
166                         for (i = 0; i < found.nr; i++)
167                                 found.objects[i].item->flags |= SEEN;
168                 }
169         }
170         /* clear flags from the objects we traversed */
171         for (i = 0; i < found.nr; i++)
172                 found.objects[i].item->flags &= ~STUDYING;
173         if (is_incomplete)
174                 commit->object.flags |= INCOMPLETE;
175         else {
176                 /*
177                  * If we come here, we have (1) traversed the ancestry chain
178                  * from the "commit" until we reach SEEN commits (which are
179                  * known to be complete), and (2) made sure that the commits
180                  * encountered during the above traversal refer to trees that
181                  * are complete.  Which means that we know *all* the commits
182                  * we have seen during this process are complete.
183                  */
184                 for (i = 0; i < found.nr; i++)
185                         found.objects[i].item->flags |= SEEN;
186         }
187         /* free object arrays */
188         free(study.objects);
189         free(found.objects);
190         return !is_incomplete;
191 }
192
193 static int keep_entry(struct commit **it, unsigned char *sha1)
194 {
195         struct commit *commit;
196
197         if (is_null_sha1(sha1))
198                 return 1;
199         commit = lookup_commit_reference_gently(sha1, 1);
200         if (!commit)
201                 return 0;
202
203         /*
204          * Make sure everything in this commit exists.
205          *
206          * We have walked all the objects reachable from the refs
207          * and cache earlier.  The commits reachable by this commit
208          * must meet SEEN commits -- and then we should mark them as
209          * SEEN as well.
210          */
211         if (!commit_is_complete(commit))
212                 return 0;
213         *it = commit;
214         return 1;
215 }
216
217 /*
218  * Starting from commits in the cb->mark_list, mark commits that are
219  * reachable from them.  Stop the traversal at commits older than
220  * the expire_limit and queue them back, so that the caller can call
221  * us again to restart the traversal with longer expire_limit.
222  */
223 static void mark_reachable(struct expire_reflog_cb *cb)
224 {
225         struct commit *commit;
226         struct commit_list *pending;
227         unsigned long expire_limit = cb->mark_limit;
228         struct commit_list *leftover = NULL;
229
230         for (pending = cb->mark_list; pending; pending = pending->next)
231                 pending->item->object.flags &= ~REACHABLE;
232
233         pending = cb->mark_list;
234         while (pending) {
235                 struct commit_list *entry = pending;
236                 struct commit_list *parent;
237                 pending = entry->next;
238                 commit = entry->item;
239                 free(entry);
240                 if (commit->object.flags & REACHABLE)
241                         continue;
242                 if (parse_commit(commit))
243                         continue;
244                 commit->object.flags |= REACHABLE;
245                 if (commit->date < expire_limit) {
246                         commit_list_insert(commit, &leftover);
247                         continue;
248                 }
249                 commit->object.flags |= REACHABLE;
250                 parent = commit->parents;
251                 while (parent) {
252                         commit = parent->item;
253                         parent = parent->next;
254                         if (commit->object.flags & REACHABLE)
255                                 continue;
256                         commit_list_insert(commit, &pending);
257                 }
258         }
259         cb->mark_list = leftover;
260 }
261
262 static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
263 {
264         /*
265          * We may or may not have the commit yet - if not, look it
266          * up using the supplied sha1.
267          */
268         if (!commit) {
269                 if (is_null_sha1(sha1))
270                         return 0;
271
272                 commit = lookup_commit_reference_gently(sha1, 1);
273
274                 /* Not a commit -- keep it */
275                 if (!commit)
276                         return 0;
277         }
278
279         /* Reachable from the current ref?  Don't prune. */
280         if (commit->object.flags & REACHABLE)
281                 return 0;
282
283         if (cb->mark_list && cb->mark_limit) {
284                 cb->mark_limit = 0; /* dig down to the root */
285                 mark_reachable(cb);
286         }
287
288         return !(commit->object.flags & REACHABLE);
289 }
290
291 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
292                 const char *email, unsigned long timestamp, int tz,
293                 const char *message, void *cb_data)
294 {
295         struct expire_reflog_cb *cb = cb_data;
296         struct commit *old, *new;
297
298         if (timestamp < cb->cmd->expire_total)
299                 goto prune;
300
301         if (cb->cmd->rewrite)
302                 osha1 = cb->last_kept_sha1;
303
304         old = new = NULL;
305         if (cb->cmd->stalefix &&
306             (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
307                 goto prune;
308
309         if (timestamp < cb->cmd->expire_unreachable) {
310                 if (cb->unreachable_expire_kind == UE_ALWAYS)
311                         goto prune;
312                 if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
313                         goto prune;
314         }
315
316         if (cb->cmd->recno && --(cb->cmd->recno) == 0)
317                 goto prune;
318
319         if (cb->newlog) {
320                 char sign = (tz < 0) ? '-' : '+';
321                 int zone = (tz < 0) ? (-tz) : tz;
322                 fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
323                         sha1_to_hex(osha1), sha1_to_hex(nsha1),
324                         email, timestamp, sign, zone,
325                         message);
326                 hashcpy(cb->last_kept_sha1, nsha1);
327         }
328         if (cb->cmd->verbose)
329                 printf("keep %s", message);
330         return 0;
331  prune:
332         if (!cb->newlog)
333                 printf("would prune %s", message);
334         else if (cb->cmd->verbose)
335                 printf("prune %s", message);
336         return 0;
337 }
338
339 static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
340 {
341         struct commit_list **list = cb_data;
342         struct commit *tip_commit;
343         if (flags & REF_ISSYMREF)
344                 return 0;
345         tip_commit = lookup_commit_reference_gently(sha1, 1);
346         if (!tip_commit)
347                 return 0;
348         commit_list_insert(tip_commit, list);
349         return 0;
350 }
351
352 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
353 {
354         struct cmd_reflog_expire_cb *cmd = cb_data;
355         struct expire_reflog_cb cb;
356         struct ref_lock *lock;
357         char *log_file, *newlog_path = NULL;
358         struct commit *tip_commit;
359         struct commit_list *tips;
360         int status = 0;
361
362         memset(&cb, 0, sizeof(cb));
363
364         /*
365          * we take the lock for the ref itself to prevent it from
366          * getting updated.
367          */
368         lock = lock_any_ref_for_update(ref, sha1, 0);
369         if (!lock)
370                 return error("cannot lock ref '%s'", ref);
371         log_file = git_pathdup("logs/%s", ref);
372         if (!file_exists(log_file))
373                 goto finish;
374         if (!cmd->dry_run) {
375                 newlog_path = git_pathdup("logs/%s.lock", ref);
376                 cb.newlog = fopen(newlog_path, "w");
377         }
378
379         cb.cmd = cmd;
380
381         if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
382                 tip_commit = NULL;
383                 cb.unreachable_expire_kind = UE_HEAD;
384         } else {
385                 tip_commit = lookup_commit_reference_gently(sha1, 1);
386                 if (!tip_commit)
387                         cb.unreachable_expire_kind = UE_ALWAYS;
388                 else
389                         cb.unreachable_expire_kind = UE_NORMAL;
390         }
391
392         if (cmd->expire_unreachable <= cmd->expire_total)
393                 cb.unreachable_expire_kind = UE_ALWAYS;
394
395         cb.mark_list = NULL;
396         tips = NULL;
397         if (cb.unreachable_expire_kind != UE_ALWAYS) {
398                 if (cb.unreachable_expire_kind == UE_HEAD) {
399                         struct commit_list *elem;
400                         for_each_ref(push_tip_to_list, &tips);
401                         for (elem = tips; elem; elem = elem->next)
402                                 commit_list_insert(elem->item, &cb.mark_list);
403                 } else {
404                         commit_list_insert(tip_commit, &cb.mark_list);
405                 }
406                 cb.mark_limit = cmd->expire_total;
407                 mark_reachable(&cb);
408         }
409
410         for_each_reflog_ent(ref, expire_reflog_ent, &cb);
411
412         if (cb.unreachable_expire_kind != UE_ALWAYS) {
413                 if (cb.unreachable_expire_kind == UE_HEAD) {
414                         struct commit_list *elem;
415                         for (elem = tips; elem; elem = elem->next)
416                                 clear_commit_marks(elem->item, REACHABLE);
417                         free_commit_list(tips);
418                 } else {
419                         clear_commit_marks(tip_commit, REACHABLE);
420                 }
421         }
422  finish:
423         if (cb.newlog) {
424                 if (fclose(cb.newlog)) {
425                         status |= error("%s: %s", strerror(errno),
426                                         newlog_path);
427                         unlink(newlog_path);
428                 } else if (cmd->updateref &&
429                         (write_in_full(lock->lock_fd,
430                                 sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
431                          write_str_in_full(lock->lock_fd, "\n") != 1 ||
432                          close_ref(lock) < 0)) {
433                         status |= error("Couldn't write %s",
434                                 lock->lk->filename);
435                         unlink(newlog_path);
436                 } else if (rename(newlog_path, log_file)) {
437                         status |= error("cannot rename %s to %s",
438                                         newlog_path, log_file);
439                         unlink(newlog_path);
440                 } else if (cmd->updateref && commit_ref(lock)) {
441                         status |= error("Couldn't set %s", lock->ref_name);
442                 } else {
443                         adjust_shared_perm(log_file);
444                 }
445         }
446         free(newlog_path);
447         free(log_file);
448         unlock_ref(lock);
449         return status;
450 }
451
452 static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
453 {
454         struct collected_reflog *e;
455         struct collect_reflog_cb *cb = cb_data;
456         size_t namelen = strlen(ref);
457
458         e = xmalloc(sizeof(*e) + namelen + 1);
459         hashcpy(e->sha1, sha1);
460         memcpy(e->reflog, ref, namelen + 1);
461         ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
462         cb->e[cb->nr++] = e;
463         return 0;
464 }
465
466 static struct reflog_expire_cfg {
467         struct reflog_expire_cfg *next;
468         unsigned long expire_total;
469         unsigned long expire_unreachable;
470         size_t len;
471         char pattern[FLEX_ARRAY];
472 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
473
474 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
475 {
476         struct reflog_expire_cfg *ent;
477
478         if (!reflog_expire_cfg_tail)
479                 reflog_expire_cfg_tail = &reflog_expire_cfg;
480
481         for (ent = reflog_expire_cfg; ent; ent = ent->next)
482                 if (ent->len == len &&
483                     !memcmp(ent->pattern, pattern, len))
484                         return ent;
485
486         ent = xcalloc(1, (sizeof(*ent) + len));
487         memcpy(ent->pattern, pattern, len);
488         ent->len = len;
489         *reflog_expire_cfg_tail = ent;
490         reflog_expire_cfg_tail = &(ent->next);
491         return ent;
492 }
493
494 static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
495 {
496         if (!value)
497                 return config_error_nonbool(var);
498         if (parse_expiry_date(value, expire))
499                 return error(_("%s' for '%s' is not a valid timestamp"),
500                              value, var);
501         return 0;
502 }
503
504 /* expiry timer slot */
505 #define EXPIRE_TOTAL   01
506 #define EXPIRE_UNREACH 02
507
508 static int reflog_expire_config(const char *var, const char *value, void *cb)
509 {
510         const char *pattern, *key;
511         int pattern_len;
512         unsigned long expire;
513         int slot;
514         struct reflog_expire_cfg *ent;
515
516         if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
517                 return git_default_config(var, value, cb);
518
519         if (!strcmp(key, "reflogexpire")) {
520                 slot = EXPIRE_TOTAL;
521                 if (parse_expire_cfg_value(var, value, &expire))
522                         return -1;
523         } else if (!strcmp(key, "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);
529
530         if (!pattern) {
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         }
541
542         ent = find_cfg_ent(pattern, pattern_len);
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;
554 }
555
556 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
557 {
558         struct reflog_expire_cfg *ent;
559
560         if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
561                 return; /* both given explicitly -- nothing to tweak */
562
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         }
572
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         }
583
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;
589 }
590
591 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
592 {
593         struct cmd_reflog_expire_cb cb;
594         unsigned long now = time(NULL);
595         int i, status, do_all;
596         int explicit_expiry = 0;
597
598         default_reflog_expire_unreachable = now - 30 * 24 * 3600;
599         default_reflog_expire = now - 90 * 24 * 3600;
600         git_config(reflog_expire_config, NULL);
601
602         save_commit_buffer = 0;
603         do_all = status = 0;
604         memset(&cb, 0, sizeof(cb));
605
606         cb.expire_total = default_reflog_expire;
607         cb.expire_unreachable = default_reflog_expire_unreachable;
608
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                         if (parse_expiry_date(arg + 9, &cb.expire_total))
615                                 die(_("'%s' is not a valid timestamp"), arg);
616                         explicit_expiry |= EXPIRE_TOTAL;
617                 }
618                 else if (!prefixcmp(arg, "--expire-unreachable=")) {
619                         if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
620                                 die(_("'%s' is not a valid timestamp"), arg);
621                         explicit_expiry |= EXPIRE_UNREACH;
622                 }
623                 else if (!strcmp(arg, "--stale-fix"))
624                         cb.stalefix = 1;
625                 else if (!strcmp(arg, "--rewrite"))
626                         cb.rewrite = 1;
627                 else if (!strcmp(arg, "--updateref"))
628                         cb.updateref = 1;
629                 else if (!strcmp(arg, "--all"))
630                         do_all = 1;
631                 else if (!strcmp(arg, "--verbose"))
632                         cb.verbose = 1;
633                 else if (!strcmp(arg, "--")) {
634                         i++;
635                         break;
636                 }
637                 else if (arg[0] == '-')
638                         usage(reflog_expire_usage);
639                 else
640                         break;
641         }
642
643         /*
644          * We can trust the commits and objects reachable from refs
645          * even in older repository.  We cannot trust what's reachable
646          * from reflog if the repository was pruned with older git.
647          */
648         if (cb.stalefix) {
649                 init_revisions(&cb.revs, prefix);
650                 if (cb.verbose)
651                         printf("Marking reachable objects...");
652                 mark_reachable_objects(&cb.revs, 0, NULL);
653                 if (cb.verbose)
654                         putchar('\n');
655         }
656
657         if (do_all) {
658                 struct collect_reflog_cb collected;
659                 int i;
660
661                 memset(&collected, 0, sizeof(collected));
662                 for_each_reflog(collect_reflog, &collected);
663                 for (i = 0; i < collected.nr; i++) {
664                         struct collected_reflog *e = collected.e[i];
665                         set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
666                         status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
667                         free(e);
668                 }
669                 free(collected.e);
670         }
671
672         for (; i < argc; i++) {
673                 char *ref;
674                 unsigned char sha1[20];
675                 if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
676                         status |= error("%s points nowhere!", argv[i]);
677                         continue;
678                 }
679                 set_reflog_expiry_param(&cb, explicit_expiry, ref);
680                 status |= expire_reflog(ref, sha1, 0, &cb);
681         }
682         return status;
683 }
684
685 static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
686                 const char *email, unsigned long timestamp, int tz,
687                 const char *message, void *cb_data)
688 {
689         struct cmd_reflog_expire_cb *cb = cb_data;
690         if (!cb->expire_total || timestamp < cb->expire_total)
691                 cb->recno++;
692         return 0;
693 }
694
695 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
696 {
697         struct cmd_reflog_expire_cb cb;
698         int i, status = 0;
699
700         memset(&cb, 0, sizeof(cb));
701
702         for (i = 1; i < argc; i++) {
703                 const char *arg = argv[i];
704                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
705                         cb.dry_run = 1;
706                 else if (!strcmp(arg, "--rewrite"))
707                         cb.rewrite = 1;
708                 else if (!strcmp(arg, "--updateref"))
709                         cb.updateref = 1;
710                 else if (!strcmp(arg, "--verbose"))
711                         cb.verbose = 1;
712                 else if (!strcmp(arg, "--")) {
713                         i++;
714                         break;
715                 }
716                 else if (arg[0] == '-')
717                         usage(reflog_delete_usage);
718                 else
719                         break;
720         }
721
722         if (argc - i < 1)
723                 return error("Nothing to delete?");
724
725         for ( ; i < argc; i++) {
726                 const char *spec = strstr(argv[i], "@{");
727                 unsigned char sha1[20];
728                 char *ep, *ref;
729                 int recno;
730
731                 if (!spec) {
732                         status |= error("Not a reflog: %s", argv[i]);
733                         continue;
734                 }
735
736                 if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
737                         status |= error("no reflog for '%s'", argv[i]);
738                         continue;
739                 }
740
741                 recno = strtoul(spec + 2, &ep, 10);
742                 if (*ep == '}') {
743                         cb.recno = -recno;
744                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
745                 } else {
746                         cb.expire_total = approxidate(spec + 2);
747                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
748                         cb.expire_total = 0;
749                 }
750
751                 status |= expire_reflog(ref, sha1, 0, &cb);
752                 free(ref);
753         }
754         return status;
755 }
756
757 /*
758  * main "reflog"
759  */
760
761 static const char reflog_usage[] =
762 "git reflog [ show | expire | delete ]";
763
764 int cmd_reflog(int argc, const char **argv, const char *prefix)
765 {
766         if (argc > 1 && !strcmp(argv[1], "-h"))
767                 usage(reflog_usage);
768
769         /* With no command, we default to showing it. */
770         if (argc < 2 || *argv[1] == '-')
771                 return cmd_log_reflog(argc, argv, prefix);
772
773         if (!strcmp(argv[1], "show"))
774                 return cmd_log_reflog(argc - 1, argv + 1, prefix);
775
776         if (!strcmp(argv[1], "expire"))
777                 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
778
779         if (!strcmp(argv[1], "delete"))
780                 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
781
782         return cmd_log_reflog(argc, argv, prefix);
783 }