git-read-tree: fix up three-way merge tests
[git.git] / read-tree.c
index 604884a..3746984 100644 (file)
@@ -11,11 +11,14 @@ static int unpack_tree(unsigned char *sha1)
 {
        void *buffer;
        unsigned long size;
+       int ret;
 
-       buffer = read_object_with_reference(sha1, "tree", &size, 0);
+       buffer = read_object_with_reference(sha1, "tree", &size, NULL);
        if (!buffer)
                return -1;
-       return read_tree(buffer, size, stage);
+       ret = read_tree(buffer, size, stage);
+       free(buffer);
+       return ret;
 }
 
 static char *lockfile_name;
@@ -90,11 +93,42 @@ static struct cache_entry *merge_entries(struct cache_entry *a,
        return NULL;
 }
 
+/*
+ * When a CE gets turned into an unmerged entry, we
+ * want it to be up-to-date
+ */
+static void verify_uptodate(struct cache_entry *ce)
+{
+       struct stat st;
+
+       if (!lstat(ce->name, &st)) {
+               unsigned changed = ce_match_stat(ce, &st);
+               if (!changed)
+                       return;
+               errno = 0;
+       }
+       if (errno == ENOENT)
+               return;
+       die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+}
+
+/*
+ * If the old tree contained a CE that isn't even in the
+ * result, that's always a problem, regardless of whether
+ * it's up-to-date or not (ie it can be a file that we
+ * have updated but not committed yet).
+ */
+static void reject_merge(struct cache_entry *ce)
+{
+       die("Entry '%s' would be overwritten by merge. Cannot merge.", ce->name);
+}
+
+#define CHECK_OLD(ce) if (old && same(old, ce)) { verify_uptodate(old); old = NULL; }
+
 static void trivially_merge_cache(struct cache_entry **src, int nr)
 {
-       static struct cache_entry null_entry;
        struct cache_entry **dst = src;
-       struct cache_entry *old = &null_entry;
+       struct cache_entry *old = NULL;
 
        while (nr) {
                struct cache_entry *ce, *result;
@@ -103,29 +137,46 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
 
                /* We throw away original cache entries except for the stat information */
                if (!ce_stage(ce)) {
+                       if (old)
+                               reject_merge(old);
                        old = ce;
                        src++;
                        nr--;
                        active_nr--;
                        continue;
                }
+               if (old && !path_matches(old, ce))
+                       reject_merge(old);
                if (nr > 2 && (result = merge_entries(ce, src[1], src[2])) != NULL) {
                        /*
                         * See if we can re-use the old CE directly?
                         * That way we get the uptodate stat info.
                         */
-                       if (path_matches(result, old) && same(result, old))
+                       if (old && same(old, result)) {
                                *result = *old;
+                               old = NULL;
+                       }
+                       CHECK_OLD(ce);
+                       CHECK_OLD(src[1]);
+                       CHECK_OLD(src[2]);
                        ce = result;
                        ce->ce_flags &= ~htons(CE_STAGEMASK);
                        src += 2;
                        nr -= 2;
                        active_nr -= 2;
                }
+
+               /*
+                * If we had an old entry that we now effectively
+                * overwrite, make sure it wasn't dirty.
+                */
+               CHECK_OLD(ce);
                *dst++ = ce;
                src++;
                nr--;
        }
+       if (old)
+               reject_merge(old);
 }
 
 static void merge_stat_info(struct cache_entry **src, int nr)
@@ -156,7 +207,7 @@ static void merge_stat_info(struct cache_entry **src, int nr)
        }
 }
 
-static char *read_tree_usage = "read-tree (<sha> | -m <sha1> [<sha2> <sha3>])";
+static char *read_tree_usage = "git-read-tree (<sha> | -m <sha1> [<sha2> <sha3>])";
 
 int main(int argc, char **argv)
 {
@@ -191,7 +242,7 @@ int main(int argc, char **argv)
                        merge = 1;
                        continue;
                }
-               if (get_sha1_hex(arg, sha1) < 0)
+               if (get_sha1(arg, sha1) < 0)
                        usage(read_tree_usage);
                if (stage > 3)
                        usage(read_tree_usage);