Add more header dependencies.
[git.git] / read-tree.c
index 59e6950..f507f16 100644 (file)
@@ -15,7 +15,7 @@ static int read_one_entry(unsigned char *sha1, const char *base, int baselen, co
 
        memset(ce, 0, size);
 
-       ce->ce_mode = htonl(mode);
+       ce->ce_mode = create_ce_mode(mode);
        ce->ce_flags = create_ce_flags(baselen + len, stage);
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len+1);
@@ -73,26 +73,61 @@ static void remove_lock_file(void)
                unlink(".git/index.lock");
 }
 
+static int same(struct cache_entry *a, struct cache_entry *b)
+{
+       return a->ce_mode == b->ce_mode && 
+               !memcmp(a->sha1, b->sha1, 20);
+}
+
+
 /*
- * This removes all identical entries and collapses them to state 0.
+ * This removes all trivial merges that don't change the tree
+ * and collapses them to state 0.
  *
- * _Any_ other merge (even a trivial one, like both ) is left to user policy.
- * That includes "both created the same file", and "both removed the same
- * file" - which are trivial, but the user might still want to _note_ it.
+ * _Any_ other merge is left to user policy.  That includes "both
+ * created the same file", and "both removed the same file" - which are
+ * trivial, but the user might still want to _note_ it. 
  */
-static int same_entry(struct cache_entry *a,
-                       struct cache_entry *b,
-                       struct cache_entry *c)
+static struct cache_entry *merge_entries(struct cache_entry *a,
+                                        struct cache_entry *b,
+                                        struct cache_entry *c)
 {
        int len = ce_namelen(a);
-       return  a->ce_mode == b->ce_mode &&
-               a->ce_mode == c->ce_mode &&
-               ce_namelen(b) == len &&
-               ce_namelen(c) == len &&
-               !memcmp(a->name, b->name, len) &&
-               !memcmp(a->name, c->name, len) &&
-               !memcmp(a->sha1, b->sha1, 20) &&
-               !memcmp(a->sha1, c->sha1, 20);
+
+       /*
+        * Are they all the same filename? We won't do
+        * any name merging
+        */
+       if (ce_namelen(b) != len ||
+           ce_namelen(c) != len ||
+           memcmp(a->name, b->name, len) ||
+           memcmp(a->name, c->name, len))
+               return NULL;
+
+       /*
+        * Ok, all three entries describe the same
+        * filename, but maybe the contents or file
+        * mode have changed?
+        *
+        * The trivial cases end up being the ones where two
+        * out of three files are the same:
+        *  - both destinations the same, trivially take either
+        *  - one of the destination versions hasn't changed,
+        *    take the other.
+        *
+        * The "all entries exactly the same" case falls out as
+        * a special case of any of the "two same" cases.
+        *
+        * Here "a" is "original", and "b" and "c" are the two
+        * trees we are merging.
+        */
+       if (same(b,c))
+               return c;
+       if (same(a,b))
+               return c;
+       if (same(a,c))
+               return b;
+       return NULL;
 }
 
 static void trivially_merge_cache(struct cache_entry **src, int nr)
@@ -100,10 +135,11 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
        struct cache_entry **dst = src;
 
        while (nr) {
-               struct cache_entry *ce;
+               struct cache_entry *ce, *result;
 
                ce = src[0];
-               if (nr > 2 && same_entry(ce, src[1], src[2])) {
+               if (nr > 2 && (result = merge_entries(ce, src[1], src[2])) != NULL) {
+                       ce = result;
                        ce->ce_flags &= ~htons(CE_STAGEMASK);
                        src += 2;
                        nr -= 2;