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