summaryrefslogtreecommitdiff
path: root/xdiff/xmerge.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdiff/xmerge.c')
-rw-r--r--xdiff/xmerge.c105
1 files changed, 79 insertions, 26 deletions
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 1659edb453..af40c88a5b 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,
dest ? dest + size : NULL);
- if (style == XDL_MERGE_DIFF3) {
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
/* Shared preimage */
if (!dest) {
size += marker_size + 1 + needs_cr + marker3_size;
@@ -322,6 +322,40 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
return size;
}
+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
+{
+ return xdl_recmatch(rec1->ptr, rec1->size,
+ rec2->ptr, rec2->size, flags);
+}
+
+/*
+ * Remove any common lines from the beginning and end of the conflicted region.
+ */
+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;
+ for (; m; m = m->next) {
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ while(m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ m->i1++;
+ m->i2++;
+ }
+ while (m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1 + m->chg1 - 1],
+ rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ }
+ }
+}
+
/*
* Sometimes, changes are not quite identical, but differ in only a few
* lines. Try hard to show only these few lines as conflicting.
@@ -482,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
int style = xmp->style;
int favor = xmp->favor;
- if (style == XDL_MERGE_DIFF3) {
+ /*
+ * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking
+ * at common areas of sides 1 & 2, because the base (side 0) does
+ * not match and is being shown. Similarly, simplification of
+ * non-conflicts is also skipped due to the skipping of conflict
+ * refinement.
+ *
+ * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to
+ * refine conflicts looking for common areas of sides 1 & 2.
+ * However, since the base is being shown and does not match,
+ * it will only look for common areas at the beginning or end
+ * of the conflict block. Since XDL_MERGE_ZEALOUS_DIFF3's
+ * conflict refinement is much more limited in this fashion, the
+ * conflict simplification will be skipped.
+ */
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
/*
* "diff3 -m" output does not make sense for anything
* more aggressive than XDL_MERGE_EAGER.
@@ -603,10 +652,12 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
if (!changes)
changes = c;
/* refine conflicts */
- if (XDL_MERGE_ZEALOUS <= level &&
- (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
- xdl_simplify_non_conflicts(xe1, changes,
- XDL_MERGE_ZEALOUS < level) < 0)) {
+ if (style == XDL_MERGE_ZEALOUS_DIFF3) {
+ xdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);
+ } else if (XDL_MERGE_ZEALOUS <= level &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes,
+ XDL_MERGE_ZEALOUS < level) < 0)) {
xdl_cleanup_merge(changes);
return -1;
}
@@ -633,42 +684,42 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
xmparam_t const *xmp, mmbuffer_t *result)
{
- xdchange_t *xscr1, *xscr2;
+ xdchange_t *xscr1 = NULL, *xscr2 = NULL;
xdfenv_t xe1, xe2;
- int status;
+ int status = -1;
xpparam_t const *xpp = &xmp->xpp;
result->ptr = NULL;
result->size = 0;
- if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0) {
- return -1;
- }
- if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
- xdl_free_env(&xe1);
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0)
return -1;
- }
+
+ if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0)
+ goto free_xe1; /* avoid double free of xe2 */
+
if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe1, &xscr1) < 0) {
- xdl_free_env(&xe1);
- return -1;
- }
+ xdl_build_script(&xe1, &xscr1) < 0)
+ goto out;
+
if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
- xdl_build_script(&xe2, &xscr2) < 0) {
- xdl_free_script(xscr1);
- xdl_free_env(&xe1);
- xdl_free_env(&xe2);
- return -1;
- }
- status = 0;
+ xdl_build_script(&xe2, &xscr2) < 0)
+ goto out;
+
if (!xscr1) {
result->ptr = xdl_malloc(mf2->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
memcpy(result->ptr, mf2->ptr, mf2->size);
result->size = mf2->size;
} else if (!xscr2) {
result->ptr = xdl_malloc(mf1->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
memcpy(result->ptr, mf1->ptr, mf1->size);
result->size = mf1->size;
} else {
@@ -676,11 +727,13 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
&xe2, xscr2,
xmp, result);
}
+ out:
xdl_free_script(xscr1);
xdl_free_script(xscr2);
- xdl_free_env(&xe1);
xdl_free_env(&xe2);
+ free_xe1:
+ xdl_free_env(&xe1);
return status;
}