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