builtin-gc.con commit Implement git gc --auto (2c3c439)
   1/*
   2 * git gc builtin command
   3 *
   4 * Cleanup unreachable files and optimize the repository.
   5 *
   6 * Copyright (c) 2007 James Bowes
   7 *
   8 * Based on git-gc.sh, which is
   9 *
  10 * Copyright (c) 2006 Shawn O. Pearce
  11 */
  12
  13#include "builtin.h"
  14#include "cache.h"
  15#include "run-command.h"
  16
  17#define FAILED_RUN "failed to run %s"
  18
  19static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]";
  20
  21static int pack_refs = 1;
  22static int aggressive_window = -1;
  23static int gc_auto_threshold = 6700;
  24
  25#define MAX_ADD 10
  26static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
  27static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
  28static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL};
  29static const char *argv_prune[] = {"prune", NULL};
  30static const char *argv_rerere[] = {"rerere", "gc", NULL};
  31
  32static const char *argv_repack_auto[] = {"repack", "-d", "-l", NULL};
  33
  34static int gc_config(const char *var, const char *value)
  35{
  36        if (!strcmp(var, "gc.packrefs")) {
  37                if (!strcmp(value, "notbare"))
  38                        pack_refs = -1;
  39                else
  40                        pack_refs = git_config_bool(var, value);
  41                return 0;
  42        }
  43        if (!strcmp(var, "gc.aggressivewindow")) {
  44                aggressive_window = git_config_int(var, value);
  45                return 0;
  46        }
  47        if (!strcmp(var, "gc.auto")) {
  48                gc_auto_threshold = git_config_int(var, value);
  49                return 0;
  50        }
  51        return git_default_config(var, value);
  52}
  53
  54static void append_option(const char **cmd, const char *opt, int max_length)
  55{
  56        int i;
  57
  58        for (i = 0; cmd[i]; i++)
  59                ;
  60
  61        if (i + 2 >= max_length)
  62                die("Too many options specified");
  63        cmd[i++] = opt;
  64        cmd[i] = NULL;
  65}
  66
  67static int need_to_gc(void)
  68{
  69        /*
  70         * Quickly check if a "gc" is needed, by estimating how
  71         * many loose objects there are.  Because SHA-1 is evenly
  72         * distributed, we can check only one and get a reasonable
  73         * estimate.
  74         */
  75        char path[PATH_MAX];
  76        const char *objdir = get_object_directory();
  77        DIR *dir;
  78        struct dirent *ent;
  79        int auto_threshold;
  80        int num_loose = 0;
  81        int needed = 0;
  82
  83        if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
  84                warning("insanely long object directory %.*s", 50, objdir);
  85                return 0;
  86        }
  87        dir = opendir(path);
  88        if (!dir)
  89                return 0;
  90
  91        auto_threshold = (gc_auto_threshold + 255) / 256;
  92        while ((ent = readdir(dir)) != NULL) {
  93                if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
  94                    ent->d_name[38] != '\0')
  95                        continue;
  96                if (++num_loose > auto_threshold) {
  97                        needed = 1;
  98                        break;
  99                }
 100        }
 101        closedir(dir);
 102        return needed;
 103}
 104
 105int cmd_gc(int argc, const char **argv, const char *prefix)
 106{
 107        int i;
 108        int prune = 0;
 109        int auto_gc = 0;
 110        char buf[80];
 111
 112        git_config(gc_config);
 113
 114        if (pack_refs < 0)
 115                pack_refs = !is_bare_repository();
 116
 117        for (i = 1; i < argc; i++) {
 118                const char *arg = argv[i];
 119                if (!strcmp(arg, "--prune")) {
 120                        prune = 1;
 121                        continue;
 122                }
 123                if (!strcmp(arg, "--aggressive")) {
 124                        append_option(argv_repack, "-f", MAX_ADD);
 125                        if (aggressive_window > 0) {
 126                                sprintf(buf, "--window=%d", aggressive_window);
 127                                append_option(argv_repack, buf, MAX_ADD);
 128                        }
 129                        continue;
 130                }
 131                if (!strcmp(arg, "--auto")) {
 132                        if (gc_auto_threshold <= 0)
 133                                return 0;
 134                        auto_gc = 1;
 135                        continue;
 136                }
 137                break;
 138        }
 139        if (i != argc)
 140                usage(builtin_gc_usage);
 141
 142        if (auto_gc) {
 143                /*
 144                 * Auto-gc should be least intrusive as possible.
 145                 */
 146                prune = 0;
 147                for (i = 0; i < ARRAY_SIZE(argv_repack_auto); i++)
 148                        argv_repack[i] = argv_repack_auto[i];
 149                if (!need_to_gc())
 150                        return 0;
 151        }
 152
 153        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
 154                return error(FAILED_RUN, argv_pack_refs[0]);
 155
 156        if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
 157                return error(FAILED_RUN, argv_reflog[0]);
 158
 159        if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
 160                return error(FAILED_RUN, argv_repack[0]);
 161
 162        if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
 163                return error(FAILED_RUN, argv_prune[0]);
 164
 165        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
 166                return error(FAILED_RUN, argv_rerere[0]);
 167
 168        return 0;
 169}