combine-diff: fix placement of deletion.
[gitweb.git] / combine-diff.c
index 243f96775aaa058581491544f53f05a3bd038cc0..36363bddac0e23bf23a923d8bab5c6ea261c894e 100644 (file)
@@ -235,7 +235,10 @@ static void combine_diff(const unsigned char *parent, const char *ourtmp,
                                 */
                                nb = 1;
                        }
-                       lost_bucket = &sline[nb-1]; /* sline is 0 based */
+                       if (nn == 0)
+                               lost_bucket = &sline[nb];
+                       else
+                               lost_bucket = &sline[nb-1];
                        continue;
                }
                if (!lost_bucket)
@@ -262,58 +265,6 @@ static int interesting(struct sline *sline, unsigned long all_mask)
        return ((sline->flag & all_mask) != all_mask || sline->lost_head);
 }
 
-static unsigned long line_common_diff(struct sline *sline, unsigned long all_mask)
-{
-       /*
-        * Look at the line and see from which parents we have the
-        * same difference.
-        */
-
-       /* Lower bits of sline->flag records if the parent had this
-        * line, so XOR with all_mask gives us on-bits for parents we
-        * have differences with.
-        */
-       unsigned long common_adds = (sline->flag ^ all_mask) & all_mask;
-       unsigned long common_removes = all_mask;
-
-       /* If all the parents have this line, that also counts as
-        * having the same difference.
-        */
-       if (!common_adds)
-               common_adds = all_mask;
-
-       if (sline->lost_head) {
-               /* Lost head list records the lines removed from
-                * the parents, and parent_map records from which
-                * parent the line was removed.
-                */
-               struct lline *ll;
-               for (ll = sline->lost_head; ll; ll = ll->next) {
-                       common_removes &= ll->parent_map;
-               }
-       }
-       return common_adds & common_removes;
-}
-
-static unsigned long line_all_diff(struct sline *sline, unsigned long all_mask)
-{
-       /*
-        * Look at the line and see from which parents we have some difference.
-        */
-       unsigned long different = (sline->flag ^ all_mask) & all_mask;
-       if (sline->lost_head) {
-               /* Lost head list records the lines removed from
-                * the parents, and parent_map records from which
-                * parent the line was removed.
-                */
-               struct lline *ll;
-               for (ll = sline->lost_head; ll; ll = ll->next) {
-                       different |= ll->parent_map;
-               }
-       }
-       return different;
-}
-
 static unsigned long adjust_hunk_tail(struct sline *sline,
                                      unsigned long all_mask,
                                      unsigned long hunk_begin,
@@ -417,8 +368,7 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
        i = 0;
        while (i < cnt) {
                unsigned long j, hunk_begin, hunk_end;
-               int same, diff;
-               unsigned long same_diff, all_diff;
+               unsigned long same_diff;
                while (i < cnt && !(sline[i].flag & mark))
                        i++;
                if (cnt <= i)
@@ -449,23 +399,56 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
                }
                hunk_end = j;
 
-               /* [i..hunk_end) are interesting.  Now does it have
-                * the same change with all but one parent?
+               /* [i..hunk_end) are interesting.  Now is it really
+                * interesting?  We check if there are only two versions
+                * and the result matches one of them.  That is, we look
+                * at:
+                *   (+) line, which records lines added to which parents;
+                *       this line appears in the result.
+                *   (-) line, which records from what parents the line
+                *       was removed; this line does not appear in the result.
+                * then check the set of parents the result has difference
+                * from, from all lines.  If there are lines that has
+                * different set of parents that the result has differences
+                * from, that means we have more than two versions.
+                *
+                * Even when we have only two versions, if the result does
+                * not match any of the parents, the it should be considered
+                * interesting.  In such a case, we would have all '+' line.
+                * After passing the above "two versions" test, that would
+                * appear as "the same set of parents" to be "all parents".
                 */
-               same_diff = all_mask;
-               all_diff = 0;
-               for (j = i; j < hunk_end; j++) {
-                       same_diff &= line_common_diff(sline + j, all_mask);
-                       all_diff |= line_all_diff(sline + j, all_mask);
-               }
-               diff = same = 0;
-               for (j = 0; j < num_parent; j++) {
-                       if (same_diff & (1UL<<j))
-                               same++;
-                       if (all_diff & (1UL<<j))
-                               diff++;
+               same_diff = 0;
+               has_interesting = 0;
+               for (j = i; j < hunk_end && !has_interesting; j++) {
+                       unsigned long this_diff = ~sline[j].flag & all_mask;
+                       struct lline *ll = sline[j].lost_head;
+                       if (this_diff) {
+                               /* This has some changes.  Is it the
+                                * same as others?
+                                */
+                               if (!same_diff)
+                                       same_diff = this_diff;
+                               else if (same_diff != this_diff) {
+                                       has_interesting = 1;
+                                       break;
+                               }
+                       }
+                       while (ll && !has_interesting) {
+                               /* Lost this line from these parents;
+                                * who are they?  Are they the same?
+                                */
+                               this_diff = ll->parent_map;
+                               if (!same_diff)
+                                       same_diff = this_diff;
+                               else if (same_diff != this_diff) {
+                                       has_interesting = 1;
+                               }
+                               ll = ll->next;
+                       }
                }
-               if ((num_parent - 1 <= same) || (diff == 1)) {
+
+               if (!has_interesting && same_diff != all_mask) {
                        /* This hunk is not that interesting after all */
                        for (j = hunk_begin; j < hunk_end; j++)
                                sline[j].flag &= ~mark;
@@ -523,6 +506,30 @@ static void dump_sline(struct sline *sline, int cnt, int num_parent)
        }
 }
 
+static void reuse_combine_diff(struct sline *sline, unsigned long cnt,
+                              int i, int j)
+{
+       /* We have already examined parent j and we know parent i
+        * and parent j are the same, so reuse the combined result
+        * of parent j for parent i.
+        */
+       unsigned long lno, imask, jmask;
+       imask = (1UL<<i);
+       jmask = (1UL<<j);
+
+       for (lno = 0; lno < cnt; lno++) {
+               struct lline *ll = sline->lost_head;
+               while (ll) {
+                       if (ll->parent_map & jmask)
+                               ll->parent_map |= imask;
+                       ll = ll->next;
+               }
+               if (!(sline->flag & jmask))
+                       sline->flag &= ~imask;
+               sline++;
+       }
+}
+
 int show_combined_diff(struct combine_diff_path *elem, int num_parent,
                       int dense, const char *header, int show_empty)
 {
@@ -596,8 +603,19 @@ int show_combined_diff(struct combine_diff_path *elem, int num_parent,
                sline[cnt-1].flag = (1UL<<num_parent) - 1;
        }
 
-       for (i = 0; i < num_parent; i++)
-               combine_diff(elem->parent_sha1[i], ourtmp, sline, cnt, i);
+       for (i = 0; i < num_parent; i++) {
+               int j;
+               for (j = 0; j < i; j++) {
+                       if (!memcmp(elem->parent_sha1[i],
+                                   elem->parent_sha1[j], 20)) {
+                               reuse_combine_diff(sline, cnt, i, j);
+                               break;
+                       }
+               }
+               if (i <= j)
+                       combine_diff(elem->parent_sha1[i], ourtmp, sline,
+                                    cnt, i);
+       }
 
        show_hunks = make_hunks(sline, cnt, num_parent, dense);
 
@@ -612,6 +630,15 @@ int show_combined_diff(struct combine_diff_path *elem, int num_parent,
                else
                        printf("%s", elem->path);
                putchar('\n');
+               printf("index ");
+               for (i = 0; i < num_parent; i++) {
+                       printf("%s%s",
+                              i ? ".." : "",
+                              find_unique_abbrev(elem->parent_sha1[i],
+                                                 DEFAULT_ABBREV));
+               }
+               printf("->%s\n",
+                      find_unique_abbrev(elem->sha1, DEFAULT_ABBREV));
                dump_sline(sline, cnt, num_parent);
        }
        if (ourtmp == ourtmp_buf)