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