Merge branch 'ap/maint-diff-rename-avoid-overlap'
[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 *pattern, *key;
514         int pattern_len;
515         unsigned long expire;
516         int slot;
517         struct reflog_expire_cfg *ent;
518
519         if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
520                 return git_default_config(var, value, cb);
521
522         if (!strcmp(key, "reflogexpire")) {
523                 slot = EXPIRE_TOTAL;
524                 if (parse_expire_cfg_value(var, value, &expire))
525                         return -1;
526         } else if (!strcmp(key, "reflogexpireunreachable")) {
527                 slot = EXPIRE_UNREACH;
528                 if (parse_expire_cfg_value(var, value, &expire))
529                         return -1;
530         } else
531                 return git_default_config(var, value, cb);
532
533         if (!pattern) {
534                 switch (slot) {
535                 case EXPIRE_TOTAL:
536                         default_reflog_expire = expire;
537                         break;
538                 case EXPIRE_UNREACH:
539                         default_reflog_expire_unreachable = expire;
540                         break;
541                 }
542                 return 0;
543         }
544
545         ent = find_cfg_ent(pattern, pattern_len);
546         if (!ent)
547                 return -1;
548         switch (slot) {
549         case EXPIRE_TOTAL:
550                 ent->expire_total = expire;
551                 break;
552         case EXPIRE_UNREACH:
553                 ent->expire_unreachable = expire;
554                 break;
555         }
556         return 0;
557 }
558
559 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
560 {
561         struct reflog_expire_cfg *ent;
562
563         if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
564                 return; /* both given explicitly -- nothing to tweak */
565
566         for (ent = reflog_expire_cfg; ent; ent = ent->next) {
567                 if (!fnmatch(ent->pattern, ref, 0)) {
568                         if (!(slot & EXPIRE_TOTAL))
569                                 cb->expire_total = ent->expire_total;
570                         if (!(slot & EXPIRE_UNREACH))
571                                 cb->expire_unreachable = ent->expire_unreachable;
572                         return;
573                 }
574         }
575
576         /*
577          * If unconfigured, make stash never expire
578          */
579         if (!strcmp(ref, "refs/stash")) {
580                 if (!(slot & EXPIRE_TOTAL))
581                         cb->expire_total = 0;
582                 if (!(slot & EXPIRE_UNREACH))
583                         cb->expire_unreachable = 0;
584                 return;
585         }
586
587         /* Nothing matched -- use the default value */
588         if (!(slot & EXPIRE_TOTAL))
589                 cb->expire_total = default_reflog_expire;
590         if (!(slot & EXPIRE_UNREACH))
591                 cb->expire_unreachable = default_reflog_expire_unreachable;
592 }
593
594 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
595 {
596         struct cmd_reflog_expire_cb cb;
597         unsigned long now = time(NULL);
598         int i, status, do_all;
599         int explicit_expiry = 0;
600
601         default_reflog_expire_unreachable = now - 30 * 24 * 3600;
602         default_reflog_expire = now - 90 * 24 * 3600;
603         git_config(reflog_expire_config, NULL);
604
605         save_commit_buffer = 0;
606         do_all = status = 0;
607         memset(&cb, 0, sizeof(cb));
608
609         cb.expire_total = default_reflog_expire;
610         cb.expire_unreachable = default_reflog_expire_unreachable;
611
612         for (i = 1; i < argc; i++) {
613                 const char *arg = argv[i];
614                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
615                         cb.dry_run = 1;
616                 else if (!prefixcmp(arg, "--expire=")) {
617                         cb.expire_total = approxidate(arg + 9);
618                         explicit_expiry |= EXPIRE_TOTAL;
619                 }
620                 else if (!prefixcmp(arg, "--expire-unreachable=")) {
621                         cb.expire_unreachable = approxidate(arg + 21);
622                         explicit_expiry |= EXPIRE_UNREACH;
623                 }
624                 else if (!strcmp(arg, "--stale-fix"))
625                         cb.stalefix = 1;
626                 else if (!strcmp(arg, "--rewrite"))
627                         cb.rewrite = 1;
628                 else if (!strcmp(arg, "--updateref"))
629                         cb.updateref = 1;
630                 else if (!strcmp(arg, "--all"))
631                         do_all = 1;
632                 else if (!strcmp(arg, "--verbose"))
633                         cb.verbose = 1;
634                 else if (!strcmp(arg, "--")) {
635                         i++;
636                         break;
637                 }
638                 else if (arg[0] == '-')
639                         usage(reflog_expire_usage);
640                 else
641                         break;
642         }
643
644         /*
645          * We can trust the commits and objects reachable from refs
646          * even in older repository.  We cannot trust what's reachable
647          * from reflog if the repository was pruned with older git.
648          */
649         if (cb.stalefix) {
650                 init_revisions(&cb.revs, prefix);
651                 if (cb.verbose)
652                         printf("Marking reachable objects...");
653                 mark_reachable_objects(&cb.revs, 0, NULL);
654                 if (cb.verbose)
655                         putchar('\n');
656         }
657
658         if (do_all) {
659                 struct collect_reflog_cb collected;
660                 int i;
661
662                 memset(&collected, 0, sizeof(collected));
663                 for_each_reflog(collect_reflog, &collected);
664                 for (i = 0; i < collected.nr; i++) {
665                         struct collected_reflog *e = collected.e[i];
666                         set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
667                         status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
668                         free(e);
669                 }
670                 free(collected.e);
671         }
672
673         for (; i < argc; i++) {
674                 char *ref;
675                 unsigned char sha1[20];
676                 if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
677                         status |= error("%s points nowhere!", argv[i]);
678                         continue;
679                 }
680                 set_reflog_expiry_param(&cb, explicit_expiry, ref);
681                 status |= expire_reflog(ref, sha1, 0, &cb);
682         }
683         return status;
684 }
685
686 static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
687                 const char *email, unsigned long timestamp, int tz,
688                 const char *message, void *cb_data)
689 {
690         struct cmd_reflog_expire_cb *cb = cb_data;
691         if (!cb->expire_total || timestamp < cb->expire_total)
692                 cb->recno++;
693         return 0;
694 }
695
696 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
697 {
698         struct cmd_reflog_expire_cb cb;
699         int i, status = 0;
700
701         memset(&cb, 0, sizeof(cb));
702
703         for (i = 1; i < argc; i++) {
704                 const char *arg = argv[i];
705                 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
706                         cb.dry_run = 1;
707                 else if (!strcmp(arg, "--rewrite"))
708                         cb.rewrite = 1;
709                 else if (!strcmp(arg, "--updateref"))
710                         cb.updateref = 1;
711                 else if (!strcmp(arg, "--verbose"))
712                         cb.verbose = 1;
713                 else if (!strcmp(arg, "--")) {
714                         i++;
715                         break;
716                 }
717                 else if (arg[0] == '-')
718                         usage(reflog_delete_usage);
719                 else
720                         break;
721         }
722
723         if (argc - i < 1)
724                 return error("Nothing to delete?");
725
726         for ( ; i < argc; i++) {
727                 const char *spec = strstr(argv[i], "@{");
728                 unsigned char sha1[20];
729                 char *ep, *ref;
730                 int recno;
731
732                 if (!spec) {
733                         status |= error("Not a reflog: %s", argv[i]);
734                         continue;
735                 }
736
737                 if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
738                         status |= error("no reflog for '%s'", argv[i]);
739                         continue;
740                 }
741
742                 recno = strtoul(spec + 2, &ep, 10);
743                 if (*ep == '}') {
744                         cb.recno = -recno;
745                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
746                 } else {
747                         cb.expire_total = approxidate(spec + 2);
748                         for_each_reflog_ent(ref, count_reflog_ent, &cb);
749                         cb.expire_total = 0;
750                 }
751
752                 status |= expire_reflog(ref, sha1, 0, &cb);
753                 free(ref);
754         }
755         return status;
756 }
757
758 /*
759  * main "reflog"
760  */
761
762 static const char reflog_usage[] =
763 "git reflog [ show | expire | delete ]";
764
765 int cmd_reflog(int argc, const char **argv, const char *prefix)
766 {
767         if (argc > 1 && !strcmp(argv[1], "-h"))
768                 usage(reflog_usage);
769
770         /* With no command, we default to showing it. */
771         if (argc < 2 || *argv[1] == '-')
772                 return cmd_log_reflog(argc, argv, prefix);
773
774         if (!strcmp(argv[1], "show"))
775                 return cmd_log_reflog(argc - 1, argv + 1, prefix);
776
777         if (!strcmp(argv[1], "expire"))
778                 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
779
780         if (!strcmp(argv[1], "delete"))
781                 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
782
783         return cmd_log_reflog(argc, argv, prefix);
784 }