Merge branch 'jc/maint-abbrev-option-cli'
[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);
98         tree->buffer = NULL;
99
100         if (complete)
101                 tree->object.flags |= SEEN;
102         return complete;
103 }
104
105 static int commit_is_complete(struct commit *commit)
106 {
107         struct object_array study;
108         struct object_array found;
109         int is_incomplete = 0;
110         int i;
111
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;
132
133                 c = (struct commit *)study.objects[--study.nr].item;
134                 if (!c->object.parsed && !parse_object(c->object.sha1))
135                         c->object.flags |= INCOMPLETE;
136
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;
192 }
193
194 static int keep_entry(struct commit **it, unsigned char *sha1)
195 {
196         struct commit *commit;
197
198         if (is_null_sha1(sha1))
199                 return 1;
200         commit = lookup_commit_reference_gently(sha1, 1);
201         if (!commit)
202                 return 0;
203
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;
216 }
217
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)
225 {
226         struct commit *commit;
227         struct commit_list *pending;
228         unsigned long 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 *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;
261 }
262
263 static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
264 {
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;
272
273                 commit = lookup_commit_reference_gently(sha1, 1);
274
275                 /* Not a commit -- keep it */
276                 if (!commit)
277                         return 0;
278         }
279
280         /* Reachable from the current ref?  Don't prune. */
281         if (commit->object.flags & REACHABLE)
282                 return 0;
283
284         if (cb->mark_list && cb->mark_limit) {
285                 cb->mark_limit = 0; /* dig down to the root */
286                 mark_reachable(cb);
287         }
288
289         return !(commit->object.flags & REACHABLE);
290 }
291
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)
295 {
296         struct expire_reflog_cb *cb = cb_data;
297         struct commit *old, *new;
298
299         if (timestamp < cb->cmd->expire_total)
300                 goto prune;
301
302         if (cb->cmd->rewrite)
303                 osha1 = cb->last_kept_sha1;
304
305         old = new = NULL;
306         if (cb->cmd->stalefix &&
307             (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
308                 goto prune;
309
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         }
316
317         if (cb->cmd->recno && --(cb->cmd->recno) == 0)
318                 goto prune;
319
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)
334                 printf("would prune %s", message);
335         else if (cb->cmd->verbose)
336                 printf("prune %s", message);
337         return 0;
338 }
339
340 static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
341 {
342         struct commit_list **list = cb_data;
343         struct commit *tip_commit;
344         if (flags & REF_ISSYMREF)
345                 return 0;
346         tip_commit = lookup_commit_reference_gently(sha1, 1);
347         if (!tip_commit)
348                 return 0;
349         commit_list_insert(tip_commit, list);
350         return 0;
351 }
352
353 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
354 {
355         struct cmd_reflog_expire_cb *cmd = cb_data;
356         struct expire_reflog_cb cb;
357         struct ref_lock *lock;
358         char *log_file, *newlog_path = NULL;
359         struct commit *tip_commit;
360         struct commit_list *tips;
361         int status = 0;
362
363         memset(&cb, 0, sizeof(cb));
364
365         /*
366          * we take the lock for the ref itself to prevent it from
367          * getting updated.
368          */
369         lock = lock_any_ref_for_update(ref, sha1, 0);
370         if (!lock)
371                 return error("cannot lock ref '%s'", ref);
372         log_file = git_pathdup("logs/%s", ref);
373         if (!file_exists(log_file))
374                 goto finish;
375         if (!cmd->dry_run) {
376                 newlog_path = git_pathdup("logs/%s.lock", ref);
377                 cb.newlog = fopen(newlog_path, "w");
378         }
379
380         cb.cmd = cmd;
381
382         if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
383                 tip_commit = NULL;
384                 cb.unreachable_expire_kind = UE_HEAD;
385         } else {
386                 tip_commit = lookup_commit_reference_gently(sha1, 1);
387                 if (!tip_commit)
388                         cb.unreachable_expire_kind = UE_ALWAYS;
389                 else
390                         cb.unreachable_expire_kind = UE_NORMAL;
391         }
392
393         if (cmd->expire_unreachable <= cmd->expire_total)
394                 cb.unreachable_expire_kind = UE_ALWAYS;
395
396         cb.mark_list = NULL;
397         tips = NULL;
398         if (cb.unreachable_expire_kind != UE_ALWAYS) {
399                 if (cb.unreachable_expire_kind == UE_HEAD) {
400                         struct commit_list *elem;
401                         for_each_ref(push_tip_to_list, &tips);
402                         for (elem = tips; elem; elem = elem->next)
403                                 commit_list_insert(elem->item, &cb.mark_list);
404                 } else {
405                         commit_list_insert(tip_commit, &cb.mark_list);
406                 }
407                 cb.mark_limit = cmd->expire_total;
408                 mark_reachable(&cb);
409         }
410
411         for_each_reflog_ent(ref, expire_reflog_ent, &cb);
412
413         if (cb.unreachable_expire_kind != UE_ALWAYS) {
414                 if (cb.unreachable_expire_kind == UE_HEAD) {
415                         struct commit_list *elem;
416                         for (elem = tips; elem; elem = elem->next)
417                                 clear_commit_marks(tip_commit, REACHABLE);
418                         free_commit_list(tips);
419                 } else {
420                         clear_commit_marks(tip_commit, REACHABLE);
421                 }
422         }
423  finish:
424         if (cb.newlog) {
425                 if (fclose(cb.newlog)) {
426                         status |= error("%s: %s", strerror(errno),
427                                         newlog_path);
428                         unlink(newlog_path);
429                 } else if (cmd->updateref &&
430                         (write_in_full(lock->lock_fd,
431                                 sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
432                          write_str_in_full(lock->lock_fd, "\n") != 1 ||
433                          close_ref(lock) < 0)) {
434                         status |= error("Couldn't write %s",
435                                 lock->lk->filename);
436                         unlink(newlog_path);
437                 } else if (rename(newlog_path, log_file)) {
438                         status |= error("cannot rename %s to %s",
439                                         newlog_path, log_file);
440                         unlink(newlog_path);
441                 } else if (cmd->updateref && commit_ref(lock)) {
442                         status |= error("Couldn't set %s", lock->ref_name);
443                 } else {
444                         adjust_shared_perm(log_file);
445                 }
446         }
447         free(newlog_path);
448         free(log_file);
449         unlock_ref(lock);
450         return status;
451 }
452
453 static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
454 {
455         struct collected_reflog *e;
456         struct collect_reflog_cb *cb = cb_data;
457         size_t namelen = strlen(ref);
458
459         e = xmalloc(sizeof(*e) + namelen + 1);
460         hashcpy(e->sha1, sha1);
461         memcpy(e->reflog, ref, namelen + 1);
462         ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
463         cb->e[cb->nr++] = e;
464         return 0;
465 }
466
467 static struct reflog_expire_cfg {
468         struct reflog_expire_cfg *next;
469         unsigned long expire_total;
470         unsigned long expire_unreachable;
471         size_t len;
472         char pattern[FLEX_ARRAY];
473 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
474
475 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
476 {
477         struct reflog_expire_cfg *ent;
478
479         if (!reflog_expire_cfg_tail)
480                 reflog_expire_cfg_tail = &reflog_expire_cfg;
481
482         for (ent = reflog_expire_cfg; ent; ent = ent->next)
483                 if (ent->len == len &&
484                     !memcmp(ent->pattern, pattern, len))
485                         return ent;
486
487         ent = xcalloc(1, (sizeof(*ent) + len));
488         memcpy(ent->pattern, pattern, len);
489         ent->len = len;
490         *reflog_expire_cfg_tail = ent;
491         reflog_expire_cfg_tail = &(ent->next);
492         return ent;
493 }
494
495 static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
496 {
497         if (!value)
498                 return config_error_nonbool(var);
499         if (!strcmp(value, "never") || !strcmp(value, "false")) {
500                 *expire = 0;
501                 return 0;
502         }
503         *expire = approxidate(value);
504         return 0;
505 }
506
507 /* expiry timer slot */
508 #define EXPIRE_TOTAL   01
509 #define EXPIRE_UNREACH 02
510
511 static int reflog_expire_config(const char *var, const char *value, void *cb)
512 {
513         const char *lastdot = strrchr(var, '.');
514         unsigned long expire;
515         int slot;
516         struct reflog_expire_cfg *ent;
517
518         if (!lastdot || prefixcmp(var, "gc."))
519                 return git_default_config(var, value, cb);
520
521         if (!strcmp(lastdot, ".reflogexpire")) {
522                 slot = EXPIRE_TOTAL;
523                 if (parse_expire_cfg_value(var, value, &expire))
524                         return -1;
525         } else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
526                 slot = EXPIRE_UNREACH;
527                 if (parse_expire_cfg_value(var, value, &expire))
528                         return -1;
529         } else
530                 return git_default_config(var, value, cb);
531
532         if (lastdot == var + 2) {
533                 switch (slot) {
534                 case EXPIRE_TOTAL:
535                         default_reflog_expire = expire;
536                         break;
537                 case EXPIRE_UNREACH:
538                         default_reflog_expire_unreachable = expire;
539                         break;
540                 }
541                 return 0;
542         }
543
544         ent = find_cfg_ent(var + 3, lastdot - (var+3));
545         if (!ent)
546                 return -1;
547         switch (slot) {
548         case EXPIRE_TOTAL:
549                 ent->expire_total = expire;
550                 break;
551         case EXPIRE_UNREACH:
552                 ent->expire_unreachable = expire;
553                 break;
554         }
555         return 0;
556 }
557
558 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
559 {
560         struct reflog_expire_cfg *ent;
561
562         if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
563                 return; /* both given explicitly -- nothing to tweak */
564
565         for (ent = reflog_expire_cfg; ent; ent = ent->next) {
566                 if (!fnmatch(ent->pattern, ref, 0)) {
567                         if (!(slot & EXPIRE_TOTAL))
568                                 cb->expire_total = ent->expire_total;
569                         if (!(slot & EXPIRE_UNREACH))
570                                 cb->expire_unreachable = ent->expire_unreachable;
571                         return;
572                 }
573         }
574
575         /*
576          * If unconfigured, make stash never expire
577          */
578         if (!strcmp(ref, "refs/stash")) {
579                 if (!(slot & EXPIRE_TOTAL))
580                         cb->expire_total = 0;
581                 if (!(slot & EXPIRE_UNREACH))
582                         cb->expire_unreachable = 0;
583                 return;
584         }
585
586         /* Nothing matched -- use the default value */
587         if (!(slot & EXPIRE_TOTAL))
588                 cb->expire_total = default_reflog_expire;
589         if (!(slot & EXPIRE_UNREACH))
590                 cb->expire_unreachable = default_reflog_expire_unreachable;
591 }
592
593 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
594 {
595         struct cmd_reflog_expire_cb cb;
596         unsigned long now = time(NULL);
597         int i, status, do_all;
598         int explicit_expiry = 0;
599
600         default_reflog_expire_unreachable = now - 30 * 24 * 3600;
601         default_reflog_expire = now - 90 * 24 * 3600;
602         git_config(reflog_expire_config, NULL);
603
604         save_commit_buffer = 0;
605         do_all = status = 0;
606         memset(&cb, 0, sizeof(cb));
607
608         cb.expire_total = default_reflog_expire;
609         cb.expire_unreachable = default_reflog_expire_unreachable;
610
611         for (i = 1; i < argc; i++) {
612                 const char *arg = argv[i];
613                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
614                         cb.dry_run = 1;
615                 else if (!prefixcmp(arg, "--expire=")) {
616                         cb.expire_total = approxidate(arg + 9);
617                         explicit_expiry |= EXPIRE_TOTAL;
618                 }
619                 else if (!prefixcmp(arg, "--expire-unreachable=")) {
620                         cb.expire_unreachable = approxidate(arg + 21);
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 }