From f0807e62b42df51a079c730dcf4868de9ad44ea5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 13 Apr 2007 09:26:04 -0700 Subject: Teach "git-read-tree -u" to check out submodules as a directory This actually allows us to check out a supermodule after cloning, although the submodules themselves will obviously not be checked out, and will just be empty directories. Checking out the submodules will be up to higher levels - we may not even want to! Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- entry.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index d72f811580..50ffae40c7 100644 --- a/entry.c +++ b/entry.c @@ -62,26 +62,33 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } +static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) +{ + enum object_type type; + void *new = read_sha1_file(ce->sha1, &type, size); + + if (new) { + if (type == OBJ_BLOB) + return new; + free(new); + } + return NULL; +} + static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile) { int fd; - void *new; - unsigned long size; long wrote; - enum object_type type; - 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)); - } switch (ntohl(ce->ce_mode) & S_IFMT) { - char *buf; - unsigned long nsize; + char *buf, *new; + unsigned long size, nsize; 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)); if (to_tempfile) { strcpy(path, ".merge_file_XXXXXX"); fd = mkstemp(path); @@ -111,6 +118,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 +147,13 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat "symlink %s (%s)", path, strerror(errno)); } break; + case S_IFDIRLNK: + 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); } @@ -179,6 +195,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_ISDIRLNK(ntohl(ce->ce_mode))) + return 0; if (!state->force) return error("%s is a directory", path); remove_subtree(path); -- cgit v1.2.3 From ac78e548049f4e86b38368d2c4b4dbb546c64ac6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 19 Apr 2007 02:05:03 +0200 Subject: Simplify calling of CR/LF conversion routines Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- entry.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index d72f811580..3771209f19 100644 --- a/entry.c +++ b/entry.c @@ -79,7 +79,6 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat } switch (ntohl(ce->ce_mode) & S_IFMT) { char *buf; - unsigned long nsize; case S_IFREG: if (to_tempfile) { @@ -96,12 +95,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat /* * Convert from git internal format to working tree format */ - buf = new; - nsize = size; - if (convert_to_working_tree(ce->name, &buf, &nsize)) { + buf = convert_to_working_tree(ce->name, new, &size); + if (buf) { free(new); new = buf; - size = nsize; } wrote = write_in_full(fd, new, size); -- cgit v1.2.3 From cc2903fc70ea9e1bb7788a6f5f46b5d3dd1b6fe4 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Wed, 25 Apr 2007 11:17:56 -0300 Subject: remove_subtree(): Use strerror() when possible Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- entry.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 84f78025ff..b51addbf8d 100644 --- a/entry.c +++ b/entry.c @@ -33,7 +33,7 @@ static void remove_subtree(const char *path) 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 +45,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) -- cgit v1.2.3 From efbc5831264fbe3696e6fc264bba9319265d7344 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Wed, 25 Apr 2007 11:18:08 -0300 Subject: entry.c: Use const qualifier for 'struct checkout' parameters Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Junio C Hamano --- entry.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index b51addbf8d..82bf7259a7 100644 --- a/entry.c +++ b/entry.c @@ -1,7 +1,7 @@ #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); @@ -75,7 +75,7 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned return NULL; } -static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile) +static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { int fd; long wrote; @@ -163,7 +163,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; -- cgit v1.2.3 From 302b9282c9ddfcc704ca759bdc98c1d5f75eba2f Mon Sep 17 00:00:00 2001 From: Martin Waitz Date: Mon, 21 May 2007 22:08:28 +0200 Subject: rename dirlink to gitlink. Unify naming of plumbing dirlink/gitlink concept: git ls-files -z '*.[ch]' | xargs -0 perl -pi -e 's/dirlink/gitlink/g;' -e 's/DIRLNK/GITLINK/g;' Signed-off-by: Junio C Hamano --- entry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 82bf7259a7..ae6476496a 100644 --- a/entry.c +++ b/entry.c @@ -145,7 +145,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout "symlink %s (%s)", path, strerror(errno)); } break; - case S_IFDIRLNK: + case S_IFGITLINK: if (to_tempfile) return error("git-checkout-index: cannot create temporary subproject %s", path); if (mkdir(path, 0777) < 0) @@ -194,7 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t unlink(path); if (S_ISDIR(st.st_mode)) { /* If it is a gitlink, leave it alone! */ - if (S_ISDIRLNK(ntohl(ce->ce_mode))) + if (S_ISGITLINK(ntohl(ce->ce_mode))) return 0; if (!state->force) return error("%s is a directory", path); -- cgit v1.2.3 From a6080a0a44d5ead84db3dabbbc80e82df838533d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2007 00:04:01 -0700 Subject: War on whitespace This uses "git-apply --whitespace=strip" to fix whitespace errors that have crept in to our source files over time. There are a few files that need to have trailing whitespaces (most notably, test vectors). The results still passes the test, and build result in Documentation/ area is unchanged. Signed-off-by: Junio C Hamano --- entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index ae6476496a..c540ae13e8 100644 --- a/entry.c +++ b/entry.c @@ -31,7 +31,7 @@ static void remove_subtree(const char *path) struct dirent *de; char pathbuf[PATH_MAX]; char *name; - + if (!dir) die("cannot opendir %s (%s)", path, strerror(errno)); strcpy(pathbuf, path); -- cgit v1.2.3 From fa2e71c9e794c43634670b62d1b4bf58d1ae7e60 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 17 Jul 2007 22:58:28 -0700 Subject: Do not expect unlink(2) to fail on a directory. When "git checkout-index" checks out path A/B/C, it makes sure A and A/B are truly directories; if there is a regular file or symlink at A, we prefer to remove it. We used to do this by catching an error return from mkdir(2), and on EEXIST did unlink(2), and when it succeeded, tried another mkdir(2). Thomas Glanzmann found out the above does not work on Solaris for a root user, as unlink(2) was so old fashioned there that it allowed to unlink a directory. As pointed out, this still doesn't guarantee that git won't call "unlink()" on a directory (race conditions etc), but that's fundamentally true (there is no "funlink()" like there is "fstat()"), and besides, that is in no way git-specific (ie it's true of any application that gets run as root). Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- entry.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 82bf7259a7..23687af7d5 100644 --- a/entry.c +++ b/entry.c @@ -8,17 +8,40 @@ static void create_directories(const char *path, const struct checkout *state) 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); } } -- cgit v1.2.3 From 1a9d7e9b484e77436edc7f5cacd39c24ec605e6d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 14 Aug 2007 01:41:02 -0700 Subject: attr.c: read .gitattributes from index as well. This makes .gitattributes files to be read from the index when they are not checked out to the work tree. This is in line with the way we always allowed low-level tools to operate in sparsely checked out work tree in a reasonable way. It swaps the order of new file creation and converting the blob to work tree representation; otherwise when we are in the middle of checking out .gitattributes we would notice an empty but unwritten .gitattributes file in the work tree and will ignore the copy in the index. Signed-off-by: Junio C Hamano --- entry.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 0625112339..fc3a506ece 100644 --- a/entry.c +++ b/entry.c @@ -112,6 +112,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout 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 + */ + buf = convert_to_working_tree(ce->name, new, &size); + if (buf) { + free(new); + new = buf; + } + if (to_tempfile) { strcpy(path, ".merge_file_XXXXXX"); fd = mkstemp(path); @@ -123,15 +133,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout path, strerror(errno)); } - /* - * Convert from git internal format to working tree format - */ - buf = convert_to_working_tree(ce->name, new, &size); - if (buf) { - free(new); - new = buf; - } - wrote = write_in_full(fd, new, size); close(fd); free(new); -- cgit v1.2.3 From 5ecd293d1420bf641a927a015877950f4d79c067 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 15:51:04 +0200 Subject: Rewrite convert_to_{git,working_tree} to use strbuf's. * Now, those functions take an "out" strbuf argument, where they store their result if any. In that case, it also returns 1, else it returns 0. * those functions support "in place" editing, in the sense that it's OK to call them this way: convert_to_git(path, sb->buf, sb->len, sb); When doable, conversions are done in place for real, else the strbuf content is just replaced with the new one, transparentely for the caller. If you want to create a new filter working this way, being the accumulation of filter1, filter2, ... filtern, then your meta_filter would be: int meta_filter(..., const char *src, size_t len, struct strbuf *sb) { int ret = 0; ret |= filter1(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } ret |= filter2(...., src, len, sb); if (ret) { src = sb->buf; len = sb->len; } .... return ret | filtern(..., src, len, sb); } That's why subfilters the convert_to_* functions called were also rewritten to work this way. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- entry.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index fc3a506ece..4a8c73bfae 100644 --- a/entry.c +++ b/entry.c @@ -104,7 +104,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout long wrote; switch (ntohl(ce->ce_mode) & S_IFMT) { - char *buf, *new; + char *new; + struct strbuf buf; unsigned long size; case S_IFREG: @@ -116,10 +117,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout /* * Convert from git internal format to working tree format */ - buf = convert_to_working_tree(ce->name, new, &size); - if (buf) { + strbuf_init(&buf, 0); + if (convert_to_working_tree(ce->name, new, size, &buf)) { free(new); - new = buf; + new = buf.buf; + size = buf.len; } if (to_tempfile) { -- cgit v1.2.3 From b315c5c08139c0d3c1e4867a305334e29da01d07 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 12:58:23 +0200 Subject: strbuf change: be sure ->buf is never ever NULL. For that purpose, the ->buf is always initialized with a char * buf living in the strbuf module. It is made a char * so that we can sloppily accept things that perform: sb->buf[0] = '\0', and because you can't pass "" as an initializer for ->buf without making gcc unhappy for very good reasons. strbuf_init/_detach/_grow have been fixed to trust ->alloc and not ->buf anymore. as a consequence strbuf_detach is _mandatory_ to detach a buffer, copying ->buf isn't an option anymore, if ->buf is going to escape from the scope, and eventually be free'd. API changes: * strbuf_setlen now always works, so just make strbuf_reset a convenience macro. * strbuf_detatch takes a size_t* optional argument (meaning it can be NULL) to copy the buffer's len, as it was needed for this refactor to make the code more readable, and working like the callers. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- entry.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 4a8c73bfae..98f5f6d4ec 100644 --- a/entry.c +++ b/entry.c @@ -120,8 +120,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout strbuf_init(&buf, 0); if (convert_to_working_tree(ce->name, new, size, &buf)) { free(new); - new = buf.buf; - size = buf.len; + new = strbuf_detach(&buf, &size); } if (to_tempfile) { -- cgit v1.2.3 From c32f749fec69f92ce3b076128e6322f8130bd881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 21 Oct 2007 11:23:49 +0200 Subject: Correct some sizeof(size_t) != sizeof(unsigned long) typing errors Fix size_t vs. unsigned long pointer mismatch warnings introduced with the addition of strbuf_detach(). Signed-off-by: Rene Scharfe Signed-off-by: Shawn O. Pearce --- entry.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 98f5f6d4ec..cfadc6a292 100644 --- a/entry.c +++ b/entry.c @@ -119,8 +119,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout */ strbuf_init(&buf, 0); if (convert_to_working_tree(ce->name, new, size, &buf)) { + size_t newsize = 0; free(new); - new = strbuf_detach(&buf, &size); + new = strbuf_detach(&buf, &newsize); + size = newsize; } if (to_tempfile) { -- cgit v1.2.3 From 4bd5b7dacc404e6b733d9ab6744429c5027bb5e1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 10 Nov 2007 00:15:03 -0800 Subject: ce_match_stat, run_diff_files: use symbolic constants for readability ce_match_stat() can be told: (1) to ignore CE_VALID bit (used under "assume unchanged" mode) and perform the stat comparison anyway; (2) not to perform the contents comparison for racily clean entries and report mismatch of cached stat information; using its "option" parameter. Give them symbolic constants. Similarly, run_diff_files() can be told not to report anything on removed paths. Also give it a symbolic constant for that. Signed-off-by: Junio C Hamano --- entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index fc3a506ece..ef88f62ce8 100644 --- a/entry.c +++ b/entry.c @@ -200,7 +200,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t strcpy(path + len, ce->name); if (!lstat(path, &st)) { - unsigned changed = ce_match_stat(ce, &st, 1); + unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); if (!changed) return 0; if (!state->force) { -- cgit v1.2.3 From 7a51ed66f653c248993b3c4a61932e47933d835e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 14 Jan 2008 16:03:17 -0800 Subject: Make on-disk index representation separate from in-core one This converts the index explicitly on read and write to its on-disk format, allowing the in-core format to contain more flags, and be simpler. In particular, the in-core format is now host-endian (as opposed to the on-disk one that is network endian in order to be able to be shared across machines) and as a result we can dispense with all the htonl/ntohl on accesses to the cache_entry fields. This will make it easier to make use of various temporary flags that do not exist in the on-disk format. Signed-off-by: Linus Torvalds --- entry.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 257ab46e94..44f4b897d4 100644 --- a/entry.c +++ b/entry.c @@ -103,7 +103,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout int fd; long wrote; - switch (ntohl(ce->ce_mode) & S_IFMT) { + switch (ce->ce_mode & S_IFMT) { char *new; struct strbuf buf; unsigned long size; @@ -129,7 +129,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout strcpy(path, ".merge_file_XXXXXX"); fd = mkstemp(path); } else - fd = create_file(path, ntohl(ce->ce_mode)); + fd = create_file(path, ce->ce_mode); if (fd < 0) { free(new); return error("git-checkout-index: unable to create file %s (%s)", @@ -221,7 +221,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t unlink(path); if (S_ISDIR(st.st_mode)) { /* If it is a gitlink, leave it alone! */ - if (S_ISGITLINK(ntohl(ce->ce_mode))) + if (S_ISGITLINK(ce->ce_mode)) return 0; if (!state->force) return error("%s is a directory", path); -- cgit v1.2.3 From 971f229c50aeace83d6fd30de1de755f419d4cb8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 17 Mar 2008 08:56:27 -0700 Subject: Fix possible Solaris problem in 'checkout_entry()' Currently when checking out an entry "path", we try to unlink(2) it first (because there could be stale file), and if there is a directory there, try to deal with it (typically we run recursive rmdir). We ignore the error return from this unlink because there may not even be any file there. However if you are root on Solaris, you can unlink(2) a directory successfully and corrupt your filesystem. This moves the code around and check the directory first, and then unlink(2). Also we check the error code from it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- entry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 44f4b897d4..222aaa374b 100644 --- a/entry.c +++ b/entry.c @@ -218,7 +218,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t * to emulate by hand - much easier to let the system * just do the right thing) */ - unlink(path); if (S_ISDIR(st.st_mode)) { /* If it is a gitlink, leave it alone! */ if (S_ISGITLINK(ce->ce_mode)) @@ -226,7 +225,8 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t if (!state->force) return error("%s is a directory", path); remove_subtree(path); - } + } else if (unlink(path)) + return error("unable to unlink old '%s' (%s)", path, strerror(errno)); } else if (state->not_new) return 0; create_directories(path, state); -- cgit v1.2.3 From 7e44c93558e7c0b12624d76cf07753d0480ed96a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 31 Aug 2008 09:39:19 -0700 Subject: 'git foo' program identifies itself without dash in die() messages This is a mechanical conversion of all '*.c' files with: s/((?:die|error|warning)\("git)-(\S+:)/$1 $2/; The result was manually inspected and no false positive was found. Signed-off-by: Junio C Hamano --- entry.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 222aaa374b..aa2ee46a84 100644 --- a/entry.c +++ b/entry.c @@ -111,7 +111,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout 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)", + return error("git checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); /* @@ -132,7 +132,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout fd = create_file(path, ce->ce_mode); if (fd < 0) { free(new); - return error("git-checkout-index: unable to create file %s (%s)", + return error("git checkout-index: unable to create file %s (%s)", path, strerror(errno)); } @@ -140,12 +140,12 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout close(fd); free(new); if (wrote != size) - return error("git-checkout-index: unable to write file %s", path); + 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)", + 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) { @@ -155,31 +155,31 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout fd = create_file(path, 0666); if (fd < 0) { free(new); - return error("git-checkout-index: unable to create " + return error("git checkout-index: unable to create " "file %s (%s)", path, strerror(errno)); } wrote = write_in_full(fd, new, size); close(fd); free(new); if (wrote != size) - return error("git-checkout-index: unable to write file %s", + return error("git checkout-index: unable to write file %s", path); } else { wrote = symlink(new, path); free(new); if (wrote) - return error("git-checkout-index: unable to create " + return error("git checkout-index: unable to create " "symlink %s (%s)", path, strerror(errno)); } break; case S_IFGITLINK: if (to_tempfile) - return error("git-checkout-index: cannot create temporary subproject %s", path); + 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); + return error("git checkout-index: cannot create subproject directory %s", path); break; default: - return error("git-checkout-index: unknown file mode for %s", path); + return error("git checkout-index: unknown file mode for %s", path); } if (state->refresh_cache) { -- cgit v1.2.3 From 8ca12c0d62c0be4a4987c4a936467ea2a92e915a Mon Sep 17 00:00:00 2001 From: Alexander Potashev Date: Sat, 10 Jan 2009 15:07:50 +0300 Subject: add is_dot_or_dotdot inline function A new inline function is_dot_or_dotdot is used to check if the directory name is either "." or "..". It returns a non-zero value if the given string is "." or "..". It's applicable to a lot of Git source code. Signed-off-by: Alexander Potashev Signed-off-by: Junio C Hamano --- entry.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index aa2ee46a84..5f24816eb9 100644 --- a/entry.c +++ b/entry.c @@ -1,5 +1,6 @@ #include "cache.h" #include "blob.h" +#include "dir.h" static void create_directories(const char *path, const struct checkout *state) { @@ -62,9 +63,7 @@ static void remove_subtree(const char *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))) + if (is_dot_or_dotdot(de->d_name)) continue; strcpy(name, de->d_name); if (lstat(pathbuf, &st)) -- cgit v1.2.3 From bad4a54fa6fe8a9bbdfb5c3e413268eefde69b75 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Sun, 18 Jan 2009 16:14:52 +0100 Subject: lstat_cache(): introduce has_dirs_only_path() function The create_directories() function in entry.c currently calls stat() or lstat() for each path component of the pathname 'path' each and every time. For the 'git checkout' command, this function is called on each file for which we must do an update (ce->ce_flags & CE_UPDATE), so we get lots and lots of calls. To fix this, we make a new wrapper to the lstat_cache() function, and call the wrapper function instead of the calls to the stat() or the lstat() functions. Since the paths given to the create_directories() function, is sorted alphabetically, the new wrapper would be very cache effective in this situation. To support it we must update the lstat_cache() function to be able to say that "please test the complete length of 'name'", and also to give it the length of a prefix, where the cache should use the stat() function instead of the lstat() function to test each path component. Thanks to Junio C Hamano, Linus Torvalds and Rene Scharfe for valuable comments to this patch! Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index aa2ee46a84..01a683ee5d 100644 --- a/entry.c +++ b/entry.c @@ -8,35 +8,25 @@ static void create_directories(const char *path, const struct checkout *state) 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)) + /* + * For 'checkout-index --prefix=', is + * allowed to be a symlink to an existing directory, + * and we set 'state->base_dir_len' below, such that + * we test the path components of the prefix with the + * stat() function instead of the lstat() function. + */ + if (has_dirs_only_path(len, buf, state->base_dir_len)) 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 this mkdir() would fail, it could be that there + * is already a symlink or something else exists + * there, therefore we then try to unlink it and try + * one more time to create the directory. */ if (mkdir(buf, 0777)) { if (errno == EEXIST && state->force && -- cgit v1.2.3 From 571998921d8fd4ee674545406aabb86987921252 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:06 +0100 Subject: lstat_cache(): swap func(length, string) into func(string, length) Swap function argument pair (length, string) into (string, length) to conform with the commonly used order inside the GIT source code. Also, add a note about this fact into the coding guidelines. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 05aa58d348..bb6bdb90e3 100644 --- a/entry.c +++ b/entry.c @@ -20,7 +20,7 @@ static void create_directories(const char *path, const struct checkout *state) * we test the path components of the prefix with the * stat() function instead of the lstat() function. */ - if (has_dirs_only_path(len, buf, state->base_dir_len)) + if (has_dirs_only_path(buf, len, state->base_dir_len)) continue; /* ok, it is already a directory. */ /* -- cgit v1.2.3 From 81a9aa60a193f1181149b69920930e15c4e34059 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:08 +0100 Subject: create_directories(): remove some memcpy() and strchr() calls Remove the call to memcpy() and strchr() for each path component tested, and instead add each path component as we go forward inside the while-loop. Impact: small optimisation Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index bb6bdb90e3..cc8f0c6077 100644 --- a/entry.c +++ b/entry.c @@ -2,15 +2,19 @@ #include "blob.h" #include "dir.h" -static void create_directories(const char *path, const struct checkout *state) +static void create_directories(const char *path, int path_len, + const struct checkout *state) { - 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); + char *buf = xmalloc(path_len + 1); + int len = 0; + + while (len < path_len) { + do { + buf[len] = path[len]; + len++; + } while (len < path_len && path[len] != '/'); + if (len >= path_len) + break; buf[len] = 0; /* @@ -190,6 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t memcpy(path, state->base_dir, len); strcpy(path + len, ce->name); + len += ce_namelen(ce); if (!lstat(path, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); @@ -218,6 +223,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t return error("unable to unlink old '%s' (%s)", path, strerror(errno)); } else if (state->not_new) return 0; - create_directories(path, state); + create_directories(path, len, state); return write_entry(ce, path, state, 0); } -- cgit v1.2.3 From 4857c761e35b07c12ff2ef1140e93b071b8ac4e7 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:50 +0100 Subject: write_entry(): cleanup of some duplicated code The switch-cases for S_IFREG and S_IFLNK was so similar that it will be better to do some cleanup and use the common parts of it. And the entry.c file should now be clean for 'gcc -Wextra' warnings. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 75 ++++++++++++++++++++++++++--------------------------------------- 1 file changed, 30 insertions(+), 45 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index cc8f0c6077..1f53588a11 100644 --- a/entry.c +++ b/entry.c @@ -78,7 +78,7 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) +static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) { enum object_type type; void *new = read_sha1_file(ce->sha1, &type, size); @@ -93,36 +93,51 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { - int fd; - long wrote; - - switch (ce->ce_mode & S_IFMT) { - char *new; - struct strbuf buf; - unsigned long size; - + unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; + int fd, ret; + char *new; + struct strbuf buf = STRBUF_INIT; + unsigned long size; + size_t wrote, newsize = 0; + + switch (ce_mode_s_ifmt) { case S_IFREG: - new = read_blob_entry(ce, path, &size); + case S_IFLNK: + new = read_blob_entry(ce, &size); if (!new) return error("git checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); + if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) { + ret = symlink(new, path); + free(new); + if (ret) + return error("git checkout-index: unable to create symlink %s (%s)", + path, strerror(errno)); + break; + } + /* * 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; + if (ce_mode_s_ifmt == S_IFREG && + convert_to_working_tree(ce->name, new, size, &buf)) { free(new); new = strbuf_detach(&buf, &newsize); size = newsize; } if (to_tempfile) { - strcpy(path, ".merge_file_XXXXXX"); + if (ce_mode_s_ifmt == S_IFREG) + strcpy(path, ".merge_file_XXXXXX"); + else + strcpy(path, ".merge_link_XXXXXX"); fd = mkstemp(path); - } else + } else if (ce_mode_s_ifmt == S_IFREG) { fd = create_file(path, ce->ce_mode); + } else { + fd = create_file(path, 0666); + } if (fd < 0) { free(new); return error("git checkout-index: unable to create file %s (%s)", @@ -135,36 +150,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout if (wrote != size) 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"); - fd = mkstemp(path); - } else - fd = create_file(path, 0666); - if (fd < 0) { - free(new); - return error("git checkout-index: unable to create " - "file %s (%s)", path, strerror(errno)); - } - wrote = write_in_full(fd, new, size); - close(fd); - free(new); - if (wrote != size) - return error("git checkout-index: unable to write file %s", - path); - } else { - wrote = symlink(new, path); - free(new); - if (wrote) - return error("git checkout-index: unable to create " - "symlink %s (%s)", path, strerror(errno)); - } - break; case S_IFGITLINK: if (to_tempfile) return error("git checkout-index: cannot create temporary subproject %s", path); -- cgit v1.2.3 From e4c7292353dbef39feac1c6a60c5cde9140520a6 Mon Sep 17 00:00:00 2001 From: Kjetil Barvik Date: Mon, 9 Feb 2009 21:54:51 +0100 Subject: write_entry(): use fstat() instead of lstat() when file is open Currently inside write_entry() we do an lstat(path, &st) call on a file which have just been opened inside the exact same function. It should be better to call fstat(fd, &st) on the file while it is open, and it should be at least as fast as the lstat() method. Signed-off-by: Kjetil Barvik Signed-off-by: Junio C Hamano --- entry.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 1f53588a11..5daacc2fe5 100644 --- a/entry.c +++ b/entry.c @@ -94,11 +94,12 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; - int fd, ret; + int fd, ret, fstat_done = 0; char *new; struct strbuf buf = STRBUF_INIT; unsigned long size; size_t wrote, newsize = 0; + struct stat st; switch (ce_mode_s_ifmt) { case S_IFREG: @@ -145,6 +146,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } wrote = write_in_full(fd, new, size); + /* use fstat() only when path == ce->name */ + if (state->refresh_cache && !to_tempfile && !state->base_dir_len) { + fstat(fd, &st); + fstat_done = 1; + } close(fd); free(new); if (wrote != size) @@ -161,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } if (state->refresh_cache) { - struct stat st; - lstat(ce->name, &st); + if (!fstat_done) + lstat(ce->name, &st); fill_stat_cache_info(ce, &st); } return 0; -- cgit v1.2.3 From 34779c535c4e121414197ab08b4b502a22a75433 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 20 Apr 2009 10:17:00 +0200 Subject: Windows: Skip fstat/lstat optimization in write_entry() Commit e4c72923 (write_entry(): use fstat() instead of lstat() when file is open, 2009-02-09) introduced an optimization of write_entry(). Unfortunately, we cannot take advantage of this optimization on Windows because there is no guarantee that the time stamps are updated before the file is closed: "The only guarantee about a file timestamp is that the file time is correctly reflected when the handle that makes the change is closed." (http://msdn.microsoft.com/en-us/library/ms724290(VS.85).aspx) The failure of this optimization on Windows can be observed most easily by running a 'git checkout' that has to update several large files. In this case, 'git checkout' will report modified files, but infact only the timestamps were incorrectly recorded in the index, as can be verified by a subsequent 'git diff', which shows no change. Dmitry Potapov reports the same fix needs on Cygwin; this commit contains his updates for that. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- entry.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 5daacc2fe5..915514aa5c 100644 --- a/entry.c +++ b/entry.c @@ -147,7 +147,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout wrote = write_in_full(fd, new, size); /* use fstat() only when path == ce->name */ - if (state->refresh_cache && !to_tempfile && !state->base_dir_len) { + if (fstat_is_reliable() && + state->refresh_cache && !to_tempfile && !state->base_dir_len) { fstat(fd, &st); fstat_done = 1; } -- cgit v1.2.3 From 691f1a28bf57618d8b44a193b1d28013c858aba6 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 29 Apr 2009 23:22:56 +0200 Subject: replace direct calls to unlink(2) with unlink_or_warn This helps to notice when something's going wrong, especially on systems which lock open files. I used the following criteria when selecting the code for replacement: - it was already printing a warning for the unlink failures - it is in a function which already printing something or is called from such a function - it is in a static function, returning void and the function is only called from a builtin main function (cmd_) - it is in a function which handles emergency exit (signal handlers) - it is in a function which is obvously cleaning up the lockfiles Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 915514aa5c..cc841edf90 100644 --- a/entry.c +++ b/entry.c @@ -35,7 +35,7 @@ static void create_directories(const char *path, int path_len, */ if (mkdir(buf, 0777)) { if (errno == EEXIST && state->force && - !unlink(buf) && !mkdir(buf, 0777)) + !unlink_or_warn(buf) && !mkdir(buf, 0777)) continue; die("cannot create directory at %s", buf); } -- cgit v1.2.3 From d824cbba02a4061400a0e382f9bd241fbbff34f0 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 27 Jun 2009 17:58:46 +0200 Subject: Convert existing die(..., strerror(errno)) to die_errno() Change calls to die(..., strerror(errno)) to use the new die_errno(). In the process, also make slight style adjustments: at least state _something_ about the function that failed (instead of just printing the pathname), and put paths in single quotes. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- entry.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index cc841edf90..8ec880bdbd 100644 --- a/entry.c +++ b/entry.c @@ -51,7 +51,7 @@ static void remove_subtree(const char *path) char *name; if (!dir) - die("cannot opendir %s (%s)", path, strerror(errno)); + die_errno("cannot opendir '%s'", path); strcpy(pathbuf, path); name = pathbuf + strlen(path); *name++ = '/'; @@ -61,15 +61,15 @@ static void remove_subtree(const char *path) continue; strcpy(name, de->d_name); if (lstat(pathbuf, &st)) - die("cannot lstat %s (%s)", pathbuf, strerror(errno)); + die_errno("cannot lstat '%s'", pathbuf); if (S_ISDIR(st.st_mode)) remove_subtree(pathbuf); else if (unlink(pathbuf)) - die("cannot unlink %s (%s)", pathbuf, strerror(errno)); + die_errno("cannot unlink '%s'", pathbuf); } closedir(dir); if (rmdir(path)) - die("cannot rmdir %s (%s)", path, strerror(errno)); + die_errno("cannot rmdir '%s'", path); } static int create_file(const char *path, unsigned int mode) -- cgit v1.2.3 From 0721c314a5c8fddc877140ab5a333c42c62f780d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 27 Jun 2009 17:58:47 +0200 Subject: Use die_errno() instead of die() when checking syscalls Lots of die() calls did not actually report the kind of error, which can leave the user confused as to the real problem. Use die_errno() where we check a system/library call that sets errno on failure, or one of the following that wrap such calls: Function Passes on error from -------- -------------------- odb_pack_keep open read_ancestry fopen read_in_full xread strbuf_read xread strbuf_read_file open or strbuf_read_file strbuf_readlink readlink write_in_full xwrite Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- entry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'entry.c') diff --git a/entry.c b/entry.c index 8ec880bdbd..d3e86c722a 100644 --- a/entry.c +++ b/entry.c @@ -37,7 +37,7 @@ static void create_directories(const char *path, int path_len, if (errno == EEXIST && state->force && !unlink_or_warn(buf) && !mkdir(buf, 0777)) continue; - die("cannot create directory at %s", buf); + die_errno("cannot create directory at '%s'", buf); } } free(buf); -- cgit v1.2.3