X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=entry.c;h=cfadc6a292033d349f6b1efff75d2c4f9f2525fe;hb=5410a02ab9e6a1987147724f8ea65e6a077b3832;hp=d72f811580ad10e792e38b40fe79bf4af3868846;hpb=99e6ac503b175ef38ca98e88a6ec1e290ad79d69;p=git.git diff --git a/entry.c b/entry.c index d72f81158..cfadc6a29 100644 --- a/entry.c +++ b/entry.c @@ -1,24 +1,47 @@ #include "cache.h" #include "blob.h" -static void create_directories(const char *path, struct checkout *state) +static void create_directories(const char *path, const struct checkout *state) { int len = strlen(path); char *buf = xmalloc(len + 1); const char *slash = path; while ((slash = strchr(slash+1, '/')) != NULL) { + struct stat st; + int stat_status; + len = slash - path; memcpy(buf, path, len); buf[len] = 0; + + if (len <= state->base_dir_len) + /* + * checkout-index --prefix=; is + * allowed to be a symlink to an existing + * directory. + */ + stat_status = stat(buf, &st); + else + /* + * if there currently is a symlink, we would + * want to replace it with a real directory. + */ + stat_status = lstat(buf, &st); + + if (!stat_status && S_ISDIR(st.st_mode)) + continue; /* ok, it is already a directory. */ + + /* + * We know stat_status == 0 means something exists + * there and this mkdir would fail, but that is an + * error codepath; we do not care, as we unlink and + * mkdir again in such a case. + */ if (mkdir(buf, 0777)) { - if (errno == EEXIST) { - struct stat st; - if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0777)) - continue; - if (!stat(buf, &st) && S_ISDIR(st.st_mode)) - continue; /* ok */ - } + if (errno == EEXIST && state->force && + !unlink(buf) && !mkdir(buf, 0777)) + continue; die("cannot create directory at %s", buf); } } @@ -31,9 +54,9 @@ static void remove_subtree(const char *path) struct dirent *de; char pathbuf[PATH_MAX]; char *name; - + if (!dir) - die("cannot opendir %s", path); + die("cannot opendir %s (%s)", path, strerror(errno)); strcpy(pathbuf, path); name = pathbuf + strlen(path); *name++ = '/'; @@ -45,15 +68,15 @@ static void remove_subtree(const char *path) continue; strcpy(name, de->d_name); if (lstat(pathbuf, &st)) - die("cannot lstat %s", pathbuf); + die("cannot lstat %s (%s)", pathbuf, strerror(errno)); if (S_ISDIR(st.st_mode)) remove_subtree(pathbuf); else if (unlink(pathbuf)) - die("cannot unlink %s", pathbuf); + die("cannot unlink %s (%s)", pathbuf, strerror(errno)); } closedir(dir); if (rmdir(path)) - die("cannot rmdir %s", path); + die("cannot rmdir %s (%s)", path, strerror(errno)); } static int create_file(const char *path, unsigned int mode) @@ -62,26 +85,46 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile) +static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) { - int fd; - void *new; - unsigned long size; - long wrote; enum object_type type; + void *new = read_sha1_file(ce->sha1, &type, size); - new = read_sha1_file(ce->sha1, &type, &size); - if (!new || type != OBJ_BLOB) { - if (new) - free(new); - return error("git-checkout-index: unable to read sha1 file of %s (%s)", - path, sha1_to_hex(ce->sha1)); + if (new) { + if (type == OBJ_BLOB) + return new; + free(new); } + return NULL; +} + +static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) +{ + int fd; + long wrote; + switch (ntohl(ce->ce_mode) & S_IFMT) { - char *buf; - unsigned long nsize; + char *new; + struct strbuf buf; + unsigned long size; case S_IFREG: + new = read_blob_entry(ce, path, &size); + if (!new) + return error("git-checkout-index: unable to read sha1 file of %s (%s)", + path, sha1_to_hex(ce->sha1)); + + /* + * Convert from git internal format to working tree format + */ + strbuf_init(&buf, 0); + if (convert_to_working_tree(ce->name, new, size, &buf)) { + size_t newsize = 0; + free(new); + new = strbuf_detach(&buf, &newsize); + size = newsize; + } + if (to_tempfile) { strcpy(path, ".merge_file_XXXXXX"); fd = mkstemp(path); @@ -93,17 +136,6 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat path, strerror(errno)); } - /* - * Convert from git internal format to working tree format - */ - buf = new; - nsize = size; - if (convert_to_working_tree(ce->name, &buf, &nsize)) { - free(new); - new = buf; - size = nsize; - } - wrote = write_in_full(fd, new, size); close(fd); free(new); @@ -111,6 +143,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat return error("git-checkout-index: unable to write file %s", path); break; case S_IFLNK: + new = read_blob_entry(ce, path, &size); + if (!new) + return error("git-checkout-index: unable to read sha1 file of %s (%s)", + path, sha1_to_hex(ce->sha1)); if (to_tempfile || !has_symlinks) { if (to_tempfile) { strcpy(path, ".merge_link_XXXXXX"); @@ -136,8 +172,13 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat "symlink %s (%s)", path, strerror(errno)); } break; + case S_IFGITLINK: + if (to_tempfile) + return error("git-checkout-index: cannot create temporary subproject %s", path); + if (mkdir(path, 0777) < 0) + return error("git-checkout-index: cannot create subproject directory %s", path); + break; default: - free(new); return error("git-checkout-index: unknown file mode for %s", path); } @@ -149,7 +190,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat return 0; } -int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath) +int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; struct stat st; @@ -179,6 +220,9 @@ int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath) */ unlink(path); if (S_ISDIR(st.st_mode)) { + /* If it is a gitlink, leave it alone! */ + if (S_ISGITLINK(ntohl(ce->ce_mode))) + return 0; if (!state->force) return error("%s is a directory", path); remove_subtree(path);