git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/12] Towards a better merge resolution support
@ 2008-08-30  0:42 Junio C Hamano
  2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
                   ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This consists of two loosely related topics on improving conflicted merge
resolution support.

The early part of the series is what you already saw.  In addition to
recording a conflicted merge in the RCS merge style we have traditionally
used, this allows you to optionally use "diff3 -m" style.  The difference
is that the latter format shows the part from the common ancestor that
corresponds to the parts both sides modified to cause the conflict, in
addition to the changes done on each side.  This can be chosen by setting
a configuration variable.  Rerere mechanism is updated to understand this
new format as well, and conflicts from either formats interoperate well,
because rerere mechanism only records and uses the changes made on each
side, not what was in the common ancestor.

The last four patches are to "git checkout" that checks things out of the
index.  When resolving conflicts, sometimes you would screw up the state
of the working tree so badly that you would wish to redo the merge from
the beginning for one file, without having to redo the whole merge.  Some
other times, you already know changes made on one side already solves
everything the other side attempted to do, and would want to take the
change from that side as a whole.  New options supported by "git checkout"
when checking out from the index for these purposes are:

 * git checkout -m -- path

   This recreates the merge using information staged in stages 1/2/3;
 
 * git checkout --ours -- path

   This writes 'our' version (stage #2) out for the path to the working
   tree;

 * git checkout --theirs -- path

   This writes 'their' version (stage #3) out for the path to the working
   tree;

None of these operations mark the path resolved.  They are to help you
prepare the working tree into a shape suitable as the resolution, and you
will still conclude it with "git add path".

Junio C Hamano (12):
  xdl_fill_merge_buffer(): separate out a too deeply nested function
  xdiff-merge: optionally show conflicts in "diff3 -m" style
  xmerge.c: minimum readability fixups
  xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or
    less
  rerere.c: use symbolic constants to keep track of parsing states
  rerere: understand "diff3 -m" style conflicts with the original
  merge.conflictstyle: choose between "merge" and "diff3 -m" styles
  git-merge-recursive: learn to honor merge.conflictstyle
  checkout: do not check out unmerged higher stages randomly
  checkout: allow ignoring unmerged paths when checking out of the
    index
  checkout --ours/--theirs
  checkout -m: recreate merge when checking out of unmerged index

 Documentation/config.txt  |    8 ++
 builtin-checkout.c        |  206 +++++++++++++++++++++++++++++++++++----
 builtin-merge-file.c      |   17 +++-
 builtin-merge-recursive.c |    2 +-
 ll-merge.c                |   16 +++-
 rerere.c                  |   29 ++++--
 t/t6023-merge-file.sh     |   44 +++++++++
 t/t7201-co.sh             |  133 +++++++++++++++++++++++++
 xdiff-interface.c         |   20 ++++
 xdiff-interface.h         |    2 +
 xdiff/xdiff.h             |    6 +
 xdiff/xmerge.c            |  237 +++++++++++++++++++++++++++++++--------------
 12 files changed, 612 insertions(+), 108 deletions(-)

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function
  2008-08-30  0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano
@ 2008-08-30  0:42 ` Junio C Hamano
  2008-08-30  0:42   ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano
  2008-08-30  9:14   ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin
  2008-09-01  9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen
  2008-09-01  9:44 ` Alex Riesen
  2 siblings, 2 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This simply moves code around to make a separate function that prepares
a single conflicted hunk with markers into the buffer.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 xdiff/xmerge.c |  121 ++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 82b3573..6ffaa4f 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -113,65 +113,84 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 	return size;
 }
 
-static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+			      xdfenv_t *xe2, const char *name2,
+			      int size, int i,
+			      xdmerge_t *m, char *dest)
 {
 	const int marker_size = 7;
 	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
 	int marker2_size = (name2 ? strlen(name2) + 1 : 0);
-	int conflict_marker_size = 3 * (marker_size + 1)
-		+ marker1_size + marker2_size;
-	int size, i1, j;
-
-	for (size = i1 = 0; m; m = m->next) {
-		if (m->mode == 0) {
-			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '<';
-				if (marker1_size) {
-					dest[size] = ' ';
-					memcpy(dest + size + 1, name1,
-							marker1_size - 1);
-					size += marker1_size;
-				}
-				dest[size++] = '\n';
-			} else
-				size += conflict_marker_size;
-			size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '=';
-				dest[size++] = '\n';
-			}
-			size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
-					dest ? dest + size : NULL);
-			if (dest) {
-				for (j = 0; j < marker_size; j++)
-					dest[size++] = '>';
-				if (marker2_size) {
-					dest[size] = ' ';
-					memcpy(dest + size + 1, name2,
-							marker2_size - 1);
-					size += marker2_size;
-				}
-				dest[size++] = '\n';
-			}
-		} else if (m->mode == 1)
-			size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0,
-					dest ? dest + size : NULL);
+	int j;
+
+	/* Before conflicting part */
+	size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+			      dest ? dest + size : NULL);
+
+	if (!dest) {
+		size += marker_size + 1 + marker1_size;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '<';
+		if (marker1_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name1, marker1_size - 1);
+			size += marker1_size;
+		}
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #1 */
+	size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+			      dest ? dest + size : NULL);
+	if (!dest) {
+		size += marker_size + 1;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '=';
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #2 */
+	size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+			      dest ? dest + size : NULL);
+	if (!dest) {
+		size += marker_size + 1 + marker2_size;
+	} else {
+		for (j = 0; j < marker_size; j++)
+			dest[size++] = '>';
+		if (marker2_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name2, marker2_size - 1);
+			size += marker2_size;
+		}
+		dest[size++] = '\n';
+	}
+	return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+{
+	int size, i;
+
+	for (size = i = 0; m; m = m->next) {
+		if (m->mode == 0)
+			size = fill_conflict_hunk(xe1, name1, xe2, name2,
+						  size, i, m, dest);
+		else if (m->mode == 1)
+			size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
+					      dest ? dest + size : NULL);
 		else if (m->mode == 2)
-			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1,
-					m->i1 + m->chg2 - i1, 0,
-					dest ? dest + size : NULL);
+			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i,
+					      m->i1 + m->chg2 - i, 0,
+					      dest ? dest + size : NULL);
 		else
 			continue;
-		i1 = m->i1 + m->chg1;
+		i = m->i1 + m->chg1;
 	}
-	size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0,
-			dest ? dest + size : NULL);
+	size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
+			      dest ? dest + size : NULL);
 	return size;
 }
 
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style
  2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
