Merge branch 'fixes'
[git.git] / update-index.c
index d10dfd9..b825a11 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "strbuf.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -12,7 +13,7 @@
  * like "git-update-index *" and suddenly having all the object
  * files be revision controlled.
  */
-static int allow_add = 0, allow_remove = 0, allow_replace = 0, not_new = 0, quiet = 0, info_only = 0;
+static int allow_add = 0, allow_remove = 0, allow_replace = 0, allow_unmerged = 0, not_new = 0, quiet = 0, info_only = 0;
 static int force_remove;
 
 /* Three functions to allow overloaded pointer return; see linux/err.h */
@@ -31,7 +32,7 @@ static inline long IS_ERR(const void *ptr)
        return (unsigned long)ptr > (unsigned long)-1000L;
 }
 
-static int add_file_to_cache(char *path)
+static int add_file_to_cache(const char *path)
 {
        int size, namelen, option, status;
        struct cache_entry *ce;
@@ -50,11 +51,20 @@ static int add_file_to_cache(char *path)
                 * case just fine without --force-remove.
                 */
                if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
-                       if (allow_remove)
-                               return remove_file_from_cache(path);
+                       if (allow_remove) {
+                               if (remove_file_from_cache(path))
+                                       return error("%s: cannot remove from the index",
+                                                    path);
+                               else
+                                       return 0;
+                       } else if (status < 0) {
+                               return error("%s: does not exist and --remove not passed",
+                                            path);
+                       }
                }
                if (0 == status)
-                       return error("%s: is a directory", path);
+                       return error("%s: is a directory - add files inside instead",
+                                    path);
                else
                        return error("lstat(\"%s\"): %s", path,
                                     strerror(errno));
@@ -71,15 +81,17 @@ static int add_file_to_cache(char *path)
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
-                       return -1;
+                       return error("open(\"%s\"): %s", path, strerror(errno));
                if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0)
-                       return -1;
+                       return error("%s: failed to insert into database", path);
                break;
        case S_IFLNK:
                target = xmalloc(st.st_size+1);
                if (readlink(path, target, st.st_size+1) != st.st_size) {
+                       char *errstr = strerror(errno);
                        free(target);
-                       return -1;
+                       return error("readlink(\"%s\"): %s", path,
+                                    errstr);
                }
                if (info_only) {
                        unsigned char hdr[50];
@@ -87,56 +99,18 @@ static int add_file_to_cache(char *path)
                        write_sha1_file_prepare(target, st.st_size, "blob",
                                                ce->sha1, hdr, &hdrlen);
                } else if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
-                       return -1;
+                       return error("%s: failed to insert into database", path);
                free(target);
                break;
        default:
-               return -1;
+               return error("%s: unsupported file type", path);
        }
        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 compare_data(struct cache_entry *ce, struct stat *st)
