Make fiel checkout function available to the git library
[git/git.git] / checkout-cache.c
index 9d09c39..82ddbe4 100644 (file)
  * of "-a" causing problems (not possible in the above example,
  * but get used to it in scripting!).
  */
-#include <sys/types.h>
-#include <dirent.h>
 #include "cache.h"
 
-static int force = 0, quiet = 0, not_new = 0, refresh_cache = 0;
-static const char *base_dir = "";
-static int base_dir_len = 0;
-
-static void create_directories(const char *path)
-{
-       int len = strlen(path);
-       char *buf = xmalloc(len + 1);
-       const char *slash = path;
-
-       while ((slash = strchr(slash+1, '/')) != NULL) {
-               len = slash - path;
-               memcpy(buf, path, len);
-               buf[len] = 0;
-               if (mkdir(buf, 0755)) {
-                       if (errno == EEXIST) {
-                               struct stat st;
-                               if (len > base_dir_len && force && !unlink(buf) && !mkdir(buf, 0755))
-                                       continue;
-                               if (!stat(buf, &st) && S_ISDIR(st.st_mode))
-                                       continue; /* ok */
-                       }
-                       die("cannot create directory at %s", buf);
-               }
-       }
-       free(buf);
-}
-
-static void remove_subtree(const char *path)
-{
-       DIR *dir = opendir(path);
-       struct dirent *de;
-       char pathbuf[PATH_MAX];
-       char *name;
-       
-       if (!dir)
-               die("cannot opendir %s", path);
-       strcpy(pathbuf, path);
-       name = pathbuf + strlen(path);
-       *name++ = '/';
-       while ((de = readdir(dir)) != NULL) {
-               struct stat st;
-               if ((de->d_name[0] == '.') &&
-                   ((de->d_name[1] == 0) ||
-                    ((de->d_name[1] == '.') && de->d_name[2] == 0)))
-                       continue;
-               strcpy(name, de->d_name);
-               if (lstat(pathbuf, &st))
-                       die("cannot lstat %s", pathbuf);
-               if (S_ISDIR(st.st_mode))
-                       remove_subtree(pathbuf);
-               else if (unlink(pathbuf))
-                       die("cannot unlink %s", pathbuf);
-       }
-       closedir(dir);
-       if (rmdir(path))
-               die("cannot rmdir %s", path);
-}
-
-static int create_file(const char *path, unsigned int mode)
-{
-       int fd;
-
-       mode = (mode & 0100) ? 0777 : 0666;
-       create_directories(path);
-       fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
-       if (fd < 0) {
-               if (errno == EISDIR && force) {
-                       remove_subtree(path);
-                       fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
-               }
-       }
-       return fd;
-}
-
-static int write_entry(struct cache_entry *ce, const char *path)
-{
-       int fd;
-       void *new;
-       unsigned long size;
-       long wrote;
-       char type[20];
-       char target[1024];
-
-       new = read_sha1_file(ce->sha1, type, &size);
-       if (!new || strcmp(type, "blob")) {
-               if (new)
-                       free(new);
-               return error("git-checkout-cache: unable to read sha1 file of %s (%s)",
-                       path, sha1_to_hex(ce->sha1));
-       }
-       switch (ntohl(ce->ce_mode) & S_IFMT) {
-       case S_IFREG:
-               fd = create_file(path, ntohl(ce->ce_mode));
-               if (fd < 0) {
-                       free(new);
-                       return error("git-checkout-cache: unable to create file %s (%s)",
-                               path, strerror(errno));
-               }
-               wrote = write(fd, new, size);
-               close(fd);
-               free(new);
-               if (wrote != size)
-                       return error("git-checkout-cache: unable to write file %s", path);
-               break;
-       case S_IFLNK:
-               memcpy(target, new, size);
-               target[size] = '\0';
-               create_directories(path);
-               if (symlink(target, path)) {
-                       free(new);
-                       return error("git-checkout-cache: unable to create symlink %s (%s)",
-                               path, strerror(errno));
-               }
-               free(new);
-               break;
-       default:
-               free(new);
-               return error("git-checkout-cache: unknown file mode for %s", path);
-       }
-
-       if (refresh_cache) {
-               struct stat st;
-               lstat(ce->name, &st);
-               fill_stat_cache_info(ce, &st);
-       }
-       return 0;
-}
-
-static int checkout_entry(struct cache_entry *ce)
-{
-       struct stat st;
-       static char path[MAXPATHLEN+1];
-       int len = base_dir_len;
-
-       memcpy(path, base_dir, len);
-       strcpy(path + len, ce->name);
-
-       if (!lstat(path, &st)) {
-               unsigned changed = ce_match_stat(ce, &st);
-               if (!changed)
-                       return 0;
-               if (!force) {
-                       if (!quiet)
-                               fprintf(stderr, "git-checkout-cache: %s already exists\n", path);
-                       return 0;
-               }
-
-               /*
-                * We unlink the old file, to get the new one with the
-                * right permissions (including umask, which is nasty
-                * to emulate by hand - much easier to let the system
-                * just do the right thing)
-                */
-               unlink(path);
-       } else if (not_new) 
-               return 0;
-       return write_entry(ce, path);
-}
+static struct checkout state = {
+       .base_dir = "",
+       .base_dir_len = 0,
+       .force = 0,
+       .quiet = 0,
+       .not_new = 0,
+       .refresh_cache = 0,
+};
 
 static int checkout_file(const char *name)
 {
        int pos = cache_name_pos(name, strlen(name));
        if (pos < 0) {
-               if (!quiet) {
+               if (!state.quiet) {
                        pos = -pos - 1;
                        fprintf(stderr,
                                "git-checkout-cache: %s is %s.\n",
@@ -211,7 +58,7 @@ static int checkout_file(const char *name)
                }
                return -1;
        }
-       return checkout_entry(active_cache[pos]);
+       return checkout_entry(active_cache[pos], &state);
 }
 
 static int checkout_all(void)
@@ -222,7 +69,7 @@ static int checkout_all(void)
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce))
                        continue;
