Code

Make 'read-tree' do a few more of the trivial merge cases.
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 16 Apr 2005 16:11:49 +0000 (09:11 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Sat, 16 Apr 2005 16:11:49 +0000 (09:11 -0700)
This cuts down the work for the "real merge" to stuff where
people might actually disagree on the algorithm. The trivial
cases would seem to be totally independent of any policy.

read-tree.c

index 59e6950a4c0e38f41274d95ab9d8cdbf6b540b43..c4ca86f48607cc7f83b88739a539b30315b4cc12 100644 (file)
@@ -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;