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 void finish_bulk_checkin(struct bulk_checkin_state *state) { struct object_id oid; struct strbuf packname = STRBUF_INIT; int i; if (!state->f) return; if (state->nr_written == 0) { close(state->f->fd); unlink(state->pack_tmp_name); goto clear_exit; } else if (state->nr_written == 1) { sha1close(state->f, oid.hash, CSUM_FSYNC); } else { int fd = sha1close(state->f, oid.hash, 0); fixup_pack_header_footer(fd, oid.hash, state->pack_tmp_name, state->nr_written, oid.hash, state->offset); close(fd); } strbuf_addf(&packname, "%s/pack/pack-", get_object_directory()); finish_tmp_packfile(&packname, state->pack_tmp_name, state->written, state->nr_written, &state->pack_idx_opts, oid.hash); for (i = 0; i < state->nr_written; i++) free(state->written[i]); clear_exit: free(state->written); memset(state, 0, sizeof(*state)); strbuf_release(&packname); /* Make objects we just wrote available to ourselves */ reprepare_packed_git(); }
static void get_info_packs(struct strbuf *hdr, char *arg) { size_t objdirlen = strlen(get_object_directory()); struct strbuf buf = STRBUF_INIT; struct packed_git *p; size_t cnt = 0; select_getanyfile(hdr); for (p = get_all_packs(the_repository); p; p = p->next) { if (p->pack_local) cnt++; } strbuf_grow(&buf, cnt * 53 + 2); for (p = get_all_packs(the_repository); p; p = p->next) { if (p->pack_local) strbuf_addf(&buf, "P %s\n", p->pack_name + objdirlen + 6); } strbuf_addch(&buf, '\n'); hdr_nocache(hdr); send_strbuf(hdr, "text/plain; charset=utf-8", &buf); strbuf_release(&buf); }
static int rsync_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; int result = 0, i; struct child_process rsync; const char *args[10]; if (flags & TRANSPORT_PUSH_MIRROR) return error("rsync transport does not support mirror mode"); /* first push the objects */ strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addch(&buf, '/'); memset(&rsync, 0, sizeof(rsync)); rsync.argv = args; rsync.stdout_to_stderr = 1; i = 0; args[i++] = "rsync"; args[i++] = "-a"; if (flags & TRANSPORT_PUSH_DRY_RUN) args[i++] = "--dry-run"; if (transport->verbose > 1) args[i++] = "-v"; args[i++] = "--ignore-existing"; args[i++] = "--exclude"; args[i++] = "info"; args[i++] = get_object_directory(); args[i++] = buf.buf; args[i++] = NULL; if (run_command(&rsync)) return error("Could not push objects to %s", rsync_url(transport->url)); /* copy the refs to the temporary directory; they could be packed. */ strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX")); if (!mkdtemp(temp_dir.buf)) die_errno ("Could not make temporary directory"); strbuf_addch(&temp_dir, '/'); if (flags & TRANSPORT_PUSH_ALL) { if (for_each_ref(write_one_ref, &temp_dir)) return -1; } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec)) return -1; i = 2; if (flags & TRANSPORT_PUSH_DRY_RUN) args[i++] = "--dry-run"; if (!(flags & TRANSPORT_PUSH_FORCE)) args[i++] = "--ignore-existing"; args[i++] = temp_dir.buf; args[i++] = rsync_url(transport->url); args[i++] = NULL; if (run_command(&rsync)) result = error("Could not push to %s", rsync_url(transport->url)); if (remove_dir_recursively(&temp_dir, 0)) warning ("Could not remove temporary directory %s.", temp_dir.buf); strbuf_release(&buf); strbuf_release(&temp_dir); return result; }
int cmd_gc(int argc, const char **argv, const char *prefix) { int aggressive = 0; int auto_gc = 0; int quiet = 0; int force = 0; const char *name; pid_t pid; int daemonized = 0; int keep_base_pack = -1; timestamp_t dummy; struct option builtin_gc_options[] = { OPT__QUIET(&quiet, N_("suppress progress reporting")), { OPTION_STRING, 0, "prune", &prune_expire, N_("date"), N_("prune unreferenced objects"), PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), PARSE_OPT_NOCOMPLETE), OPT_BOOL_F(0, "force", &force, N_("force running gc even if there may be another gc running"), PARSE_OPT_NOCOMPLETE), OPT_BOOL(0, "keep-largest-pack", &keep_base_pack, N_("repack all other packs except the largest pack")), OPT_END() }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_gc_usage, builtin_gc_options); argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL); argv_array_pushl(&repack, "repack", "-d", "-l", NULL); argv_array_pushl(&prune, "prune", "--expire", NULL); argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); /* default expiry time, overwritten in gc_config */ gc_config(); if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) die(_("failed to parse gc.logexpiry value %s"), gc_log_expire); if (pack_refs < 0) pack_refs = !is_bare_repository(); argc = parse_options(argc, argv, prefix, builtin_gc_options, builtin_gc_usage, 0); if (argc > 0) usage_with_options(builtin_gc_usage, builtin_gc_options); if (prune_expire && parse_expiry_date(prune_expire, &dummy)) die(_("failed to parse prune expiry value %s"), prune_expire); if (aggressive) { argv_array_push(&repack, "-f"); if (aggressive_depth > 0) argv_array_pushf(&repack, "--depth=%d", aggressive_depth); if (aggressive_window > 0) argv_array_pushf(&repack, "--window=%d", aggressive_window); } if (quiet) argv_array_push(&repack, "-q"); if (auto_gc) { /* * Auto-gc should be least intrusive as possible. */ if (!need_to_gc()) return 0; if (!quiet) { if (detach_auto) fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); else fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n")); } if (detach_auto) { int ret = report_last_gc_error(); if (ret < 0) /* an I/O error occured, already reported */ exit(128); if (ret == 1) /* Last gc --auto failed. Skip this one. */ return 0; if (lock_repo_for_gc(force, &pid)) return 0; gc_before_repack(); /* dies on failure */ delete_tempfile(&pidfile); /* * failure to daemonize is ok, we'll continue * in foreground */ daemonized = !daemonize(); } } else { struct string_list keep_pack = STRING_LIST_INIT_NODUP; if (keep_base_pack != -1) { if (keep_base_pack) find_base_packs(&keep_pack, 0); } else if (big_pack_threshold) { find_base_packs(&keep_pack, big_pack_threshold); } add_repack_all_option(&keep_pack); string_list_clear(&keep_pack, 0); } name = lock_repo_for_gc(force, &pid); if (name) { if (auto_gc) return 0; /* be quiet on --auto */ die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"), name, (uintmax_t)pid); } if (daemonized) { hold_lock_file_for_update(&log_lock, git_path("gc.log"), LOCK_DIE_ON_ERROR); dup2(get_lock_file_fd(&log_lock), 2); sigchain_push_common(process_log_file_on_signal); atexit(process_log_file_at_exit); } gc_before_repack(); if (!repository_format_precious_objects) { close_all_packs(the_repository->objects); if (run_command_v_opt(repack.argv, RUN_GIT_CMD)) die(FAILED_RUN, repack.argv[0]); if (prune_expire) { argv_array_push(&prune, prune_expire); if (quiet) argv_array_push(&prune, "--no-progress"); if (repository_format_partial_clone) argv_array_push(&prune, "--exclude-promisor-objects"); if (run_command_v_opt(prune.argv, RUN_GIT_CMD)) die(FAILED_RUN, prune.argv[0]); } } if (prune_worktrees_expire) { argv_array_push(&prune_worktrees, prune_worktrees_expire); if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD)) die(FAILED_RUN, prune_worktrees.argv[0]); } if (run_command_v_opt(rerere.argv, RUN_GIT_CMD)) die(FAILED_RUN, rerere.argv[0]); report_garbage = report_pack_garbage; reprepare_packed_git(the_repository); if (pack_garbage.nr > 0) clean_pack_garbage(); if (gc_write_commit_graph) write_commit_graph_reachable(get_object_directory(), 0, !quiet && !daemonized); if (auto_gc && too_many_loose_objects()) warning(_("There are too many unreachable loose objects; " "run 'git prune' to remove them.")); if (!daemonized) unlink(git_path("gc.log")); return 0; }
int cmd_fsck(int argc, const char **argv, const char *prefix) { int i; struct alternate_object_database *alt; /* fsck knows how to handle missing promisor objects */ fetch_if_missing = 0; errors_found = 0; check_replace_refs = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); fsck_walk_options.walk = mark_object; fsck_obj_options.walk = mark_used; fsck_obj_options.error_func = fsck_error_func; if (check_strict) fsck_obj_options.strict = 1; if (show_progress == -1) show_progress = isatty(2); if (verbose) show_progress = 0; if (write_lost_and_found) { check_full = 1; include_reflogs = 0; } if (name_objects) fsck_walk_options.object_names = xcalloc(1, sizeof(struct decoration)); git_config(fsck_config, NULL); fsck_head_link(); if (connectivity_only) { for_each_loose_object(mark_loose_for_connectivity, NULL, 0); for_each_packed_object(mark_packed_for_connectivity, NULL, 0); } else { struct alternate_object_database *alt_odb_list; fsck_object_dir(get_object_directory()); prepare_alt_odb(the_repository); alt_odb_list = the_repository->objects->alt_odb_list; for (alt = alt_odb_list; alt; alt = alt->next) fsck_object_dir(alt->path); if (check_full) { struct packed_git *p; uint32_t total = 0, count = 0; struct progress *progress = NULL; if (show_progress) { for (p = get_packed_git(the_repository); p; p = p->next) { if (open_pack_index(p)) continue; total += p->num_objects; } progress = start_progress(_("Checking objects"), total); } for (p = get_packed_git(the_repository); p; p = p->next) { /* verify gives error messages itself */ if (verify_pack(p, fsck_obj_buffer, progress, count)) errors_found |= ERROR_PACK; count += p->num_objects; } stop_progress(&progress); } } for (i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; if (!get_oid(arg, &oid)) { struct object *obj = lookup_object(oid.hash); if (!obj || !(obj->flags & HAS_OBJ)) { if (is_promisor_object(&oid)) continue; error("%s: object missing", oid_to_hex(&oid)); errors_found |= ERROR_OBJECT; continue; } obj->flags |= USED; if (name_objects) add_decoration(fsck_walk_options.object_names, obj, xstrdup(arg)); mark_object_reachable(obj); continue; } error("invalid parameter: expected sha1, got '%s'", arg); errors_found |= ERROR_OBJECT; } /* * If we've not been given any explicit head information, do the * default ones from .git/refs. We also consider the index file * in this case (ie this implies --cache). */ if (!argc) { get_default_heads(); keep_cache_objects = 1; } if (keep_cache_objects) { verify_index_checksum = 1; verify_ce_order = 1; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; struct blob *blob; struct object *obj; mode = active_cache[i]->ce_mode; if (S_ISGITLINK(mode)) continue; blob = lookup_blob(&active_cache[i]->oid); if (!blob) continue; obj = &blob->object; obj->flags |= USED; if (name_objects) add_decoration(fsck_walk_options.object_names, obj, xstrfmt(":%s", active_cache[i]->name)); mark_object_reachable(obj); } if (active_cache_tree) fsck_cache_tree(active_cache_tree); } check_connectivity(); return errors_found; }
int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; int exclude_promisor_objects = 0; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, N_("limit traversal to objects outside promisor packfiles")), OPT_END() }; char *s; git_config(git_default_config, NULL); expire = TIME_MAX; save_commit_buffer = 0; check_replace_refs = 0; ref_paranoia = 1; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); if (repository_format_precious_objects) die(_("cannot prune in a precious-objects repo")); while (argc--) { struct object_id oid; const char *name = *argv++; if (!get_oid(name, &oid)) { struct object *object = parse_object_or_die(&oid, name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } if (show_progress == -1) show_progress = isatty(2); if (show_progress) progress = start_delayed_progress(_("Checking connectivity"), 0); if (exclude_promisor_objects) { fetch_if_missing = 0; revs.exclude_promisor_objects = 1; } mark_reachable_objects(&revs, 1, expire, progress); stop_progress(&progress); for_each_loose_file_in_objdir(get_object_directory(), prune_object, prune_cruft, prune_subdir, NULL); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); remove_temporary_files(get_object_directory()); s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); if (is_repository_shallow()) prune_shallow(show_only); return 0; }
static int graph_write(int argc, const char **argv) { struct string_list *pack_indexes = NULL; struct string_list *commit_hex = NULL; struct string_list lines; static struct option builtin_commit_graph_write_options[] = { OPT_STRING(0, "object-dir", &opts.obj_dir, N_("dir"), N_("The object directory to store the graph")), OPT_BOOL(0, "reachable", &opts.reachable, N_("start walk at all refs")), OPT_BOOL(0, "stdin-packs", &opts.stdin_packs, N_("scan pack-indexes listed by stdin for commits")), OPT_BOOL(0, "stdin-commits", &opts.stdin_commits, N_("start walk at commits listed by stdin")), OPT_BOOL(0, "append", &opts.append, N_("include all commits already in the commit-graph file")), OPT_END(), }; argc = parse_options(argc, argv, NULL, builtin_commit_graph_write_options, builtin_commit_graph_write_usage, 0); if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1) die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs")); if (!opts.obj_dir) opts.obj_dir = get_object_directory(); read_replace_refs = 0; if (opts.reachable) { write_commit_graph_reachable(opts.obj_dir, opts.append, 1); return 0; } string_list_init(&lines, 0); if (opts.stdin_packs || opts.stdin_commits) { struct strbuf buf = STRBUF_INIT; while (strbuf_getline(&buf, stdin) != EOF) string_list_append(&lines, strbuf_detach(&buf, NULL)); if (opts.stdin_packs) pack_indexes = &lines; if (opts.stdin_commits) commit_hex = &lines; UNLEAK(buf); } write_commit_graph(opts.obj_dir, pack_indexes, commit_hex, opts.append, 1); UNLEAK(lines); return 0; }
int cmd_count_objects(int argc, const char **argv, const char *prefix) { int human_readable = 0; struct option opts[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT_BOOL('H', "human-readable", &human_readable, N_("print sizes in human readable format")), OPT_END(), }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ if (argc) usage_with_options(count_objects_usage, opts); if (verbose) { report_garbage = real_report_garbage; report_linked_checkout_garbage(); } for_each_loose_file_in_objdir(get_object_directory(), count_loose, count_cruft, NULL, NULL); if (verbose) { struct packed_git *p; unsigned long num_pack = 0; off_t size_pack = 0; struct strbuf loose_buf = STRBUF_INIT; struct strbuf pack_buf = STRBUF_INIT; struct strbuf garbage_buf = STRBUF_INIT; if (!packed_git) prepare_packed_git(); for (p = packed_git; p; p = p->next) { if (!p->pack_local) continue; if (open_pack_index(p)) continue; packed += p->num_objects; size_pack += p->pack_size + p->index_size; num_pack++; } if (human_readable) { strbuf_humanise_bytes(&loose_buf, loose_size); strbuf_humanise_bytes(&pack_buf, size_pack); strbuf_humanise_bytes(&garbage_buf, size_garbage); } else { strbuf_addf(&loose_buf, "%lu", (unsigned long)(loose_size / 1024)); strbuf_addf(&pack_buf, "%lu", (unsigned long)(size_pack / 1024)); strbuf_addf(&garbage_buf, "%lu", (unsigned long)(size_garbage / 1024)); } printf("count: %lu\n", loose); printf("size: %s\n", loose_buf.buf); printf("in-pack: %lu\n", packed); printf("packs: %lu\n", num_pack); printf("size-pack: %s\n", pack_buf.buf); printf("prune-packable: %lu\n", packed_loose); printf("garbage: %lu\n", garbage); printf("size-garbage: %s\n", garbage_buf.buf); foreach_alt_odb(print_alternate, NULL); strbuf_release(&loose_buf); strbuf_release(&pack_buf); strbuf_release(&garbage_buf); } else { struct strbuf buf = STRBUF_INIT; if (human_readable) strbuf_humanise_bytes(&buf, loose_size); else strbuf_addf(&buf, "%lu kilobytes", (unsigned long)(loose_size / 1024)); printf("%lu objects, %s\n", loose, buf.buf); strbuf_release(&buf); } return 0; }
int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; int prune_repos = 0; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_BOOL(0, "repos", &prune_repos, N_("prune .git/repos/")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_END() }; char *s; expire = ULONG_MAX; save_commit_buffer = 0; check_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); if (prune_repos) { if (argc) die(_("--repos does not take extra arguments")); prune_repos_dir(); return 0; } while (argc--) { unsigned char sha1[20]; const char *name = *argv++; if (!get_sha1(name, sha1)) { struct object *object = parse_object_or_die(sha1, name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } if (show_progress == -1) show_progress = isatty(2); if (show_progress) progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); mark_reachable_objects(&revs, 1, progress); stop_progress(&progress); prune_object_dir(get_object_directory()); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); remove_temporary_files(get_object_directory()); s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); if (is_repository_shallow()) prune_shallow(show_only); return 0; }