-               if (checkout_entry(ce) < 0)
+               if (checkout_entry(ce, &state) < 0)
                        return -1;
        }
        return 0;
@@ -250,19 +97,19 @@ int main(int argc, char **argv)
                                continue;
                        }
                        if (!strcmp(arg, "-f")) {
-                               force = 1;
+                               state.force = 1;
                                continue;
                        }
                        if (!strcmp(arg, "-q")) {
-                               quiet = 1;
+                               state.quiet = 1;
                                continue;
                        }
                        if (!strcmp(arg, "-n")) {
-                               not_new = 1;
+                               state.not_new = 1;
                                continue;
                        }
                        if (!strcmp(arg, "-u")) {
-                               refresh_cache = 1;
+                               state.refresh_cache = 1;
                                if (newfd < 0)
                                        newfd = hold_index_file_for_update
                                                (&cache_file,
@@ -272,20 +119,20 @@ int main(int argc, char **argv)
                                continue;
                        }
                        if (!memcmp(arg, "--prefix=", 9)) {
-                               base_dir = arg+9;
-                               base_dir_len = strlen(base_dir);
+                               state.base_dir = arg+9;
+                               state.base_dir_len = strlen(state.base_dir);
                                continue;
                        }
                }
-               if (base_dir_len) {
+               if (state.base_dir_len) {
                        /* when --prefix is specified we do not
                         * want to update cache.
                         */
-                       if (refresh_cache) {
+                       if (state.refresh_cache) {
                                close(newfd); newfd = -1;
                                rollback_index_file(&cache_file);
                        }
-                       refresh_cache = 0;
+                       state.refresh_cache = 0;
                }
                checkout_file(arg);
        }