]> asedeno.scripts.mit.edu Git - git.git/blobdiff - xdiff/xmerge.c
Merge branch 'db/cover-letter'
[git.git] / xdiff / xmerge.c
index 286f177b542811f67fc77f633445a12cb1b5f198..82b3573e7ada8c6df13ac24a78650b80af91ea73 100644 (file)
@@ -38,8 +38,9 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
                long i1, long chg1, long i2, long chg2)
 {
        xdmerge_t *m = *merge;
-       if (m && mode == m->mode &&
-                       (i1 == m->i1 + m->chg1 || i2 == m->i2 + m->chg2)) {
+       if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+               if (mode != m->mode)
+                       m->mode = 0;
                m->chg1 = i1 + chg1 - m->i1;
                m->chg2 = i2 + chg2 - m->i2;
        } else {
@@ -165,6 +166,8 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
                        size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1,
                                        m->i1 + m->chg2 - i1, 0,
                                        dest ? dest + size : NULL);
+               else
+                       continue;
                i1 = m->i1 + m->chg1;
        }
        size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0,
@@ -189,16 +192,20 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
                if (m->mode)
                        continue;
 
+               /* no sense refining a conflict when one side is empty */
+               if (m->chg1 == 0 || m->chg2 == 0)
+                       continue;
+
                /*
                 * This probably does not work outside git, since
                 * we have a very simple mmfile structure.
                 */
                t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
-               t1.size = xe1->xdf2.recs[m->i1 + m->chg1]->ptr
-                       + xe1->xdf2.recs[m->i1 + m->chg1]->size - t1.ptr;
-               t2.ptr = (char *)xe2->xdf2.recs[m->i1]->ptr;
-               t2.size = xe2->xdf2.recs[m->i1 + m->chg1]->ptr
-                       + xe2->xdf2.recs[m->i1 + m->chg1]->size - t2.ptr;
+               t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+                       + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+               t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+               t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+                       + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
                if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
                        return -1;
                if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
@@ -208,9 +215,10 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
                        return -1;
                }
                if (!xscr) {
-                       /* If this happens, it's a bug. */
+                       /* If this happens, the changes are identical. */
                        xdl_free_env(&xe);
-                       return -2;
+                       m->mode = 4;
+                       continue;
                }
                x = xscr;
                m->i1 = xscr->i1 + i1;
@@ -240,10 +248,76 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
        return 0;
 }
 
+static int line_contains_alnum(const char *ptr, long size)
+{
+       while (size--)
+               if (isalnum(*(ptr++)))
+                       return 1;
+       return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+       for (; chg; chg--, i++)
+               if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+                               xe->xdf2.recs[i]->size))
+                       return 1;
+       return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+       xdmerge_t *next_m = m->next;
+       m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+       m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+       m->next = next_m->next;
+       free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+                                     int simplify_if_no_alnum)
+{
+       int result = 0;
+
+       if (!m)
+               return result;
+       for (;;) {
+               xdmerge_t *next_m = m->next;
+               int begin, end;
+
+               if (!next_m)
+                       return result;
+
+               begin = m->i1 + m->chg1;
+               end = next_m->i1;
+
+               if (m->mode != 0 || next_m->mode != 0 ||
+                   (end - begin > 3 &&
+                    (!simplify_if_no_alnum ||
+                     lines_contain_alnum(xe1, begin, end - begin)))) {
+                       m = next_m;
+               } else {
+                       result++;
+                       xdl_merge_two_conflicts(m);
+               }
+       }
+}
+
 /*
  * level == 0: mark all overlapping changes as conflict
  * level == 1: mark overlapping changes as conflict only if not identical
  * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ *             treat hunks not containing any letter or number as conflicting
  *
  * returns < 0 on error, == 0 for no conflicts, else number of conflicts
  */
@@ -313,22 +387,10 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
                i1 = xscr1->i1 + xscr1->chg1;
                i2 = xscr2->i1 + xscr2->chg1;
 
-               if (i1 > i2) {
-                       xscr1->chg1 -= i1 - i2;
-                       xscr1->i1 = i2;
-                       xscr1->i2 += xscr1->chg2;
-                       xscr1->chg2 = 0;
-                       xscr1 = xscr1->next;
-               } else if (i2 > i1) {
-                       xscr2->chg1 -= i2 - i1;
-                       xscr2->i1 = i1;
-                       xscr2->i2 += xscr2->chg2;
-                       xscr2->chg2 = 0;
+               if (i1 >= i2)
                        xscr2 = xscr2->next;
-               } else {
+               if (i2 >= i1)
                        xscr1 = xscr1->next;
-                       xscr2 = xscr2->next;
-               }
        }
        while (xscr1) {
                if (!changes)
@@ -359,7 +421,9 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
        if (!changes)
                changes = c;
        /* refine conflicts */
-       if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) {
+       if (level > 1 &&
+           (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+            xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {
                xdl_cleanup_merge(changes);
                return -1;
        }