builtin-gc.con commit git-gc --auto: restructure the way "repack" command line is built. (95143f9)
   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 int gc_config(const char *var, const char *value)
  33{
  34        if (!strcmp(var, "gc.packrefs")) {
  35                if (!strcmp(value, "notbare"))
  36                        pack_refs = -1;
  37                else
  38                        pack_refs = git_config_bool(var, value);
  39                return 0;
  40        }
  41        if (!strcmp(var, "gc.aggressivewindow")) {
  42                aggressive_window = git_config_int(var, value);
  43                return 0;
  44        }
  45        if (!strcmp(var, "gc.auto")) {
  46                gc_auto_threshold = git_config_int(var, value);
  47                return 0;
  48        }
  49        return git_default_config(var, value);
  50}
  51
  52static void append_option(const char **cmd, const char *opt, int max_length)
  53{
  54        int i;
  55
  56        for (i = 0; cmd[i]; i++)
  57                ;
  58
  59        if (i + 2 >= max_length)
  60                die("Too many options specified");
  61        cmd[i++] = opt;
  62        cmd[i] = NULL;
  63}
  64
  65static int too_many_loose_objects(void)
  66{
  67        /*
  68         * Quickly check if a "gc" is needed, by estimating how
  69         * many loose objects there are.  Because SHA-1 is evenly
  70         * distributed, we can check only one and get a reasonable
  71         * estimate.
  72         */
  73        char path[PATH_MAX];
  74        const char *objdir = get_object_directory();
  75        DIR *dir;
  76        struct dirent *ent;
  77        int auto_threshold;
  78        int num_loose = 0;
  79        int needed = 0;
  80
  81        if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
  82                warning("insanely long object directory %.*s", 50, objdir);
  83                return 0;
  84        }
  85        dir = opendir(path);
  86        if (!dir)
  87                return 0;
  88
  89        auto_threshold = (gc_auto_threshold + 255) / 256;
  90        while ((ent = readdir(dir)) != NULL) {
  91                if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
  92                    ent->d_name[38] != '\0')
  93                        continue;
  94                if (++num_loose > auto_threshold) {
  95                        needed = 1;
  96                        break;
  97                }
  98        }
  99        closedir(dir);
 100        return needed;
 101}
 102
 103static int need_to_gc(void)
 104{
 105        int ac = 0;
 106
 107        /*
 108         * Setting gc.auto to 0 or negative can disable the
 109         * automatic gc
 110         */
 111        if (gc_auto_threshold <= 0)
 112                return 0;
 113
 114        if (!too_many_loose_objects())
 115                return 0;
 116
 117        argv_repack[ac++] = "repack";
 118        argv_repack[ac++] = "-d";
 119        argv_repack[ac++] = "-l";
 120        argv_repack[ac++] = NULL;
 121        return 1;
 122}
 123
 124int cmd_gc(int argc, const char **argv, const char *prefix)
 125{
 126        int i;
 127        int prune = 0;
 128        int auto_gc = 0;
 129        char buf[80];
 130
 131        git_config(gc_config);
 132
 133        if (pack_refs < 0)
 134                pack_refs = !is_bare_repository();
 135
 136        for (i = 1; i < argc; i++) {
 137                const char *arg = argv[i];
 138                if (!strcmp(arg, "--prune")) {
 139                        prune = 1;
 140                        continue;
 141                }
 142                if (!strcmp(arg, "--aggressive")) {
 143                        append_option(argv_repack, "-f", MAX_ADD);
 144                        if (aggressive_window > 0) {
 145                                sprintf(buf, "--window=%d", aggressive_window);
 146                                append_option(argv_repack, buf, MAX_ADD);
 147                        }
 148                        continue;
 149                }
 150                if (!strcmp(arg, "--auto")) {
 151                        auto_gc = 1;
 152                        continue;
 153                }
 154                break;
 155        }
 156        if (i != argc)
 157                usage(builtin_gc_usage);
 158
 159        if (auto_gc) {
 160                /*
 161                 * Auto-gc should be least intrusive as possible.
 162                 */
 163                prune = 0;
 164                if (!need_to_gc())
 165                        return 0;
 166        }
 167
 168        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
 169                return error(FAILED_RUN, argv_pack_refs[0]);
 170
 171        if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
 172                return error(FAILED_RUN, argv_reflog[0]);
 173
 174        if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
 175                return error(FAILED_RUN, argv_repack[0]);
 176
 177        if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
 178                return error(FAILED_RUN, argv_prune[0]);
 179
 180        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
 181                return error(FAILED_RUN, argv_rerere[0]);
 182
 183        if (auto_gc && too_many_loose_objects())
 184                warning("There are too many unreachable loose objects; "
 185                        "run 'git prune' to remove them.");
 186
 187        return 0;
 188}