diff options
Diffstat (limited to 'builtin-gc.c')
-rw-r--r-- | builtin-gc.c | 131 |
1 files changed, 130 insertions, 1 deletions
diff --git a/builtin-gc.c b/builtin-gc.c index 9397482610..23ad2b6a21 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -20,6 +20,8 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; static int pack_refs = 1; static int aggressive_window = -1; +static int gc_auto_threshold = 6700; +static int gc_auto_pack_limit = 20; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; @@ -41,6 +43,14 @@ static int gc_config(const char *var, const char *value) aggressive_window = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.auto")) { + gc_auto_threshold = git_config_int(var, value); + return 0; + } + if (!strcmp(var, "gc.autopacklimit")) { + gc_auto_pack_limit = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } @@ -57,10 +67,113 @@ static void append_option(const char **cmd, const char *opt, int max_length) cmd[i] = NULL; } +static int too_many_loose_objects(void) +{ + /* + * Quickly check if a "gc" is needed, by estimating how + * many loose objects there are. Because SHA-1 is evenly + * distributed, we can check only one and get a reasonable + * estimate. + */ + char path[PATH_MAX]; + const char *objdir = get_object_directory(); + DIR *dir; + struct dirent *ent; + int auto_threshold; + int num_loose = 0; + int needed = 0; + + if (gc_auto_threshold <= 0) + return 0; + + if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { + warning("insanely long object directory %.*s", 50, objdir); + return 0; + } + dir = opendir(path); + if (!dir) + return 0; + + auto_threshold = (gc_auto_threshold + 255) / 256; + while ((ent = readdir(dir)) != NULL) { + if (strspn(ent->d_name, "0123456789abcdef") != 38 || + ent->d_name[38] != '\0') + continue; + if (++num_loose > auto_threshold) { + needed = 1; + break; + } + } + closedir(dir); + return needed; +} + +static int too_many_packs(void) +{ + struct packed_git *p; + int cnt; + + if (gc_auto_pack_limit <= 0) + return 0; + + prepare_packed_git(); + for (cnt = 0, p = packed_git; p; p = p->next) { + char path[PATH_MAX]; + size_t len; + int keep; + + if (!p->pack_local) + continue; + len = strlen(p->pack_name); + if (PATH_MAX <= len + 1) + continue; /* oops, give up */ + memcpy(path, p->pack_name, len-5); + memcpy(path + len - 5, ".keep", 6); + keep = access(p->pack_name, F_OK) && (errno == ENOENT); + if (keep) + continue; + /* + * Perhaps check the size of the pack and count only + * very small ones here? + */ + cnt++; + } + return gc_auto_pack_limit <= cnt; +} + +static int need_to_gc(void) +{ + int ac = 0; + + /* + * Setting gc.auto and gc.autopacklimit to 0 or negative can + * disable the automatic gc. + */ + if (gc_auto_threshold <= 0 && gc_auto_pack_limit <= 0) + return 0; + + /* + * If there are too many loose objects, but not too many + * packs, we run "repack -d -l". If there are too many packs, + * we run "repack -A -d -l". Otherwise we tell the caller + * there is no need. + */ + argv_repack[ac++] = "repack"; + if (too_many_packs()) + argv_repack[ac++] = "-A"; + else if (!too_many_loose_objects()) + return 0; + argv_repack[ac++] = "-d"; + argv_repack[ac++] = "-l"; + argv_repack[ac++] = NULL; + return 1; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int i; int prune = 0; + int auto_gc = 0; char buf[80]; git_config(gc_config); @@ -82,12 +195,24 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } continue; } - /* perhaps other parameters later... */ + if (!strcmp(arg, "--auto")) { + auto_gc = 1; + continue; + } break; } if (i != argc) usage(builtin_gc_usage); + if (auto_gc) { + /* + * Auto-gc should be least intrusive as possible. + */ + prune = 0; + if (!need_to_gc()) + return 0; + } + if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); @@ -103,5 +228,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) return error(FAILED_RUN, argv_rerere[0]); + if (auto_gc && too_many_loose_objects()) + warning("There are too many unreachable loose objects; " + "run 'git prune' to remove them."); + return 0; } |