Git fork
at reftables-rust 217 lines 5.4 kB view raw
1#define DISABLE_SIGN_COMPARE_WARNINGS 2 3#include "builtin.h" 4#include "commit.h" 5#include "diff.h" 6#include "dir.h" 7#include "environment.h" 8#include "gettext.h" 9#include "hex.h" 10#include "revision.h" 11#include "reachable.h" 12#include "parse-options.h" 13#include "path.h" 14#include "progress.h" 15#include "prune-packed.h" 16#include "replace-object.h" 17#include "object-file.h" 18#include "object-name.h" 19#include "odb.h" 20#include "shallow.h" 21 22static const char * const prune_usage[] = { 23 N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"), 24 NULL 25}; 26static int show_only; 27static int verbose; 28static timestamp_t expire; 29static int show_progress = -1; 30 31static int prune_tmp_file(const char *fullpath) 32{ 33 struct stat st; 34 if (lstat(fullpath, &st)) 35 return error("Could not stat '%s'", fullpath); 36 if (st.st_mtime > expire) 37 return 0; 38 if (S_ISDIR(st.st_mode)) { 39 if (show_only || verbose) 40 printf("Removing stale temporary directory %s\n", fullpath); 41 if (!show_only) { 42 struct strbuf remove_dir_buf = STRBUF_INIT; 43 44 strbuf_addstr(&remove_dir_buf, fullpath); 45 remove_dir_recursively(&remove_dir_buf, 0); 46 strbuf_release(&remove_dir_buf); 47 } 48 } else { 49 if (show_only || verbose) 50 printf("Removing stale temporary file %s\n", fullpath); 51 if (!show_only) 52 unlink_or_warn(fullpath); 53 } 54 return 0; 55} 56 57static void perform_reachability_traversal(struct rev_info *revs) 58{ 59 static int initialized; 60 struct progress *progress = NULL; 61 62 if (initialized) 63 return; 64 65 if (show_progress) 66 progress = start_delayed_progress(revs->repo, 67 _("Checking connectivity"), 0); 68 mark_reachable_objects(revs, 1, expire, progress); 69 stop_progress(&progress); 70 initialized = 1; 71} 72 73static int is_object_reachable(const struct object_id *oid, 74 struct rev_info *revs) 75{ 76 struct object *obj; 77 78 perform_reachability_traversal(revs); 79 80 obj = lookup_object(revs->repo, oid); 81 return obj && (obj->flags & SEEN); 82} 83 84static int prune_object(const struct object_id *oid, const char *fullpath, 85 void *data) 86{ 87 struct rev_info *revs = data; 88 struct stat st; 89 90 if (is_object_reachable(oid, revs)) 91 return 0; 92 93 if (lstat(fullpath, &st)) { 94 /* report errors, but do not stop pruning */ 95 error("Could not stat '%s'", fullpath); 96 return 0; 97 } 98 if (st.st_mtime > expire) 99 return 0; 100 if (show_only || verbose) { 101 enum object_type type = 102 odb_read_object_info(revs->repo->objects, oid, NULL); 103 printf("%s %s\n", oid_to_hex(oid), 104 (type > 0) ? type_name(type) : "unknown"); 105 } 106 if (!show_only) 107 unlink_or_warn(fullpath); 108 return 0; 109} 110 111static int prune_cruft(const char *basename, const char *path, 112 void *data UNUSED) 113{ 114 if (starts_with(basename, "tmp_obj_")) 115 prune_tmp_file(path); 116 else 117 fprintf(stderr, "bad sha1 file: %s\n", path); 118 return 0; 119} 120 121static int prune_subdir(unsigned int nr UNUSED, const char *path, 122 void *data UNUSED) 123{ 124 if (!show_only) 125 rmdir(path); 126 return 0; 127} 128 129/* 130 * Write errors (particularly out of space) can result in 131 * failed temporary packs (and more rarely indexes and other 132 * files beginning with "tmp_") accumulating in the object 133 * and the pack directories. 134 */ 135static void remove_temporary_files(const char *path) 136{ 137 DIR *dir; 138 struct dirent *de; 139 140 dir = opendir(path); 141 if (!dir) { 142 if (errno != ENOENT) 143 fprintf(stderr, "Unable to open directory %s: %s\n", 144 path, strerror(errno)); 145 return; 146 } 147 while ((de = readdir(dir)) != NULL) 148 if (starts_with(de->d_name, "tmp_")) 149 prune_tmp_file(mkpath("%s/%s", path, de->d_name)); 150 closedir(dir); 151} 152 153int cmd_prune(int argc, 154 const char **argv, 155 const char *prefix, 156 struct repository *repo) 157{ 158 struct rev_info revs; 159 int exclude_promisor_objects = 0; 160 const struct option options[] = { 161 OPT__DRY_RUN(&show_only, N_("do not remove, show only")), 162 OPT__VERBOSE(&verbose, N_("report pruned objects")), 163 OPT_BOOL(0, "progress", &show_progress, N_("show progress")), 164 OPT_EXPIRY_DATE(0, "expire", &expire, 165 N_("expire objects older than <time>")), 166 OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, 167 N_("limit traversal to objects outside promisor packfiles")), 168 OPT_END() 169 }; 170 char *s; 171 172 expire = TIME_MAX; 173 save_commit_buffer = 0; 174 disable_replace_refs(); 175 176 argc = parse_options(argc, argv, prefix, options, prune_usage, 0); 177 178 repo_init_revisions(repo, &revs, prefix); 179 if (repo->repository_format_precious_objects) 180 die(_("cannot prune in a precious-objects repo")); 181 182 while (argc--) { 183 struct object_id oid; 184 const char *name = *argv++; 185 186 if (!repo_get_oid(repo, name, &oid)) { 187 struct object *object = parse_object_or_die(repo, &oid, name); 188 add_pending_object(&revs, object, ""); 189 } 190 else 191 die("unrecognized argument: %s", name); 192 } 193 194 if (show_progress == -1) 195 show_progress = isatty(2); 196 if (exclude_promisor_objects) { 197 fetch_if_missing = 0; 198 revs.exclude_promisor_objects = 1; 199 } 200 201 for_each_loose_file_in_source(repo->objects->sources, 202 prune_object, prune_cruft, prune_subdir, &revs); 203 204 prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); 205 remove_temporary_files(repo_get_object_directory(repo)); 206 s = mkpathdup("%s/pack", repo_get_object_directory(repo)); 207 remove_temporary_files(s); 208 free(s); 209 210 if (is_repository_shallow(repo)) { 211 perform_reachability_traversal(&revs); 212 prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0); 213 } 214 215 release_revisions(&revs); 216 return 0; 217}