f046a2a66579bafd4f705f56838ed8ab3752890e
   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        /*
  84         * Setting gc.auto to 0 or negative can disable the
  85         * automatic gc
  86         */
  87        if (gc_auto_threshold <= 0)
  88                return 0;
  89
  90        if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
  91                warning("insanely long object directory %.*s", 50, objdir);
  92                return 0;
  93        }
  94        dir = opendir(path);
  95        if (!dir)
  96                return 0;
  97
  98        auto_threshold = (gc_auto_threshold + 255) / 256;
  99        while ((ent = readdir(dir)) != NULL) {
 100                if (strspn(ent->d_name, "0123456789abcdef") != 38 ||
 101                    ent->d_name[38] != '\0')
 102                        continue;
 103                if (++num_loose > auto_threshold) {
 104                        needed = 1;
 105                        break;
 106                }
 107        }
 108        closedir(dir);
 109        return needed;
 110}
 111
 112int cmd_gc(int argc, const char **argv, const char *prefix)
 113{
 114        int i;
 115        int prune = 0;
 116        int auto_gc = 0;
 117        char buf[80];
 118
 119        git_config(gc_config);
 120
 121        if (pack_refs < 0)
 122                pack_refs = !is_bare_repository();
 123
 124        for (i = 1; i < argc; i++) {
 125                const char *arg = argv[i];
 126                if (!strcmp(arg, "--prune")) {
 127                        prune = 1;
 128                        continue;
 129                }
 130                if (!strcmp(arg, "--aggressive")) {
 131                        append_option(argv_repack, "-f", MAX_ADD);
 132                        if (aggressive_window > 0) {
 133                                sprintf(buf, "--window=%d", aggressive_window);
 134                                append_option(argv_repack, buf, MAX_ADD);
 135                        }
 136                        continue;
 137                }
 138                if (!strcmp(arg, "--auto")) {
 139                        auto_gc = 1;
 140                        continue;
 141                }
 142                break;
 143        }
 144        if (i != argc)
 145                usage(builtin_gc_usage);
 146
 147        if (auto_gc) {
 148                /*
 149                 * Auto-gc should be least intrusive as possible.
 150                 */
 151                prune = 0;
 152                for (i = 0; i < ARRAY_SIZE(argv_repack_auto); i++)
 153                        argv_repack[i] = argv_repack_auto[i];
 154                if (!need_to_gc())
 155                        return 0;
 156        }
 157
 158        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
 159                return error(FAILED_RUN, argv_pack_refs[0]);
 160
 161        if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
 162                return error(FAILED_RUN, argv_reflog[0]);
 163
 164        if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
 165                return error(FAILED_RUN, argv_repack[0]);
 166
 167        if (prune && run_command_v_opt(argv_prune, RUN_GIT_CMD))
 168                return error(FAILED_RUN, argv_prune[0]);
 169
 170        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
 171                return error(FAILED_RUN, argv_rerere[0]);
 172
 173        return 0;
 174}