Add new "git replace" command
[gitweb.git] / sha1_file.c
index 3d93d936e4e3ddf24f46af2ef92e62e5f7db3877..9119b795bd2c39d91731f181a304e9575c0564f5 100644 (file)
@@ -720,6 +720,8 @@ static int open_packed_git_1(struct packed_git *p)
                return error("packfile %s index unavailable", p->pack_name);
 
        p->pack_fd = open(p->pack_name, O_RDONLY);
+       while (p->pack_fd < 0 && errno == EMFILE && unuse_one_window(p, -1))
+               p->pack_fd = open(p->pack_name, O_RDONLY);
        if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
                return -1;
 
@@ -791,7 +793,7 @@ static int in_window(struct pack_window *win, off_t offset)
                && (offset + 20) <= (win_off + win->len);
 }
 
-unsigned charuse_pack(struct packed_git *p,
+unsigned char *use_pack(struct packed_git *p,
                struct pack_window **w_cursor,
                off_t offset,
                unsigned int *left)
@@ -801,7 +803,7 @@ unsigned char* use_pack(struct packed_git *p,
        if (p->pack_fd == -1 && open_packed_git(p))
                die("packfile %s cannot be accessed", p->pack_name);
 
-       /* Since packfiles end in a hash of their content and its
+       /* Since packfiles end in a hash of their content and it's
         * pointless to ask for an offset into the middle of that
         * hash, and the in_window function above wouldn't match
         * don't allow an offset too close to the end of the file.
@@ -937,6 +939,8 @@ static void prepare_packed_git_one(char *objdir, int local)
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
        dir = opendir(path);
+       while (!dir && errno == EMFILE && unuse_one_window(packed_git, -1))
+               dir = opendir(path);
        if (!dir) {
                if (errno != ENOENT)
                        error("unable to open object pack directory: %s: %s",
@@ -1708,6 +1712,9 @@ static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        delta_base_cache_lru.prev = &ent->lru;
 }
 
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+                        unsigned long *size);
+
 static void *unpack_delta_entry(struct packed_git *p,
                                struct pack_window **w_curs,
                                off_t curpos,
@@ -1916,25 +1923,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        return 0;
 }
 
-int matches_pack_name(struct packed_git *p, const char *name)
-{
-       const char *last_c, *c;
-
-       if (!strcmp(p->pack_name, name))
-               return 1;
-
-       for (c = p->pack_name, last_c = c; *c;)
-               if (*c == '/')
-                       last_c = ++c;
-               else
-                       ++c;
-       if (!strcmp(last_c, name))
-               return 1;
-
-       return 0;
-}
-
-static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
+static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
 {
        static struct packed_git *last_found = (void *)1;
        struct packed_git *p;
@@ -1946,15 +1935,6 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
        p = (last_found == (void *)1) ? packed_git : last_found;
 
        do {
-               if (ignore_packed) {
-                       const char **ig;
-                       for (ig = ignore_packed; *ig; ig++)
-                               if (matches_pack_name(p, *ig))
-                                       break;
-                       if (*ig)
-                               goto next;
-               }
-
                if (p->num_bad_objects) {
                        unsigned i;
                        for (i = 0; i < p->num_bad_objects; i++)
@@ -2035,7 +2015,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
        struct pack_entry e;
        int status;
 
-       if (!find_pack_entry(sha1, &e, NULL)) {
+       if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
                status = sha1_loose_object_info(sha1, sizep);
                if (status >= 0)
@@ -2043,7 +2023,7 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
-               if (!find_pack_entry(sha1, &e, NULL))
+               if (!find_pack_entry(sha1, &e))
                        return status;
        }
 
@@ -2062,7 +2042,7 @@ static void *read_packed_sha1(const unsigned char *sha1,
        struct pack_entry e;
        void *data;
 
-       if (!find_pack_entry(sha1, &e, NULL))
+       if (!find_pack_entry(sha1, &e))
                return NULL;
        data = cache_or_unpack_entry(e.p, e.offset, size, type, 1);
        if (!data) {
@@ -2138,8 +2118,8 @@ int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
        return 0;
 }
 
-void *read_object(const unsigned char *sha1, enum object_type *type,
-                 unsigned long *size)
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+                        unsigned long *size)
 {
        unsigned long mapsize;
        void *map, *buf;
@@ -2165,13 +2145,26 @@ void *read_object(const unsigned char *sha1, enum object_type *type,
        return read_packed_sha1(sha1, type, size);
 }
 
-void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
-                    unsigned long *size)
+void *read_sha1_file_repl(const unsigned char *sha1,
+                         enum object_type *type,
+                         unsigned long *size,
+                         const unsigned char **replacement)
 {
-       void *data = read_object(sha1, type, size);
+       const unsigned char *repl = lookup_replace_object(sha1);
+       void *data = read_object(repl, type, size);
+
+       /* die if we replaced an object with one that does not exist */
+       if (!data && repl != sha1)
+               die("replacement %s not found for %s",
+                   sha1_to_hex(repl), sha1_to_hex(sha1));
+
        /* legacy behavior is to die on corrupted objects */
-       if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
-               die("object %s is corrupted", sha1_to_hex(sha1));
+       if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
+               die("object %s is corrupted", sha1_to_hex(repl));
+
+       if (replacement)
+               *replacement = repl;
+
        return data;
 }
 
@@ -2240,12 +2233,18 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
 }
 
 /*
- * Move the just written object into its final resting place
+ * Move the just written object into its final resting place.
+ * NEEDSWORK: this should be renamed to finalize_temp_file() as
+ * "moving" is only a part of what it does, when no patch between
+ * master to pu changes the call sites of this function.
  */
 int move_temp_to_file(const char *tmpfile, const char *filename)
 {
        int ret = 0;
-       if (link(tmpfile, filename))
+
+       if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
+               goto try_rename;
+       else if (link(tmpfile, filename))
                ret = errno;
 
        /*
@@ -2256,15 +2255,16 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
         *
         * The same holds for FAT formatted media.
         *
-        * When this succeeds, we just return 0. We have nothing
+        * When this succeeds, we just return We have nothing
         * left to unlink.
         */
        if (ret && ret != EEXIST) {
+       try_rename:
                if (!rename(tmpfile, filename))
-                       return 0;
+                       goto out;
                ret = errno;
        }
-       unlink(tmpfile);
+       unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
                        return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
@@ -2272,6 +2272,9 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
                /* FIXME!!! Collision check here ? */
        }
 
+out:
+       if (set_shared_perm(filename, (S_IFREG|0444)))
+               return error("unable to set permission to '%s'", filename);
        return 0;
 }
 
@@ -2296,7 +2299,6 @@ static void close_sha1_file(int fd)
 {
        if (fsync_object_files)
                fsync_or_die(fd, "sha1 file");
-       fchmod(fd, 0444);
        if (close(fd) != 0)
                die("error when closing sha1 file (%s)", strerror(errno));
 }
@@ -2354,6 +2356,8 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 
        filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
+       while (fd < 0 && errno == EMFILE && unuse_one_window(packed_git, -1))
+               fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
@@ -2461,17 +2465,17 @@ int has_pack_file(const unsigned char *sha1)
        return 1;
 }
 
-int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed)
+int has_sha1_pack(const unsigned char *sha1)
 {
        struct pack_entry e;
-       return find_pack_entry(sha1, &e, ignore_packed);
+       return find_pack_entry(sha1, &e);
 }
 
 int has_sha1_file(const unsigned char *sha1)
 {
        struct pack_entry e;
 
-       if (find_pack_entry(sha1, &e, NULL))
+       if (find_pack_entry(sha1, &e))
                return 1;
        return has_loose_object(sha1);
 }