Merge branch 'ar/clone'
[gitweb.git] / xdiff / xdiffi.c
index 641362d056edb61ef38b43dd2706bbdecb2c4863..5cb7171a8f528881c6171defa5b102e87d7aa522 100644 (file)
@@ -49,6 +49,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 
 
 
+
 /*
  * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
  * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
@@ -82,7 +83,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
                 * We need to extent the diagonal "domain" by one. If the next
                 * values exits the box boundaries we need to change it in the
                 * opposite direction because (max - min) must be a power of two.
-                * Also we initialize the extenal K value to -1 so that we can
+                * Also we initialize the external K value to -1 so that we can
                 * avoid extra conditions check inside the core loop.
                 */
                if (fmin > dmin)
@@ -117,7 +118,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
                 * We need to extent the diagonal "domain" by one. If the next
                 * values exits the box boundaries we need to change it in the
                 * opposite direction because (max - min) must be a power of two.
-                * Also we initialize the extenal K value to -1 so that we can
+                * Also we initialize the external K value to -1 so that we can
                 * avoid extra conditions check inside the core loop.
                 */
                if (bmin > dmin)
@@ -395,6 +396,110 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+       long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+       char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+       xrecord_t **recs = xdf->recs;
+
+       /*
+        * This is the same of what GNU diff does. Move back and forward
+        * change groups for a consistent and pretty diff output. This also
+        * helps in finding joinable change groups and reduce the diff size.
+        */
+       for (ix = ixo = 0;;) {
+               /*
+                * Find the first changed line in the to-be-compacted file.
+                * We need to keep track of both indexes, so if we find a
+                * changed lines group on the other file, while scanning the
+                * to-be-compacted file, we need to skip it properly. Note
+                * that loops that are testing for changed lines on rchg* do
+                * not need index bounding since the array is prepared with
+                * a zero at position -1 and N.
+                */
+               for (; ix < nrec && !rchg[ix]; ix++)
+                       while (rchgo[ixo++]);
+               if (ix == nrec)
+                       break;
+
+               /*
+                * Record the start of a changed-group in the to-be-compacted file
+                * and find the end of it, on both to-be-compacted and other file
+                * indexes (ix and ixo).
+                */
+               ixs = ix;
+               for (ix++; rchg[ix]; ix++);
+               for (; rchgo[ixo]; ixo++);
+
+               do {
+                       grpsiz = ix - ixs;
+
+                       /*
+                        * If the line before the current change group, is equal to
+                        * the last line of the current change group, shift backward
+                        * the group.
+                        */
+                       while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+                              xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+                               rchg[--ixs] = 1;
+                               rchg[--ix] = 0;
+
+                               /*
+                                * This change might have joined two change groups,
+                                * so we try to take this scenario in account by moving
+                                * the start index accordingly (and so the other-file
+                                * end-of-group index).
+                                */
+                               for (; rchg[ixs - 1]; ixs--);
+                               while (rchgo[--ixo]);
+                       }
+
+                       /*
+                        * Record the end-of-group position in case we are matched
+                        * with a group of changes in the other file (that is, the
+                        * change record before the enf-of-group index in the other
+                        * file is set).
+                        */
+                       ixref = rchgo[ixo - 1] ? ix: nrec;
+
+                       /*
+                        * If the first line of the current change group, is equal to
+                        * the line next of the current change group, shift forward
+                        * the group.
+                        */
+                       while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+                              xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+                               rchg[ixs++] = 0;
+                               rchg[ix++] = 1;
+
+                               /*
+                                * This change might have joined two change groups,
+                                * so we try to take this scenario in account by moving
+                                * the start index accordingly (and so the other-file
+                                * end-of-group index). Keep tracking the reference
+                                * index in case we are shifting together with a
+                                * corresponding group of changes in the other file.
+                                */
+                               for (; rchg[ix]; ix++);
+                               while (rchgo[++ixo])
+                                       ixref = ix;
+                       }
+               } while (grpsiz != ix - ixs);
+
+               /*
+                * Try to move back the possibly merged group of changes, to match
+                * the recorded postion in the other file.
+                */
+               while (ixref < ix) {
+                       rchg[--ixs] = 1;
+                       rchg[--ix] = 0;
+                       while (rchgo[--ixo]);
+               }
+       }
+
+       return 0;
+}
+
+
 int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
        xdchange_t *cscr = NULL, *xch;
        char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
@@ -440,13 +545,13 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 
                return -1;
        }
-
-       if (xdl_build_script(&xe, &xscr) < 0) {
+       if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+           xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+           xdl_build_script(&xe, &xscr) < 0) {
 
                xdl_free_env(&xe);
                return -1;
        }
-
        if (xscr) {
                if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
 
@@ -454,12 +559,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                        xdl_free_env(&xe);
                        return -1;
                }
-
                xdl_free_script(xscr);
        }
-
        xdl_free_env(&xe);
 
        return 0;
 }
-