@ 2008-08-30  0:42   ` Junio C Hamano
  2008-08-30  0:42     ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano
  2008-08-30  9:29     ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin
  2008-08-30  9:14   ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin
  1 sibling, 2 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

When showing conflicting merges, we traditionally followed RCS's merge
output format.  The output shows:

 <<<<<<<
 postimage from one side;
 =======
 postimage of the other side; and
 >>>>>>>

Some poeple find it easier to be able to understand what is going on when
they can view the common ancestor's version, which is used by "diff3 -m",
which shows:

 <<<<<<<
 postimage from one side;
 |||||||
 shared preimage;
 =======
 postimage of the other side; and
 >>>>>>>

This is an initial step to bring that as an optional feature to git.
Only "git merge-file" has been converted, with "--diff3" option.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-merge-file.c  |   10 ++++-
 t/t6023-merge-file.sh |   37 +++++++++++++++++
 xdiff/xdiff.h         |    6 +++
 xdiff/xmerge.c        |  103 ++++++++++++++++++++++++++++++++++++++++---------
 4 files changed, 135 insertions(+), 21 deletions(-)

diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 3605960..5b4f020 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -4,7 +4,7 @@
 #include "xdiff-interface.h"
 
 static const char merge_file_usage[] =
-"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
 int cmd_merge_file(int argc, const char **argv, const char *prefix)
 {
@@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
 	int ret = 0, i = 0, to_stdout = 0;
+	int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
+	int merge_style = 0;
 
 	while (argc > 4) {
 		if (!strcmp(argv[1], "-L") && i < 3) {
@@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-q") ||
 				!strcmp(argv[1], "--quiet"))
 			freopen("/dev/null", "w", stderr);
+		else if (!strcmp(argv[1], "--diff3")) {
+			merge_style = XDL_MERGE_DIFF3;
+			merge_level = XDL_MERGE_EAGER;
+		}
 		else
 			usage(merge_file_usage);
 		argc--;
@@ -46,7 +52,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 	}
 
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
-			&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);
+			&xpp, merge_level | merge_style, &result);
 
 	for (i = 0; i < 3; i++)
 		free(mmfs[i].ptr);
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 42620e0..f3484e3 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' '
 
 '
 
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+|||||||
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+=======
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>> new9.txt
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success '"diff3 -m" style output' '
+	test_must_fail git merge-file -p --diff3 \
+		new8.txt new5.txt new9.txt >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 413082e..deebe02 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -50,10 +50,16 @@ extern "C" {
 #define XDL_BDOP_CPY 2
 #define XDL_BDOP_INSB 3
 
+/* merge simplification levels */
 #define XDL_MERGE_MINIMAL 0
 #define XDL_MERGE_EAGER 1
 #define XDL_MERGE_ZEALOUS 2
 #define XDL_MERGE_ZEALOUS_ALNUM 3
+#define XDL_MERGE_LEVEL_MASK 0x0f
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 0x8000
+#define XDL_MERGE_STYLE_MASK 0x8000
 
 typedef struct s_mmfile {
 	char *ptr;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 6ffaa4f..29cdbea 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -30,17 +30,32 @@ typedef struct s_xdmerge {
 	 * 2 = no conflict, take second.
 	 */
 	int mode;
+	/*
+	 * These point at the respective postimages.  E.g. <i1,chg1> is
+	 * how side #1 wants to change the common ancestor; if there is no
+	 * overlap, lines before i1 in the postimage of side #1 appear
+	 * in the merge result as a region touched by neither side.
+	 */
 	long i1, i2;
 	long chg1, chg2;
+	/*
+	 * These point at the preimage; of course there is just one
+	 * preimage, that is from the shared common ancestor.
+	 */
+	long i0;
+	long chg0;
 } xdmerge_t;
 
 static int xdl_append_merge(xdmerge_t **merge, int mode,
-		long i1, long chg1, long i2, long chg2)
+			    long i0, long chg0,
+			    long i1, long chg1,
+			    long i2, long chg2)
 {
 	xdmerge_t *m = *merge;
 	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
 		if (mode != m->mode)
 			m->mode = 0;
+		m->chg0 = i0 + chg0 - m->i0;
 		m->chg1 = i1 + chg1 - m->i1;
 		m->chg2 = i2 + chg2 - m->i2;
 	} else {
@@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
 			return -1;
 		m->next = NULL;
 		m->mode = mode;
+		m->i0 = i0;
+		m->chg0 = chg0;
 		m->i1 = i1;
 		m->chg1 = chg1;
 		m->i2 = i2;
@@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
 	return 0;
 }
 
-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 {
-	xrecord_t **recs = xe->xdf2.recs + i;
+	xrecord_t **recs;
 	int size = 0;
 
+	recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
 	if (count < 1)
 		return 0;
 
@@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
 	return size;
 }
 
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
 static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 			      xdfenv_t *xe2, const char *name2,
-			      int size, int i,
+			      int size, int i, int style,
 			      xdmerge_t *m, char *dest)
 {
 	const int marker_size = 7;
@@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 	/* Postimage from side #1 */
 	size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
 			      dest ? dest + size : NULL);
+
+	if (style == XDL_MERGE_DIFF3) {
+		/* Shared preimage */
+		if (!dest) {
+			size += marker_size + 1;
+		} else {
+			for (j = 0; j < marker_size; j++)
+				dest[size++] = '|';
+			dest[size++] = '\n';
+		}
+		size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+				      dest ? dest + size : NULL);
+	}
+
 	if (!dest) {
 		size += marker_size + 1;
 	} else {
@@ -170,14 +213,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
 }
 
 static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
-		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+				 xdfenv_t *xe2, const char *name2,
+				 xdmerge_t *m, char *dest, int style)
 {
 	int size, i;
 
 	for (size = i = 0; m; m = m->next) {
 		if (m->mode == 0)
 			size = fill_conflict_hunk(xe1, name1, xe2, name2,
-						  size, i, m, dest);
+						  size, i, style, m, dest);
 		else if (m->mode == 1)
 			size += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,
 					      dest ? dest + size : NULL);
@@ -342,9 +386,11 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
  */
 static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
-		int level, xpparam_t const *xpp, mmbuffer_t *result) {
+		int flags, xpparam_t const *xpp, mmbuffer_t *result) {
 	xdmerge_t *changes, *c;
-	int i1, i2, chg1, chg2;
+	int i0, i1, i2, chg0, chg1, chg2;
+	int level = flags & XDL_MERGE_LEVEL_MASK;
+	int style = flags & XDL_MERGE_STYLE_MASK;
 
 	c = changes = NULL;
 
@@ -352,11 +398,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 		if (!changes)
 			changes = c;
 		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+			i0 = xscr1->i1;
 			i1 = xscr1->i2;
 			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+			chg0 = xscr1->chg1;
 			chg1 = xscr1->chg2;
 			chg2 = xscr1->chg1;
-			if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+			if (xdl_append_merge(&c, 1,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
@@ -364,11 +413,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 			continue;
 		}
 		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+			i0 = xscr2->i1;
 			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
 			i2 = xscr2->i2;
+			chg0 = xscr2->chg1;
 			chg1 = xscr2->chg1;
 			chg2 = xscr2->chg2;
-			if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+			if (xdl_append_merge(&c, 2,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
@@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 			int off = xscr1->i1 - xscr2->i1;
 			int ffo = off + xscr1->chg1 - xscr2->chg1;
 
+			i0 = xscr1->i1;
 			i1 = xscr1->i2;
 			i2 = xscr2->i2;
-			if (off > 0)
+			if (off > 0) {
+				i0 -= off;
 				i1 -= off;
+			}
 			else
 				i2 += off;
+			chg0 = xscr1->i1 + xscr1->chg1 - i0;
 			chg1 = xscr1->i2 + xscr1->chg2 - i1;
 			chg2 = xscr2->i2 + xscr2->chg2 - i2;
 			if (ffo > 0)
 				chg2 += ffo;
-			else
+			else {
+				chg0 -= ffo;
 				chg1 -= ffo;
-			if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {
+			}
+			if (xdl_append_merge(&c, 0,
+					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
 				return -1;
 			}
@@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	while (xscr1) {
 		if (!changes)
 			changes = c;
+		i0 = xscr1->i1;
 		i1 = xscr1->i2;
 		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+		chg0 = xscr1->chg1;
 		chg1 = xscr1->chg2;
 		chg2 = xscr1->chg1;
-		if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+		if (xdl_append_merge(&c, 1,
+				     i0, chg0, i1, chg1, i2, chg2)) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
@@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	while (xscr2) {
 		if (!changes)
 			changes = c;
+		i0 = xscr2->i1;
 		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
 		i2 = xscr2->i2;
+		chg0 = xscr2->chg1;
 		chg1 = xscr2->chg1;
 		chg2 = xscr2->chg2;
-		if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+		if (xdl_append_merge(&c, 2,
+				     i0, chg0, i1, chg1, i2, chg2)) {
 			xdl_cleanup_merge(changes);
 			return -1;
 		}
@@ -449,7 +514,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	/* output */
 	if (result) {
 		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
-			changes, NULL);
+			changes, NULL, style);
 		result->ptr = xdl_malloc(size);
 		if (!result->ptr) {
 			xdl_cleanup_merge(changes);
@@ -457,14 +522,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 		}
 		result->size = size;
 		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
-				result->ptr);
+				      result->ptr, style);
 	}
 	return xdl_cleanup_merge(changes);
 }
 
 int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		mmfile_t *mf2, const char *name2,
-		xpparam_t const *xpp, int level, mmbuffer_t *result) {
+		xpparam_t const *xpp, int flags, mmbuffer_t *result) {
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
 	int status;
@@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		} else {
 			status = xdl_do_merge(&xe1, xscr1, name1,
 					      &xe2, xscr2, name2,
-					      level, xpp, result);
+					      flags, xpp, result);
 		}
 		xdl_free_script(xscr1);
 		xdl_free_script(xscr2);
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 03/12] xmerge.c: minimum readability fixups
  2008-08-30  0:42   ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano
@ 2008-08-30  0:42     ` Junio C Hamano
  2008-08-30  0:42       ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano
  2008-08-30  9:31       ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin
  2008-08-30  9:29     ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin
  1 sibling, 2 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This replaces hardcoded magic constants with symbolic ones for
readability, and swaps one if/else blocks to better match the
order in which 0/1/2 variables are handled to nearby codepath.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 xdiff/xmerge.c |   14 +++++++-------
 1 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 29cdbea..7dcd405 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 			xscr2 = xscr2->next;
 			continue;
 		}
