X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=update-cache.c;h=97d5e8b6b0695b82a235026430050a4140a1586a;hb=a02ebff6127c5fc981668fb570f0a80f2b7657ca;hp=bb25fa114c1017380c8441f514ae2e66beef1e68;hpb=0ff5bf7cfcb20a6c2404fe7b9cc6ff18e47db812;p=git.git diff --git a/update-cache.c b/update-cache.c index bb25fa11..97d5e8b6 100644 --- a/update-cache.c +++ b/update-cache.c @@ -15,6 +15,12 @@ */ static int allow_add = 0, allow_remove = 0, not_new = 0; +/* + * update-cache --refresh may not touch anything at all, in which case + * writing 1.6MB of the same thing is a waste. + */ +static int cache_changed = 0; + /* Three functions to allow overloaded pointer return; see linux/err.h */ static inline void *ERR_PTR(long error) { @@ -51,25 +57,21 @@ static void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) ce->ce_size = htonl(st->st_size); } -static int add_file_to_cache(char *path) +static int add_file_to_cache_1(char *path) { int size, namelen; struct cache_entry *ce; struct stat st; int fd; + char *target; - fd = open(path, O_RDONLY); - if (fd < 0) { + if (lstat(path, &st) < 0) { if (errno == ENOENT || errno == ENOTDIR) { if (allow_remove) return remove_file_from_cache(path); } return -1; } - if (fstat(fd, &st) < 0) { - close(fd); - return -1; - } namelen = strlen(path); size = cache_entry_size(namelen); ce = xmalloc(size); @@ -78,13 +80,56 @@ static int add_file_to_cache(char *path) fill_stat_cache_info(ce, &st); ce->ce_mode = create_ce_mode(st.st_mode); ce->ce_flags = htons(namelen); - - if (index_fd(ce->sha1, fd, &st) < 0) + switch (st.st_mode & S_IFMT) { + case S_IFREG: + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + if (index_fd(ce->sha1, fd, &st) < 0) + return -1; + break; + case S_IFLNK: + target = xmalloc(st.st_size+1); + if (readlink(path, target, st.st_size+1) != st.st_size) { + free(target); + return -1; + } + if (write_sha1_file(target, st.st_size, "blob", ce->sha1)) + return -1; + free(target); + break; + default: return -1; - + } + if (!cache_changed) { + /* If we have not smudged the cache, be careful + * to keep it clean. Find out if we have a matching + * cache entry that add_cache_entry would replace with, + * and if it matches then do not bother calling it. + */ + int pos = cache_name_pos(ce->name, namelen); + if ((0 <= pos) && + !memcmp(active_cache[pos], ce, sizeof(*ce))) { + free(ce); + /* magic to tell add_file_to_cache that + * we have not updated anything. + */ + return 999; + } + } return add_cache_entry(ce, allow_add); } +static int add_file_to_cache(char *path) +{ + int ret = add_file_to_cache_1(path); + if (ret == 0) + cache_changed = 1; + else if (ret == 999) + ret = 0; + return ret; +} + static int match_data(int fd, void *buffer, unsigned long size) { while (size) { @@ -120,6 +165,33 @@ static int compare_data(struct cache_entry *ce, unsigned long expected_size) return match; } +static int compare_link(struct cache_entry *ce, unsigned long expected_size) +{ + int match = -1; + char *target; + void *buffer; + unsigned long size; + char type[10]; + int len; + + target = xmalloc(expected_size); + len = readlink(ce->name, target, expected_size); + if (len != expected_size) { + free(target); + return -1; + } + buffer = read_sha1_file(ce->sha1, type, &size); + if (!buffer) { + free(target); + return -1; + } + if (size == expected_size) + match = memcmp(buffer, target, size); + free(buffer); + free(target); + return match; +} + /* * "refresh" does not calculate a new sha1 file or bring the * cache up-to-date for mode/content changes. But what it @@ -137,7 +209,7 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce) struct cache_entry *updated; int changed, size; - if (stat(ce->name, &st) < 0) + if (lstat(ce->name, &st) < 0) return ERR_PTR(-errno); changed = cache_match_stat(ce, &st); @@ -145,15 +217,26 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce) return ce; /* - * If the mode has changed, there's no point in trying + * If the mode or type has changed, there's no point in trying * to refresh the entry - it's not going to match */ - if (changed & MODE_CHANGED) + if (changed & (MODE_CHANGED | TYPE_CHANGED)) return ERR_PTR(-EINVAL); - if (compare_data(ce, st.st_size)) + switch (st.st_mode & S_IFMT) { + case S_IFREG: + if (compare_data(ce, st.st_size)) + return ERR_PTR(-EINVAL); + break; + case S_IFLNK: + if (compare_link(ce, st.st_size)) + return ERR_PTR(-EINVAL); + break; + default: return ERR_PTR(-EINVAL); + } + cache_changed = 1; size = ce_size(ce); updated = xmalloc(size); memcpy(updated, ce, size); @@ -234,6 +317,7 @@ static int add_cacheinfo(char *arg1, char *arg2, char *arg3) if (!verify_path(arg3)) return -1; + cache_changed = 1; len = strlen(arg3); size = cache_entry_size(len); ce = xmalloc(size); @@ -328,9 +412,13 @@ int main(int argc, char **argv) if (add_file_to_cache(path)) die("Unable to add %s to database", path); } - if (write_cache(newfd, active_cache, active_nr) || rename(lockfile, indexfile)) + + if (!cache_changed) + unlink(lockfile); + else if (write_cache(newfd, active_cache, active_nr) || + rename(lockfile, indexfile)) die("Unable to write new cachefile"); lockfile_name = NULL; - return has_errors; + return has_errors ? 1 : 0; }