Prevent send-pack from segfaulting when a branch doesn't match
[gitweb.git] / unpack-trees.c
index 675a9998dcbab528faf2cf34ab1a66f884a36300..ccfeb6e245f32d88170ae51f0367ee15aa950a37 100644 (file)
@@ -5,41 +5,30 @@
 #include "cache-tree.h"
 #include "unpack-trees.h"
 #include "progress.h"
+#include "refs.h"
 
 #define DBRT_DEBUG 1
 
 struct tree_entry_list {
        struct tree_entry_list *next;
-       unsigned directory : 1;
-       unsigned executable : 1;
-       unsigned symlink : 1;
        unsigned int mode;
        const char *name;
        const unsigned char *sha1;
 };
 
-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
+static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
 {
-       struct tree_desc desc;
        struct name_entry one;
        struct tree_entry_list *ret = NULL;
        struct tree_entry_list **list_p = &ret;
 
-       if (!tree->object.parsed)
-               parse_tree(tree);
-
-       init_tree_desc(&desc, tree->buffer, tree->size);
-
-       while (tree_entry(&desc, &one)) {
+       while (tree_entry(desc, &one)) {
                struct tree_entry_list *entry;
 
                entry = xmalloc(sizeof(struct tree_entry_list));
                entry->name = one.path;
                entry->sha1 = one.sha1;
                entry->mode = one.mode;
-               entry->directory = S_ISDIR(one.mode) != 0;
-               entry->executable = (one.mode & S_IXUSR) != 0;
-               entry->symlink = S_ISLNK(one.mode) != 0;
                entry->next = NULL;
 
                *list_p = entry;
@@ -69,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
        return ret;
 }
 
+static inline void remove_entry(int remove)
+{
+       if (remove >= 0)
+               remove_cache_entry_at(remove);
+}
+
 static int unpack_trees_rec(struct tree_entry_list **posns, int len,
                            const char *base, struct unpack_trees_options *o,
                            struct tree_entry_list *df_conflict_list)
 {
+       int remove;
        int baselen = strlen(base);
        int src_size = len + 1;
        int i_stk = i_stk;
@@ -140,9 +136,9 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 #endif
                        if (!first || entcmp(first, firstdir,
                                             posns[i]->name,
-                                            posns[i]->directory) > 0) {
+                                            S_ISDIR(posns[i]->mode)) > 0) {
                                first = posns[i]->name;
-                               firstdir = posns[i]->directory;
+                               firstdir = S_ISDIR(posns[i]->mode);
                        }
                }
                /* No name means we're done */
@@ -156,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 
                subposns = xcalloc(len, sizeof(struct tree_list_entry *));
 
+               remove = -1;
                if (cache_name && !strcmp(cache_name, first)) {
                        any_files = 1;
                        src[0] = active_cache[o->pos];
-                       remove_cache_entry_at(o->pos);
+                       remove = o->pos;
                }
 
                for (i = 0; i < len; i++) {
@@ -176,11 +173,13 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
                                continue;
                        }
 
-                       if (posns[i]->directory) {
+                       if (S_ISDIR(posns[i]->mode)) {
                                struct tree *tree = lookup_tree(posns[i]->sha1);
+                               struct tree_desc t;
                                any_dirs = 1;
                                parse_tree(tree);
-                               subposns[i] = create_tree_entry_list(tree);
+                               init_tree_desc(&t, tree->buffer, tree->size);
+                               subposns[i] = create_tree_entry_list(&t);
                                posns[i] = posns[i]->next;
                                src[i + o->merge] = o->df_conflict_entry;
                                continue;
@@ -223,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
                                                printf("\n");
                                }
 #endif
-                               ret = o->fn(src, o);
+                               ret = o->fn(src, o, remove);
 
 #if DBRT_DEBUG > 1
                                printf("Added %d entries\n", ret);
 #endif
                                o->pos += ret;
                        } else {
+                               remove_entry(remove);
                                for (i = 0; i < src_size; i++) {
                                        if (src[i]) {
                                                add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
@@ -264,10 +264,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
  * directories, in case this unlink is the removal of the
  * last entry in the directory -- empty directories are removed.
  */
-static void unlink_entry(char *name)
+static void unlink_entry(char *name, char *last_symlink)
 {
        char *cp, *prev;
 
+       if (has_symlink_leading_path(name, last_symlink))
+               return;
        if (unlink(name))
                return;
        prev = NULL;
@@ -291,11 +293,12 @@ static void unlink_entry(char *name)
 
 static struct checkout state;
 static void check_updates(struct cache_entry **src, int nr,
-               struct unpack_trees_options *o)
+                       struct unpack_trees_options *o)
 {
        unsigned short mask = htons(CE_UPDATE);
        unsigned cnt = 0, total = 0;
        struct progress progress;
+       char last_symlink[PATH_MAX];
 
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < nr; cnt++) {
@@ -309,6 +312,7 @@ static void check_updates(struct cache_entry **src, int nr,
                cnt = 0;
        }
 
+       *last_symlink = '\0';
        while (nr--) {
                struct cache_entry *ce = *src++;
 
@@ -317,25 +321,25 @@ static void check_updates(struct cache_entry **src, int nr,
                                display_progress(&progress, ++cnt);
                if (!ce->ce_mode) {
                        if (o->update)
-                               unlink_entry(ce->name);
+                               unlink_entry(ce->name, last_symlink);
                        continue;
                }
                if (ce->ce_flags & mask) {
                        ce->ce_flags &= ~mask;
-                       if (o->update)
+                       if (o->update) {
                                checkout_entry(ce, &state, NULL);
+                               *last_symlink = '\0';
+                       }
                }
        }
        if (total)
                stop_progress(&progress);;
 }
 
-int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
+int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
-       unsigned len = object_list_length(trees);
        struct tree_entry_list **posns;
        int i;
-       struct object_list *posn = trees;
        struct tree_entry_list df_conflict_list;
        static struct cache_entry *dfc;
 
@@ -355,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
 
        if (len) {
                posns = xmalloc(len * sizeof(struct tree_entry_list *));
-               for (i = 0; i < len; i++) {
-                       posns[i] = create_tree_entry_list((struct tree *) posn->item);
-                       posn = posn->next;
-               }
+               for (i = 0; i < len; i++)
+                       posns[i] = create_tree_entry_list(t+i);
+
                if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
                                     o, &df_conflict_list))
                        return -1;
@@ -406,12 +409,17 @@ static void verify_uptodate(struct cache_entry *ce,
                unsigned changed = ce_match_stat(ce, &st, 1);
                if (!changed)
                        return;
+               /*
+                * NEEDSWORK: the current default policy is to allow
+                * submodule to be out of sync wrt the supermodule
+                * index.  This needs to be tightened later for
+                * submodules that are marked to be automatically
+                * checked out.
+                */
+               if (S_ISGITLINK(ntohl(ce->ce_mode)))
+                       return;
                errno = 0;
        }
-       if (o->reset) {
-               ce->ce_flags |= htons(CE_UPDATE);
-               return;
-       }
        if (errno == ENOENT)
                return;
        die("Entry '%s' not uptodate. Cannot merge.", ce->name);
@@ -423,11 +431,24 @@ static void invalidate_ce_path(struct cache_entry *ce)
                cache_tree_invalidate_path(active_cache_tree, ce->name);
 }
 
-static int verify_clean_subdirectory(const char *path, const char *action,
+/*
+ * Check that checking out ce->sha1 in subdir ce->name is not
+ * going to overwrite any working files.
+ *
+ * Currently, git does not checkout subprojects during a superproject
+ * checkout, so it is not going to overwrite anything.
+ */
+static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+                                     struct unpack_trees_options *o)
+{
+       return 0;
+}
+
+static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                                      struct unpack_trees_options *o)
 {
        /*
-        * we are about to extract "path"; we would not want to lose
+        * we are about to extract "ce->name"; we would not want to lose
         * anything in the existing directory there.
         */
        int namelen;
@@ -435,13 +456,24 @@ static int verify_clean_subdirectory(const char *path, const char *action,
        struct dir_struct d;
        char *pathbuf;
        int cnt = 0;
+       unsigned char sha1[20];
+
+       if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
+           resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
+               /* If we are not going to update the submodule, then
+                * we don't care.
+                */
+               if (!hashcmp(sha1, ce->sha1))
+                       return 0;
+               return verify_clean_submodule(ce, action, o);
+       }
 
        /*
         * First let's make sure we do not have a local modification
         * in that directory.
         */
-       namelen = strlen(path);
-       pos = cache_name_pos(path, namelen);
+       namelen = strlen(ce->name);
+       pos = cache_name_pos(ce->name, namelen);
        if (0 <= pos)
                return cnt; /* we have it as nondirectory */
        pos = -pos - 1;
@@ -449,7 +481,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
                struct cache_entry *ce = active_cache[i];
                int len = ce_namelen(ce);
                if (len < namelen ||
-                   strncmp(path, ce->name, namelen) ||
+                   strncmp(ce->name, ce->name, namelen) ||
                    ce->name[namelen] != '/')
                        break;
                /*
@@ -467,16 +499,16 @@ static int verify_clean_subdirectory(const char *path, const char *action,
         * present file that is not ignored.
         */
        pathbuf = xmalloc(namelen + 2);
-       memcpy(pathbuf, path, namelen);
+       memcpy(pathbuf, ce->name, namelen);
        strcpy(pathbuf+namelen, "/");
 
        memset(&d, 0, sizeof(d));
        if (o->dir)
                d.exclude_per_dir = o->dir->exclude_per_dir;
-       i = read_directory(&d, path, pathbuf, namelen+1, NULL);
+       i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
        if (i)
                die("Updating '%s' would lose untracked files in it",
-                   path);
+                   ce->name);
        free(pathbuf);
        return cnt;
 }
@@ -485,7 +517,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static void verify_absent(const char *path, const char *action,
+static void verify_absent(struct cache_entry *ce, const char *action,
                struct unpack_trees_options *o)
 {
        struct stat st;
@@ -493,12 +525,15 @@ static void verify_absent(const char *path, const char *action,
        if (o->index_only || o->reset || !o->update)
                return;
 
-       if (!lstat(path, &st)) {
+       if (has_symlink_leading_path(ce->name, NULL))
+               return;
+
+       if (!lstat(ce->name, &st)) {
                int cnt;
 
-               if (o->dir && excluded(o->dir, path))
+               if (o->dir && excluded(o->dir, ce->name))
                        /*
-                        * path is explicitly excluded, so it is Ok to
+                        * ce->name is explicitly excluded, so it is Ok to
                         * overwrite it.
                         */
                        return;
@@ -510,7 +545,7 @@ static void verify_absent(const char *path, const char *action,
                         * files that are in "foo/" we would lose
                         * it.
                         */
-                       cnt = verify_clean_subdirectory(path, action, o);
+                       cnt = verify_clean_subdirectory(ce, action, o);
 
                        /*
                         * If this removed entries from the index,
@@ -538,7 +573,7 @@ static void verify_absent(const char *path, const char *action,
                 * delete this path, which is in a subdirectory that
                 * is being replaced with a blob.
                 */
-               cnt = cache_name_pos(path, strlen(path));
+               cnt = cache_name_pos(ce->name, strlen(ce->name));
                if (0 <= cnt) {
                        struct cache_entry *ce = active_cache[cnt];
                        if (!ce_stage(ce) && !ce->ce_mode)
@@ -546,7 +581,7 @@ static void verify_absent(const char *path, const char *action,
                }
 
                die("Untracked working tree file '%s' "
-                   "would be %s by merge.", path, action);
+                   "would be %s by merge.", ce->name, action);
        }
 }
 
@@ -570,7 +605,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                }
        }
        else {
-               verify_absent(merge->name, "overwritten", o);
+               verify_absent(merge, "overwritten", o);
                invalidate_ce_path(merge);
        }
 
@@ -585,7 +620,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
        if (old)
                verify_uptodate(old, o);
        else
-               verify_absent(ce->name, "removed", o);
+               verify_absent(ce, "removed", o);
        ce->ce_mode = 0;
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
        invalidate_ce_path(ce);
@@ -615,7 +650,8 @@ static void show_stage_entry(FILE *o,
 #endif
 
 int threeway_merge(struct cache_entry **stages,
-               struct unpack_trees_options *o)
+               struct unpack_trees_options *o,
+               int remove)
 {
        struct cache_entry *index;
        struct cache_entry *head;
@@ -693,8 +729,10 @@ int threeway_merge(struct cache_entry **stages,
        }
 
        /* #1 */
-       if (!head && !remote && any_anc_missing)
+       if (!head && !remote && any_anc_missing) {
+               remove_entry(remove);
                return 0;
+       }
 
        /* Under the new "aggressive" rule, we resolve mostly trivial
         * cases that we historically had git-merge-one-file resolve.
@@ -702,18 +740,18 @@ int threeway_merge(struct cache_entry **stages,
        if (o->aggressive) {
                int head_deleted = !head && !df_conflict_head;
                int remote_deleted = !remote && !df_conflict_remote;
-               const char *path = NULL;
+               struct cache_entry *ce = NULL;
 
                if (index)
-                       path = index->name;
+                       ce = index;
                else if (head)
-                       path = head->name;
+                       ce = head;
                else if (remote)
-                       path = remote->name;
+                       ce = remote;
                else {
                        for (i = 1; i < o->head_idx; i++) {
                                if (stages[i] && stages[i] != o->df_conflict_entry) {
-                                       path = stages[i]->name;
+                                       ce = stages[i];
                                        break;
                                }
                        }
@@ -726,10 +764,11 @@ int threeway_merge(struct cache_entry **stages,
                if ((head_deleted && remote_deleted) ||
                    (head_deleted && remote && remote_match) ||
                    (remote_deleted && head && head_match)) {
+                       remove_entry(remove);
                        if (index)
                                return deleted_entry(index, index, o);
-                       else if (path && !head_deleted)
-                               verify_absent(path, "removed", o);
+                       else if (ce && !head_deleted)
+                               verify_absent(ce, "removed", o);
                        return 0;
                }
                /*
@@ -748,6 +787,7 @@ int threeway_merge(struct cache_entry **stages,
                verify_uptodate(index, o);
        }
 
+       remove_entry(remove);
        o->nontrivial_merge = 1;
 
        /* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -783,7 +823,8 @@ int threeway_merge(struct cache_entry **stages,
  *
  */
 int twoway_merge(struct cache_entry **src,
-               struct unpack_trees_options *o)
+               struct unpack_trees_options *o,
+               int remove)
 {
        struct cache_entry *current = src[0];
        struct cache_entry *oldtree = src[1];
@@ -811,6 +852,7 @@ int twoway_merge(struct cache_entry **src,
                }
                else if (oldtree && !newtree && same(current, oldtree)) {
                        /* 10 or 11 */
+                       remove_entry(remove);
                        return deleted_entry(oldtree, current, o);
                }
                else if (oldtree && newtree &&
@@ -820,6 +862,7 @@ int twoway_merge(struct cache_entry **src,
                }
                else {
                        /* all other failures */
+                       remove_entry(remove);
                        if (oldtree)
                                reject_merge(oldtree);
                        if (current)
@@ -831,8 +874,8 @@ int twoway_merge(struct cache_entry **src,
        }
        else if (newtree)
                return merged_entry(newtree, current, o);
-       else
-               return deleted_entry(oldtree, current, o);
+       remove_entry(remove);
+       return deleted_entry(oldtree, current, o);
 }
 
 /*
@@ -842,7 +885,8 @@ int twoway_merge(struct cache_entry **src,
  * stage0 does not have anything there.
  */
 int bind_merge(struct cache_entry **src,
-               struct unpack_trees_options *o)
+               struct unpack_trees_options *o,
+               int remove)
 {
        struct cache_entry *old = src[0];
        struct cache_entry *a = src[1];
@@ -865,7 +909,8 @@ int bind_merge(struct cache_entry **src,
  * - take the stat information from stage0, take the data from stage1
  */
 int oneway_merge(struct cache_entry **src,
-               struct unpack_trees_options *o)
+               struct unpack_trees_options *o,
+               int remove)
 {
        struct cache_entry *old = src[0];
        struct cache_entry *a = src[1];
@@ -874,8 +919,10 @@ int oneway_merge(struct cache_entry **src,
                return error("Cannot do a oneway merge of %d trees",
                             o->merge_size);
 
-       if (!a)
+       if (!a) {
+               remove_entry(remove);
                return deleted_entry(old, old, o);
+       }
        if (old && same(old, a)) {
                if (o->reset) {
                        struct stat st;