94c34fc5d86ee5a7085e766ef499085bb41bc730
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
15static const char reflog_expire_usage[] =
16"git reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
17static const char reflog_delete_usage[] =
18"git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
19
20static unsigned long default_reflog_expire;
21static unsigned long default_reflog_expire_unreachable;
22
23enum expire_reflog_flags {
24 EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
25 EXPIRE_REFLOGS_UPDATE_REF = 1 << 1
26};
27
28struct 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
38struct expire_reflog_policy_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
53struct collected_reflog {
54 unsigned char sha1[20];
55 char reflog[FLEX_ARRAY];
56};
57struct 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
67static 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
109static 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
198static 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 */
228static void mark_reachable(struct expire_reflog_policy_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
267static int unreachable(struct expire_reflog_policy_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 */
299static 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_policy_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
327static 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_policy_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
358static int push_tip_to_list(const char *refname, const unsigned char *sha1,
359 int flags, void *cb_data)
360{
361 struct commit_list **list = cb_data;
362 struct commit *tip_commit;
363 if (flags & REF_ISSYMREF)
364 return 0;
365 tip_commit = lookup_commit_reference_gently(sha1, 1);
366 if (!tip_commit)
367 return 0;
368 commit_list_insert(tip_commit, list);
369 return 0;
370}
371
372static void reflog_expiry_prepare(const char *refname,
373 const unsigned char *sha1,
374 struct expire_reflog_policy_cb *cb)
375{
376 if (!cb->cmd->expire_unreachable || !strcmp(refname, "HEAD")) {
377 cb->tip_commit = NULL;
378 cb->unreachable_expire_kind = UE_HEAD;
379 } else {
380 cb->tip_commit = lookup_commit_reference_gently(sha1, 1);
381 if (!cb->tip_commit)
382 cb->unreachable_expire_kind = UE_ALWAYS;
383 else
384 cb->unreachable_expire_kind = UE_NORMAL;
385 }
386
387 if (cb->cmd->expire_unreachable <= cb->cmd->expire_total)
388 cb->unreachable_expire_kind = UE_ALWAYS;
389
390 cb->mark_list = NULL;
391 cb->tips = NULL;
392 if (cb->unreachable_expire_kind != UE_ALWAYS) {
393 if (cb->unreachable_expire_kind == UE_HEAD) {
394 struct commit_list *elem;
395 for_each_ref(push_tip_to_list, &cb->tips);
396 for (elem = cb->tips; elem; elem = elem->next)
397 commit_list_insert(elem->item, &cb->mark_list);
398 } else {
399 commit_list_insert(cb->tip_commit, &cb->mark_list);
400 }
401 cb->mark_limit = cb->cmd->expire_total;
402 mark_reachable(cb);
403 }
404}
405
406static void reflog_expiry_cleanup(struct expire_reflog_policy_cb *cb)
407{
408 if (cb->unreachable_expire_kind != UE_ALWAYS) {
409 if (cb->unreachable_expire_kind == UE_HEAD) {
410 struct commit_list *elem;
411 for (elem = cb->tips; elem; elem = elem->next)
412 clear_commit_marks(elem->item, REACHABLE);
413 free_commit_list(cb->tips);
414 } else {
415 clear_commit_marks(cb->tip_commit, REACHABLE);
416 }
417 }
418}
419
420static int expire_reflog(const char *refname, const unsigned char *sha1,
421 unsigned int flags, struct cmd_reflog_expire_cb *cmd)
422{
423 static struct lock_file reflog_lock;
424 struct expire_reflog_policy_cb cb;
425 struct ref_lock *lock;
426 char *log_file;
427 int status = 0;
428
429 memset(&cb, 0, sizeof(cb));
430
431 /*
432 * The reflog file is locked by holding the lock on the
433 * reference itself, plus we might need to update the
434 * reference if --updateref was specified:
435 */
436 lock = lock_any_ref_for_update(refname, sha1, 0, NULL);
437 if (!lock)
438 return error("cannot lock ref '%s'", refname);
439 if (!reflog_exists(refname)) {
440 unlock_ref(lock);
441 return 0;
442 }
443
444 log_file = git_pathdup("logs/%s", refname);
445 if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
446 /*
447 * Even though holding $GIT_DIR/logs/$reflog.lock has
448 * no locking implications, we use the lock_file
449 * machinery here anyway because it does a lot of the
450 * work we need, including cleaning up if the program
451 * exits unexpectedly.
452 */
453 if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
454 struct strbuf err = STRBUF_INIT;
455 unable_to_lock_message(log_file, errno, &err);
456 error("%s", err.buf);
457 strbuf_release(&err);
458 goto failure;
459 }
460 cb.newlog = fdopen_lock_file(&reflog_lock, "w");
461 if (!cb.newlog) {
462 error("cannot fdopen %s (%s)",
463 reflog_lock.filename.buf, strerror(errno));
464 goto failure;
465 }
466 }
467
468 cb.cmd = cmd;
469
470 reflog_expiry_prepare(refname, sha1, &cb);
471 for_each_reflog_ent(refname, expire_reflog_ent, &cb);
472 reflog_expiry_cleanup(&cb);
473
474 if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
475 if (close_lock_file(&reflog_lock)) {
476 status |= error("couldn't write %s: %s", log_file,
477 strerror(errno));
478 } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) &&
479 (write_in_full(lock->lock_fd,
480 sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
481 write_str_in_full(lock->lock_fd, "\n") != 1 ||
482 close_ref(lock) < 0)) {
483 status |= error("couldn't write %s",
484 lock->lk->filename.buf);
485 rollback_lock_file(&reflog_lock);
486 } else if (commit_lock_file(&reflog_lock)) {
487 status |= error("unable to commit reflog '%s' (%s)",
488 log_file, strerror(errno));
489 } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) {
490 status |= error("couldn't set %s", lock->ref_name);
491 }
492 }
493 free(log_file);
494 unlock_ref(lock);
495 return status;
496
497 failure:
498 rollback_lock_file(&reflog_lock);
499 free(log_file);
500 unlock_ref(lock);
501 return -1;
502}
503
504static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
505{
506 struct collected_reflog *e;
507 struct collect_reflog_cb *cb = cb_data;
508 size_t namelen = strlen(ref);
509
510 e = xmalloc(sizeof(*e) + namelen + 1);
511 hashcpy(e->sha1, sha1);
512 memcpy(e->reflog, ref, namelen + 1);
513 ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
514 cb->e[cb->nr++] = e;
515 return 0;
516}
517
518static struct reflog_expire_cfg {
519 struct reflog_expire_cfg *next;
520 unsigned long expire_total;
521 unsigned long expire_unreachable;
522 size_t len;
523 char pattern[FLEX_ARRAY];
524} *reflog_expire_cfg, **reflog_expire_cfg_tail;
525
526static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
527{
528 struct reflog_expire_cfg *ent;
529
530 if (!reflog_expire_cfg_tail)
531 reflog_expire_cfg_tail = &reflog_expire_cfg;
532
533 for (ent = reflog_expire_cfg; ent; ent = ent->next)
534 if (ent->len == len &&
535 !memcmp(ent->pattern, pattern, len))
536 return ent;
537
538 ent = xcalloc(1, (sizeof(*ent) + len));
539 memcpy(ent->pattern, pattern, len);
540 ent->len = len;
541 *reflog_expire_cfg_tail = ent;
542 reflog_expire_cfg_tail = &(ent->next);
543 return ent;
544}
545
546static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
547{
548 if (!value)
549 return config_error_nonbool(var);
550 if (parse_expiry_date(value, expire))
551 return error(_("%s' for '%s' is not a valid timestamp"),
552 value, var);
553 return 0;
554}
555
556/* expiry timer slot */
557#define EXPIRE_TOTAL 01
558#define EXPIRE_UNREACH 02
559
560static int reflog_expire_config(const char *var, const char *value, void *cb)
561{
562 const char *pattern, *key;
563 int pattern_len;
564 unsigned long expire;
565 int slot;
566 struct reflog_expire_cfg *ent;
567
568 if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
569 return git_default_config(var, value, cb);
570
571 if (!strcmp(key, "reflogexpire")) {
572 slot = EXPIRE_TOTAL;
573 if (parse_expire_cfg_value(var, value, &expire))
574 return -1;
575 } else if (!strcmp(key, "reflogexpireunreachable")) {
576 slot = EXPIRE_UNREACH;
577 if (parse_expire_cfg_value(var, value, &expire))
578 return -1;
579 } else
580 return git_default_config(var, value, cb);
581
582 if (!pattern) {
583 switch (slot) {
584 case EXPIRE_TOTAL:
585 default_reflog_expire = expire;
586 break;
587 case EXPIRE_UNREACH:
588 default_reflog_expire_unreachable = expire;
589 break;
590 }
591 return 0;
592 }
593
594 ent = find_cfg_ent(pattern, pattern_len);
595 if (!ent)
596 return -1;
597 switch (slot) {
598 case EXPIRE_TOTAL:
599 ent->expire_total = expire;
600 break;
601 case EXPIRE_UNREACH:
602 ent->expire_unreachable = expire;
603 break;
604 }
605 return 0;
606}
607
608static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
609{
610 struct reflog_expire_cfg *ent;
611
612 if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
613 return; /* both given explicitly -- nothing to tweak */
614
615 for (ent = reflog_expire_cfg; ent; ent = ent->next) {
616 if (!wildmatch(ent->pattern, ref, 0, NULL)) {
617 if (!(slot & EXPIRE_TOTAL))
618 cb->expire_total = ent->expire_total;
619 if (!(slot & EXPIRE_UNREACH))
620 cb->expire_unreachable = ent->expire_unreachable;
621 return;
622 }
623 }
624
625 /*
626 * If unconfigured, make stash never expire
627 */
628 if (!strcmp(ref, "refs/stash")) {
629 if (!(slot & EXPIRE_TOTAL))
630 cb->expire_total = 0;
631 if (!(slot & EXPIRE_UNREACH))
632 cb->expire_unreachable = 0;
633 return;
634 }
635
636 /* Nothing matched -- use the default value */
637 if (!(slot & EXPIRE_TOTAL))
638 cb->expire_total = default_reflog_expire;
639 if (!(slot & EXPIRE_UNREACH))
640 cb->expire_unreachable = default_reflog_expire_unreachable;
641}
642
643static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
644{
645 struct cmd_reflog_expire_cb cb;
646 unsigned long now = time(NULL);
647 int i, status, do_all;
648 int explicit_expiry = 0;
649 unsigned int flags = 0;
650
651 default_reflog_expire_unreachable = now - 30 * 24 * 3600;
652 default_reflog_expire = now - 90 * 24 * 3600;
653 git_config(reflog_expire_config, NULL);
654
655 save_commit_buffer = 0;
656 do_all = status = 0;
657 memset(&cb, 0, sizeof(cb));
658
659 cb.expire_total = default_reflog_expire;
660 cb.expire_unreachable = default_reflog_expire_unreachable;
661
662 for (i = 1; i < argc; i++) {
663 const char *arg = argv[i];
664 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
665 flags |= EXPIRE_REFLOGS_DRY_RUN;
666 else if (starts_with(arg, "--expire=")) {
667 if (parse_expiry_date(arg + 9, &cb.expire_total))
668 die(_("'%s' is not a valid timestamp"), arg);
669 explicit_expiry |= EXPIRE_TOTAL;
670 }
671 else if (starts_with(arg, "--expire-unreachable=")) {
672 if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
673 die(_("'%s' is not a valid timestamp"), arg);
674 explicit_expiry |= EXPIRE_UNREACH;
675 }
676 else if (!strcmp(arg, "--stale-fix"))
677 cb.stalefix = 1;
678 else if (!strcmp(arg, "--rewrite"))
679 cb.rewrite = 1;
680 else if (!strcmp(arg, "--updateref"))
681 flags |= EXPIRE_REFLOGS_UPDATE_REF;
682 else if (!strcmp(arg, "--all"))
683 do_all = 1;
684 else if (!strcmp(arg, "--verbose"))
685 cb.verbose = 1;
686 else if (!strcmp(arg, "--")) {
687 i++;
688 break;
689 }
690 else if (arg[0] == '-')
691 usage(reflog_expire_usage);
692 else
693 break;
694 }
695
696 /*
697 * We can trust the commits and objects reachable from refs
698 * even in older repository. We cannot trust what's reachable
699 * from reflog if the repository was pruned with older git.
700 */
701 if (cb.stalefix) {
702 init_revisions(&cb.revs, prefix);
703 if (cb.verbose)
704 printf("Marking reachable objects...");
705 mark_reachable_objects(&cb.revs, 0, 0, NULL);
706 if (cb.verbose)
707 putchar('\n');
708 }
709
710 if (do_all) {
711 struct collect_reflog_cb collected;
712 int i;
713
714 memset(&collected, 0, sizeof(collected));
715 for_each_reflog(collect_reflog, &collected);
716 for (i = 0; i < collected.nr; i++) {
717 struct collected_reflog *e = collected.e[i];
718 set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
719 status |= expire_reflog(e->reflog, e->sha1, flags, &cb);
720 free(e);
721 }
722 free(collected.e);
723 }
724
725 for (; i < argc; i++) {
726 char *ref;
727 unsigned char sha1[20];
728 if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
729 status |= error("%s points nowhere!", argv[i]);
730 continue;
731 }
732 set_reflog_expiry_param(&cb, explicit_expiry, ref);
733 status |= expire_reflog(ref, sha1, flags, &cb);
734 }
735 return status;
736}
737
738static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
739 const char *email, unsigned long timestamp, int tz,
740 const char *message, void *cb_data)
741{
742 struct cmd_reflog_expire_cb *cb = cb_data;
743 if (!cb->expire_total || timestamp < cb->expire_total)
744 cb->recno++;
745 return 0;
746}
747
748static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
749{
750 struct cmd_reflog_expire_cb cb;
751 int i, status = 0;
752 unsigned int flags = 0;
753
754 memset(&cb, 0, sizeof(cb));
755
756 for (i = 1; i < argc; i++) {
757 const char *arg = argv[i];
758 if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
759 flags |= EXPIRE_REFLOGS_DRY_RUN;
760 else if (!strcmp(arg, "--rewrite"))
761 cb.rewrite = 1;
762 else if (!strcmp(arg, "--updateref"))
763 flags |= EXPIRE_REFLOGS_UPDATE_REF;
764 else if (!strcmp(arg, "--verbose"))
765 cb.verbose = 1;
766 else if (!strcmp(arg, "--")) {
767 i++;
768 break;
769 }
770 else if (arg[0] == '-')
771 usage(reflog_delete_usage);
772 else
773 break;
774 }
775
776 if (argc - i < 1)
777 return error("Nothing to delete?");
778
779 for ( ; i < argc; i++) {
780 const char *spec = strstr(argv[i], "@{");
781 unsigned char sha1[20];
782 char *ep, *ref;
783 int recno;
784
785 if (!spec) {
786 status |= error("Not a reflog: %s", argv[i]);
787 continue;
788 }
789
790 if (!dwim_log(argv[i], spec - argv[i], sha1, &ref)) {
791 status |= error("no reflog for '%s'", argv[i]);
792 continue;
793 }
794
795 recno = strtoul(spec + 2, &ep, 10);
796 if (*ep == '}') {
797 cb.recno = -recno;
798 for_each_reflog_ent(ref, count_reflog_ent, &cb);
799 } else {
800 cb.expire_total = approxidate(spec + 2);
801 for_each_reflog_ent(ref, count_reflog_ent, &cb);
802 cb.expire_total = 0;
803 }
804
805 status |= expire_reflog(ref, sha1, flags, &cb);
806 free(ref);
807 }
808 return status;
809}
810
811/*
812 * main "reflog"
813 */
814
815static const char reflog_usage[] =
816"git reflog [ show | expire | delete ]";
817
818int cmd_reflog(int argc, const char **argv, const char *prefix)
819{
820 if (argc > 1 && !strcmp(argv[1], "-h"))
821 usage(reflog_usage);
822
823 /* With no command, we default to showing it. */
824 if (argc < 2 || *argv[1] == '-')
825 return cmd_log_reflog(argc, argv, prefix);
826
827 if (!strcmp(argv[1], "show"))
828 return cmd_log_reflog(argc - 1, argv + 1, prefix);
829
830 if (!strcmp(argv[1], "expire"))
831 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
832
833 if (!strcmp(argv[1], "delete"))
834 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
835
836 return cmd_log_reflog(argc, argv, prefix);
837}