-		if (level < 1 || xscr1->i1 != xscr2->i1 ||
+		if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
 				xscr1->chg1 != xscr2->chg1 ||
 				xscr1->chg2 != xscr2->chg2 ||
 				xdl_merge_cmp_lines(xe1, xscr1->i2,
@@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 			chg0 = xscr1->i1 + xscr1->chg1 - i0;
 			chg1 = xscr1->i2 + xscr1->chg2 - i1;
 			chg2 = xscr2->i2 + xscr2->chg2 - i2;
-			if (ffo > 0)
-				chg2 += ffo;
-			else {
+			if (ffo < 0) {
 				chg0 -= ffo;
 				chg1 -= ffo;
-			}
+			} else
+				chg2 += ffo;
 			if (xdl_append_merge(&c, 0,
 					     i0, chg0, i1, chg1, i2, chg2)) {
 				xdl_cleanup_merge(changes);
@@ -505,9 +504,10 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	if (!changes)
 		changes = c;
 	/* refine conflicts */
-	if (level > 1 &&
+	if (XDL_MERGE_ZEALOUS <= level &&
 	    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
-	     xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {
+	     xdl_simplify_non_conflicts(xe1, changes,
+					XDL_MERGE_ZEALOUS < level) < 0)) {
 		xdl_cleanup_merge(changes);
 		return -1;
 	}
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less
  2008-08-30  0:42     ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano
@ 2008-08-30  0:42       ` Junio C Hamano
  2008-08-30  0:42         ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano
  2008-08-30  9:34         ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin
  2008-08-30  9:31       ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin
  1 sibling, 2 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

When showing a conflicting merge result, and "--diff3 -m" style is asked
for, this patch makes sure that the merge reduction level does not exceed
XDL_MERGE_EAGER.  This is because "diff3 -m" style output would not make
sense for anything more aggressive than XDL_MERGE_EAGER, because of the
way how the merge reduction works.

"git merge-file" no longer has to force MERGE_EAGER when "--diff3" is
asked for because of this change.

Suppose a common ancestor (shared preimage) is modified to postimage #1
and #2 (each letter represents one line):

                     #####
    postimage#1: 1234ABCDE789
                    |    /
                    |   /
    preimage:    123456789
                    |   \
    postimage#2: 1234AXYE789
                     ####

XDL_MERGE_MINIMAL and XDL_MERGE_EAGER would:

 (1) find the s/56/ABCDE/ done on one side and s/56/AXYE/ done on the
     other side,

 (2) notice that they touch an overlapping area, and

 (3) mark it as a conflict, "ABCDE vs AXYE".

The difference between the two algorithms is that EAGER drops the hunk
altogether if the postimages match (i.e. both sides modified the same
way), while MINIMAL keeps it.  There is no other operation performed to
the hunk.  As the result, lines marked with "#" in the above picure will
be in the RCS merge style output like this (letters <, = and > represent
conflict marker lines):

    output:      1234<ABCDE=AXYE>789    ; with MINIMAL/EAGER

The part from the preimage that corresponds to these conflicting changes
is "56", which is what "diff3 -m" style output adds to it:

    output:      1234<ABCDE|56=AXYE>789 ; in "diff3 -m" style

Now, XDL_MERGE_ZEALOUS looks at the differences between the changes two
postimages made in order to reduce the number of lines in the conflicting
regions.  It notices that both sides start their new contents with "A",
and excludes it from the output (it also excludes "E" for the same
reason).  The conflict that used to be "ABCDE vs AXYE" is now "BCD vs XY":

    output:      1234A<BCD=XY>E789      ; with ZEALOUS

There could even be matching parts between two postimages in the middle.
Instead of one side rewriting the shared "56" to "ABCDE" and the other
side to "AXYE", imagine the case where the postimages are "ABCDE" and
"AXCYE", in which case instead of having one conflicted hunk "BCD vs XY",
you would have two conflicting hunks "B vs X" and "D vs Y".

In either case, once you reduce "ABCDE vs AXYE" to "BCD vs XY" (or "ABCDE
vs AXCYE" to "B vs X" and "D vs Y"), there is no part from the preimage
that corresponds to the conflicting change made in both postimages
anymore.  In other words, conflict reduced by ZEALOUS algorithm cannot be
expressed in "diff3 -m" style.  Representing the last illustration like
this is misleading to say the least:

    output:      1234A<BCD|56=XY>E789   ; broken "diff3 -m" style

because the preimage was not ...4A56E... to begin with.  "A" and "E" are
common only between the postimages.

Even worse, once a single conflicting hunk is split into multiple ones
(recall the example of breaking "ABCDE vs AXCYE" to "B vs X" and "D vs
Y"), there is no sane way to distribute the preimage text across split
conflicting hunks.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-merge-file.c |    4 +---
 xdiff/xmerge.c       |    9 +++++++++
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 5b4f020..1e92510 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -27,10 +27,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-q") ||
 				!strcmp(argv[1], "--quiet"))
 			freopen("/dev/null", "w", stderr);
-		else if (!strcmp(argv[1], "--diff3")) {
+		else if (!strcmp(argv[1], "--diff3"))
 			merge_style = XDL_MERGE_DIFF3;
-			merge_level = XDL_MERGE_EAGER;
-		}
 		else
 			usage(merge_file_usage);
 		argc--;
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 7dcd405..d9737f0 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -392,6 +392,15 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 	int level = flags & XDL_MERGE_LEVEL_MASK;
 	int style = flags & XDL_MERGE_STYLE_MASK;
 
+	if (style == XDL_MERGE_DIFF3) {
+		/*
+		 * "diff3 -m" output does not make sense for anything
+		 * more aggressive than XDL_MERGE_EAGER.
+		 */
+		if (XDL_MERGE_EAGER < level)
+			level = XDL_MERGE_EAGER;
+	}
+
 	c = changes = NULL;
 
 	while (xscr1 && xscr2) {
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states
  2008-08-30  0:42       ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano
@ 2008-08-30  0:42         ` Junio C Hamano
  2008-08-30  0:42           ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano
  2008-08-30  9:34         ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin
  1 sibling, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

These hardcoded integers make the code harder to follow than necessary;
replace them with enums to make it easier to read, before adding support
for optionally parsing "diff3 -m" style conflict markers.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 rerere.c |   23 +++++++++++++----------
 1 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/rerere.c b/rerere.c
index 323e493..bf74b26 100644
--- a/rerere.c
+++ b/rerere.c
@@ -75,7 +75,10 @@ static int handle_file(const char *path,
 {
 	SHA_CTX ctx;
 	char buf[1024];
-	int hunk = 0, hunk_no = 0;
+	int hunk_no = 0;
+	enum {
+		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2,
+	} hunk = RR_CONTEXT;
 	struct strbuf one, two;
 	FILE *f = fopen(path, "r");
 	FILE *out = NULL;
@@ -98,20 +101,20 @@ static int handle_file(const char *path,
 	strbuf_init(&two,  0);
 	while (fgets(buf, sizeof(buf), f)) {
 		if (!prefixcmp(buf, "<<<<<<< ")) {
-			if (hunk)
+			if (hunk != RR_CONTEXT)
 				goto bad;
-			hunk = 1;
+			hunk = RR_SIDE_1;
 		} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
-			if (hunk != 1)
+			if (hunk != RR_SIDE_1)
 				goto bad;
-			hunk = 2;
+			hunk = RR_SIDE_2;
 		} else if (!prefixcmp(buf, ">>>>>>> ")) {
-			if (hunk != 2)
+			if (hunk != RR_SIDE_2)
 				goto bad;
 			if (strbuf_cmp(&one, &two) > 0)
 				strbuf_swap(&one, &two);
 			hunk_no++;
-			hunk = 0;
+			hunk = RR_CONTEXT;
 			if (out) {
 				fputs("<<<<<<<\n", out);
 				fwrite(one.buf, one.len, 1, out);
@@ -127,9 +130,9 @@ static int handle_file(const char *path,
 			}
 			strbuf_reset(&one);
 			strbuf_reset(&two);
-		} else if (hunk == 1)
+		} else if (hunk == RR_SIDE_1)
 			strbuf_addstr(&one, buf);
-		else if (hunk == 2)
+		else if (hunk == RR_SIDE_2)
 			strbuf_addstr(&two, buf);
 		else if (out)
 			fputs(buf, out);
@@ -146,7 +149,7 @@ static int handle_file(const char *path,
 		fclose(out);
 	if (sha1)
 		SHA1_Final(sha1, &ctx);
-	if (hunk) {
+	if (hunk != RR_CONTEXT) {
 		if (output)
 			unlink(output);
 		return error("Could not parse conflict hunks in %s", path);
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original
  2008-08-30  0:42         ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano
@ 2008-08-30  0:42           ` Junio C Hamano
  2008-08-30  0:42             ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This teaches rerere to grok conflicts expressed in "diff3 -m" style
output, where the version from the common ancestor is output after the
first side, preceded by a "|||||||" line.

The rerere database needs to keep only the versions from two sides, so the
code parses the original copy and discards it.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 rerere.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/rerere.c b/rerere.c
index bf74b26..4e2c9dd 100644
--- a/rerere.c
+++ b/rerere.c
@@ -77,7 +77,7 @@ static int handle_file(const char *path,
 	char buf[1024];
 	int hunk_no = 0;
 	enum {
-		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2,
+		RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
 	} hunk = RR_CONTEXT;
 	struct strbuf one, two;
 	FILE *f = fopen(path, "r");
@@ -104,9 +104,13 @@ static int handle_file(const char *path,
 			if (hunk != RR_CONTEXT)
 				goto bad;
 			hunk = RR_SIDE_1;
-		} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
+		} else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
 			if (hunk != RR_SIDE_1)
 				goto bad;
+			hunk = RR_ORIGINAL;
+		} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
+			if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
+				goto bad;
 			hunk = RR_SIDE_2;
 		} else if (!prefixcmp(buf, ">>>>>>> ")) {
 			if (hunk != RR_SIDE_2)
@@ -132,6 +136,8 @@ static int handle_file(const char *path,
 			strbuf_reset(&two);
 		} else if (hunk == RR_SIDE_1)
 			strbuf_addstr(&one, buf);
+		else if (hunk == RR_ORIGINAL)
+			; /* discard */
 		else if (hunk == RR_SIDE_2)
 			strbuf_addstr(&two, buf);
 		else if (out)
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles
  2008-08-30  0:42           ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano
@ 2008-08-30  0:42             ` Junio C Hamano
  2008-08-30  0:42               ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano
  2008-08-30  9:42               ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin
  0 siblings, 2 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This teaches "git merge-file" to honor merge.conflictstyle configuration
variable, whose value can be "merge" (default) or "diff3".

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config.txt |    8 ++++++++
 builtin-merge-file.c     |    9 +++++++++
 t/t6023-merge-file.sh    |    9 ++++++++-
 xdiff-interface.c        |   20 ++++++++++++++++++++
 xdiff-interface.h        |    2 ++
 5 files changed, 47 insertions(+), 1 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index af57d94..cb4c4ca 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -893,6 +893,14 @@ man.<tool>.path::
 	Override the path for the given tool that may be used to
 	display help in the 'man' format. See linkgit:git-help[1].
 
+merge.conflictstyle::
+	Specify the style in which conflicted hunks are written out to
+	working tree files upon merge.  The default is "merge", which
+	shows `<<<<<<<` conflict marker, change made by one side,
+	`=======` marker, change made by the other side, and then
+	`>>>>>>>` marker.  An alternate style, "diff3", adds `|||||||`
+	marker and the original text before `=======` marker.
+
 mergetool.<tool>.path::
 	Override the path for the given tool.  This is useful in case
 	your tool is not in the PATH.
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 1e92510..f009e73 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -15,6 +15,15 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 	int ret = 0, i = 0, to_stdout = 0;
 	int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
 	int merge_style = 0;
+	int nongit;
+
+	prefix = setup_git_directory_gently(&nongit);
+	if (!nongit) {
+		/* Read the configuration file */
+		git_config(git_xmerge_config, NULL);
+		if (git_xmerge_style > 0)
+			merge_style = git_xmerge_style;
+	}
 
 	while (argc > 4) {
 		if (!strcmp(argv[1], "-L") && i < 3) {
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index f3484e3..93ec517 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -192,10 +192,17 @@ non timebo mala, quoniam TU mecum es:
 virga tua et baculus tuus ipsa me consolata sunt.
 EOF
 
-test_expect_success '"diff3 -m" style output' '
+test_expect_success '"diff3 -m" style output (1)' '
 	test_must_fail git merge-file -p --diff3 \
 		new8.txt new5.txt new9.txt >actual &&
 	test_cmp expect actual
 '
 
+test_expect_success '"diff3 -m" style output (2)' '
+	git config merge.conflictstyle diff3 &&
+	test_must_fail git merge-file -p \
+		new8.txt new5.txt new9.txt >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 944ad98..8457601 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -249,3 +249,23 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)
 		value = ep + 1;
 	}
 }
+
+int git_xmerge_style = -1;
+
+int git_xmerge_config(const char *var, const char *value, void *cb)
+{
+	if (!strcasecmp(var, "merge.conflictstyle")) {
+		if (!value)
+			die("'%s' is not a boolean", var);
+		if (!strcmp(value, "diff3"))
+			git_xmerge_style = XDL_MERGE_DIFF3;
+		else if (!strcmp(value, "merge"))
+			git_xmerge_style = 0;
+		else
+			die("unknown style '%s' given for '%s'",
+			    value, var);
+		return 0;
+	}
+	return git_default_config(var, value, cb);
+}
+
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 558492b..b3b5c93 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -17,5 +17,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line);
+extern int git_xmerge_config(const char *var, const char *value, void *cb);
+extern int git_xmerge_style;
 
 #endif
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle
  2008-08-30  0:42             ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano
@ 2008-08-30  0:42               ` Junio C Hamano
  2008-08-30  0:42                 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano
  2008-08-30  9:42               ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin
  1 sibling, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This teaches the low-level ll_xdl_merge() routine to honor
merge.conflictstyle configuration variable, so that merge-recursive
strategy can show the conflicts in the style of user's choice.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-merge-recursive.c |    2 +-
 ll-merge.c                |   16 ++++++++++++----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index dfb363e..f3b6ede 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -1348,7 +1348,7 @@ static int merge_config(const char *var, const char *value, void *cb)
 		merge_rename_limit = git_config_int(var, value);
 		return 0;
 	}
-	return git_default_config(var, value, cb);
+	return git_xmerge_config(var, value, cb);
 }
 
 int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
diff --git a/ll-merge.c b/ll-merge.c
index 9837c84..4a71614 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -63,6 +63,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 			int virtual_ancestor)
 {
 	xpparam_t xpp;
+	int style = 0;
 
 	if (buffer_is_binary(orig->ptr, orig->size) ||
 	    buffer_is_binary(src1->ptr, src1->size) ||
@@ -77,10 +78,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 	}
 
 	memset(&xpp, 0, sizeof(xpp));
+	if (git_xmerge_style >= 0)
+		style = git_xmerge_style;
 	return xdl_merge(orig,
 			 src1, name1,
 			 src2, name2,
-			 &xpp, XDL_MERGE_ZEALOUS,
+			 &xpp, XDL_MERGE_ZEALOUS | style,
 			 result);
 }
 
@@ -95,10 +98,15 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
 	char *src, *dst;
 	long size;
 	const int marker_size = 7;
-
-	int status = ll_xdl_merge(drv_unused, result, path_unused,
-				  orig, src1, NULL, src2, NULL,
-				  virtual_ancestor);
+	int status, saved_style;
+
+	/* We have to force the RCS "merge" style */
+	saved_style = git_xmerge_style;
+	git_xmerge_style = 0;
+	status = ll_xdl_merge(drv_unused, result, path_unused,
+			      orig, src1, NULL, src2, NULL,
+			      virtual_ancestor);
+	git_xmerge_style = saved_style;
 	if (status <= 0)
 		return status;
 	size = result->size;
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 09/12] checkout: do not check out unmerged higher stages randomly
  2008-08-30  0:42               ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano
@ 2008-08-30  0:42                 ` Junio C Hamano
  2008-08-30  0:42                   ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

During a conflicted merge when you have unmerged stages for a
path F in the index, if you said:

    $ git checkout F

we rewrote F as many times as we have stages for it, and the
last one (typically "theirs") was left in the work tree, without
resolving the conflict.

This fixes it by noticing that a specified pathspec pattern
matches an unmerged path, and by erroring out.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-checkout.c |   27 +++++++++++++++++++++++++++
 t/t7201-co.sh      |   23 +++++++++++++++++++++++
 2 files changed, 50 insertions(+), 0 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index b380ad6..9b33f3a 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
 	return 0;
 }
 
+static int skip_same_name(struct cache_entry *ce, int pos)
+{
+	while (++pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name))
+		; /* skip */
+	return pos;
+}
+
+
 static int checkout_paths(struct tree *source_tree, const char **pathspec)
 {
 	int pos;
@@ -107,6 +116,20 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
 	if (report_path_error(ps_matched, pathspec, 0))
 		return 1;
 
+	/* Any unmerged paths? */
+	for (pos = 0; pos < active_nr; pos++) {
+		struct cache_entry *ce = active_cache[pos];
+		if (pathspec_match(pathspec, NULL, ce->name, 0) &&
+		    ce_stage(ce)) {
+			errs = 1;
+			error("path '%s' is unmerged", ce->name);
+			pos = skip_same_name(ce, pos) - 1;
+			continue;
+		}
+	}
+	if (errs)
+		return 1;
+
 	/* Now we are committed to check them out */
 	memset(&state, 0, sizeof(state));
 	state.force = 1;
@@ -114,6 +137,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
 		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+			if (ce_stage(ce)) {
+				pos = skip_same_name(ce, pos) - 1;
+				continue;
+			}
 			errs |= checkout_entry(ce, &state, NULL);
 		}
 	}
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 1dff84d..303cf62 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -369,4 +369,27 @@ test_expect_success \
     'checkout with --track, but without -b, fails with too short tracked name' '
     test_must_fail git checkout --track renamer'
 
+test_expect_success 'checkout an unmerged path should fail' '
+	rm -f .git/index &&
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	(
+		echo "100644 $A 0	fild" &&
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file" &&
+		echo "100644 $A 0	filf"
+	) | git update-index --index-info &&
+	echo "none of the above" >sample &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	test_must_fail git checkout fild file filf &&
+	test_cmp sample fild &&
+	test_cmp sample filf &&
+	test_cmp sample file
+'
+
 test_done
+
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index
  2008-08-30  0:42                 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano
@ 2008-08-30  0:42                   ` Junio C Hamano
  2008-08-30  0:42                     ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

Earlier we made "git checkout $pathspec" to atomically refuse
the operation of $pathspec matched any path with unmerged
stages.  This patch allows:

    $ git checkout -f a b c

to ignore, instead of error out on, such unmerged paths.  The
fix to prevent checkout of an unmerged path from random stages
is still there.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-checkout.c |   41 +++++++++++++++++++++++------------------
 t/t7201-co.sh      |   24 +++++++++++++++++++++++-
 2 files changed, 46 insertions(+), 19 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index 9b33f3a..49c43d9 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -20,6 +20,17 @@ static const char * const checkout_usage[] = {
 	NULL,
 };
 
+struct checkout_opts {
+	int quiet;
+	int merge;
+	int force;
+	int writeout_error;
+
+	const char *new_branch;
+	int new_branch_log;
+	enum branch_track track;
+};
+
 static int post_checkout_hook(struct commit *old, struct commit *new,
 			      int changed)
 {
@@ -85,7 +96,8 @@ static int skip_same_name(struct cache_entry *ce, int pos)
 }
 
 
-static int checkout_paths(struct tree *source_tree, const char **pathspec)
+static int checkout_paths(struct tree *source_tree, const char **pathspec,
+			  struct checkout_opts *opts)
 {
 	int pos;
 	struct checkout state;
@@ -121,8 +133,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
 		struct cache_entry *ce = active_cache[pos];
 		if (pathspec_match(pathspec, NULL, ce->name, 0) &&
 		    ce_stage(ce)) {
-			errs = 1;
-			error("path '%s' is unmerged", ce->name);
+			if (!opts->force) {
+				errs = 1;
+				error("path '%s' is unmerged", ce->name);
+			} else {
+				warning("path '%s' is unmerged", ce->name);
+			}
 			pos = skip_same_name(ce, pos) - 1;
 			continue;
 		}
@@ -178,17 +194,6 @@ static void describe_detached_head(char *msg, struct commit *commit)
 	strbuf_release(&sb);
 }
 
-struct checkout_opts {
-	int quiet;
-	int merge;
-	int force;
-	int writeout_error;
-
-	const char *new_branch;
-	int new_branch_log;
-	enum branch_track track;
-};
-
 static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)
 {
 	struct unpack_trees_options opts;
@@ -569,15 +574,15 @@ no_reference:
 			die("invalid path specification");
 
 		/* Checkout paths */
-		if (opts.new_branch || opts.force || opts.merge) {
+		if (opts.new_branch || opts.merge) {
 			if (argc == 1) {
-				die("git checkout: updating paths is incompatible with switching branches/forcing\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
+				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
 			} else {
-				die("git checkout: updating paths is incompatible with switching branches/forcing");
+				die("git checkout: updating paths is incompatible with switching branches.");
 			}
 		}
 
-		return checkout_paths(source_tree, pathspec);
+		return checkout_paths(source_tree, pathspec, &opts);
 	}
 
 	if (new.name && !new.commit) {
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 303cf62..b7274eb 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -391,5 +391,27 @@ test_expect_success 'checkout an unmerged path should fail' '
 	test_cmp sample file
 '
 
-test_done
+test_expect_success 'checkout with an unmerged path can be ignored' '
+	rm -f .git/index &&
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	(
+		echo "100644 $A 0	fild" &&
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file" &&
+		echo "100644 $A 0	filf"
+	) | git update-index --index-info &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -f fild file filf &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp sample file
+'
 
+test_done
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 11/12] checkout --ours/--theirs
  2008-08-30  0:42                   ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano
@ 2008-08-30  0:42                     ` Junio C Hamano
  2008-08-30  0:42                       ` [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This lets you to check out 'our' (or 'their') version of an
unmerged path out of the index while resolving conflicts.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-checkout.c |   47 ++++++++++++++++++++++++++++++++++++++++++-----
 t/t7201-co.sh      |   25 +++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 5 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index 49c43d9..bdfdb65 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -24,6 +24,7 @@ struct checkout_opts {
 	int quiet;
 	int merge;
 	int force;
+	int writeout_stage;
 	int writeout_error;
 
 	const char *new_branch;
@@ -95,6 +96,32 @@ static int skip_same_name(struct cache_entry *ce, int pos)
 	return pos;
 }
 
+static int check_stage(int stage, struct cache_entry *ce, int pos)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return 0;
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
+
+static int checkout_stage(int stage, struct cache_entry *ce, int pos,
+			  struct checkout *state)
+{
+	while (pos < active_nr &&
+	       !strcmp(active_cache[pos]->name, ce->name)) {
+		if (ce_stage(active_cache[pos]) == stage)
+			return checkout_entry(active_cache[pos], state, NULL);
+		pos++;
+	}
+	return error("path '%s' does not have %s version",
+		     ce->name,
+		     (stage == 2) ? "our" : "their");
+}
 
 static int checkout_paths(struct tree *source_tree, const char **pathspec,
 			  struct checkout_opts *opts)
@@ -106,6 +133,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 	int flag;
 	struct commit *head;
 	int errs = 0;
+	int stage = opts->writeout_stage;
 
 	int newfd;
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
@@ -131,13 +159,16 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 	/* Any unmerged paths? */
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
-		if (pathspec_match(pathspec, NULL, ce->name, 0) &&
-		    ce_stage(ce)) {
-			if (!opts->force) {
+		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+			if (!ce_stage(ce))
+				continue;
+			if (stage) {
+				errs |= check_stage(stage, ce, pos);
+			} else if (opts->force) {
+				warning("path '%s' is unmerged", ce->name);
+			} else {
 				errs = 1;
 				error("path '%s' is unmerged", ce->name);
-			} else {
-				warning("path '%s' is unmerged", ce->name);
 			}
 			pos = skip_same_name(ce, pos) - 1;
 			continue;
@@ -154,6 +185,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 		struct cache_entry *ce = active_cache[pos];
 		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
 			if (ce_stage(ce)) {
+				if (stage)
+					errs |= checkout_stage(stage, ce, pos, &state);
 				pos = skip_same_name(ce, pos) - 1;
 				continue;
 			}
@@ -458,6 +491,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 		OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
 		OPT_SET_INT('t', "track",  &opts.track, "track",
 			BRANCH_TRACK_EXPLICIT),
+		OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
+			    2),
+		OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
+			    3),
 		OPT_BOOLEAN('f', NULL, &opts.force, "force"),
 		OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
 		OPT_END(),
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index b7274eb..85c792c 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -414,4 +414,29 @@ test_expect_success 'checkout with an unmerged path can be ignored' '
 	test_cmp sample file
 '
 
+test_expect_success 'checkout unmerged stage' '
+	rm -f .git/index &&
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	(
+		echo "100644 $A 0	fild" &&
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file" &&
+		echo "100644 $A 0	filf"
+	) | git update-index --index-info &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout --ours . &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp expect file &&
+	git checkout --theirs file &&
+	test ztheirside = "z$(cat file)"
+'
+
 test_done
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index
  2008-08-30  0:42                     ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano
@ 2008-08-30  0:42                       ` Junio C Hamano
  0 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30  0:42 UTC (permalink / raw)
  To: git

This teaches git-checkout to recreate a merge out of unmerged
index entries while resolving conflicts.

With this patch, checking out an unmerged path from the index
now have the following possibilities:

 * Without any option, an attempt to checkout an unmerged path
   will atomically fail (i.e. no other cleanly-merged paths are
   checked out either);

 * With "-f", other cleanly-merged paths are checked out, and
   unmerged paths are ignored;

 * With "--ours" or "--theirs, the contents from the specified
   stage is checked out;

 * With "-m" (we should add "--merge" as synonym), the 3-way merge
   is recreated from the staged object names and checked out.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin-checkout.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++++----
 t/t7201-co.sh      |   63 ++++++++++++++++++++++++++++
 2 files changed, 170 insertions(+), 8 deletions(-)

diff --git a/builtin-checkout.c b/builtin-checkout.c
index bdfdb65..8b5da7f 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -13,6 +13,9 @@
 #include "diff.h"
 #include "revision.h"
 #include "remote.h"
+#include "blob.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
 
 static const char * const checkout_usage[] = {
 	"git checkout [options] <branch>",
@@ -109,6 +112,19 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)
 		     (stage == 2) ? "our" : "their");
 }
 
+static int check_all_stage(struct cache_entry *ce, int pos)
+{
+	if (ce_stage(ce) != 1 ||
+	    active_nr <= pos + 2 ||
+	    strcmp(active_cache[pos+1]->name, ce->name) ||
+	    ce_stage(active_cache[pos+1]) != 2 ||
+	    strcmp(active_cache[pos+2]->name, ce->name) ||
+	    ce_stage(active_cache[pos+2]) != 3)
+		return error("path '%s' does not have all three versions",
+			     ce->name);
+	return 0;
+}
+
 static int checkout_stage(int stage, struct cache_entry *ce, int pos,
 			  struct checkout *state)
 {
@@ -123,6 +139,77 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,
 		     (stage == 2) ? "our" : "their");
 }
 
+/* NEEDSWORK: share with merge-recursive */
+static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
+{
+	unsigned long size;
+	enum object_type type;
+
+	if (!hashcmp(sha1, null_sha1)) {
+		mm->ptr = xstrdup("");
+		mm->size = 0;
+		return;
+	}
+
+	mm->ptr = read_sha1_file(sha1, &type, &size);
+	if (!mm->ptr || type != OBJ_BLOB)
+		die("unable to read blob object %s", sha1_to_hex(sha1));
+	mm->size = size;
+}
+
+static int checkout_merged(int pos, struct checkout *state)
+{
+	struct cache_entry *ce = active_cache[pos];
+	const char *path = ce->name;
+	mmfile_t ancestor, ours, theirs;
+	int status;
+	unsigned char sha1[20];
+	mmbuffer_t result_buf;
+
+	if (ce_stage(ce) != 1 ||
+	    active_nr <= pos + 2 ||
+	    strcmp(active_cache[pos+1]->name, path) ||
+	    ce_stage(active_cache[pos+1]) != 2 ||
+	    strcmp(active_cache[pos+2]->name, path) ||
+	    ce_stage(active_cache[pos+2]) != 3)
+		return error("path '%s' does not have all 3 versions", path);
+
+	fill_mm(active_cache[pos]->sha1, &ancestor);
+	fill_mm(active_cache[pos+1]->sha1, &ours);
+	fill_mm(active_cache[pos+2]->sha1, &theirs);
+
+	status = ll_merge(&result_buf, path, &ancestor,
+			  &ours, "ours", &theirs, "theirs", 1);
+	free(ancestor.ptr);
+	free(ours.ptr);
+	free(theirs.ptr);
+	if (status < 0 || !result_buf.ptr) {
+		free(result_buf.ptr);
+		return error("path '%s': cannot merge", path);
+	}
+
+	/*
+	 * NEEDSWORK:
+	 * There is absolutely no reason to write this as a blob object
+	 * and create a phoney cache entry just to leak.  This hack is
+	 * primarily to get to the write_entry() machinery that massages
+	 * the contents to work-tree format and writes out which only
+	 * allows it for a cache entry.  The code in write_entry() needs
+	 * to be refactored to allow us to feed a <buffer, size, mode>
+	 * instead of a cache entry.  Such a refactoring would help
+	 * merge_recursive as well (it also writes the merge result to the
+	 * object database even when it may contain conflicts).
+	 */
+	if (write_sha1_file(result_buf.ptr, result_buf.size,
+			    blob_type, sha1))
+		die("Unable to add merge result for '%s'", path);
+	ce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),
+			      sha1,
+			      path, 2, 0);
+	status = checkout_entry(ce, state, NULL);
+	return status;
+}
+
 static int checkout_paths(struct tree *source_tree, const char **pathspec,
 			  struct checkout_opts *opts)
 {
@@ -134,7 +221,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 	struct commit *head;
 	int errs = 0;
 	int stage = opts->writeout_stage;
-
+	int merge = opts->merge;
 	int newfd;
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
@@ -166,6 +253,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 				errs |= check_stage(stage, ce, pos);
 			} else if (opts->force) {
 				warning("path '%s' is unmerged", ce->name);
+			} else if (opts->merge) {
+				errs |= check_all_stage(ce, pos);
 			} else {
 				errs = 1;
 				error("path '%s' is unmerged", ce->name);
@@ -184,13 +273,15 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
 	for (pos = 0; pos < active_nr; pos++) {
 		struct cache_entry *ce = active_cache[pos];
 		if (pathspec_match(pathspec, NULL, ce->name, 0)) {
-			if (ce_stage(ce)) {
-				if (stage)
-					errs |= checkout_stage(stage, ce, pos, &state);
-				pos = skip_same_name(ce, pos) - 1;
+			if (!ce_stage(ce)) {
+				errs |= checkout_entry(ce, &state, NULL);
 				continue;
 			}
-			errs |= checkout_entry(ce, &state, NULL);
+			if (stage)
+				errs |= checkout_stage(stage, ce, pos, &state);
+			else if (merge)
+				errs |= checkout_merged(pos, &state);
+			pos = skip_same_name(ce, pos) - 1;
 		}
 	}
 
@@ -478,6 +569,11 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	return ret || opts->writeout_error;
 }
 
+static int git_checkout_config(const char *var, const char *value, void *cb)
+{
+	return git_xmerge_config(var, value, cb);
+}
+
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
 	struct checkout_opts opts;
@@ -504,7 +600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 	memset(&opts, 0, sizeof(opts));
 	memset(&new, 0, sizeof(new));
 
-	git_config(git_default_config, NULL);
+	git_config(git_checkout_config, NULL);
 
 	opts.track = BRANCH_TRACK_UNSPECIFIED;
 
@@ -611,7 +707,7 @@ no_reference:
 			die("invalid path specification");
 
 		/* Checkout paths */
-		if (opts.new_branch || opts.merge) {
+		if (opts.new_branch) {
 			if (argc == 1) {
 				die("git checkout: updating paths is incompatible with switching branches.\nDid you intend to checkout '%s' which can not be resolved as commit?", argv[0]);
 			} else {
@@ -619,6 +715,9 @@ no_reference:
 			}
 		}
 
+		if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
+			die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
+
 		return checkout_paths(source_tree, pathspec, &opts);
 	}
 
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 85c792c..6016915 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -439,4 +439,67 @@ test_expect_success 'checkout unmerged stage' '
 	test ztheirside = "z$(cat file)"
 '
 
+test_expect_success 'checkout with --merge' '
+	rm -f .git/index &&
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	(
+		echo "100644 $A 0	fild" &&
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file" &&
+		echo "100644 $A 0	filf"
+	) | git update-index --index-info &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -m -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
+test_expect_success 'checkout with --merge, in diff3 -m style' '
+	git config merge.conflictstyle diff3 &&
+	rm -f .git/index &&
+	O=$(echo original | git hash-object -w --stdin) &&
+	A=$(echo ourside | git hash-object -w --stdin) &&
+	B=$(echo theirside | git hash-object -w --stdin) &&
+	(
+		echo "100644 $A 0	fild" &&
+		echo "100644 $O 1	file" &&
+		echo "100644 $A 2	file" &&
+		echo "100644 $B 3	file" &&
+		echo "100644 $A 0	filf"
+	) | git update-index --index-info &&
+	echo "none of the above" >sample &&
+	echo ourside >expect &&
+	cat sample >fild &&
+	cat sample >file &&
+	cat sample >filf &&
+	git checkout -m -- fild file filf &&
+	(
+		echo "<<<<<<< ours"
+		echo ourside
+		echo "|||||||"
+		echo original
+		echo "======="
+		echo theirside
+		echo ">>>>>>> theirs"
+	) >merged &&
+	test_cmp expect fild &&
+	test_cmp expect filf &&
+	test_cmp merged file
+'
+
 test_done
-- 
1.6.0.1.149.ga4c44

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function
  2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
  2008-08-30  0:42   ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano
@ 2008-08-30  9:14   ` Johannes Schindelin
  1 sibling, 0 replies; 26+ messages in thread
From: Johannes Schindelin @ 2008-08-30  9:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Fri, 29 Aug 2008, Junio C Hamano wrote:

> This simply moves code around to make a separate function that prepares 
> a single conflicted hunk with markers into the buffer.

Apart from renaming "i1" to "i", inverting the order of the if clauses, 
and making it more obvious that the calculation of size without dest is 
correct, this is a straight forward refactoring.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style
  2008-08-30  0:42   ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano
  2008-08-30  0:42     ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano
@ 2008-08-30  9:29     ` Johannes Schindelin
  1 sibling, 0 replies; 26+ messages in thread
From: Johannes Schindelin @ 2008-08-30  9:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Fri, 29 Aug 2008, Junio C Hamano wrote:

> diff --git a/builtin-merge-file.c b/builtin-merge-file.c
> index 3605960..5b4f020 100644
> --- a/builtin-merge-file.c
> +++ b/builtin-merge-file.c
> @@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
>  		else if (!strcmp(argv[1], "-q") ||
>  				!strcmp(argv[1], "--quiet"))
>  			freopen("/dev/null", "w", stderr);
> +		else if (!strcmp(argv[1], "--diff3")) {
> +			merge_style = XDL_MERGE_DIFF3;
> +			merge_level = XDL_MERGE_EAGER;
> +		}

FWIW I do not follow your reasoning why --diff3 does not make sense for 
anything more eager than MERGE_EAGER.  All that ZEALOUS and ZEALOUS_ALNUM 
(the latter of which is useless at the moment, since it is not enabled for 
git-merge) do is change the way the conflicting regions are displayed, but 
they do not leave out conflicting regions.

So I actually suspect that ZEALOUS_ALNUM will be _especially_ useful with 
--diff3, since it is designed to skip the single curly brackets that would 
disrupt the reading pleasure otherwise.

> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
> index 413082e..deebe02 100644
> --- a/xdiff/xdiff.h
> +++ b/xdiff/xdiff.h
> @@ -50,10 +50,16 @@ extern "C" {
>  #define XDL_BDOP_CPY 2
>  #define XDL_BDOP_INSB 3
>  
> +/* merge simplification levels */
>  #define XDL_MERGE_MINIMAL 0
>  #define XDL_MERGE_EAGER 1
>  #define XDL_MERGE_ZEALOUS 2
>  #define XDL_MERGE_ZEALOUS_ALNUM 3
> +#define XDL_MERGE_LEVEL_MASK 0x0f
> +
> +/* merge output styles */
> +#define XDL_MERGE_DIFF3 0x8000
> +#define XDL_MERGE_STYLE_MASK 0x8000

Hmm.  This is not the Linux kernel, I think we could safely pass around 
two integers instead of one.

> @@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
>  	return 0;
>  }
>  
> -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
> +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)

