builtin-prune.con commit merge-file: handle empty files gracefully (381b851)
   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
   8static const char prune_usage[] = "git-prune [-n]";
   9static int show_only;
  10static unsigned long expire;
  11
  12static int prune_object(char *path, const char *filename, const unsigned char *sha1)
  13{
  14        const char *fullpath = mkpath("%s/%s", path, filename);
  15        if (expire) {
  16                struct stat st;
  17                if (lstat(fullpath, &st))
  18                        return error("Could not stat '%s'", fullpath);
  19                if (st.st_mtime > expire)
  20                        return 0;
  21        }
  22        if (show_only) {
  23                enum object_type type = sha1_object_info(sha1, NULL);
  24                printf("%s %s\n", sha1_to_hex(sha1),
  25                       (type > 0) ? typename(type) : "unknown");
  26        } else
  27                unlink(fullpath);
  28        return 0;
  29}
  30
  31static int prune_dir(int i, char *path)
  32{
  33        DIR *dir = opendir(path);
  34        struct dirent *de;
  35
  36        if (!dir)
  37                return 0;
  38
  39        while ((de = readdir(dir)) != NULL) {
  40                char name[100];
  41                unsigned char sha1[20];
  42                int len = strlen(de->d_name);
  43
  44                switch (len) {
  45                case 2:
  46                        if (de->d_name[1] != '.')
  47                                break;
  48                case 1:
  49                        if (de->d_name[0] != '.')
  50                                break;
  51                        continue;
  52                case 38:
  53                        sprintf(name, "%02x", i);
  54                        memcpy(name+2, de->d_name, len+1);
  55                        if (get_sha1_hex(name, sha1) < 0)
  56                                break;
  57
  58                        /*
  59                         * Do we know about this object?
  60                         * It must have been reachable
  61                         */
  62                        if (lookup_object(sha1))
  63                                continue;
  64
  65                        prune_object(path, de->d_name, sha1);
  66                        continue;
  67                }
  68                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
  69        }
  70        if (!show_only)
  71                rmdir(path);
  72        closedir(dir);
  73        return 0;
  74}
  75
  76static void prune_object_dir(const char *path)
  77{
  78        int i;
  79        for (i = 0; i < 256; i++) {
  80                static char dir[4096];
  81                sprintf(dir, "%s/%02x", path, i);
  82                prune_dir(i, dir);
  83        }
  84}
  85
  86/*
  87 * Write errors (particularly out of space) can result in
  88 * failed temporary packs (and more rarely indexes and other
  89 * files begining with "tmp_") accumulating in the
  90 * object directory.
  91 */
  92static void remove_temporary_files(void)
  93{
  94        DIR *dir;
  95        struct dirent *de;
  96        char* dirname=get_object_directory();
  97
  98        dir = opendir(dirname);
  99        if (!dir) {
 100                fprintf(stderr, "Unable to open object directory %s\n",
 101                        dirname);
 102                return;
 103        }
 104        while ((de = readdir(dir)) != NULL) {
 105                if (!prefixcmp(de->d_name, "tmp_")) {
 106                        char name[PATH_MAX];
 107                        int c = snprintf(name, PATH_MAX, "%s/%s",
 108                                         dirname, de->d_name);
 109                        if (c < 0 || c >= PATH_MAX)
 110                                continue;
 111                        if (expire) {
 112                                struct stat st;
 113                                if (stat(name, &st) != 0 || st.st_mtime >= expire)
 114                                        continue;
 115                        }
 116                        printf("Removing stale temporary file %s\n", name);
 117                        if (!show_only)
 118                                unlink(name);
 119                }
 120        }
 121        closedir(dir);
 122}
 123
 124int cmd_prune(int argc, const char **argv, const char *prefix)
 125{
 126        int i;
 127        struct rev_info revs;
 128
 129        for (i = 1; i < argc; i++) {
 130                const char *arg = argv[i];
 131                if (!strcmp(arg, "-n")) {
 132                        show_only = 1;
 133                        continue;
 134                }
 135                if (!strcmp(arg, "--expire")) {
 136                        if (++i < argc) {
 137                                expire = approxidate(argv[i]);
 138                                continue;
 139                        }
 140                }
 141                else if (!prefixcmp(arg, "--expire=")) {
 142                        expire = approxidate(arg + 9);
 143                        continue;
 144                }
 145                usage(prune_usage);
 146        }
 147
 148        save_commit_buffer = 0;
 149        init_revisions(&revs, prefix);
 150        mark_reachable_objects(&revs, 1);
 151
 152        prune_object_dir(get_object_directory());
 153
 154        sync();
 155        prune_packed_objects(show_only);
 156        remove_temporary_files();
 157        return 0;
 158}