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