-{
-       int match = -1;
-       int fd = open(ce->name, O_RDONLY);
-
-       if (fd >= 0) {
-               unsigned char sha1[20];
-               if (!index_fd(sha1, fd, st, 0, NULL))
-                       match = memcmp(sha1, ce->sha1, 20);
-               close(fd);
-       }
-       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;
+       if (add_cache_entry(ce, option))
+               return error("%s: cannot add to the index - missing --add option?",
+                            path);
+       return 0;
 }
 
 /*
@@ -161,28 +135,11 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce)
 
        changed = ce_match_stat(ce, &st);
        if (!changed)
-               return ce;
+               return NULL;
 
-       /*
-        * 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 | TYPE_CHANGED))
+       if (ce_modified(ce, &st))
                return ERR_PTR(-EINVAL);
 
-       switch (st.st_mode & S_IFMT) {
-       case S_IFREG:
-               if (compare_data(ce, &st))
-                       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);
        memcpy(updated, ce, size);
@@ -199,16 +156,20 @@ static int refresh_cache(void)
                struct cache_entry *ce, *new;
                ce = active_cache[i];
                if (ce_stage(ce)) {
-                       printf("%s: needs merge\n", ce->name);
-                       has_errors = 1;
                        while ((i < active_nr) &&
                               ! strcmp(active_cache[i]->name, ce->name))
                                i++;
                        i--;
+                       if (allow_unmerged)
+                               continue;
+                       printf("%s: needs merge\n", ce->name);
+                       has_errors = 1;
                        continue;
                }
 
                new = refresh_entry(ce);
+               if (!new)
+                       continue;
                if (IS_ERR(new)) {
                        if (not_new && PTR_ERR(new) == -ENOENT)
                                continue;
@@ -265,7 +226,7 @@ static int verify_dotfile(const char *rest)
        return 1;
 }
 
-static int verify_path(char *path)
+static int verify_path(const char *path)
 {
        char c;
 
@@ -291,7 +252,7 @@ inside:
        }
 }
 
-static int add_cacheinfo(char *arg1, char *arg2, char *arg3)
+static int add_cacheinfo(const char *arg1, const char *arg2, const char *arg3)
 {
        int size, len, option;
        unsigned int mode;
@@ -321,11 +282,30 @@ static int add_cacheinfo(char *arg1, char *arg2, char *arg3)
 
 static struct cache_file cache_file;
 
-int main(int argc, char **argv)
+
+static void update_one(const char *path, const char *prefix, int prefix_length)
+{
+       const char *p = prefix_path(prefix, prefix_length, path);
+       if (!verify_path(p)) {
+               fprintf(stderr, "Ignoring path %s\n", path);
+               return;
+       }
+       if (force_remove) {
+               if (remove_file_from_cache(p))
+                       die("git-update-index: unable to remove %s", path);
+               return;
+       }
+       if (add_file_to_cache(p))
+               die("Unable to process file %s", path);
+}
+
+int main(int argc, const char **argv)
 {
-       int i, newfd, entries, has_errors = 0;
+       int i, newfd, entries, has_errors = 0, line_termination = '\n';
        int allow_options = 1;
+       int read_from_stdin = 0;
        const char *prefix = setup_git_directory();
+       int prefix_length = prefix ? strlen(prefix) : 0;
 
        newfd = hold_index_file_for_update(&cache_file, get_index_file());
        if (newfd < 0)
@@ -336,7 +316,7 @@ int main(int argc, char **argv)
                die("cache corrupted");
 
        for (i = 1 ; i < argc; i++) {
-               char *path = argv[i];
+               const char *path = argv[i];
 
                if (allow_options && *path == '-') {
                        if (!strcmp(path, "--")) {
@@ -359,6 +339,10 @@ int main(int argc, char **argv)
                                allow_remove = 1;
                                continue;
                        }
+                       if (!strcmp(path, "--unmerged")) {
+                               allow_unmerged = 1;
+                               continue;
+                       }
                        if (!strcmp(path, "--refresh")) {
                                has_errors |= refresh_cache();
                                continue;
@@ -379,29 +363,39 @@ int main(int argc, char **argv)
                                force_remove = 1;
                                continue;
                        }
-
+                       if (!strcmp(path, "-z")) {
+                               line_termination = 0;
+                               continue;
+                       }
+                       if (!strcmp(path, "--stdin")) {
+                               if (i != argc - 1)
+                                       die("--stdin must be at the end");
+                               read_from_stdin = 1;
+                               break;
+                       }
                        if (!strcmp(path, "--ignore-missing")) {
                                not_new = 1;
                                continue;
                        }
                        die("unknown option %s", path);
                }
-               path = prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
-               if (!verify_path(path)) {
-                       fprintf(stderr, "Ignoring path %s\n", argv[i]);
-                       continue;
-               }
-               if (force_remove) {
-                       if (remove_file_from_cache(path))
-                               die("git-update-index: --force-remove cannot remove %s", path);
-                       continue;
+               update_one(path, prefix, prefix_length);
+       }
+       if (read_from_stdin) {
+               struct strbuf buf;
+               strbuf_init(&buf);
+               while (1) {
+                       read_line(&buf, stdin, line_termination);
+                       if (buf.eof)
+                               break;
+                       update_one(buf.buf, prefix, prefix_length);
                }
-               if (add_file_to_cache(path))
-                       die("Unable to add %s to database; maybe you want to use --add option?", path);
        }
-       if (write_cache(newfd, active_cache, active_nr) ||
-           commit_index_file(&cache_file))
-               die("Unable to write new cachefile");
+       if (active_cache_changed) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_index_file(&cache_file))
+                       die("Unable to write new cachefile");
+       }
 
        return has_errors ? 1 : 0;
 }