* like "update-cache *" and suddenly having all the object
* files be revision controlled.
*/
-static int allow_add = 0, allow_remove = 0, not_new = 0;
+static int allow_add = 0, allow_remove = 0, allow_replace = 0, not_new = 0;
/* Three functions to allow overloaded pointer return; see linux/err.h */
static inline void *ERR_PTR(long error)
static int add_file_to_cache(char *path)
{
- int size, namelen;
+ int size, namelen, option, status;
struct cache_entry *ce;
struct stat st;
int fd;
- unsigned int len;
- char target[1024];
-
- if (lstat(path, &st) < 0) {
- if (errno == ENOENT || errno == ENOTDIR) {
+ char *target;
+
+ status = lstat(path, &st);
+ if (status < 0 || S_ISDIR(st.st_mode)) {
+ /* When we used to have "path" and now we want to add
+ * "path/file", we need a way to remove "path" before
+ * being able to add "path/file". However,
+ * "git-update-cache --remove path" would not work.
+ * --force-remove can be used but this is more user
+ * friendly, especially since we can do the opposite
+ * case just fine without --force-remove.
+ */
+ if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
if (allow_remove)
return remove_file_from_cache(path);
}
return -1;
break;
case S_IFLNK:
- len = readlink(path, target, sizeof(target));
- if (len == -1 || len+1 > sizeof(target))
+ 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, len, "blob", ce->sha1))
+ }
+ if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
return -1;
+ free(target);
break;
default:
return -1;
}
- return add_cache_entry(ce, allow_add);
+ option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+ option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+ return add_cache_entry(ce, option);
}
static int match_data(int fd, void *buffer, unsigned long 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
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);
+ }
size = ce_size(ce);
updated = xmalloc(size);
}
continue;
}
+ active_cache_changed = 1;
active_cache[i] = new;
}
return has_errors;
static int add_cacheinfo(char *arg1, char *arg2, char *arg3)
{
- int size, len;
+ int size, len, option;
unsigned int mode;
unsigned char sha1[20];
struct cache_entry *ce;
memcpy(ce->name, arg3, len);
ce->ce_flags = htons(len);
ce->ce_mode = create_ce_mode(mode);
- return add_cache_entry(ce, allow_add);
+ option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
+ option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
+ return add_cache_entry(ce, option);
}
static const char *lockfile_name = NULL;
allow_add = 1;
continue;
}
+ if (!strcmp(path, "--replace")) {
+ allow_replace = 1;
+ continue;
+ }
if (!strcmp(path, "--remove")) {
allow_remove = 1;
continue;
continue;
}
if (!strcmp(path, "--cacheinfo")) {
- if (i+3 >= argc || add_cacheinfo(argv[i+1], argv[i+2], argv[i+3]))
+ if (i+3 >= argc)
die("update-cache: --cacheinfo <mode> <sha1> <path>");
+ if (add_cacheinfo(argv[i+1], argv[i+2], argv[i+3]))
+ die("update-cache: --cacheinfo cannot add %s", argv[i+3]);
i += 3;
continue;
}
die("Unable to write new cachefile");
lockfile_name = NULL;
- return has_errors;
+ return has_errors ? 1 : 0;
}