You rewrapped many function headers already; I wonder why this one was 
left out.

The rest looks pretty much obviously correct to me; I was too lazy/ran out 
of time to apply the patch and look through the resulting code, though, 
but I guess that you searched for "i1" and "chg1" and added the code for 
i0 and chg0 where necessary.

So: ACK.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 03/12] xmerge.c: minimum readability fixups
  2008-08-30  0:42     ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano
  2008-08-30  0:42       ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano
@ 2008-08-30  9:31       ` Johannes Schindelin
  2008-08-30 15:42         ` Junio C Hamano
  1 sibling, 1 reply; 26+ messages in thread
From: Johannes Schindelin @ 2008-08-30  9:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Fri, 29 Aug 2008, Junio C Hamano wrote:

> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
> index 29cdbea..7dcd405 100644
> --- a/xdiff/xmerge.c
> +++ b/xdiff/xmerge.c
> @@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
>  			xscr2 = xscr2->next;
>  			continue;
>  		}
> -		if (level < 1 || xscr1->i1 != xscr2->i1 ||
> +		if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||

Yeah, okay, sorry.

> @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
>  			chg0 = xscr1->i1 + xscr1->chg1 - i0;
>  			chg1 = xscr1->i2 + xscr1->chg2 - i1;
>  			chg2 = xscr2->i2 + xscr2->chg2 - i2;
> -			if (ffo > 0)
> -				chg2 += ffo;
> -			else {
> +			if (ffo < 0) {
>  				chg0 -= ffo;
>  				chg1 -= ffo;
> -			}
> +			} else
> +				chg2 += ffo;

I do not understand why the order was changed, but hey, I do not care that 
deeply.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less
  2008-08-30  0:42       ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano
  2008-08-30  0:42         ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano
@ 2008-08-30  9:34         ` Johannes Schindelin
  1 sibling, 0 replies; 26+ messages in thread
From: Johannes Schindelin @ 2008-08-30  9:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Fri, 29 Aug 2008, Junio C Hamano wrote:

> Representing the last illustration like this is misleading to say the 
> least:
> 
>     output:      1234A<BCD|56=XY>E789   ; broken "diff3 -m" style
> 
> because the preimage was not ...4A56E... to begin with.  "A" and "E" are
> common only between the postimages.

Okay, I understand now.

Thanks,
Dscho

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles
  2008-08-30  0:42             ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano
  2008-08-30  0:42               ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano
@ 2008-08-30  9:42               ` Johannes Schindelin
  1 sibling, 0 replies; 26+ messages in thread
From: Johannes Schindelin @ 2008-08-30  9:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Hi,

On Fri, 29 Aug 2008, Junio C Hamano wrote:

> diff --git a/builtin-merge-file.c b/builtin-merge-file.c
> index 1e92510..f009e73 100644
> --- a/builtin-merge-file.c
> +++ b/builtin-merge-file.c
> @@ -15,6 +15,15 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
>  	int ret = 0, i = 0, to_stdout = 0;
>  	int merge_level = XDL_MERGE_ZEALOUS_ALNUM;
>  	int merge_style = 0;
> +	int nongit;
> +
> +	prefix = setup_git_directory_gently(&nongit);
> +	if (!nongit) {
> +		/* Read the configuration file */
> +		git_config(git_xmerge_config, NULL);
> +		if (git_xmerge_style > 0)
> +			merge_style = git_xmerge_style;

Did you not mean ">="?  In the future, the default merge style could very 
well change...

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 03/12] xmerge.c: minimum readability fixups
  2008-08-30  9:31       ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin
@ 2008-08-30 15:42         ` Junio C Hamano
  0 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-08-30 15:42 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
>>  			chg0 = xscr1->i1 + xscr1->chg1 - i0;
>>  			chg1 = xscr1->i2 + xscr1->chg2 - i1;
>>  			chg2 = xscr2->i2 + xscr2->chg2 - i2;
>> -			if (ffo > 0)
>> -				chg2 += ffo;
>> -			else {
>> +			if (ffo < 0) {
>>  				chg0 -= ffo;
>>  				chg1 -= ffo;
>> -			}
>> +			} else
>> +				chg2 += ffo;
>
> I do not understand why the order was changed, but hey, I do not care that 
> deeply.

Everywhere else the code deals with variables var0, var1 and var2 in this
order, and that is because "if" blocks are consistently about changes made
on side#1 while "else" blocks are about changes made on side#2.  This
statement alone was inconsistent, and now it all reads 0, 1 and then 2.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-08-30  0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano
  2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
@ 2008-09-01  9:39 ` Alex Riesen
  2008-09-01  9:44 ` Alex Riesen
  2 siblings, 0 replies; 26+ messages in thread
From: Alex Riesen @ 2008-09-01  9:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200:
> The early part of the series is what you already saw.  In addition to
> recording a conflicted merge in the RCS merge style we have traditionally
> used, this allows you to optionally use "diff3 -m" style.  The difference
> is that the latter format shows the part from the common ancestor that
> corresponds to the parts both sides modified to cause the conflict, in
> addition to the changes done on each side.  This can be chosen by setting
> a configuration variable.  Rerere mechanism is updated to understand this
> new format as well, and conflicts from either formats interoperate well,
> because rerere mechanism only records and uses the changes made on each
> side, not what was in the common ancestor.
> 
> The last four patches are to "git checkout" that checks things out of the
> index.  ...

I like that and started using the patches. Do you have any specific
area I should pay a special attention to?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-08-30  0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano
  2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
  2008-09-01  9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen
@ 2008-09-01  9:44 ` Alex Riesen
  2008-09-01  9:50   ` Abhijit Menon-Sen
  2008-09-01 10:38   ` Junio C Hamano
  2 siblings, 2 replies; 26+ messages in thread
From: Alex Riesen @ 2008-09-01  9:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200:
> This consists of two loosely related topics on improving conflicted merge
> resolution support.
> 
> The early part of the series is what you already saw.  In addition to
> recording a conflicted merge in the RCS merge style we have traditionally
> used, this allows you to optionally use "diff3 -m" style.  The difference
> is that the latter format shows the part from the common ancestor that
> corresponds to the parts both sides modified to cause the conflict, in
> addition to the changes done on each side.  This can be chosen by setting
> a configuration variable.  Rerere mechanism is updated to understand this
> new format as well, and conflicts from either formats interoperate well,
> because rerere mechanism only records and uses the changes made on each
> side, not what was in the common ancestor.

This reminds me: when resolving a conflict in a git repo (when trying
something from next or pu), I often notice that I'd like to resolve it
the same way it was done on next or pu. IOW, copy the commit
resolution from some other merge commit. Maybe can be a way to use
rerere mechanism with that?

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-09-01  9:44 ` Alex Riesen
@ 2008-09-01  9:50   ` Abhijit Menon-Sen
  2008-09-01 12:20     ` Thomas Rast
  2008-09-01 10:38   ` Junio C Hamano
  1 sibling, 1 reply; 26+ messages in thread
From: Abhijit Menon-Sen @ 2008-09-01  9:50 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Junio C Hamano, git

At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote:
>
> IOW, copy the commit resolution from some other merge commit. Maybe
> can be a way to use rerere mechanism with that?

That's what I'm trying to implement on Dscho's suggestion. I'm still
just trying to understand the code, so any suggestions about how to
do this are very welcome.

-- ams

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-09-01  9:44 ` Alex Riesen
  2008-09-01  9:50   ` Abhijit Menon-Sen
@ 2008-09-01 10:38   ` Junio C Hamano
  2008-09-01 11:34     ` Alex Riesen
  1 sibling, 1 reply; 26+ messages in thread
From: Junio C Hamano @ 2008-09-01 10:38 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git

Alex Riesen <raa.lkml@gmail.com> writes:

> ... IOW, copy the commit
> resolution from some other merge commit. Maybe can be a way to use
> rerere mechanism with that?

If you know which merge I did you want to steal from, you can prime your
rerere database by pretending to be me, doing the merge.  Something like:

	$ git checkout $merge^1 ;# detach to the parent of merge
        $ git merge $merge^2 ;# pretend you were me to redo it
        $ git diff -R $merge | git apply --index ;# and get what I did
	$ git rerere ;# have rerere record the resolution

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-09-01 10:38   ` Junio C Hamano
@ 2008-09-01 11:34     ` Alex Riesen
  2008-09-01 17:26       ` Junio C Hamano
  0 siblings, 1 reply; 26+ messages in thread
From: Alex Riesen @ 2008-09-01 11:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200:
> Alex Riesen <raa.lkml@gmail.com> writes:
> 
> > ... IOW, copy the commit
> > resolution from some other merge commit. Maybe can be a way to use
> > rerere mechanism with that?
> 
> If you know which merge I did you want to steal from, you can prime your
> rerere database by pretending to be me, doing the merge.  Something like:
> 
> 	$ git checkout $merge^1 ;# detach to the parent of merge
>         $ git merge $merge^2 ;# pretend you were me to redo it
>         $ git diff -R $merge | git apply --index ;# and get what I did

I ended up using

    $ git checkout Merge^1
    $ git merge Merge^2
    $ git diff -R Merge | git apply
    $ git diff -R Merge --name-only -z | git update-index -z --stdin
    $ git rerere

Just git apply --index complained about the files missing from the
index:

    $ git tag Merge c5e2ace70271b481632aaf987361027ca4592df6
    $ gco Merge^1
    Previous HEAD position was c5e2ace... Merge branch 'jc/better-conflict-resolution' into next
    HEAD is now at 2392877... Merge branch 'master' into next
    $ git merge Merge^2
    Auto-merging Documentation/config.txt
    Auto-merging Documentation/git-checkout.txt
    CONFLICT (content): Merge conflict in Documentation/git-checkout.txt
    Auto-merging builtin-checkout.c
    CONFLICT (content): Merge conflict in builtin-checkout.c
    Auto-merging builtin-merge-recursive.c
    Auto-merging t/t6023-merge-file.sh
    Auto-merging t/t7201-co.sh
    CONFLICT (content): Merge conflict in t/t7201-co.sh
    Auto-merging xdiff-interface.c
    Auto-merging xdiff-interface.h
    Recorded preimage for 'Documentation/git-checkout.txt'
    Recorded preimage for 'builtin-checkout.c'
    Recorded preimage for 't/t7201-co.sh'
    Automatic merge failed; fix conflicts and then commit the result.
    $ git diff -R Merge |git apply --index
    error: Documentation/git-checkout.txt: does not exist in index
    error: builtin-checkout.c: does not exist in index
    error: t/t7201-co.sh: does not exist in index

> 	$ git rerere ;# have rerere record the resolution

Well, it works, but it's a bit of work and hard to automate (needs a
working tree). An option to merge:

    $ git merge <branch>
    conflict ... investigate ... find a resolution in <resolution>
    $ git reset --hard
    $ git merge --rerere <resolution> branch
    check... Ok.
    $ git commit

or rerere:

    $ git merge <branch>
    conflict ... investigate ... find a resolution in <resolution>
    $ git rerere <resolution>
    $ git reset --hard
    $ git merge <branch>
    check... Ok.
    $ git commit

These could be more convenient.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-09-01  9:50   ` Abhijit Menon-Sen
@ 2008-09-01 12:20     ` Thomas Rast
  0 siblings, 0 replies; 26+ messages in thread
From: Thomas Rast @ 2008-09-01 12:20 UTC (permalink / raw)
  To: Abhijit Menon-Sen; +Cc: Alex Riesen, Junio C Hamano, git

[-- Attachment #1: Type: text/plain, Size: 1560 bytes --]

Abhijit Menon-Sen wrote:
> At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote:
> >
> > IOW, copy the commit resolution from some other merge commit. Maybe
> > can be a way to use rerere mechanism with that?
> 
> That's what I'm trying to implement on Dscho's suggestion. I'm still
> just trying to understand the code, so any suggestions about how to
> do this are very welcome.

Random idea: you could use a script that just replays the merges
present in history and lets rerere record them.  Like so:

-- 8< --
#!/bin/sh

. "$(git --exec-path)/git-sh-setup"

require_work_tree
git update-index --refresh || die "can't run with dirty index"

git rev-list --parents "$@" |
grep '.* .* .*' |
while read merge firstparent otherparents
do
	git checkout $firstparent >/dev/null 2>/dev/null
	git merge $otherparents >/dev/null
	if test -z "$(git ls-files -u)"; then
		echo -n 'no conflicts: '
		git --no-pager log -1 --pretty=oneline --abbrev-commit $merge
		continue
	fi
	git rerere
	git ls-files -t | grep ^M | cut -c 3- | xargs git checkout $merge --
	git rerere
	git reset --hard >/dev/null 2>/dev/null
	echo -n 'recorded:     '
	git --no-pager log -1 --pretty=oneline --abbrev-commit $merge
done
-- >8 --

The intended usage is like

  ./rerereimport.sh v1.6.0..origin/next

to import all resolutions in the specified range.  Granted, it's
probably not as good and definitely not as fast as an automatic
feature integrated with the merge machinery.

- Thomas

-- 
Thomas Rast
trast@student.ethz.ch



[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH 00/12] Towards a better merge resolution support
  2008-09-01 11:34     ` Alex Riesen
@ 2008-09-01 17:26       ` Junio C Hamano
  0 siblings, 0 replies; 26+ messages in thread
From: Junio C Hamano @ 2008-09-01 17:26 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git

Alex Riesen <raa.lkml@gmail.com> writes:

> Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200:
>> Alex Riesen <raa.lkml@gmail.com> writes:
>> 
>> > ... IOW, copy the commit
>> > resolution from some other merge commit. Maybe can be a way to use
>> > rerere mechanism with that?
>> 
>> If you know which merge I did you want to steal from, you can prime your
>> rerere database by pretending to be me, doing the merge.  Something like:
>> 
>> 	$ git checkout $merge^1 ;# detach to the parent of merge
>>         $ git merge $merge^2 ;# pretend you were me to redo it
>>         $ git diff -R $merge | git apply --index ;# and get what I did
>
> I ended up using

I think the last step should be "git read-tree --reset -u $merge" if we
really want the minimum  sequence.

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2008-09-01 17:27 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-30  0:42 [PATCH 00/12] Towards a better merge resolution support Junio C Hamano
2008-08-30  0:42 ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Junio C Hamano
2008-08-30  0:42   ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Junio C Hamano
2008-08-30  0:42     ` [PATCH 03/12] xmerge.c: minimum readability fixups Junio C Hamano
2008-08-30  0:42       ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Junio C Hamano
2008-08-30  0:42         ` [PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states Junio C Hamano
2008-08-30  0:42           ` [PATCH 06/12] rerere: understand "diff3 -m" style conflicts with the original Junio C Hamano
2008-08-30  0:42             ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Junio C Hamano
2008-08-30  0:42               ` [PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle Junio C Hamano
2008-08-30  0:42                 ` [PATCH 09/12] checkout: do not check out unmerged higher stages randomly Junio C Hamano
2008-08-30  0:42                   ` [PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index Junio C Hamano
2008-08-30  0:42                     ` [PATCH 11/12] checkout --ours/--theirs Junio C Hamano
2008-08-30  0:42                       ` [PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index Junio C Hamano
2008-08-30  9:42               ` [PATCH 07/12] merge.conflictstyle: choose between "merge" and "diff3 -m" styles Johannes Schindelin
2008-08-30  9:34         ` [PATCH 04/12] xmerge.c: "diff3 -m" style clips merge reduction level to EAGER or less Johannes Schindelin
2008-08-30  9:31       ` [PATCH 03/12] xmerge.c: minimum readability fixups Johannes Schindelin
2008-08-30 15:42         ` Junio C Hamano
2008-08-30  9:29     ` [PATCH 02/12] xdiff-merge: optionally show conflicts in "diff3 -m" style Johannes Schindelin
2008-08-30  9:14   ` [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function Johannes Schindelin
2008-09-01  9:39 ` [PATCH 00/12] Towards a better merge resolution support Alex Riesen
2008-09-01  9:44 ` Alex Riesen
2008-09-01  9:50   ` Abhijit Menon-Sen
2008-09-01 12:20     ` Thomas Rast
2008-09-01 10:38   ` Junio C Hamano
2008-09-01 11:34     ` Alex Riesen
2008-09-01 17:26       ` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).