read-tree: narrow scope of index expansion for '--prefix'
[alt-git.git] / builtin / reflog.c
blob85b838720c364ae54c5ebf410a58921e6f8fd73e
1 #include "builtin.h"
2 #include "config.h"
3 #include "lockfile.h"
4 #include "object-store.h"
5 #include "repository.h"
6 #include "commit.h"
7 #include "refs.h"
8 #include "dir.h"
9 #include "tree-walk.h"
10 #include "diff.h"
11 #include "revision.h"
12 #include "reachable.h"
13 #include "worktree.h"
15 static const char reflog_exists_usage[] =
16 N_("git reflog exists <ref>");
18 static timestamp_t default_reflog_expire;
19 static timestamp_t default_reflog_expire_unreachable;
21 struct cmd_reflog_expire_cb {
22 int stalefix;
23 int explicit_expiry;
24 timestamp_t expire_total;
25 timestamp_t expire_unreachable;
26 int recno;
29 struct expire_reflog_policy_cb {
30 enum {
31 UE_NORMAL,
32 UE_ALWAYS,
33 UE_HEAD
34 } unreachable_expire_kind;
35 struct commit_list *mark_list;
36 unsigned long mark_limit;
37 struct cmd_reflog_expire_cb cmd;
38 struct commit *tip_commit;
39 struct commit_list *tips;
40 unsigned int dry_run:1;
43 struct worktree_reflogs {
44 struct worktree *worktree;
45 struct string_list reflogs;
48 /* Remember to update object flag allocation in object.h */
49 #define INCOMPLETE (1u<<10)
50 #define STUDYING (1u<<11)
51 #define REACHABLE (1u<<12)
53 static int tree_is_complete(const struct object_id *oid)
55 struct tree_desc desc;
56 struct name_entry entry;
57 int complete;
58 struct tree *tree;
60 tree = lookup_tree(the_repository, oid);
61 if (!tree)
62 return 0;
63 if (tree->object.flags & SEEN)
64 return 1;
65 if (tree->object.flags & INCOMPLETE)
66 return 0;
68 if (!tree->buffer) {
69 enum object_type type;
70 unsigned long size;
71 void *data = read_object_file(oid, &type, &size);
72 if (!data) {
73 tree->object.flags |= INCOMPLETE;
74 return 0;
76 tree->buffer = data;
77 tree->size = size;
79 init_tree_desc(&desc, tree->buffer, tree->size);
80 complete = 1;
81 while (tree_entry(&desc, &entry)) {
82 if (!has_object_file(&entry.oid) ||
83 (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
84 tree->object.flags |= INCOMPLETE;
85 complete = 0;
88 free_tree_buffer(tree);
90 if (complete)
91 tree->object.flags |= SEEN;
92 return complete;
95 static int commit_is_complete(struct commit *commit)
97 struct object_array study;
98 struct object_array found;
99 int is_incomplete = 0;
100 int i;
102 /* early return */
103 if (commit->object.flags & SEEN)
104 return 1;
105 if (commit->object.flags & INCOMPLETE)
106 return 0;
108 * Find all commits that are reachable and are not marked as
109 * SEEN. Then make sure the trees and blobs contained are
110 * complete. After that, mark these commits also as SEEN.
111 * If some of the objects that are needed to complete this
112 * commit are missing, mark this commit as INCOMPLETE.
114 memset(&study, 0, sizeof(study));
115 memset(&found, 0, sizeof(found));
116 add_object_array(&commit->object, NULL, &study);
117 add_object_array(&commit->object, NULL, &found);
118 commit->object.flags |= STUDYING;
119 while (study.nr) {
120 struct commit *c;
121 struct commit_list *parent;
123 c = (struct commit *)object_array_pop(&study);
124 if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
125 c->object.flags |= INCOMPLETE;
127 if (c->object.flags & INCOMPLETE) {
128 is_incomplete = 1;
129 break;
131 else if (c->object.flags & SEEN)
132 continue;
133 for (parent = c->parents; parent; parent = parent->next) {
134 struct commit *p = parent->item;
135 if (p->object.flags & STUDYING)
136 continue;
137 p->object.flags |= STUDYING;
138 add_object_array(&p->object, NULL, &study);
139 add_object_array(&p->object, NULL, &found);
142 if (!is_incomplete) {
144 * make sure all commits in "found" array have all the
145 * necessary objects.
147 for (i = 0; i < found.nr; i++) {
148 struct commit *c =
149 (struct commit *)found.objects[i].item;
150 if (!tree_is_complete(get_commit_tree_oid(c))) {
151 is_incomplete = 1;
152 c->object.flags |= INCOMPLETE;
155 if (!is_incomplete) {
156 /* mark all found commits as complete, iow SEEN */
157 for (i = 0; i < found.nr; i++)
158 found.objects[i].item->flags |= SEEN;
161 /* clear flags from the objects we traversed */
162 for (i = 0; i < found.nr; i++)
163 found.objects[i].item->flags &= ~STUDYING;
164 if (is_incomplete)
165 commit->object.flags |= INCOMPLETE;
166 else {
168 * If we come here, we have (1) traversed the ancestry chain
169 * from the "commit" until we reach SEEN commits (which are
170 * known to be complete), and (2) made sure that the commits
171 * encountered during the above traversal refer to trees that
172 * are complete. Which means that we know *all* the commits
173 * we have seen during this process are complete.
175 for (i = 0; i < found.nr; i++)
176 found.objects[i].item->flags |= SEEN;
178 /* free object arrays */
179 object_array_clear(&study);
180 object_array_clear(&found);
181 return !is_incomplete;
184 static int keep_entry(struct commit **it, struct object_id *oid)
186 struct commit *commit;
188 if (is_null_oid(oid))
189 return 1;
190 commit = lookup_commit_reference_gently(the_repository, oid, 1);
191 if (!commit)
192 return 0;
195 * Make sure everything in this commit exists.
197 * We have walked all the objects reachable from the refs
198 * and cache earlier. The commits reachable by this commit
199 * must meet SEEN commits -- and then we should mark them as
200 * SEEN as well.
202 if (!commit_is_complete(commit))
203 return 0;
204 *it = commit;
205 return 1;
209 * Starting from commits in the cb->mark_list, mark commits that are
210 * reachable from them. Stop the traversal at commits older than
211 * the expire_limit and queue them back, so that the caller can call
212 * us again to restart the traversal with longer expire_limit.
214 static void mark_reachable(struct expire_reflog_policy_cb *cb)
216 struct commit_list *pending;
217 timestamp_t expire_limit = cb->mark_limit;
218 struct commit_list *leftover = NULL;
220 for (pending = cb->mark_list; pending; pending = pending->next)
221 pending->item->object.flags &= ~REACHABLE;
223 pending = cb->mark_list;
224 while (pending) {
225 struct commit_list *parent;
226 struct commit *commit = pop_commit(&pending);
227 if (commit->object.flags & REACHABLE)
228 continue;
229 if (parse_commit(commit))
230 continue;
231 commit->object.flags |= REACHABLE;
232 if (commit->date < expire_limit) {
233 commit_list_insert(commit, &leftover);
234 continue;
236 commit->object.flags |= REACHABLE;
237 parent = commit->parents;
238 while (parent) {
239 commit = parent->item;
240 parent = parent->next;
241 if (commit->object.flags & REACHABLE)
242 continue;
243 commit_list_insert(commit, &pending);
246 cb->mark_list = leftover;
249 static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
252 * We may or may not have the commit yet - if not, look it
253 * up using the supplied sha1.
255 if (!commit) {
256 if (is_null_oid(oid))
257 return 0;
259 commit = lookup_commit_reference_gently(the_repository, oid,
262 /* Not a commit -- keep it */
263 if (!commit)
264 return 0;
267 /* Reachable from the current ref? Don't prune. */
268 if (commit->object.flags & REACHABLE)
269 return 0;
271 if (cb->mark_list && cb->mark_limit) {
272 cb->mark_limit = 0; /* dig down to the root */
273 mark_reachable(cb);
276 return !(commit->object.flags & REACHABLE);
280 * Return true iff the specified reflog entry should be expired.
282 static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
283 const char *email, timestamp_t timestamp, int tz,
284 const char *message, void *cb_data)
286 struct expire_reflog_policy_cb *cb = cb_data;
287 struct commit *old_commit, *new_commit;
289 if (timestamp < cb->cmd.expire_total)
290 return 1;
292 old_commit = new_commit = NULL;
293 if (cb->cmd.stalefix &&
294 (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
295 return 1;
297 if (timestamp < cb->cmd.expire_unreachable) {
298 switch (cb->unreachable_expire_kind) {
299 case UE_ALWAYS:
300 return 1;
301 case UE_NORMAL:
302 case UE_HEAD:
303 if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
304 return 1;
305 break;
309 if (cb->cmd.recno && --(cb->cmd.recno) == 0)
310 return 1;
312 return 0;
315 static int should_expire_reflog_ent_verbose(struct object_id *ooid,
316 struct object_id *noid,
317 const char *email,
318 timestamp_t timestamp, int tz,
319 const char *message, void *cb_data)
321 struct expire_reflog_policy_cb *cb = cb_data;
322 int expire;
324 expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
325 message, cb);
327 if (!expire)
328 printf("keep %s", message);
329 else if (cb->dry_run)
330 printf("would prune %s", message);
331 else
332 printf("prune %s", message);
334 return expire;
337 static int push_tip_to_list(const char *refname, const struct object_id *oid,
338 int flags, void *cb_data)
340 struct commit_list **list = cb_data;
341 struct commit *tip_commit;
342 if (flags & REF_ISSYMREF)
343 return 0;
344 tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
345 if (!tip_commit)
346 return 0;
347 commit_list_insert(tip_commit, list);
348 return 0;
351 static int is_head(const char *refname)
353 switch (ref_type(refname)) {
354 case REF_TYPE_OTHER_PSEUDOREF:
355 case REF_TYPE_MAIN_PSEUDOREF:
356 if (parse_worktree_ref(refname, NULL, NULL, &refname))
357 BUG("not a worktree ref: %s", refname);
358 break;
359 default:
360 break;
362 return !strcmp(refname, "HEAD");
365 static void reflog_expiry_prepare(const char *refname,
366 const struct object_id *oid,
367 void *cb_data)
369 struct expire_reflog_policy_cb *cb = cb_data;
370 struct commit_list *elem;
371 struct commit *commit = NULL;
373 if (!cb->cmd.expire_unreachable || is_head(refname)) {
374 cb->unreachable_expire_kind = UE_HEAD;
375 } else {
376 commit = lookup_commit(the_repository, oid);
377 cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
380 if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
381 cb->unreachable_expire_kind = UE_ALWAYS;
383 switch (cb->unreachable_expire_kind) {
384 case UE_ALWAYS:
385 return;
386 case UE_HEAD:
387 for_each_ref(push_tip_to_list, &cb->tips);
388 for (elem = cb->tips; elem; elem = elem->next)
389 commit_list_insert(elem->item, &cb->mark_list);
390 break;
391 case UE_NORMAL:
392 commit_list_insert(commit, &cb->mark_list);
393 /* For reflog_expiry_cleanup() below */
394 cb->tip_commit = commit;
396 cb->mark_limit = cb->cmd.expire_total;
397 mark_reachable(cb);
400 static void reflog_expiry_cleanup(void *cb_data)
402 struct expire_reflog_policy_cb *cb = cb_data;
403 struct commit_list *elem;
405 switch (cb->unreachable_expire_kind) {
406 case UE_ALWAYS:
407 return;
408 case UE_HEAD:
409 for (elem = cb->tips; elem; elem = elem->next)
410 clear_commit_marks(elem->item, REACHABLE);
411 free_commit_list(cb->tips);
412 break;
413 case UE_NORMAL:
414 clear_commit_marks(cb->tip_commit, REACHABLE);
415 break;
419 static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data)
421 struct worktree_reflogs *cb = cb_data;
422 struct worktree *worktree = cb->worktree;
423 struct strbuf newref = STRBUF_INIT;
426 * Avoid collecting the same shared ref multiple times because
427 * they are available via all worktrees.
429 if (!worktree->is_current && ref_type(ref) == REF_TYPE_NORMAL)
430 return 0;
432 strbuf_worktree_ref(worktree, &newref, ref);
433 string_list_append_nodup(&cb->reflogs, strbuf_detach(&newref, NULL));
435 return 0;
438 static struct reflog_expire_cfg {
439 struct reflog_expire_cfg *next;
440 timestamp_t expire_total;
441 timestamp_t expire_unreachable;
442 char pattern[FLEX_ARRAY];
443 } *reflog_expire_cfg, **reflog_expire_cfg_tail;
445 static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
447 struct reflog_expire_cfg *ent;
449 if (!reflog_expire_cfg_tail)
450 reflog_expire_cfg_tail = &reflog_expire_cfg;
452 for (ent = reflog_expire_cfg; ent; ent = ent->next)
453 if (!strncmp(ent->pattern, pattern, len) &&
454 ent->pattern[len] == '\0')
455 return ent;
457 FLEX_ALLOC_MEM(ent, pattern, pattern, len);
458 *reflog_expire_cfg_tail = ent;
459 reflog_expire_cfg_tail = &(ent->next);
460 return ent;
463 /* expiry timer slot */
464 #define EXPIRE_TOTAL 01
465 #define EXPIRE_UNREACH 02
467 static int reflog_expire_config(const char *var, const char *value, void *cb)
469 const char *pattern, *key;
470 size_t pattern_len;
471 timestamp_t expire;
472 int slot;
473 struct reflog_expire_cfg *ent;
475 if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
476 return git_default_config(var, value, cb);
478 if (!strcmp(key, "reflogexpire")) {
479 slot = EXPIRE_TOTAL;
480 if (git_config_expiry_date(&expire, var, value))
481 return -1;
482 } else if (!strcmp(key, "reflogexpireunreachable")) {
483 slot = EXPIRE_UNREACH;
484 if (git_config_expiry_date(&expire, var, value))
485 return -1;
486 } else
487 return git_default_config(var, value, cb);
489 if (!pattern) {
490 switch (slot) {
491 case EXPIRE_TOTAL:
492 default_reflog_expire = expire;
493 break;
494 case EXPIRE_UNREACH:
495 default_reflog_expire_unreachable = expire;
496 break;
498 return 0;
501 ent = find_cfg_ent(pattern, pattern_len);
502 if (!ent)
503 return -1;
504 switch (slot) {
505 case EXPIRE_TOTAL:
506 ent->expire_total = expire;
507 break;
508 case EXPIRE_UNREACH:
509 ent->expire_unreachable = expire;
510 break;
512 return 0;
515 static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
517 struct reflog_expire_cfg *ent;
519 if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
520 return; /* both given explicitly -- nothing to tweak */
522 for (ent = reflog_expire_cfg; ent; ent = ent->next) {
523 if (!wildmatch(ent->pattern, ref, 0)) {
524 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
525 cb->expire_total = ent->expire_total;
526 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
527 cb->expire_unreachable = ent->expire_unreachable;
528 return;
533 * If unconfigured, make stash never expire
535 if (!strcmp(ref, "refs/stash")) {
536 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
537 cb->expire_total = 0;
538 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
539 cb->expire_unreachable = 0;
540 return;
543 /* Nothing matched -- use the default value */
544 if (!(cb->explicit_expiry & EXPIRE_TOTAL))
545 cb->expire_total = default_reflog_expire;
546 if (!(cb->explicit_expiry & EXPIRE_UNREACH))
547 cb->expire_unreachable = default_reflog_expire_unreachable;
550 static const char * reflog_expire_usage[] = {
551 N_("git reflog expire [--expire=<time>] "
552 "[--expire-unreachable=<time>] "
553 "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] "
554 "[--verbose] [--all] <refs>..."),
555 NULL
558 static int expire_unreachable_callback(const struct option *opt,
559 const char *arg,
560 int unset)
562 struct cmd_reflog_expire_cb *cmd = opt->value;
564 if (parse_expiry_date(arg, &cmd->expire_unreachable))
565 die(_("invalid timestamp '%s' given to '--%s'"),
566 arg, opt->long_name);
568 cmd->explicit_expiry |= EXPIRE_UNREACH;
569 return 0;
572 static int expire_total_callback(const struct option *opt,
573 const char *arg,
574 int unset)
576 struct cmd_reflog_expire_cb *cmd = opt->value;
578 if (parse_expiry_date(arg, &cmd->expire_total))
579 die(_("invalid timestamp '%s' given to '--%s'"),
580 arg, opt->long_name);
582 cmd->explicit_expiry |= EXPIRE_TOTAL;
583 return 0;
586 static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
588 struct cmd_reflog_expire_cb cmd = { 0 };
589 timestamp_t now = time(NULL);
590 int i, status, do_all, all_worktrees = 1;
591 unsigned int flags = 0;
592 int verbose = 0;
593 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
594 const struct option options[] = {
595 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
596 EXPIRE_REFLOGS_DRY_RUN),
597 OPT_BIT(0, "rewrite", &flags,
598 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
599 EXPIRE_REFLOGS_REWRITE),
600 OPT_BIT(0, "updateref", &flags,
601 N_("update the reference to the value of the top reflog entry"),
602 EXPIRE_REFLOGS_UPDATE_REF),
603 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen.")),
604 OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
605 N_("prune entries older than the specified time"),
606 PARSE_OPT_NONEG,
607 expire_total_callback),
608 OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
609 N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
610 PARSE_OPT_NONEG,
611 expire_unreachable_callback),
612 OPT_BOOL(0, "stale-fix", &cmd.stalefix,
613 N_("prune any reflog entries that point to broken commits")),
614 OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
615 OPT_BOOL(1, "single-worktree", &all_worktrees,
616 N_("limits processing to reflogs from the current worktree only.")),
617 OPT_END()
620 default_reflog_expire_unreachable = now - 30 * 24 * 3600;
621 default_reflog_expire = now - 90 * 24 * 3600;
622 git_config(reflog_expire_config, NULL);
624 save_commit_buffer = 0;
625 do_all = status = 0;
627 cmd.explicit_expiry = 0;
628 cmd.expire_total = default_reflog_expire;
629 cmd.expire_unreachable = default_reflog_expire_unreachable;
631 argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
633 if (verbose)
634 should_prune_fn = should_expire_reflog_ent_verbose;
637 * We can trust the commits and objects reachable from refs
638 * even in older repository. We cannot trust what's reachable
639 * from reflog if the repository was pruned with older git.
641 if (cmd.stalefix) {
642 struct rev_info revs;
644 repo_init_revisions(the_repository, &revs, prefix);
645 revs.do_not_die_on_missing_tree = 1;
646 revs.ignore_missing = 1;
647 revs.ignore_missing_links = 1;
648 if (verbose)
649 printf(_("Marking reachable objects..."));
650 mark_reachable_objects(&revs, 0, 0, NULL);
651 if (verbose)
652 putchar('\n');
655 if (do_all) {
656 struct worktree_reflogs collected = {
657 .reflogs = STRING_LIST_INIT_DUP,
659 struct string_list_item *item;
660 struct worktree **worktrees, **p;
662 worktrees = get_worktrees();
663 for (p = worktrees; *p; p++) {
664 if (!all_worktrees && !(*p)->is_current)
665 continue;
666 collected.worktree = *p;
667 refs_for_each_reflog(get_worktree_ref_store(*p),
668 collect_reflog, &collected);
670 free_worktrees(worktrees);
672 for_each_string_list_item(item, &collected.reflogs) {
673 struct expire_reflog_policy_cb cb = {
674 .cmd = cmd,
675 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
678 set_reflog_expiry_param(&cb.cmd, item->string);
679 status |= reflog_expire(item->string, flags,
680 reflog_expiry_prepare,
681 should_prune_fn,
682 reflog_expiry_cleanup,
683 &cb);
685 string_list_clear(&collected.reflogs, 0);
688 for (i = 0; i < argc; i++) {
689 char *ref;
690 struct expire_reflog_policy_cb cb = { .cmd = cmd };
692 if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
693 status |= error(_("%s points nowhere!"), argv[i]);
694 continue;
696 set_reflog_expiry_param(&cb.cmd, ref);
697 status |= reflog_expire(ref, flags,
698 reflog_expiry_prepare,
699 should_prune_fn,
700 reflog_expiry_cleanup,
701 &cb);
702 free(ref);
704 return status;
707 static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
708 const char *email, timestamp_t timestamp, int tz,
709 const char *message, void *cb_data)
711 struct cmd_reflog_expire_cb *cb = cb_data;
712 if (!cb->expire_total || timestamp < cb->expire_total)
713 cb->recno++;
714 return 0;
717 static const char * reflog_delete_usage[] = {
718 N_("git reflog delete [--rewrite] [--updateref] "
719 "[--dry-run | -n] [--verbose] <refs>..."),
720 NULL
723 static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
725 struct cmd_reflog_expire_cb cmd = { 0 };
726 int i, status = 0;
727 unsigned int flags = 0;
728 int verbose = 0;
729 reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
730 const struct option options[] = {
731 OPT_BIT(0, "dry-run", &flags, N_("do not actually prune any entries"),
732 EXPIRE_REFLOGS_DRY_RUN),
733 OPT_BIT(0, "rewrite", &flags,
734 N_("rewrite the old SHA1 with the new SHA1 of the entry that now precedes it"),
735 EXPIRE_REFLOGS_REWRITE),
736 OPT_BIT(0, "updateref", &flags,
737 N_("update the reference to the value of the top reflog entry"),
738 EXPIRE_REFLOGS_UPDATE_REF),
739 OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen.")),
740 OPT_END()
743 argc = parse_options(argc, argv, prefix, options, reflog_delete_usage, 0);
745 if (verbose)
746 should_prune_fn = should_expire_reflog_ent_verbose;
748 if (argc < 1)
749 return error(_("no reflog specified to delete"));
751 for (i = 0; i < argc; i++) {
752 const char *spec = strstr(argv[i], "@{");
753 char *ep, *ref;
754 int recno;
755 struct expire_reflog_policy_cb cb = {
756 .dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
759 if (!spec) {
760 status |= error(_("not a reflog: %s"), argv[i]);
761 continue;
764 if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
765 status |= error(_("no reflog for '%s'"), argv[i]);
766 continue;
769 recno = strtoul(spec + 2, &ep, 10);
770 if (*ep == '}') {
771 cmd.recno = -recno;
772 for_each_reflog_ent(ref, count_reflog_ent, &cmd);
773 } else {
774 cmd.expire_total = approxidate(spec + 2);
775 for_each_reflog_ent(ref, count_reflog_ent, &cmd);
776 cmd.expire_total = 0;
779 cb.cmd = cmd;
780 status |= reflog_expire(ref, flags,
781 reflog_expiry_prepare,
782 should_prune_fn,
783 reflog_expiry_cleanup,
784 &cb);
785 free(ref);
787 return status;
790 static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)
792 int i, start = 0;
794 for (i = 1; i < argc; i++) {
795 const char *arg = argv[i];
796 if (!strcmp(arg, "--")) {
797 i++;
798 break;
800 else if (arg[0] == '-')
801 usage(_(reflog_exists_usage));
802 else
803 break;
806 start = i;
808 if (argc - start != 1)
809 usage(_(reflog_exists_usage));
811 if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL))
812 die(_("invalid ref format: %s"), argv[start]);
813 return !reflog_exists(argv[start]);
817 * main "reflog"
820 static const char reflog_usage[] =
821 N_("git reflog [ show | expire | delete | exists ]");
823 int cmd_reflog(int argc, const char **argv, const char *prefix)
825 if (argc > 1 && !strcmp(argv[1], "-h"))
826 usage(_(reflog_usage));
828 /* With no command, we default to showing it. */
829 if (argc < 2 || *argv[1] == '-')
830 return cmd_log_reflog(argc, argv, prefix);
832 if (!strcmp(argv[1], "show"))
833 return cmd_log_reflog(argc - 1, argv + 1, prefix);
835 if (!strcmp(argv[1], "expire"))
836 return cmd_reflog_expire(argc - 1, argv + 1, prefix);
838 if (!strcmp(argv[1], "delete"))
839 return cmd_reflog_delete(argc - 1, argv + 1, prefix);
841 if (!strcmp(argv[1], "exists"))
842 return cmd_reflog_exists(argc - 1, argv + 1, prefix);
844 return cmd_log_reflog(argc, argv, prefix);