prune: factor out loose-object directory traversal

Prune has to walk $GIT_DIR/objects/?? in order to find the
set of loose objects to prune. Other parts of the code
(e.g., count-objects) want to do the same. Let's factor it
out into a reusable for_each-style function.

Note that this is not quite a straight code movement. The
original code had strange behavior when it found a file of
the form "[0-9a-f]{2}/.{38}" that did _not_ contain all hex
digits. It executed a "break" from the loop, meaning that we
stopped pruning in that directory (but still pruned other
directories!). This was probably a bug; we do not want to
process the file as an object, but we should keep going
otherwise (and that is how the new code handles it).

We are also a little more careful with loose object
directories which fail to open. The original code silently
ignored any failures, but the new code will complain about
any problems besides ENOENT.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jeff King
2014-10-15 18:38:55 -04:00
committed by Junio C Hamano
parent 718ccc9731
commit 27e1e22d5e
3 changed files with 143 additions and 61 deletions

View File

@ -31,11 +31,23 @@ static int prune_tmp_file(const char *fullpath)
return 0;
}
static int prune_object(const char *fullpath, const unsigned char *sha1)
static int prune_object(const unsigned char *sha1, const char *fullpath,
void *data)
{
struct stat st;
if (lstat(fullpath, &st))
return error("Could not stat '%s'", fullpath);
/*
* Do we know about this object?
* It must have been reachable
*/
if (lookup_object(sha1))
return 0;
if (lstat(fullpath, &st)) {
/* report errors, but do not stop pruning */
error("Could not stat '%s'", fullpath);
return 0;
}
if (st.st_mtime > expire)
return 0;
if (show_only || verbose) {
@ -48,68 +60,20 @@ static int prune_object(const char *fullpath, const unsigned char *sha1)
return 0;
}
static int prune_dir(int i, struct strbuf *path)
static int prune_cruft(const char *basename, const char *path, void *data)
{
size_t baselen = path->len;
DIR *dir = opendir(path->buf);
struct dirent *de;
if (!dir)
return 0;
while ((de = readdir(dir)) != NULL) {
char name[100];
unsigned char sha1[20];
if (is_dot_or_dotdot(de->d_name))
continue;
if (strlen(de->d_name) == 38) {
sprintf(name, "%02x", i);
memcpy(name+2, de->d_name, 39);
if (get_sha1_hex(name, sha1) < 0)
break;
/*
* Do we know about this object?
* It must have been reachable
*/
if (lookup_object(sha1))
continue;
strbuf_addf(path, "/%s", de->d_name);
prune_object(path->buf, sha1);
strbuf_setlen(path, baselen);
continue;
}
if (starts_with(de->d_name, "tmp_obj_")) {
strbuf_addf(path, "/%s", de->d_name);
prune_tmp_file(path->buf);
strbuf_setlen(path, baselen);
continue;
}
fprintf(stderr, "bad sha1 file: %s/%s\n", path->buf, de->d_name);
}
closedir(dir);
if (!show_only)
rmdir(path->buf);
if (starts_with(basename, "tmp_obj_"))
prune_tmp_file(path);
else
fprintf(stderr, "bad sha1 file: %s\n", path);
return 0;
}
static void prune_object_dir(const char *path)
static int prune_subdir(int nr, const char *path, void *data)
{
struct strbuf buf = STRBUF_INIT;
size_t baselen;
int i;
strbuf_addstr(&buf, path);
strbuf_addch(&buf, '/');
baselen = buf.len;
for (i = 0; i < 256; i++) {
strbuf_addf(&buf, "%02x", i);
prune_dir(i, &buf);
strbuf_setlen(&buf, baselen);
}
if (!show_only)
rmdir(path);
return 0;
}
/*
@ -173,7 +137,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
mark_reachable_objects(&revs, 1, progress);
stop_progress(&progress);
prune_object_dir(get_object_directory());
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());