{"thread":{"id":"15280","subject":"[PATCH 00/12] Towards a better merge resolution support","startedAt":"2008-08-30T00:42:31Z","lastAt":"2008-09-01T17:26:24Z","messageCount":26,"participants":["Junio C Hamano","Johannes Schindelin","Alex Riesen","Abhijit Menon-Sen","Thomas Rast"],"isPatch":true,"patchVersion":1,"patchTotal":12},"messages":[{"id":"89171","messageId":"1220056963-2352-1-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":null,"subject":"[PATCH 00/12] Towards a better merge resolution support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:31Z","receivedAt":"2008-08-30T00:42:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This consists of two loosely related topics on improving conflicted merge\nresolution support.\n\nThe early part of the series is what you already saw.  In addition to\nrecording a conflicted merge in the RCS merge style we have traditionally\nused, this allows you to optionally use \"diff3 -m\" style.  The difference\nis that the latter format shows the part from the common ancestor that\ncorresponds to the parts both sides modified to cause the conflict, in\naddition to the changes done on each side.  This can be chosen by setting\na configuration variable.  Rerere mechanism is updated to understand this\nnew format as well, and conflicts from either formats interoperate well,\nbecause rerere mechanism only records and uses the changes made on each\nside, not what was in the common ancestor.\n\nThe last four patches are to \"git checkout\" that checks things out of the\nindex.  When resolving conflicts, sometimes you would screw up the state\nof the working tree so badly that you would wish to redo the merge from\nthe beginning for one file, without having to redo the whole merge.  Some\nother times, you already know changes made on one side already solves\neverything the other side attempted to do, and would want to take the\nchange from that side as a whole.  New options supported by \"git checkout\"\nwhen checking out from the index for these purposes are:\n\n * git checkout -m -- path\n\n   This recreates the merge using information staged in stages 1/2/3;\n \n * git checkout --ours -- path\n\n   This writes 'our' version (stage #2) out for the path to the working\n   tree;\n\n * git checkout --theirs -- path\n\n   This writes 'their' version (stage #3) out for the path to the working\n   tree;\n\nNone of these operations mark the path resolved.  They are to help you\nprepare the working tree into a shape suitable as the resolution, and you\nwill still conclude it with \"git add path\".\n\nJunio C Hamano (12):\n  xdl_fill_merge_buffer(): separate out a too deeply nested function\n  xdiff-merge: optionally show conflicts in \"diff3 -m\" style\n  xmerge.c: minimum readability fixups\n  xmerge.c: \"diff3 -m\" style clips merge reduction level to EAGER or\n    less\n  rerere.c: use symbolic constants to keep track of parsing states\n  rerere: understand \"diff3 -m\" style conflicts with the original\n  merge.conflictstyle: choose between \"merge\" and \"diff3 -m\" styles\n  git-merge-recursive: learn to honor merge.conflictstyle\n  checkout: do not check out unmerged higher stages randomly\n  checkout: allow ignoring unmerged paths when checking out of the\n    index\n  checkout --ours/--theirs\n  checkout -m: recreate merge when checking out of unmerged index\n\n Documentation/config.txt  |    8 ++\n builtin-checkout.c        |  206 +++++++++++++++++++++++++++++++++++----\n builtin-merge-file.c      |   17 +++-\n builtin-merge-recursive.c |    2 +-\n ll-merge.c                |   16 +++-\n rerere.c                  |   29 ++++--\n t/t6023-merge-file.sh     |   44 +++++++++\n t/t7201-co.sh             |  133 +++++++++++++++++++++++++\n xdiff-interface.c         |   20 ++++\n xdiff-interface.h         |    2 +\n xdiff/xdiff.h             |    6 +\n xdiff/xmerge.c            |  237 +++++++++++++++++++++++++++++++--------------\n 12 files changed, 612 insertions(+), 108 deletions(-)\n"},{"id":"89172","messageId":"1220056963-2352-2-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-1-git-send-email-gitster@pobox.com","subject":"[PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:32Z","receivedAt":"2008-08-30T00:42:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This simply moves code around to make a separate function that prepares\na single conflicted hunk with markers into the buffer.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n xdiff/xmerge.c |  121 ++++++++++++++++++++++++++++++++-----------------------\n 1 files changed, 70 insertions(+), 51 deletions(-)\n\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 82b3573..6ffaa4f 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -113,65 +113,84 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n \treturn size;\n }\n \n-static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n-\t\txdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)\n+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n+\t\t\t      xdfenv_t *xe2, const char *name2,\n+\t\t\t      int size, int i,\n+\t\t\t      xdmerge_t *m, char *dest)\n {\n \tconst int marker_size = 7;\n \tint marker1_size = (name1 ? strlen(name1) + 1 : 0);\n \tint marker2_size = (name2 ? strlen(name2) + 1 : 0);\n-\tint conflict_marker_size = 3 * (marker_size + 1)\n-\t\t+ marker1_size + marker2_size;\n-\tint size, i1, j;\n-\n-\tfor (size = i1 = 0; m; m = m->next) {\n-\t\tif (m->mode == 0) {\n-\t\t\tsize += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,\n-\t\t\t\t\tdest ? dest + size : NULL);\n-\t\t\tif (dest) {\n-\t\t\t\tfor (j = 0; j < marker_size; j++)\n-\t\t\t\t\tdest[size++] = '<';\n-\t\t\t\tif (marker1_size) {\n-\t\t\t\t\tdest[size] = ' ';\n-\t\t\t\t\tmemcpy(dest + size + 1, name1,\n-\t\t\t\t\t\t\tmarker1_size - 1);\n-\t\t\t\t\tsize += marker1_size;\n-\t\t\t\t}\n-\t\t\t\tdest[size++] = '\\n';\n-\t\t\t} else\n-\t\t\t\tsize += conflict_marker_size;\n-\t\t\tsize += xdl_recs_copy(xe1, m->i1, m->chg1, 1,\n-\t\t\t\t\tdest ? dest + size : NULL);\n-\t\t\tif (dest) {\n-\t\t\t\tfor (j = 0; j < marker_size; j++)\n-\t\t\t\t\tdest[size++] = '=';\n-\t\t\t\tdest[size++] = '\\n';\n-\t\t\t}\n-\t\t\tsize += xdl_recs_copy(xe2, m->i2, m->chg2, 1,\n-\t\t\t\t\tdest ? dest + size : NULL);\n-\t\t\tif (dest) {\n-\t\t\t\tfor (j = 0; j < marker_size; j++)\n-\t\t\t\t\tdest[size++] = '>';\n-\t\t\t\tif (marker2_size) {\n-\t\t\t\t\tdest[size] = ' ';\n-\t\t\t\t\tmemcpy(dest + size + 1, name2,\n-\t\t\t\t\t\t\tmarker2_size - 1);\n-\t\t\t\t\tsize += marker2_size;\n-\t\t\t\t}\n-\t\t\t\tdest[size++] = '\\n';\n-\t\t\t}\n-\t\t} else if (m->mode == 1)\n-\t\t\tsize += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0,\n-\t\t\t\t\tdest ? dest + size : NULL);\n+\tint j;\n+\n+\t/* Before conflicting part */\n+\tsize += xdl_recs_copy(xe1, i, m->i1 - i, 0,\n+\t\t\t      dest ? dest + size : NULL);\n+\n+\tif (!dest) {\n+\t\tsize += marker_size + 1 + marker1_size;\n+\t} else {\n+\t\tfor (j = 0; j < marker_size; j++)\n+\t\t\tdest[size++] = '<';\n+\t\tif (marker1_size) {\n+\t\t\tdest[size] = ' ';\n+\t\t\tmemcpy(dest + size + 1, name1, marker1_size - 1);\n+\t\t\tsize += marker1_size;\n+\t\t}\n+\t\tdest[size++] = '\\n';\n+\t}\n+\n+\t/* Postimage from side #1 */\n+\tsize += xdl_recs_copy(xe1, m->i1, m->chg1, 1,\n+\t\t\t      dest ? dest + size : NULL);\n+\tif (!dest) {\n+\t\tsize += marker_size + 1;\n+\t} else {\n+\t\tfor (j = 0; j < marker_size; j++)\n+\t\t\tdest[size++] = '=';\n+\t\tdest[size++] = '\\n';\n+\t}\n+\n+\t/* Postimage from side #2 */\n+\tsize += xdl_recs_copy(xe2, m->i2, m->chg2, 1,\n+\t\t\t      dest ? dest + size : NULL);\n+\tif (!dest) {\n+\t\tsize += marker_size + 1 + marker2_size;\n+\t} else {\n+\t\tfor (j = 0; j < marker_size; j++)\n+\t\t\tdest[size++] = '>';\n+\t\tif (marker2_size) {\n+\t\t\tdest[size] = ' ';\n+\t\t\tmemcpy(dest + size + 1, name2, marker2_size - 1);\n+\t\t\tsize += marker2_size;\n+\t\t}\n+\t\tdest[size++] = '\\n';\n+\t}\n+\treturn size;\n+}\n+\n+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n+\t\txdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)\n+{\n+\tint size, i;\n+\n+\tfor (size = i = 0; m; m = m->next) {\n+\t\tif (m->mode == 0)\n+\t\t\tsize = fill_conflict_hunk(xe1, name1, xe2, name2,\n+\t\t\t\t\t\t  size, i, m, dest);\n+\t\telse if (m->mode == 1)\n+\t\t\tsize += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,\n+\t\t\t\t\t      dest ? dest + size : NULL);\n \t\telse if (m->mode == 2)\n-\t\t\tsize += xdl_recs_copy(xe2, m->i2 - m->i1 + i1,\n-\t\t\t\t\tm->i1 + m->chg2 - i1, 0,\n-\t\t\t\t\tdest ? dest + size : NULL);\n+\t\t\tsize += xdl_recs_copy(xe2, m->i2 - m->i1 + i,\n+\t\t\t\t\t      m->i1 + m->chg2 - i, 0,\n+\t\t\t\t\t      dest ? dest + size : NULL);\n \t\telse\n \t\t\tcontinue;\n-\t\ti1 = m->i1 + m->chg1;\n+\t\ti = m->i1 + m->chg1;\n \t}\n-\tsize += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0,\n-\t\t\tdest ? dest + size : NULL);\n+\tsize += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,\n+\t\t\t      dest ? dest + size : NULL);\n \treturn size;\n }\n \n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89174","messageId":"1220056963-2352-3-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-2-git-send-email-gitster@pobox.com","subject":"[PATCH 02/12] xdiff-merge: optionally show conflicts in \"diff3 -m\" style","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:33Z","receivedAt":"2008-08-30T00:42:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When showing conflicting merges, we traditionally followed RCS's merge\noutput format.  The output shows:\n\n <<<<<<<\n postimage from one side;\n =======\n postimage of the other side; and\n >>>>>>>\n\nSome poeple find it easier to be able to understand what is going on when\nthey can view the common ancestor's version, which is used by \"diff3 -m\",\nwhich shows:\n\n <<<<<<<\n postimage from one side;\n |||||||\n shared preimage;\n =======\n postimage of the other side; and\n >>>>>>>\n\nThis is an initial step to bring that as an optional feature to git.\nOnly \"git merge-file\" has been converted, with \"--diff3\" option.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-merge-file.c  |   10 ++++-\n t/t6023-merge-file.sh |   37 +++++++++++++++++\n xdiff/xdiff.h         |    6 +++\n xdiff/xmerge.c        |  103 ++++++++++++++++++++++++++++++++++++++++---------\n 4 files changed, 135 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin-merge-file.c b/builtin-merge-file.c\nindex 3605960..5b4f020 100644\n--- a/builtin-merge-file.c\n+++ b/builtin-merge-file.c\n@@ -4,7 +4,7 @@\n #include \"xdiff-interface.h\"\n \n static const char merge_file_usage[] =\n-\"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2\";\n+\"git merge-file [-p | --stdout] [--diff3] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2\";\n \n int cmd_merge_file(int argc, const char **argv, const char *prefix)\n {\n@@ -13,6 +13,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tmmbuffer_t result = {NULL, 0};\n \txpparam_t xpp = {XDF_NEED_MINIMAL};\n \tint ret = 0, i = 0, to_stdout = 0;\n+\tint merge_level = XDL_MERGE_ZEALOUS_ALNUM;\n+\tint merge_style = 0;\n \n \twhile (argc > 4) {\n \t\tif (!strcmp(argv[1], \"-L\") && i < 3) {\n@@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \t\telse if (!strcmp(argv[1], \"-q\") ||\n \t\t\t\t!strcmp(argv[1], \"--quiet\"))\n \t\t\tfreopen(\"/dev/null\", \"w\", stderr);\n+\t\telse if (!strcmp(argv[1], \"--diff3\")) {\n+\t\t\tmerge_style = XDL_MERGE_DIFF3;\n+\t\t\tmerge_level = XDL_MERGE_EAGER;\n+\t\t}\n \t\telse\n \t\t\tusage(merge_file_usage);\n \t\targc--;\n@@ -46,7 +52,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \t}\n \n \tret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],\n-\t\t\t&xpp, XDL_MERGE_ZEALOUS_ALNUM, &result);\n+\t\t\t&xpp, merge_level | merge_style, &result);\n \n \tfor (i = 0; i < 3; i++)\n \t\tfree(mmfs[i].ptr);\ndiff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh\nindex 42620e0..f3484e3 100755\n--- a/t/t6023-merge-file.sh\n+++ b/t/t6023-merge-file.sh\n@@ -161,4 +161,41 @@ test_expect_success 'ZEALOUS_ALNUM' '\n \n '\n \n+cat >expect <<\\EOF\n+Dominus regit me,\n+<<<<<<< new8.txt\n+et nihil mihi deerit;\n+\n+\n+\n+\n+In loco pascuae ibi me collocavit;\n+super aquam refectionis educavit me.\n+|||||||\n+et nihil mihi deerit.\n+In loco pascuae ibi me collocavit,\n+super aquam refectionis educavit me;\n+=======\n+et nihil mihi deerit,\n+\n+\n+\n+\n+In loco pascuae ibi me collocavit --\n+super aquam refectionis educavit me,\n+>>>>>>> new9.txt\n+animam meam convertit,\n+deduxit me super semitas jusitiae,\n+propter nomen suum.\n+Nam et si ambulavero in medio umbrae mortis,\n+non timebo mala, quoniam TU mecum es:\n+virga tua et baculus tuus ipsa me consolata sunt.\n+EOF\n+\n+test_expect_success '\"diff3 -m\" style output' '\n+\ttest_must_fail git merge-file -p --diff3 \\\n+\t\tnew8.txt new5.txt new9.txt >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 413082e..deebe02 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -50,10 +50,16 @@ extern \"C\" {\n #define XDL_BDOP_CPY 2\n #define XDL_BDOP_INSB 3\n \n+/* merge simplification levels */\n #define XDL_MERGE_MINIMAL 0\n #define XDL_MERGE_EAGER 1\n #define XDL_MERGE_ZEALOUS 2\n #define XDL_MERGE_ZEALOUS_ALNUM 3\n+#define XDL_MERGE_LEVEL_MASK 0x0f\n+\n+/* merge output styles */\n+#define XDL_MERGE_DIFF3 0x8000\n+#define XDL_MERGE_STYLE_MASK 0x8000\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 6ffaa4f..29cdbea 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -30,17 +30,32 @@ typedef struct s_xdmerge {\n \t * 2 = no conflict, take second.\n \t */\n \tint mode;\n+\t/*\n+\t * These point at the respective postimages.  E.g. <i1,chg1> is\n+\t * how side #1 wants to change the common ancestor; if there is no\n+\t * overlap, lines before i1 in the postimage of side #1 appear\n+\t * in the merge result as a region touched by neither side.\n+\t */\n \tlong i1, i2;\n \tlong chg1, chg2;\n+\t/*\n+\t * These point at the preimage; of course there is just one\n+\t * preimage, that is from the shared common ancestor.\n+\t */\n+\tlong i0;\n+\tlong chg0;\n } xdmerge_t;\n \n static int xdl_append_merge(xdmerge_t **merge, int mode,\n-\t\tlong i1, long chg1, long i2, long chg2)\n+\t\t\t    long i0, long chg0,\n+\t\t\t    long i1, long chg1,\n+\t\t\t    long i2, long chg2)\n {\n \txdmerge_t *m = *merge;\n \tif (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {\n \t\tif (mode != m->mode)\n \t\t\tm->mode = 0;\n+\t\tm->chg0 = i0 + chg0 - m->i0;\n \t\tm->chg1 = i1 + chg1 - m->i1;\n \t\tm->chg2 = i2 + chg2 - m->i2;\n \t} else {\n@@ -49,6 +64,8 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,\n \t\t\treturn -1;\n \t\tm->next = NULL;\n \t\tm->mode = mode;\n+\t\tm->i0 = i0;\n+\t\tm->chg0 = chg0;\n \t\tm->i1 = i1;\n \t\tm->chg1 = chg1;\n \t\tm->i2 = i2;\n@@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n \treturn 0;\n }\n \n-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n {\n-\txrecord_t **recs = xe->xdf2.recs + i;\n+\txrecord_t **recs;\n \tint size = 0;\n \n+\trecs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;\n+\n \tif (count < 1)\n \t\treturn 0;\n \n@@ -113,9 +132,19 @@ static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n \treturn size;\n }\n \n+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n+{\n+\treturn xdl_recs_copy_0(0, xe, i, count, add_nl, dest);\n+}\n+\n+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n+{\n+\treturn xdl_recs_copy_0(1, xe, i, count, add_nl, dest);\n+}\n+\n static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \t\t\t      xdfenv_t *xe2, const char *name2,\n-\t\t\t      int size, int i,\n+\t\t\t      int size, int i, int style,\n \t\t\t      xdmerge_t *m, char *dest)\n {\n \tconst int marker_size = 7;\n@@ -143,6 +172,20 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \t/* Postimage from side #1 */\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, 1,\n \t\t\t      dest ? dest + size : NULL);\n+\n+\tif (style == XDL_MERGE_DIFF3) {\n+\t\t/* Shared preimage */\n+\t\tif (!dest) {\n+\t\t\tsize += marker_size + 1;\n+\t\t} else {\n+\t\t\tfor (j = 0; j < marker_size; j++)\n+\t\t\t\tdest[size++] = '|';\n+\t\t\tdest[size++] = '\\n';\n+\t\t}\n+\t\tsize += xdl_orig_copy(xe1, m->i0, m->chg0, 1,\n+\t\t\t\t      dest ? dest + size : NULL);\n+\t}\n+\n \tif (!dest) {\n \t\tsize += marker_size + 1;\n \t} else {\n@@ -170,14 +213,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n }\n \n static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n-\t\txdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)\n+\t\t\t\t xdfenv_t *xe2, const char *name2,\n+\t\t\t\t xdmerge_t *m, char *dest, int style)\n {\n \tint size, i;\n \n \tfor (size = i = 0; m; m = m->next) {\n \t\tif (m->mode == 0)\n \t\t\tsize = fill_conflict_hunk(xe1, name1, xe2, name2,\n-\t\t\t\t\t\t  size, i, m, dest);\n+\t\t\t\t\t\t  size, i, style, m, dest);\n \t\telse if (m->mode == 1)\n \t\t\tsize += xdl_recs_copy(xe1, i, m->i1 + m->chg1 - i, 0,\n \t\t\t\t\t      dest ? dest + size : NULL);\n@@ -342,9 +386,11 @@ static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n  */\n static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\txdfenv_t *xe2, xdchange_t *xscr2, const char *name2,\n-\t\tint level, xpparam_t const *xpp, mmbuffer_t *result) {\n+\t\tint flags, xpparam_t const *xpp, mmbuffer_t *result) {\n \txdmerge_t *changes, *c;\n-\tint i1, i2, chg1, chg2;\n+\tint i0, i1, i2, chg0, chg1, chg2;\n+\tint level = flags & XDL_MERGE_LEVEL_MASK;\n+\tint style = flags & XDL_MERGE_STYLE_MASK;\n \n \tc = changes = NULL;\n \n@@ -352,11 +398,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\tif (!changes)\n \t\t\tchanges = c;\n \t\tif (xscr1->i1 + xscr1->chg1 < xscr2->i1) {\n+\t\t\ti0 = xscr1->i1;\n \t\t\ti1 = xscr1->i2;\n \t\t\ti2 = xscr2->i2 - xscr2->i1 + xscr1->i1;\n+\t\t\tchg0 = xscr1->chg1;\n \t\t\tchg1 = xscr1->chg2;\n \t\t\tchg2 = xscr1->chg1;\n-\t\t\tif (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {\n+\t\t\tif (xdl_append_merge(&c, 1,\n+\t\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\t\txdl_cleanup_merge(changes);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -364,11 +413,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\t\tcontinue;\n \t\t}\n \t\tif (xscr2->i1 + xscr2->chg1 < xscr1->i1) {\n+\t\t\ti0 = xscr2->i1;\n \t\t\ti1 = xscr1->i2 - xscr1->i1 + xscr2->i1;\n \t\t\ti2 = xscr2->i2;\n+\t\t\tchg0 = xscr2->chg1;\n \t\t\tchg1 = xscr2->chg1;\n \t\t\tchg2 = xscr2->chg2;\n-\t\t\tif (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {\n+\t\t\tif (xdl_append_merge(&c, 2,\n+\t\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\t\txdl_cleanup_merge(changes);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -385,19 +437,26 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\t\tint off = xscr1->i1 - xscr2->i1;\n \t\t\tint ffo = off + xscr1->chg1 - xscr2->chg1;\n \n+\t\t\ti0 = xscr1->i1;\n \t\t\ti1 = xscr1->i2;\n \t\t\ti2 = xscr2->i2;\n-\t\t\tif (off > 0)\n+\t\t\tif (off > 0) {\n+\t\t\t\ti0 -= off;\n \t\t\t\ti1 -= off;\n+\t\t\t}\n \t\t\telse\n \t\t\t\ti2 += off;\n+\t\t\tchg0 = xscr1->i1 + xscr1->chg1 - i0;\n \t\t\tchg1 = xscr1->i2 + xscr1->chg2 - i1;\n \t\t\tchg2 = xscr2->i2 + xscr2->chg2 - i2;\n \t\t\tif (ffo > 0)\n \t\t\t\tchg2 += ffo;\n-\t\t\telse\n+\t\t\telse {\n+\t\t\t\tchg0 -= ffo;\n \t\t\t\tchg1 -= ffo;\n-\t\t\tif (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {\n+\t\t\t}\n+\t\t\tif (xdl_append_merge(&c, 0,\n+\t\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\t\txdl_cleanup_merge(changes);\n \t\t\t\treturn -1;\n \t\t\t}\n@@ -414,11 +473,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \twhile (xscr1) {\n \t\tif (!changes)\n \t\t\tchanges = c;\n+\t\ti0 = xscr1->i1;\n \t\ti1 = xscr1->i2;\n \t\ti2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;\n+\t\tchg0 = xscr1->chg1;\n \t\tchg1 = xscr1->chg2;\n \t\tchg2 = xscr1->chg1;\n-\t\tif (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {\n+\t\tif (xdl_append_merge(&c, 1,\n+\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\txdl_cleanup_merge(changes);\n \t\t\treturn -1;\n \t\t}\n@@ -427,11 +489,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \twhile (xscr2) {\n \t\tif (!changes)\n \t\t\tchanges = c;\n+\t\ti0 = xscr2->i1;\n \t\ti1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;\n \t\ti2 = xscr2->i2;\n+\t\tchg0 = xscr2->chg1;\n \t\tchg1 = xscr2->chg1;\n \t\tchg2 = xscr2->chg2;\n-\t\tif (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {\n+\t\tif (xdl_append_merge(&c, 2,\n+\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\txdl_cleanup_merge(changes);\n \t\t\treturn -1;\n \t\t}\n@@ -449,7 +514,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t/* output */\n \tif (result) {\n \t\tint size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,\n-\t\t\tchanges, NULL);\n+\t\t\tchanges, NULL, style);\n \t\tresult->ptr = xdl_malloc(size);\n \t\tif (!result->ptr) {\n \t\t\txdl_cleanup_merge(changes);\n@@ -457,14 +522,14 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\t}\n \t\tresult->size = size;\n \t\txdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,\n-\t\t\t\tresult->ptr);\n+\t\t\t\t      result->ptr, style);\n \t}\n \treturn xdl_cleanup_merge(changes);\n }\n \n int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,\n \t\tmmfile_t *mf2, const char *name2,\n-\t\txpparam_t const *xpp, int level, mmbuffer_t *result) {\n+\t\txpparam_t const *xpp, int flags, mmbuffer_t *result) {\n \txdchange_t *xscr1, *xscr2;\n \txdfenv_t xe1, xe2;\n \tint status;\n@@ -501,7 +566,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,\n \t\t} else {\n \t\t\tstatus = xdl_do_merge(&xe1, xscr1, name1,\n \t\t\t\t\t      &xe2, xscr2, name2,\n-\t\t\t\t\t      level, xpp, result);\n+\t\t\t\t\t      flags, xpp, result);\n \t\t}\n \t\txdl_free_script(xscr1);\n \t\txdl_free_script(xscr2);\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89173","messageId":"1220056963-2352-4-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-3-git-send-email-gitster@pobox.com","subject":"[PATCH 03/12] xmerge.c: minimum readability fixups","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:34Z","receivedAt":"2008-08-30T00:42:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This replaces hardcoded magic constants with symbolic ones for\nreadability, and swaps one if/else blocks to better match the\norder in which 0/1/2 variables are handled to nearby codepath.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n xdiff/xmerge.c |   14 +++++++-------\n 1 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 29cdbea..7dcd405 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\t\txscr2 = xscr2->next;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (level < 1 || xscr1->i1 != xscr2->i1 ||\n+\t\tif (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||\n \t\t\t\txscr1->chg1 != xscr2->chg1 ||\n \t\t\t\txscr1->chg2 != xscr2->chg2 ||\n \t\t\t\txdl_merge_cmp_lines(xe1, xscr1->i2,\n@@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \t\t\tchg0 = xscr1->i1 + xscr1->chg1 - i0;\n \t\t\tchg1 = xscr1->i2 + xscr1->chg2 - i1;\n \t\t\tchg2 = xscr2->i2 + xscr2->chg2 - i2;\n-\t\t\tif (ffo > 0)\n-\t\t\t\tchg2 += ffo;\n-\t\t\telse {\n+\t\t\tif (ffo < 0) {\n \t\t\t\tchg0 -= ffo;\n \t\t\t\tchg1 -= ffo;\n-\t\t\t}\n+\t\t\t} else\n+\t\t\t\tchg2 += ffo;\n \t\t\tif (xdl_append_merge(&c, 0,\n \t\t\t\t\t     i0, chg0, i1, chg1, i2, chg2)) {\n \t\t\t\txdl_cleanup_merge(changes);\n@@ -505,9 +504,10 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \tif (!changes)\n \t\tchanges = c;\n \t/* refine conflicts */\n-\tif (level > 1 &&\n+\tif (XDL_MERGE_ZEALOUS <= level &&\n \t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n-\t     xdl_simplify_non_conflicts(xe1, changes, level > 2) < 0)) {\n+\t     xdl_simplify_non_conflicts(xe1, changes,\n+\t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n \t\txdl_cleanup_merge(changes);\n \t\treturn -1;\n \t}\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89176","messageId":"1220056963-2352-5-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-4-git-send-email-gitster@pobox.com","subject":"[PATCH 04/12] xmerge.c: \"diff3 -m\" style clips merge reduction level to EAGER or less","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:35Z","receivedAt":"2008-08-30T00:42:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"When showing a conflicting merge result, and \"--diff3 -m\" style is asked\nfor, this patch makes sure that the merge reduction level does not exceed\nXDL_MERGE_EAGER.  This is because \"diff3 -m\" style output would not make\nsense for anything more aggressive than XDL_MERGE_EAGER, because of the\nway how the merge reduction works.\n\n\"git merge-file\" no longer has to force MERGE_EAGER when \"--diff3\" is\nasked for because of this change.\n\nSuppose a common ancestor (shared preimage) is modified to postimage #1\nand #2 (each letter represents one line):\n\n                     #####\n    postimage#1: 1234ABCDE789\n                    |    /\n                    |   /\n    preimage:    123456789\n                    |   \\\n    postimage#2: 1234AXYE789\n                     ####\n\nXDL_MERGE_MINIMAL and XDL_MERGE_EAGER would:\n\n (1) find the s/56/ABCDE/ done on one side and s/56/AXYE/ done on the\n     other side,\n\n (2) notice that they touch an overlapping area, and\n\n (3) mark it as a conflict, \"ABCDE vs AXYE\".\n\nThe difference between the two algorithms is that EAGER drops the hunk\naltogether if the postimages match (i.e. both sides modified the same\nway), while MINIMAL keeps it.  There is no other operation performed to\nthe hunk.  As the result, lines marked with \"#\" in the above picure will\nbe in the RCS merge style output like this (letters <, = and > represent\nconflict marker lines):\n\n    output:      1234<ABCDE=AXYE>789    ; with MINIMAL/EAGER\n\nThe part from the preimage that corresponds to these conflicting changes\nis \"56\", which is what \"diff3 -m\" style output adds to it:\n\n    output:      1234<ABCDE|56=AXYE>789 ; in \"diff3 -m\" style\n\nNow, XDL_MERGE_ZEALOUS looks at the differences between the changes two\npostimages made in order to reduce the number of lines in the conflicting\nregions.  It notices that both sides start their new contents with \"A\",\nand excludes it from the output (it also excludes \"E\" for the same\nreason).  The conflict that used to be \"ABCDE vs AXYE\" is now \"BCD vs XY\":\n\n    output:      1234A<BCD=XY>E789      ; with ZEALOUS\n\nThere could even be matching parts between two postimages in the middle.\nInstead of one side rewriting the shared \"56\" to \"ABCDE\" and the other\nside to \"AXYE\", imagine the case where the postimages are \"ABCDE\" and\n\"AXCYE\", in which case instead of having one conflicted hunk \"BCD vs XY\",\nyou would have two conflicting hunks \"B vs X\" and \"D vs Y\".\n\nIn either case, once you reduce \"ABCDE vs AXYE\" to \"BCD vs XY\" (or \"ABCDE\nvs AXCYE\" to \"B vs X\" and \"D vs Y\"), there is no part from the preimage\nthat corresponds to the conflicting change made in both postimages\nanymore.  In other words, conflict reduced by ZEALOUS algorithm cannot be\nexpressed in \"diff3 -m\" style.  Representing the last illustration like\nthis is misleading to say the least:\n\n    output:      1234A<BCD|56=XY>E789   ; broken \"diff3 -m\" style\n\nbecause the preimage was not ...4A56E... to begin with.  \"A\" and \"E\" are\ncommon only between the postimages.\n\nEven worse, once a single conflicting hunk is split into multiple ones\n(recall the example of breaking \"ABCDE vs AXCYE\" to \"B vs X\" and \"D vs\nY\"), there is no sane way to distribute the preimage text across split\nconflicting hunks.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-merge-file.c |    4 +---\n xdiff/xmerge.c       |    9 +++++++++\n 2 files changed, 10 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin-merge-file.c b/builtin-merge-file.c\nindex 5b4f020..1e92510 100644\n--- a/builtin-merge-file.c\n+++ b/builtin-merge-file.c\n@@ -27,10 +27,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \t\telse if (!strcmp(argv[1], \"-q\") ||\n \t\t\t\t!strcmp(argv[1], \"--quiet\"))\n \t\t\tfreopen(\"/dev/null\", \"w\", stderr);\n-\t\telse if (!strcmp(argv[1], \"--diff3\")) {\n+\t\telse if (!strcmp(argv[1], \"--diff3\"))\n \t\t\tmerge_style = XDL_MERGE_DIFF3;\n-\t\t\tmerge_level = XDL_MERGE_EAGER;\n-\t\t}\n \t\telse\n \t\t\tusage(merge_file_usage);\n \t\targc--;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 7dcd405..d9737f0 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -392,6 +392,15 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n \tint level = flags & XDL_MERGE_LEVEL_MASK;\n \tint style = flags & XDL_MERGE_STYLE_MASK;\n \n+\tif (style == XDL_MERGE_DIFF3) {\n+\t\t/*\n+\t\t * \"diff3 -m\" output does not make sense for anything\n+\t\t * more aggressive than XDL_MERGE_EAGER.\n+\t\t */\n+\t\tif (XDL_MERGE_EAGER < level)\n+\t\t\tlevel = XDL_MERGE_EAGER;\n+\t}\n+\n \tc = changes = NULL;\n \n \twhile (xscr1 && xscr2) {\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89178","messageId":"1220056963-2352-6-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-5-git-send-email-gitster@pobox.com","subject":"[PATCH 05/12] rerere.c: use symbolic constants to keep track of parsing states","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:36Z","receivedAt":"2008-08-30T00:42:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"These hardcoded integers make the code harder to follow than necessary;\nreplace them with enums to make it easier to read, before adding support\nfor optionally parsing \"diff3 -m\" style conflict markers.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n rerere.c |   23 +++++++++++++----------\n 1 files changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex 323e493..bf74b26 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -75,7 +75,10 @@ static int handle_file(const char *path,\n {\n \tSHA_CTX ctx;\n \tchar buf[1024];\n-\tint hunk = 0, hunk_no = 0;\n+\tint hunk_no = 0;\n+\tenum {\n+\t\tRR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2,\n+\t} hunk = RR_CONTEXT;\n \tstruct strbuf one, two;\n \tFILE *f = fopen(path, \"r\");\n \tFILE *out = NULL;\n@@ -98,20 +101,20 @@ static int handle_file(const char *path,\n \tstrbuf_init(&two,  0);\n \twhile (fgets(buf, sizeof(buf), f)) {\n \t\tif (!prefixcmp(buf, \"<<<<<<< \")) {\n-\t\t\tif (hunk)\n+\t\t\tif (hunk != RR_CONTEXT)\n \t\t\t\tgoto bad;\n-\t\t\thunk = 1;\n+\t\t\thunk = RR_SIDE_1;\n \t\t} else if (!prefixcmp(buf, \"=======\") && isspace(buf[7])) {\n-\t\t\tif (hunk != 1)\n+\t\t\tif (hunk != RR_SIDE_1)\n \t\t\t\tgoto bad;\n-\t\t\thunk = 2;\n+\t\t\thunk = RR_SIDE_2;\n \t\t} else if (!prefixcmp(buf, \">>>>>>> \")) {\n-\t\t\tif (hunk != 2)\n+\t\t\tif (hunk != RR_SIDE_2)\n \t\t\t\tgoto bad;\n \t\t\tif (strbuf_cmp(&one, &two) > 0)\n \t\t\t\tstrbuf_swap(&one, &two);\n \t\t\thunk_no++;\n-\t\t\thunk = 0;\n+\t\t\thunk = RR_CONTEXT;\n \t\t\tif (out) {\n \t\t\t\tfputs(\"<<<<<<<\\n\", out);\n \t\t\t\tfwrite(one.buf, one.len, 1, out);\n@@ -127,9 +130,9 @@ static int handle_file(const char *path,\n \t\t\t}\n \t\t\tstrbuf_reset(&one);\n \t\t\tstrbuf_reset(&two);\n-\t\t} else if (hunk == 1)\n+\t\t} else if (hunk == RR_SIDE_1)\n \t\t\tstrbuf_addstr(&one, buf);\n-\t\telse if (hunk == 2)\n+\t\telse if (hunk == RR_SIDE_2)\n \t\t\tstrbuf_addstr(&two, buf);\n \t\telse if (out)\n \t\t\tfputs(buf, out);\n@@ -146,7 +149,7 @@ static int handle_file(const char *path,\n \t\tfclose(out);\n \tif (sha1)\n \t\tSHA1_Final(sha1, &ctx);\n-\tif (hunk) {\n+\tif (hunk != RR_CONTEXT) {\n \t\tif (output)\n \t\t\tunlink(output);\n \t\treturn error(\"Could not parse conflict hunks in %s\", path);\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89177","messageId":"1220056963-2352-7-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-6-git-send-email-gitster@pobox.com","subject":"[PATCH 06/12] rerere: understand \"diff3 -m\" style conflicts with the original","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:37Z","receivedAt":"2008-08-30T00:42:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This teaches rerere to grok conflicts expressed in \"diff3 -m\" style\noutput, where the version from the common ancestor is output after the\nfirst side, preceded by a \"|||||||\" line.\n\nThe rerere database needs to keep only the versions from two sides, so the\ncode parses the original copy and discards it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n rerere.c |   10 ++++++++--\n 1 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/rerere.c b/rerere.c\nindex bf74b26..4e2c9dd 100644\n--- a/rerere.c\n+++ b/rerere.c\n@@ -77,7 +77,7 @@ static int handle_file(const char *path,\n \tchar buf[1024];\n \tint hunk_no = 0;\n \tenum {\n-\t\tRR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2,\n+\t\tRR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,\n \t} hunk = RR_CONTEXT;\n \tstruct strbuf one, two;\n \tFILE *f = fopen(path, \"r\");\n@@ -104,9 +104,13 @@ static int handle_file(const char *path,\n \t\t\tif (hunk != RR_CONTEXT)\n \t\t\t\tgoto bad;\n \t\t\thunk = RR_SIDE_1;\n-\t\t} else if (!prefixcmp(buf, \"=======\") && isspace(buf[7])) {\n+\t\t} else if (!prefixcmp(buf, \"|||||||\") && isspace(buf[7])) {\n \t\t\tif (hunk != RR_SIDE_1)\n \t\t\t\tgoto bad;\n+\t\t\thunk = RR_ORIGINAL;\n+\t\t} else if (!prefixcmp(buf, \"=======\") && isspace(buf[7])) {\n+\t\t\tif (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)\n+\t\t\t\tgoto bad;\n \t\t\thunk = RR_SIDE_2;\n \t\t} else if (!prefixcmp(buf, \">>>>>>> \")) {\n \t\t\tif (hunk != RR_SIDE_2)\n@@ -132,6 +136,8 @@ static int handle_file(const char *path,\n \t\t\tstrbuf_reset(&two);\n \t\t} else if (hunk == RR_SIDE_1)\n \t\t\tstrbuf_addstr(&one, buf);\n+\t\telse if (hunk == RR_ORIGINAL)\n+\t\t\t; /* discard */\n \t\telse if (hunk == RR_SIDE_2)\n \t\t\tstrbuf_addstr(&two, buf);\n \t\telse if (out)\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89175","messageId":"1220056963-2352-8-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-7-git-send-email-gitster@pobox.com","subject":"[PATCH 07/12] merge.conflictstyle: choose between \"merge\" and \"diff3 -m\" styles","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:38Z","receivedAt":"2008-08-30T00:42:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This teaches \"git merge-file\" to honor merge.conflictstyle configuration\nvariable, whose value can be \"merge\" (default) or \"diff3\".\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Documentation/config.txt |    8 ++++++++\n builtin-merge-file.c     |    9 +++++++++\n t/t6023-merge-file.sh    |    9 ++++++++-\n xdiff-interface.c        |   20 ++++++++++++++++++++\n xdiff-interface.h        |    2 ++\n 5 files changed, 47 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex af57d94..cb4c4ca 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -893,6 +893,14 @@ man.<tool>.path::\n \tOverride the path for the given tool that may be used to\n \tdisplay help in the 'man' format. See linkgit:git-help[1].\n \n+merge.conflictstyle::\n+\tSpecify the style in which conflicted hunks are written out to\n+\tworking tree files upon merge.  The default is \"merge\", which\n+\tshows `<<<<<<<` conflict marker, change made by one side,\n+\t`=======` marker, change made by the other side, and then\n+\t`>>>>>>>` marker.  An alternate style, \"diff3\", adds `|||||||`\n+\tmarker and the original text before `=======` marker.\n+\n mergetool.<tool>.path::\n \tOverride the path for the given tool.  This is useful in case\n \tyour tool is not in the PATH.\ndiff --git a/builtin-merge-file.c b/builtin-merge-file.c\nindex 1e92510..f009e73 100644\n--- a/builtin-merge-file.c\n+++ b/builtin-merge-file.c\n@@ -15,6 +15,15 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tint ret = 0, i = 0, to_stdout = 0;\n \tint merge_level = XDL_MERGE_ZEALOUS_ALNUM;\n \tint merge_style = 0;\n+\tint nongit;\n+\n+\tprefix = setup_git_directory_gently(&nongit);\n+\tif (!nongit) {\n+\t\t/* Read the configuration file */\n+\t\tgit_config(git_xmerge_config, NULL);\n+\t\tif (git_xmerge_style > 0)\n+\t\t\tmerge_style = git_xmerge_style;\n+\t}\n \n \twhile (argc > 4) {\n \t\tif (!strcmp(argv[1], \"-L\") && i < 3) {\ndiff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh\nindex f3484e3..93ec517 100755\n--- a/t/t6023-merge-file.sh\n+++ b/t/t6023-merge-file.sh\n@@ -192,10 +192,17 @@ non timebo mala, quoniam TU mecum es:\n virga tua et baculus tuus ipsa me consolata sunt.\n EOF\n \n-test_expect_success '\"diff3 -m\" style output' '\n+test_expect_success '\"diff3 -m\" style output (1)' '\n \ttest_must_fail git merge-file -p --diff3 \\\n \t\tnew8.txt new5.txt new9.txt >actual &&\n \ttest_cmp expect actual\n '\n \n+test_expect_success '\"diff3 -m\" style output (2)' '\n+\tgit config merge.conflictstyle diff3 &&\n+\ttest_must_fail git merge-file -p \\\n+\t\tnew8.txt new5.txt new9.txt >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 944ad98..8457601 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -249,3 +249,23 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value)\n \t\tvalue = ep + 1;\n \t}\n }\n+\n+int git_xmerge_style = -1;\n+\n+int git_xmerge_config(const char *var, const char *value, void *cb)\n+{\n+\tif (!strcasecmp(var, \"merge.conflictstyle\")) {\n+\t\tif (!value)\n+\t\t\tdie(\"'%s' is not a boolean\", var);\n+\t\tif (!strcmp(value, \"diff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_DIFF3;\n+\t\telse if (!strcmp(value, \"merge\"))\n+\t\t\tgit_xmerge_style = 0;\n+\t\telse\n+\t\t\tdie(\"unknown style '%s' given for '%s'\",\n+\t\t\t    value, var);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value, cb);\n+}\n+\ndiff --git a/xdiff-interface.h b/xdiff-interface.h\nindex 558492b..b3b5c93 100644\n--- a/xdiff-interface.h\n+++ b/xdiff-interface.h\n@@ -17,5 +17,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);\n int buffer_is_binary(const char *ptr, unsigned long size);\n \n extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line);\n+extern int git_xmerge_config(const char *var, const char *value, void *cb);\n+extern int git_xmerge_style;\n \n #endif\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89179","messageId":"1220056963-2352-9-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-8-git-send-email-gitster@pobox.com","subject":"[PATCH 08/12] git-merge-recursive: learn to honor merge.conflictstyle","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:39Z","receivedAt":"2008-08-30T00:42:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This teaches the low-level ll_xdl_merge() routine to honor\nmerge.conflictstyle configuration variable, so that merge-recursive\nstrategy can show the conflicts in the style of user's choice.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-merge-recursive.c |    2 +-\n ll-merge.c                |   16 ++++++++++++----\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c\nindex dfb363e..f3b6ede 100644\n--- a/builtin-merge-recursive.c\n+++ b/builtin-merge-recursive.c\n@@ -1348,7 +1348,7 @@ static int merge_config(const char *var, const char *value, void *cb)\n \t\tmerge_rename_limit = git_config_int(var, value);\n \t\treturn 0;\n \t}\n-\treturn git_default_config(var, value, cb);\n+\treturn git_xmerge_config(var, value, cb);\n }\n \n int cmd_merge_recursive(int argc, const char **argv, const char *prefix)\ndiff --git a/ll-merge.c b/ll-merge.c\nindex 9837c84..4a71614 100644\n--- a/ll-merge.c\n+++ b/ll-merge.c\n@@ -63,6 +63,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,\n \t\t\tint virtual_ancestor)\n {\n \txpparam_t xpp;\n+\tint style = 0;\n \n \tif (buffer_is_binary(orig->ptr, orig->size) ||\n \t    buffer_is_binary(src1->ptr, src1->size) ||\n@@ -77,10 +78,12 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,\n \t}\n \n \tmemset(&xpp, 0, sizeof(xpp));\n+\tif (git_xmerge_style >= 0)\n+\t\tstyle = git_xmerge_style;\n \treturn xdl_merge(orig,\n \t\t\t src1, name1,\n \t\t\t src2, name2,\n-\t\t\t &xpp, XDL_MERGE_ZEALOUS,\n+\t\t\t &xpp, XDL_MERGE_ZEALOUS | style,\n \t\t\t result);\n }\n \n@@ -95,10 +98,15 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,\n \tchar *src, *dst;\n \tlong size;\n \tconst int marker_size = 7;\n-\n-\tint status = ll_xdl_merge(drv_unused, result, path_unused,\n-\t\t\t\t  orig, src1, NULL, src2, NULL,\n-\t\t\t\t  virtual_ancestor);\n+\tint status, saved_style;\n+\n+\t/* We have to force the RCS \"merge\" style */\n+\tsaved_style = git_xmerge_style;\n+\tgit_xmerge_style = 0;\n+\tstatus = ll_xdl_merge(drv_unused, result, path_unused,\n+\t\t\t      orig, src1, NULL, src2, NULL,\n+\t\t\t      virtual_ancestor);\n+\tgit_xmerge_style = saved_style;\n \tif (status <= 0)\n \t\treturn status;\n \tsize = result->size;\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89181","messageId":"1220056963-2352-10-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-9-git-send-email-gitster@pobox.com","subject":"[PATCH 09/12] checkout: do not check out unmerged higher stages randomly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:40Z","receivedAt":"2008-08-30T00:42:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"During a conflicted merge when you have unmerged stages for a\npath F in the index, if you said:\n\n    $ git checkout F\n\nwe rewrote F as many times as we have stages for it, and the\nlast one (typically \"theirs\") was left in the work tree, without\nresolving the conflict.\n\nThis fixes it by noticing that a specified pathspec pattern\nmatches an unmerged path, and by erroring out.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |   27 +++++++++++++++++++++++++++\n t/t7201-co.sh      |   23 +++++++++++++++++++++++\n 2 files changed, 50 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex b380ad6..9b33f3a 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec)\n \treturn 0;\n }\n \n+static int skip_same_name(struct cache_entry *ce, int pos)\n+{\n+\twhile (++pos < active_nr &&\n+\t       !strcmp(active_cache[pos]->name, ce->name))\n+\t\t; /* skip */\n+\treturn pos;\n+}\n+\n+\n static int checkout_paths(struct tree *source_tree, const char **pathspec)\n {\n \tint pos;\n@@ -107,6 +116,20 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)\n \tif (report_path_error(ps_matched, pathspec, 0))\n \t\treturn 1;\n \n+\t/* Any unmerged paths? */\n+\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\tstruct cache_entry *ce = active_cache[pos];\n+\t\tif (pathspec_match(pathspec, NULL, ce->name, 0) &&\n+\t\t    ce_stage(ce)) {\n+\t\t\terrs = 1;\n+\t\t\terror(\"path '%s' is unmerged\", ce->name);\n+\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t}\n+\tif (errs)\n+\t\treturn 1;\n+\n \t/* Now we are committed to check them out */\n \tmemset(&state, 0, sizeof(state));\n \tstate.force = 1;\n@@ -114,6 +137,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (pathspec_match(pathspec, NULL, ce->name, 0)) {\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\terrs |= checkout_entry(ce, &state, NULL);\n \t\t}\n \t}\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 1dff84d..303cf62 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -369,4 +369,27 @@ test_expect_success \\\n     'checkout with --track, but without -b, fails with too short tracked name' '\n     test_must_fail git checkout --track renamer'\n \n+test_expect_success 'checkout an unmerged path should fail' '\n+\trm -f .git/index &&\n+\tO=$(echo original | git hash-object -w --stdin) &&\n+\tA=$(echo ourside | git hash-object -w --stdin) &&\n+\tB=$(echo theirside | git hash-object -w --stdin) &&\n+\t(\n+\t\techo \"100644 $A 0\tfild\" &&\n+\t\techo \"100644 $O 1\tfile\" &&\n+\t\techo \"100644 $A 2\tfile\" &&\n+\t\techo \"100644 $B 3\tfile\" &&\n+\t\techo \"100644 $A 0\tfilf\"\n+\t) | git update-index --index-info &&\n+\techo \"none of the above\" >sample &&\n+\tcat sample >fild &&\n+\tcat sample >file &&\n+\tcat sample >filf &&\n+\ttest_must_fail git checkout fild file filf &&\n+\ttest_cmp sample fild &&\n+\ttest_cmp sample filf &&\n+\ttest_cmp sample file\n+'\n+\n test_done\n+\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89180","messageId":"1220056963-2352-11-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-10-git-send-email-gitster@pobox.com","subject":"[PATCH 10/12] checkout: allow ignoring unmerged paths when checking out of the index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:41Z","receivedAt":"2008-08-30T00:42:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Earlier we made \"git checkout $pathspec\" to atomically refuse\nthe operation of $pathspec matched any path with unmerged\nstages.  This patch allows:\n\n    $ git checkout -f a b c\n\nto ignore, instead of error out on, such unmerged paths.  The\nfix to prevent checkout of an unmerged path from random stages\nis still there.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |   41 +++++++++++++++++++++++------------------\n t/t7201-co.sh      |   24 +++++++++++++++++++++++-\n 2 files changed, 46 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 9b33f3a..49c43d9 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -20,6 +20,17 @@ static const char * const checkout_usage[] = {\n \tNULL,\n };\n \n+struct checkout_opts {\n+\tint quiet;\n+\tint merge;\n+\tint force;\n+\tint writeout_error;\n+\n+\tconst char *new_branch;\n+\tint new_branch_log;\n+\tenum branch_track track;\n+};\n+\n static int post_checkout_hook(struct commit *old, struct commit *new,\n \t\t\t      int changed)\n {\n@@ -85,7 +96,8 @@ static int skip_same_name(struct cache_entry *ce, int pos)\n }\n \n \n-static int checkout_paths(struct tree *source_tree, const char **pathspec)\n+static int checkout_paths(struct tree *source_tree, const char **pathspec,\n+\t\t\t  struct checkout_opts *opts)\n {\n \tint pos;\n \tstruct checkout state;\n@@ -121,8 +133,12 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (pathspec_match(pathspec, NULL, ce->name, 0) &&\n \t\t    ce_stage(ce)) {\n-\t\t\terrs = 1;\n-\t\t\terror(\"path '%s' is unmerged\", ce->name);\n+\t\t\tif (!opts->force) {\n+\t\t\t\terrs = 1;\n+\t\t\t\terror(\"path '%s' is unmerged\", ce->name);\n+\t\t\t} else {\n+\t\t\t\twarning(\"path '%s' is unmerged\", ce->name);\n+\t\t\t}\n \t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t\tcontinue;\n \t\t}\n@@ -178,17 +194,6 @@ static void describe_detached_head(char *msg, struct commit *commit)\n \tstrbuf_release(&sb);\n }\n \n-struct checkout_opts {\n-\tint quiet;\n-\tint merge;\n-\tint force;\n-\tint writeout_error;\n-\n-\tconst char *new_branch;\n-\tint new_branch_log;\n-\tenum branch_track track;\n-};\n-\n static int reset_tree(struct tree *tree, struct checkout_opts *o, int worktree)\n {\n \tstruct unpack_trees_options opts;\n@@ -569,15 +574,15 @@ no_reference:\n \t\t\tdie(\"invalid path specification\");\n \n \t\t/* Checkout paths */\n-\t\tif (opts.new_branch || opts.force || opts.merge) {\n+\t\tif (opts.new_branch || opts.merge) {\n \t\t\tif (argc == 1) {\n-\t\t\t\tdie(\"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]);\n+\t\t\t\tdie(\"git checkout: updating paths is incompatible with switching branches.\\nDid you intend to checkout '%s' which can not be resolved as commit?\", argv[0]);\n \t\t\t} else {\n-\t\t\t\tdie(\"git checkout: updating paths is incompatible with switching branches/forcing\");\n+\t\t\t\tdie(\"git checkout: updating paths is incompatible with switching branches.\");\n \t\t\t}\n \t\t}\n \n-\t\treturn checkout_paths(source_tree, pathspec);\n+\t\treturn checkout_paths(source_tree, pathspec, &opts);\n \t}\n \n \tif (new.name && !new.commit) {\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 303cf62..b7274eb 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -391,5 +391,27 @@ test_expect_success 'checkout an unmerged path should fail' '\n \ttest_cmp sample file\n '\n \n-test_done\n+test_expect_success 'checkout with an unmerged path can be ignored' '\n+\trm -f .git/index &&\n+\tO=$(echo original | git hash-object -w --stdin) &&\n+\tA=$(echo ourside | git hash-object -w --stdin) &&\n+\tB=$(echo theirside | git hash-object -w --stdin) &&\n+\t(\n+\t\techo \"100644 $A 0\tfild\" &&\n+\t\techo \"100644 $O 1\tfile\" &&\n+\t\techo \"100644 $A 2\tfile\" &&\n+\t\techo \"100644 $B 3\tfile\" &&\n+\t\techo \"100644 $A 0\tfilf\"\n+\t) | git update-index --index-info &&\n+\techo \"none of the above\" >sample &&\n+\techo ourside >expect &&\n+\tcat sample >fild &&\n+\tcat sample >file &&\n+\tcat sample >filf &&\n+\tgit checkout -f fild file filf &&\n+\ttest_cmp expect fild &&\n+\ttest_cmp expect filf &&\n+\ttest_cmp sample file\n+'\n \n+test_done\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89182","messageId":"1220056963-2352-12-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-11-git-send-email-gitster@pobox.com","subject":"[PATCH 11/12] checkout --ours/--theirs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:42Z","receivedAt":"2008-08-30T00:42:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This lets you to check out 'our' (or 'their') version of an\nunmerged path out of the index while resolving conflicts.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |   47 ++++++++++++++++++++++++++++++++++++++++++-----\n t/t7201-co.sh      |   25 +++++++++++++++++++++++++\n 2 files changed, 67 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 49c43d9..bdfdb65 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -24,6 +24,7 @@ struct checkout_opts {\n \tint quiet;\n \tint merge;\n \tint force;\n+\tint writeout_stage;\n \tint writeout_error;\n \n \tconst char *new_branch;\n@@ -95,6 +96,32 @@ static int skip_same_name(struct cache_entry *ce, int pos)\n \treturn pos;\n }\n \n+static int check_stage(int stage, struct cache_entry *ce, int pos)\n+{\n+\twhile (pos < active_nr &&\n+\t       !strcmp(active_cache[pos]->name, ce->name)) {\n+\t\tif (ce_stage(active_cache[pos]) == stage)\n+\t\t\treturn 0;\n+\t\tpos++;\n+\t}\n+\treturn error(\"path '%s' does not have %s version\",\n+\t\t     ce->name,\n+\t\t     (stage == 2) ? \"our\" : \"their\");\n+}\n+\n+static int checkout_stage(int stage, struct cache_entry *ce, int pos,\n+\t\t\t  struct checkout *state)\n+{\n+\twhile (pos < active_nr &&\n+\t       !strcmp(active_cache[pos]->name, ce->name)) {\n+\t\tif (ce_stage(active_cache[pos]) == stage)\n+\t\t\treturn checkout_entry(active_cache[pos], state, NULL);\n+\t\tpos++;\n+\t}\n+\treturn error(\"path '%s' does not have %s version\",\n+\t\t     ce->name,\n+\t\t     (stage == 2) ? \"our\" : \"their\");\n+}\n \n static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \t\t\t  struct checkout_opts *opts)\n@@ -106,6 +133,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tint flag;\n \tstruct commit *head;\n \tint errs = 0;\n+\tint stage = opts->writeout_stage;\n \n \tint newfd;\n \tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n@@ -131,13 +159,16 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \t/* Any unmerged paths? */\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\n-\t\tif (pathspec_match(pathspec, NULL, ce->name, 0) &&\n-\t\t    ce_stage(ce)) {\n-\t\t\tif (!opts->force) {\n+\t\tif (pathspec_match(pathspec, NULL, ce->name, 0)) {\n+\t\t\tif (!ce_stage(ce))\n+\t\t\t\tcontinue;\n+\t\t\tif (stage) {\n+\t\t\t\terrs |= check_stage(stage, ce, pos);\n+\t\t\t} else if (opts->force) {\n+\t\t\t\twarning(\"path '%s' is unmerged\", ce->name);\n+\t\t\t} else {\n \t\t\t\terrs = 1;\n \t\t\t\terror(\"path '%s' is unmerged\", ce->name);\n-\t\t\t} else {\n-\t\t\t\twarning(\"path '%s' is unmerged\", ce->name);\n \t\t\t}\n \t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t\tcontinue;\n@@ -154,6 +185,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (pathspec_match(pathspec, NULL, ce->name, 0)) {\n \t\t\tif (ce_stage(ce)) {\n+\t\t\t\tif (stage)\n+\t\t\t\t\terrs |= checkout_stage(stage, ce, pos, &state);\n \t\t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -458,6 +491,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOLEAN('l', NULL, &opts.new_branch_log, \"log for new branch\"),\n \t\tOPT_SET_INT('t', \"track\",  &opts.track, \"track\",\n \t\t\tBRANCH_TRACK_EXPLICIT),\n+\t\tOPT_SET_INT('2', \"ours\", &opts.writeout_stage, \"stage\",\n+\t\t\t    2),\n+\t\tOPT_SET_INT('3', \"theirs\", &opts.writeout_stage, \"stage\",\n+\t\t\t    3),\n \t\tOPT_BOOLEAN('f', NULL, &opts.force, \"force\"),\n \t\tOPT_BOOLEAN('m', NULL, &opts.merge, \"merge\"),\n \t\tOPT_END(),\ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex b7274eb..85c792c 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -414,4 +414,29 @@ test_expect_success 'checkout with an unmerged path can be ignored' '\n \ttest_cmp sample file\n '\n \n+test_expect_success 'checkout unmerged stage' '\n+\trm -f .git/index &&\n+\tO=$(echo original | git hash-object -w --stdin) &&\n+\tA=$(echo ourside | git hash-object -w --stdin) &&\n+\tB=$(echo theirside | git hash-object -w --stdin) &&\n+\t(\n+\t\techo \"100644 $A 0\tfild\" &&\n+\t\techo \"100644 $O 1\tfile\" &&\n+\t\techo \"100644 $A 2\tfile\" &&\n+\t\techo \"100644 $B 3\tfile\" &&\n+\t\techo \"100644 $A 0\tfilf\"\n+\t) | git update-index --index-info &&\n+\techo \"none of the above\" >sample &&\n+\techo ourside >expect &&\n+\tcat sample >fild &&\n+\tcat sample >file &&\n+\tcat sample >filf &&\n+\tgit checkout --ours . &&\n+\ttest_cmp expect fild &&\n+\ttest_cmp expect filf &&\n+\ttest_cmp expect file &&\n+\tgit checkout --theirs file &&\n+\ttest ztheirside = \"z$(cat file)\"\n+'\n+\n test_done\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89183","messageId":"1220056963-2352-13-git-send-email-gitster@pobox.com","threadId":"15280","inReplyTo":"1220056963-2352-12-git-send-email-gitster@pobox.com","subject":"[PATCH 12/12] checkout -m: recreate merge when checking out of unmerged index","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T00:42:43Z","receivedAt":"2008-08-30T00:42:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This teaches git-checkout to recreate a merge out of unmerged\nindex entries while resolving conflicts.\n\nWith this patch, checking out an unmerged path from the index\nnow have the following possibilities:\n\n * Without any option, an attempt to checkout an unmerged path\n   will atomically fail (i.e. no other cleanly-merged paths are\n   checked out either);\n\n * With \"-f\", other cleanly-merged paths are checked out, and\n   unmerged paths are ignored;\n\n * With \"--ours\" or \"--theirs, the contents from the specified\n   stage is checked out;\n\n * With \"-m\" (we should add \"--merge\" as synonym), the 3-way merge\n   is recreated from the staged object names and checked out.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-checkout.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++++----\n t/t7201-co.sh      |   63 ++++++++++++++++++++++++++++\n 2 files changed, 170 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex bdfdb65..8b5da7f 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -13,6 +13,9 @@\n #include \"diff.h\"\n #include \"revision.h\"\n #include \"remote.h\"\n+#include \"blob.h\"\n+#include \"xdiff-interface.h\"\n+#include \"ll-merge.h\"\n \n static const char * const checkout_usage[] = {\n \t\"git checkout [options] <branch>\",\n@@ -109,6 +112,19 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)\n \t\t     (stage == 2) ? \"our\" : \"their\");\n }\n \n+static int check_all_stage(struct cache_entry *ce, int pos)\n+{\n+\tif (ce_stage(ce) != 1 ||\n+\t    active_nr <= pos + 2 ||\n+\t    strcmp(active_cache[pos+1]->name, ce->name) ||\n+\t    ce_stage(active_cache[pos+1]) != 2 ||\n+\t    strcmp(active_cache[pos+2]->name, ce->name) ||\n+\t    ce_stage(active_cache[pos+2]) != 3)\n+\t\treturn error(\"path '%s' does not have all three versions\",\n+\t\t\t     ce->name);\n+\treturn 0;\n+}\n+\n static int checkout_stage(int stage, struct cache_entry *ce, int pos,\n \t\t\t  struct checkout *state)\n {\n@@ -123,6 +139,77 @@ static int checkout_stage(int stage, struct cache_entry *ce, int pos,\n \t\t     (stage == 2) ? \"our\" : \"their\");\n }\n \n+/* NEEDSWORK: share with merge-recursive */\n+static void fill_mm(const unsigned char *sha1, mmfile_t *mm)\n+{\n+\tunsigned long size;\n+\tenum object_type type;\n+\n+\tif (!hashcmp(sha1, null_sha1)) {\n+\t\tmm->ptr = xstrdup(\"\");\n+\t\tmm->size = 0;\n+\t\treturn;\n+\t}\n+\n+\tmm->ptr = read_sha1_file(sha1, &type, &size);\n+\tif (!mm->ptr || type != OBJ_BLOB)\n+\t\tdie(\"unable to read blob object %s\", sha1_to_hex(sha1));\n+\tmm->size = size;\n+}\n+\n+static int checkout_merged(int pos, struct checkout *state)\n+{\n+\tstruct cache_entry *ce = active_cache[pos];\n+\tconst char *path = ce->name;\n+\tmmfile_t ancestor, ours, theirs;\n+\tint status;\n+\tunsigned char sha1[20];\n+\tmmbuffer_t result_buf;\n+\n+\tif (ce_stage(ce) != 1 ||\n+\t    active_nr <= pos + 2 ||\n+\t    strcmp(active_cache[pos+1]->name, path) ||\n+\t    ce_stage(active_cache[pos+1]) != 2 ||\n+\t    strcmp(active_cache[pos+2]->name, path) ||\n+\t    ce_stage(active_cache[pos+2]) != 3)\n+\t\treturn error(\"path '%s' does not have all 3 versions\", path);\n+\n+\tfill_mm(active_cache[pos]->sha1, &ancestor);\n+\tfill_mm(active_cache[pos+1]->sha1, &ours);\n+\tfill_mm(active_cache[pos+2]->sha1, &theirs);\n+\n+\tstatus = ll_merge(&result_buf, path, &ancestor,\n+\t\t\t  &ours, \"ours\", &theirs, \"theirs\", 1);\n+\tfree(ancestor.ptr);\n+\tfree(ours.ptr);\n+\tfree(theirs.ptr);\n+\tif (status < 0 || !result_buf.ptr) {\n+\t\tfree(result_buf.ptr);\n+\t\treturn error(\"path '%s': cannot merge\", path);\n+\t}\n+\n+\t/*\n+\t * NEEDSWORK:\n+\t * There is absolutely no reason to write this as a blob object\n+\t * and create a phoney cache entry just to leak.  This hack is\n+\t * primarily to get to the write_entry() machinery that massages\n+\t * the contents to work-tree format and writes out which only\n+\t * allows it for a cache entry.  The code in write_entry() needs\n+\t * to be refactored to allow us to feed a <buffer, size, mode>\n+\t * instead of a cache entry.  Such a refactoring would help\n+\t * merge_recursive as well (it also writes the merge result to the\n+\t * object database even when it may contain conflicts).\n+\t */\n+\tif (write_sha1_file(result_buf.ptr, result_buf.size,\n+\t\t\t    blob_type, sha1))\n+\t\tdie(\"Unable to add merge result for '%s'\", path);\n+\tce = make_cache_entry(create_ce_mode(active_cache[pos+1]->ce_mode),\n+\t\t\t      sha1,\n+\t\t\t      path, 2, 0);\n+\tstatus = checkout_entry(ce, state, NULL);\n+\treturn status;\n+}\n+\n static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \t\t\t  struct checkout_opts *opts)\n {\n@@ -134,7 +221,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tstruct commit *head;\n \tint errs = 0;\n \tint stage = opts->writeout_stage;\n-\n+\tint merge = opts->merge;\n \tint newfd;\n \tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n \n@@ -166,6 +253,8 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \t\t\t\terrs |= check_stage(stage, ce, pos);\n \t\t\t} else if (opts->force) {\n \t\t\t\twarning(\"path '%s' is unmerged\", ce->name);\n+\t\t\t} else if (opts->merge) {\n+\t\t\t\terrs |= check_all_stage(ce, pos);\n \t\t\t} else {\n \t\t\t\terrs = 1;\n \t\t\t\terror(\"path '%s' is unmerged\", ce->name);\n@@ -184,13 +273,15 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,\n \tfor (pos = 0; pos < active_nr; pos++) {\n \t\tstruct cache_entry *ce = active_cache[pos];\n \t\tif (pathspec_match(pathspec, NULL, ce->name, 0)) {\n-\t\t\tif (ce_stage(ce)) {\n-\t\t\t\tif (stage)\n-\t\t\t\t\terrs |= checkout_stage(stage, ce, pos, &state);\n-\t\t\t\tpos = skip_same_name(ce, pos) - 1;\n+\t\t\tif (!ce_stage(ce)) {\n+\t\t\t\terrs |= checkout_entry(ce, &state, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\terrs |= checkout_entry(ce, &state, NULL);\n+\t\t\tif (stage)\n+\t\t\t\terrs |= checkout_stage(stage, ce, pos, &state);\n+\t\t\telse if (merge)\n+\t\t\t\terrs |= checkout_merged(pos, &state);\n+\t\t\tpos = skip_same_name(ce, pos) - 1;\n \t\t}\n \t}\n \n@@ -478,6 +569,11 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \treturn ret || opts->writeout_error;\n }\n \n+static int git_checkout_config(const char *var, const char *value, void *cb)\n+{\n+\treturn git_xmerge_config(var, value, cb);\n+}\n+\n int cmd_checkout(int argc, const char **argv, const char *prefix)\n {\n \tstruct checkout_opts opts;\n@@ -504,7 +600,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tmemset(&opts, 0, sizeof(opts));\n \tmemset(&new, 0, sizeof(new));\n \n-\tgit_config(git_default_config, NULL);\n+\tgit_config(git_checkout_config, NULL);\n \n \topts.track = BRANCH_TRACK_UNSPECIFIED;\n \n@@ -611,7 +707,7 @@ no_reference:\n \t\t\tdie(\"invalid path specification\");\n \n \t\t/* Checkout paths */\n-\t\tif (opts.new_branch || opts.merge) {\n+\t\tif (opts.new_branch) {\n \t\t\tif (argc == 1) {\n \t\t\t\tdie(\"git checkout: updating paths is incompatible with switching branches.\\nDid you intend to checkout '%s' which can not be resolved as commit?\", argv[0]);\n \t\t\t} else {\n@@ -619,6 +715,9 @@ no_reference:\n \t\t\t}\n \t\t}\n \n+\t\tif (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)\n+\t\t\tdie(\"git checkout: --ours/--theirs, --force and --merge are incompatible when\\nchecking out of the index.\");\n+\n \t\treturn checkout_paths(source_tree, pathspec, &opts);\n \t}\n \ndiff --git a/t/t7201-co.sh b/t/t7201-co.sh\nindex 85c792c..6016915 100755\n--- a/t/t7201-co.sh\n+++ b/t/t7201-co.sh\n@@ -439,4 +439,67 @@ test_expect_success 'checkout unmerged stage' '\n \ttest ztheirside = \"z$(cat file)\"\n '\n \n+test_expect_success 'checkout with --merge' '\n+\trm -f .git/index &&\n+\tO=$(echo original | git hash-object -w --stdin) &&\n+\tA=$(echo ourside | git hash-object -w --stdin) &&\n+\tB=$(echo theirside | git hash-object -w --stdin) &&\n+\t(\n+\t\techo \"100644 $A 0\tfild\" &&\n+\t\techo \"100644 $O 1\tfile\" &&\n+\t\techo \"100644 $A 2\tfile\" &&\n+\t\techo \"100644 $B 3\tfile\" &&\n+\t\techo \"100644 $A 0\tfilf\"\n+\t) | git update-index --index-info &&\n+\techo \"none of the above\" >sample &&\n+\techo ourside >expect &&\n+\tcat sample >fild &&\n+\tcat sample >file &&\n+\tcat sample >filf &&\n+\tgit checkout -m -- fild file filf &&\n+\t(\n+\t\techo \"<<<<<<< ours\"\n+\t\techo ourside\n+\t\techo \"=======\"\n+\t\techo theirside\n+\t\techo \">>>>>>> theirs\"\n+\t) >merged &&\n+\ttest_cmp expect fild &&\n+\ttest_cmp expect filf &&\n+\ttest_cmp merged file\n+'\n+\n+test_expect_success 'checkout with --merge, in diff3 -m style' '\n+\tgit config merge.conflictstyle diff3 &&\n+\trm -f .git/index &&\n+\tO=$(echo original | git hash-object -w --stdin) &&\n+\tA=$(echo ourside | git hash-object -w --stdin) &&\n+\tB=$(echo theirside | git hash-object -w --stdin) &&\n+\t(\n+\t\techo \"100644 $A 0\tfild\" &&\n+\t\techo \"100644 $O 1\tfile\" &&\n+\t\techo \"100644 $A 2\tfile\" &&\n+\t\techo \"100644 $B 3\tfile\" &&\n+\t\techo \"100644 $A 0\tfilf\"\n+\t) | git update-index --index-info &&\n+\techo \"none of the above\" >sample &&\n+\techo ourside >expect &&\n+\tcat sample >fild &&\n+\tcat sample >file &&\n+\tcat sample >filf &&\n+\tgit checkout -m -- fild file filf &&\n+\t(\n+\t\techo \"<<<<<<< ours\"\n+\t\techo ourside\n+\t\techo \"|||||||\"\n+\t\techo original\n+\t\techo \"=======\"\n+\t\techo theirside\n+\t\techo \">>>>>>> theirs\"\n+\t) >merged &&\n+\ttest_cmp expect fild &&\n+\ttest_cmp expect filf &&\n+\ttest_cmp merged file\n+'\n+\n test_done\n-- \n1.6.0.1.149.ga4c44\n"},{"id":"89193","messageId":"alpine.DEB.1.00.0808301112420.24820@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"15280","inReplyTo":"1220056963-2352-2-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 01/12] xdl_fill_merge_buffer(): separate out a too deeply nested function","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-30T09:14:15Z","receivedAt":"2008-08-30T09:14:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 29 Aug 2008, Junio C Hamano wrote:\n\n> This simply moves code around to make a separate function that prepares \n> a single conflicted hunk with markers into the buffer.\n\nApart from renaming \"i1\" to \"i\", inverting the order of the if clauses, \nand making it more obvious that the calculation of size without dest is \ncorrect, this is a straight forward refactoring.\n\nCiao,\nDscho\n"},{"id":"89197","messageId":"alpine.DEB.1.00.0808301115530.24820@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"15280","inReplyTo":"1220056963-2352-3-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 02/12] xdiff-merge: optionally show conflicts in \"diff3 -m\" style","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-30T09:29:00Z","receivedAt":"2008-08-30T09:29:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 29 Aug 2008, Junio C Hamano wrote:\n\n> diff --git a/builtin-merge-file.c b/builtin-merge-file.c\n> index 3605960..5b4f020 100644\n> --- a/builtin-merge-file.c\n> +++ b/builtin-merge-file.c\n> @@ -25,6 +27,10 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n>  \t\telse if (!strcmp(argv[1], \"-q\") ||\n>  \t\t\t\t!strcmp(argv[1], \"--quiet\"))\n>  \t\t\tfreopen(\"/dev/null\", \"w\", stderr);\n> +\t\telse if (!strcmp(argv[1], \"--diff3\")) {\n> +\t\t\tmerge_style = XDL_MERGE_DIFF3;\n> +\t\t\tmerge_level = XDL_MERGE_EAGER;\n> +\t\t}\n\nFWIW I do not follow your reasoning why --diff3 does not make sense for \nanything more eager than MERGE_EAGER.  All that ZEALOUS and ZEALOUS_ALNUM \n(the latter of which is useless at the moment, since it is not enabled for \ngit-merge) do is change the way the conflicting regions are displayed, but \nthey do not leave out conflicting regions.\n\nSo I actually suspect that ZEALOUS_ALNUM will be _especially_ useful with \n--diff3, since it is designed to skip the single curly brackets that would \ndisrupt the reading pleasure otherwise.\n\n> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\n> index 413082e..deebe02 100644\n> --- a/xdiff/xdiff.h\n> +++ b/xdiff/xdiff.h\n> @@ -50,10 +50,16 @@ extern \"C\" {\n>  #define XDL_BDOP_CPY 2\n>  #define XDL_BDOP_INSB 3\n>  \n> +/* merge simplification levels */\n>  #define XDL_MERGE_MINIMAL 0\n>  #define XDL_MERGE_EAGER 1\n>  #define XDL_MERGE_ZEALOUS 2\n>  #define XDL_MERGE_ZEALOUS_ALNUM 3\n> +#define XDL_MERGE_LEVEL_MASK 0x0f\n> +\n> +/* merge output styles */\n> +#define XDL_MERGE_DIFF3 0x8000\n> +#define XDL_MERGE_STYLE_MASK 0x8000\n\nHmm.  This is not the Linux kernel, I think we could safely pass around \ntwo integers instead of one.\n\n> @@ -91,11 +108,13 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,\n>  \treturn 0;\n>  }\n>  \n> -static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n> +static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)\n\nYou rewrapped many function headers already; I wonder why this one was \nleft out.\n\nThe rest looks pretty much obviously correct to me; I was too lazy/ran out \nof time to apply the patch and look through the resulting code, though, \nbut I guess that you searched for \"i1\" and \"chg1\" and added the code for \ni0 and chg0 where necessary.\n\nSo: ACK.\n\nCiao,\nDscho\n"},{"id":"89198","messageId":"alpine.DEB.1.00.0808301130250.24820@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"15280","inReplyTo":"1220056963-2352-4-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 03/12] xmerge.c: minimum readability fixups","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-30T09:31:17Z","receivedAt":"2008-08-30T09:31:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 29 Aug 2008, Junio C Hamano wrote:\n\n> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\n> index 29cdbea..7dcd405 100644\n> --- a/xdiff/xmerge.c\n> +++ b/xdiff/xmerge.c\n> @@ -427,7 +427,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n>  \t\t\txscr2 = xscr2->next;\n>  \t\t\tcontinue;\n>  \t\t}\n> -\t\tif (level < 1 || xscr1->i1 != xscr2->i1 ||\n> +\t\tif (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||\n\nYeah, okay, sorry.\n\n> @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n>  \t\t\tchg0 = xscr1->i1 + xscr1->chg1 - i0;\n>  \t\t\tchg1 = xscr1->i2 + xscr1->chg2 - i1;\n>  \t\t\tchg2 = xscr2->i2 + xscr2->chg2 - i2;\n> -\t\t\tif (ffo > 0)\n> -\t\t\t\tchg2 += ffo;\n> -\t\t\telse {\n> +\t\t\tif (ffo < 0) {\n>  \t\t\t\tchg0 -= ffo;\n>  \t\t\t\tchg1 -= ffo;\n> -\t\t\t}\n> +\t\t\t} else\n> +\t\t\t\tchg2 += ffo;\n\nI do not understand why the order was changed, but hey, I do not care that \ndeeply.\n\nCiao,\nDscho\n"},{"id":"89199","messageId":"alpine.DEB.1.00.0808301133550.24820@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"15280","inReplyTo":"1220056963-2352-5-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 04/12] xmerge.c: \"diff3 -m\" style clips merge reduction level to EAGER or less","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-30T09:34:43Z","receivedAt":"2008-08-30T09:34:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 29 Aug 2008, Junio C Hamano wrote:\n\n> Representing the last illustration like this is misleading to say the \n> least:\n> \n>     output:      1234A<BCD|56=XY>E789   ; broken \"diff3 -m\" style\n> \n> because the preimage was not ...4A56E... to begin with.  \"A\" and \"E\" are\n> common only between the postimages.\n\nOkay, I understand now.\n\nThanks,\nDscho\n"},{"id":"89200","messageId":"alpine.DEB.1.00.0808301141160.24820@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"15280","inReplyTo":"1220056963-2352-8-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 07/12] merge.conflictstyle: choose between \"merge\" and \"diff3 -m\" styles","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-30T09:42:14Z","receivedAt":"2008-08-30T09:42:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 29 Aug 2008, Junio C Hamano wrote:\n\n> diff --git a/builtin-merge-file.c b/builtin-merge-file.c\n> index 1e92510..f009e73 100644\n> --- a/builtin-merge-file.c\n> +++ b/builtin-merge-file.c\n> @@ -15,6 +15,15 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n>  \tint ret = 0, i = 0, to_stdout = 0;\n>  \tint merge_level = XDL_MERGE_ZEALOUS_ALNUM;\n>  \tint merge_style = 0;\n> +\tint nongit;\n> +\n> +\tprefix = setup_git_directory_gently(&nongit);\n> +\tif (!nongit) {\n> +\t\t/* Read the configuration file */\n> +\t\tgit_config(git_xmerge_config, NULL);\n> +\t\tif (git_xmerge_style > 0)\n> +\t\t\tmerge_style = git_xmerge_style;\n\nDid you not mean \">=\"?  In the future, the default merge style could very \nwell change...\n\nCiao,\nDscho\n"},{"id":"89214","messageId":"7vk5dy1pc9.fsf@gitster.siamese.dyndns.org","threadId":"15280","inReplyTo":"alpine.DEB.1.00.0808301130250.24820@pacific.mpi-cbg.de.mpi-cbg.de","subject":"Re: [PATCH 03/12] xmerge.c: minimum readability fixups","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-08-30T15:42:46Z","receivedAt":"2008-08-30T15:42:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> @@ -449,12 +449,11 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,\n>>  \t\t\tchg0 = xscr1->i1 + xscr1->chg1 - i0;\n>>  \t\t\tchg1 = xscr1->i2 + xscr1->chg2 - i1;\n>>  \t\t\tchg2 = xscr2->i2 + xscr2->chg2 - i2;\n>> -\t\t\tif (ffo > 0)\n>> -\t\t\t\tchg2 += ffo;\n>> -\t\t\telse {\n>> +\t\t\tif (ffo < 0) {\n>>  \t\t\t\tchg0 -= ffo;\n>>  \t\t\t\tchg1 -= ffo;\n>> -\t\t\t}\n>> +\t\t\t} else\n>> +\t\t\t\tchg2 += ffo;\n>\n> I do not understand why the order was changed, but hey, I do not care that \n> deeply.\n\nEverywhere else the code deals with variables var0, var1 and var2 in this\norder, and that is because \"if\" blocks are consistently about changes made\non side#1 while \"else\" blocks are about changes made on side#2.  This\nstatement alone was inconsistent, and now it all reads 0, 1 and then 2.\n"},{"id":"89382","messageId":"20080901093924.GA3993@blimp.local","threadId":"15280","inReplyTo":"1220056963-2352-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-09-01T09:39:24Z","receivedAt":"2008-09-01T09:39:24Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200:\n> The early part of the series is what you already saw.  In addition to\n> recording a conflicted merge in the RCS merge style we have traditionally\n> used, this allows you to optionally use \"diff3 -m\" style.  The difference\n> is that the latter format shows the part from the common ancestor that\n> corresponds to the parts both sides modified to cause the conflict, in\n> addition to the changes done on each side.  This can be chosen by setting\n> a configuration variable.  Rerere mechanism is updated to understand this\n> new format as well, and conflicts from either formats interoperate well,\n> because rerere mechanism only records and uses the changes made on each\n> side, not what was in the common ancestor.\n> \n> The last four patches are to \"git checkout\" that checks things out of the\n> index.  ...\n\nI like that and started using the patches. Do you have any specific\narea I should pay a special attention to?\n"},{"id":"89384","messageId":"20080901094412.GB3993@blimp.local","threadId":"15280","inReplyTo":"1220056963-2352-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-09-01T09:44:12Z","receivedAt":"2008-09-01T09:44:12Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Junio C Hamano, Sat, Aug 30, 2008 02:42:31 +0200:\n> This consists of two loosely related topics on improving conflicted merge\n> resolution support.\n> \n> The early part of the series is what you already saw.  In addition to\n> recording a conflicted merge in the RCS merge style we have traditionally\n> used, this allows you to optionally use \"diff3 -m\" style.  The difference\n> is that the latter format shows the part from the common ancestor that\n> corresponds to the parts both sides modified to cause the conflict, in\n> addition to the changes done on each side.  This can be chosen by setting\n> a configuration variable.  Rerere mechanism is updated to understand this\n> new format as well, and conflicts from either formats interoperate well,\n> because rerere mechanism only records and uses the changes made on each\n> side, not what was in the common ancestor.\n\nThis reminds me: when resolving a conflict in a git repo (when trying\nsomething from next or pu), I often notice that I'd like to resolve it\nthe same way it was done on next or pu. IOW, copy the commit\nresolution from some other merge commit. Maybe can be a way to use\nrerere mechanism with that?\n"},{"id":"89387","messageId":"20080901095008.GB6555@toroid.org","threadId":"15280","inReplyTo":"20080901094412.GB3993@blimp.local","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Abhijit Menon-Sen","fromEmail":"ams@toroid.org","sentAt":"2008-09-01T09:50:09Z","receivedAt":"2008-09-01T09:50:09Z","isPatch":true,"sender":{"key":"ams@toroid.org","avatar":null},"body":"At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote:\n>\n> IOW, copy the commit resolution from some other merge commit. Maybe\n> can be a way to use rerere mechanism with that?\n\nThat's what I'm trying to implement on Dscho's suggestion. I'm still\njust trying to understand the code, so any suggestions about how to\ndo this are very welcome.\n\n-- ams\n"},{"id":"89400","messageId":"7vod38w3q6.fsf@gitster.siamese.dyndns.org","threadId":"15280","inReplyTo":"20080901094412.GB3993@blimp.local","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-01T10:38:25Z","receivedAt":"2008-09-01T10:38:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Riesen <raa.lkml@gmail.com> writes:\n\n> ... IOW, copy the commit\n> resolution from some other merge commit. Maybe can be a way to use\n> rerere mechanism with that?\n\nIf you know which merge I did you want to steal from, you can prime your\nrerere database by pretending to be me, doing the merge.  Something like:\n\n\t$ git checkout $merge^1 ;# detach to the parent of merge\n        $ git merge $merge^2 ;# pretend you were me to redo it\n        $ git diff -R $merge | git apply --index ;# and get what I did\n\t$ git rerere ;# have rerere record the resolution\n"},{"id":"89408","messageId":"20080901113416.GA8610@blimp.localhost","threadId":"15280","inReplyTo":"7vod38w3q6.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-09-01T11:34:16Z","receivedAt":"2008-09-01T11:34:16Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200:\n> Alex Riesen <raa.lkml@gmail.com> writes:\n> \n> > ... IOW, copy the commit\n> > resolution from some other merge commit. Maybe can be a way to use\n> > rerere mechanism with that?\n> \n> If you know which merge I did you want to steal from, you can prime your\n> rerere database by pretending to be me, doing the merge.  Something like:\n> \n> \t$ git checkout $merge^1 ;# detach to the parent of merge\n>         $ git merge $merge^2 ;# pretend you were me to redo it\n>         $ git diff -R $merge | git apply --index ;# and get what I did\n\nI ended up using\n\n    $ git checkout Merge^1\n    $ git merge Merge^2\n    $ git diff -R Merge | git apply\n    $ git diff -R Merge --name-only -z | git update-index -z --stdin\n    $ git rerere\n\nJust git apply --index complained about the files missing from the\nindex:\n\n    $ git tag Merge c5e2ace70271b481632aaf987361027ca4592df6\n    $ gco Merge^1\n    Previous HEAD position was c5e2ace... Merge branch 'jc/better-conflict-resolution' into next\n    HEAD is now at 2392877... Merge branch 'master' into next\n    $ git merge Merge^2\n    Auto-merging Documentation/config.txt\n    Auto-merging Documentation/git-checkout.txt\n    CONFLICT (content): Merge conflict in Documentation/git-checkout.txt\n    Auto-merging builtin-checkout.c\n    CONFLICT (content): Merge conflict in builtin-checkout.c\n    Auto-merging builtin-merge-recursive.c\n    Auto-merging t/t6023-merge-file.sh\n    Auto-merging t/t7201-co.sh\n    CONFLICT (content): Merge conflict in t/t7201-co.sh\n    Auto-merging xdiff-interface.c\n    Auto-merging xdiff-interface.h\n    Recorded preimage for 'Documentation/git-checkout.txt'\n    Recorded preimage for 'builtin-checkout.c'\n    Recorded preimage for 't/t7201-co.sh'\n    Automatic merge failed; fix conflicts and then commit the result.\n    $ git diff -R Merge |git apply --index\n    error: Documentation/git-checkout.txt: does not exist in index\n    error: builtin-checkout.c: does not exist in index\n    error: t/t7201-co.sh: does not exist in index\n\n> \t$ git rerere ;# have rerere record the resolution\n\nWell, it works, but it's a bit of work and hard to automate (needs a\nworking tree). An option to merge:\n\n    $ git merge <branch>\n    conflict ... investigate ... find a resolution in <resolution>\n    $ git reset --hard\n    $ git merge --rerere <resolution> branch\n    check... Ok.\n    $ git commit\n\nor rerere:\n\n    $ git merge <branch>\n    conflict ... investigate ... find a resolution in <resolution>\n    $ git rerere <resolution>\n    $ git reset --hard\n    $ git merge <branch>\n    check... Ok.\n    $ git commit\n\nThese could be more convenient.\n"},{"id":"89409","messageId":"200809011421.20175.trast@student.ethz.ch","threadId":"15280","inReplyTo":"20080901095008.GB6555@toroid.org","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2008-09-01T12:20:58Z","receivedAt":"2008-09-01T12:20:58Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Abhijit Menon-Sen wrote:\n> At 2008-09-01 11:44:12 +0200, raa.lkml@gmail.com wrote:\n> >\n> > IOW, copy the commit resolution from some other merge commit. Maybe\n> > can be a way to use rerere mechanism with that?\n> \n> That's what I'm trying to implement on Dscho's suggestion. I'm still\n> just trying to understand the code, so any suggestions about how to\n> do this are very welcome.\n\nRandom idea: you could use a script that just replays the merges\npresent in history and lets rerere record them.  Like so:\n\n-- 8< --\n#!/bin/sh\n\n. \"$(git --exec-path)/git-sh-setup\"\n\nrequire_work_tree\ngit update-index --refresh || die \"can't run with dirty index\"\n\ngit rev-list --parents \"$@\" |\ngrep '.* .* .*' |\nwhile read merge firstparent otherparents\ndo\n\tgit checkout $firstparent >/dev/null 2>/dev/null\n\tgit merge $otherparents >/dev/null\n\tif test -z \"$(git ls-files -u)\"; then\n\t\techo -n 'no conflicts: '\n\t\tgit --no-pager log -1 --pretty=oneline --abbrev-commit $merge\n\t\tcontinue\n\tfi\n\tgit rerere\n\tgit ls-files -t | grep ^M | cut -c 3- | xargs git checkout $merge --\n\tgit rerere\n\tgit reset --hard >/dev/null 2>/dev/null\n\techo -n 'recorded:     '\n\tgit --no-pager log -1 --pretty=oneline --abbrev-commit $merge\ndone\n-- >8 --\n\nThe intended usage is like\n\n  ./rerereimport.sh v1.6.0..origin/next\n\nto import all resolutions in the specified range.  Granted, it's\nprobably not as good and definitely not as fast as an automatic\nfeature integrated with the merge machinery.\n\n- Thomas\n\n-- \nThomas Rast\ntrast@student.ethz.ch\n\n\n"},{"id":"89428","messageId":"7v8wubwzen.fsf@gitster.siamese.dyndns.org","threadId":"15280","inReplyTo":"20080901113416.GA8610@blimp.localhost","subject":"Re: [PATCH 00/12] Towards a better merge resolution support","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-09-01T17:26:24Z","receivedAt":"2008-09-01T17:26:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Riesen <raa.lkml@gmail.com> writes:\n\n> Junio C Hamano, Mon, Sep 01, 2008 12:38:25 +0200:\n>> Alex Riesen <raa.lkml@gmail.com> writes:\n>> \n>> > ... IOW, copy the commit\n>> > resolution from some other merge commit. Maybe can be a way to use\n>> > rerere mechanism with that?\n>> \n>> If you know which merge I did you want to steal from, you can prime your\n>> rerere database by pretending to be me, doing the merge.  Something like:\n>> \n>> \t$ git checkout $merge^1 ;# detach to the parent of merge\n>>         $ git merge $merge^2 ;# pretend you were me to redo it\n>>         $ git diff -R $merge | git apply --index ;# and get what I did\n>\n> I ended up using\n\nI think the last step should be \"git read-tree --reset -u $merge\" if we\nreally want the minimum  sequence.\n"}]}