git-svn: add join_paths() to safely concatenate paths
[git/gitweb.git] / builtin / prune.c
blobb99b635e44f6145395b5c761505099c0a0f5b6cc
1 #include "cache.h"
2 #include "commit.h"
3 #include "diff.h"
4 #include "revision.h"
5 #include "builtin.h"
6 #include "reachable.h"
7 #include "parse-options.h"
8 #include "progress.h"
9 #include "dir.h"
11 static const char * const prune_usage[] = {
12 "git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
13 NULL
15 static int show_only;
16 static int verbose;
17 static unsigned long expire;
18 static int show_progress = -1;
20 static int prune_tmp_object(const char *path, const char *filename)
22 const char *fullpath = mkpath("%s/%s", path, filename);
23 struct stat st;
24 if (lstat(fullpath, &st))
25 return error("Could not stat '%s'", fullpath);
26 if (st.st_mtime > expire)
27 return 0;
28 printf("Removing stale temporary file %s\n", fullpath);
29 if (!show_only)
30 unlink_or_warn(fullpath);
31 return 0;
34 static int prune_object(char *path, const char *filename, const unsigned char *sha1)
36 const char *fullpath = mkpath("%s/%s", path, filename);
37 struct stat st;
38 if (lstat(fullpath, &st))
39 return error("Could not stat '%s'", fullpath);
40 if (st.st_mtime > expire)
41 return 0;
42 if (show_only || verbose) {
43 enum object_type type = sha1_object_info(sha1, NULL);
44 printf("%s %s\n", sha1_to_hex(sha1),
45 (type > 0) ? typename(type) : "unknown");
47 if (!show_only)
48 unlink_or_warn(fullpath);
49 return 0;
52 static int prune_dir(int i, char *path)
54 DIR *dir = opendir(path);
55 struct dirent *de;
57 if (!dir)
58 return 0;
60 while ((de = readdir(dir)) != NULL) {
61 char name[100];
62 unsigned char sha1[20];
64 if (is_dot_or_dotdot(de->d_name))
65 continue;
66 if (strlen(de->d_name) == 38) {
67 sprintf(name, "%02x", i);
68 memcpy(name+2, de->d_name, 39);
69 if (get_sha1_hex(name, sha1) < 0)
70 break;
73 * Do we know about this object?
74 * It must have been reachable
76 if (lookup_object(sha1))
77 continue;
79 prune_object(path, de->d_name, sha1);
80 continue;
82 if (!prefixcmp(de->d_name, "tmp_obj_")) {
83 prune_tmp_object(path, de->d_name);
84 continue;
86 fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
88 closedir(dir);
89 if (!show_only)
90 rmdir(path);
91 return 0;
94 static void prune_object_dir(const char *path)
96 int i;
97 for (i = 0; i < 256; i++) {
98 static char dir[4096];
99 sprintf(dir, "%s/%02x", path, i);
100 prune_dir(i, dir);
105 * Write errors (particularly out of space) can result in
106 * failed temporary packs (and more rarely indexes and other
107 * files beginning with "tmp_") accumulating in the object
108 * and the pack directories.
110 static void remove_temporary_files(const char *path)
112 DIR *dir;
113 struct dirent *de;
115 dir = opendir(path);
116 if (!dir) {
117 fprintf(stderr, "Unable to open directory %s\n", path);
118 return;
120 while ((de = readdir(dir)) != NULL)
121 if (!prefixcmp(de->d_name, "tmp_"))
122 prune_tmp_object(path, de->d_name);
123 closedir(dir);
126 int cmd_prune(int argc, const char **argv, const char *prefix)
128 struct rev_info revs;
129 struct progress *progress = NULL;
130 const struct option options[] = {
131 OPT__DRY_RUN(&show_only, "do not remove, show only"),
132 OPT__VERBOSE(&verbose, "report pruned objects"),
133 OPT_BOOL(0, "progress", &show_progress, "show progress"),
134 OPT_DATE(0, "expire", &expire,
135 "expire objects older than <time>"),
136 OPT_END()
138 char *s;
140 expire = ULONG_MAX;
141 save_commit_buffer = 0;
142 read_replace_refs = 0;
143 init_revisions(&revs, prefix);
145 argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
146 while (argc--) {
147 unsigned char sha1[20];
148 const char *name = *argv++;
150 if (!get_sha1(name, sha1)) {
151 struct object *object = parse_object(sha1);
152 if (!object)
153 die("bad object: %s", name);
154 add_pending_object(&revs, object, "");
156 else
157 die("unrecognized argument: %s", name);
160 if (show_progress == -1)
161 show_progress = isatty(2);
162 if (show_progress)
163 progress = start_progress_delay("Checking connectivity", 0, 0, 2);
165 mark_reachable_objects(&revs, 1, progress);
166 stop_progress(&progress);
167 prune_object_dir(get_object_directory());
169 prune_packed_objects(show_only);
170 remove_temporary_files(get_object_directory());
171 s = xstrdup(mkpath("%s/pack", get_object_directory()));
172 remove_temporary_files(s);
173 free(s);
174 return 0;