{"thread":{"id":"55931","subject":"[PATCH 0/2] RFC: implement new zdiff3 conflict style","startedAt":"2021-06-15T05:16:16Z","lastAt":"2021-12-02T13:28:53Z","messageCount":39,"participants":["Elijah Newren via GitGitGadget","Junio C Hamano","Felipe Contreras","Jeff King","Elijah Newren","Sergey Organov","Johannes Sixt","Phillip Wood","Phillip Wood via GitGitGadget","Bagas Sanjaya","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"427413","messageId":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":null,"subject":"[PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-06-15T05:16:08Z","receivedAt":"2021-06-15T05:16:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\nordinary diff3 except that it allows compaction of common lines between the\ntwo sides of history, if those common lines occur at the beginning or end of\na conflict hunk.\n\nThis is just RFC, because I need to add tests. Also, while I've remerged\nevery merge, revert, or duly marked cherry-pick from both git.git and\nlinux.git with this patch using the new zdiff3 mode, that only shows it\ndoesn't segfault. (Though I also reran 10% of the linux remerges with zdiff3\nunder valgrind without issues.) I looked through some differences between\n--remerge-diff with diff3 and --remerge-diff with zdiff3, but those are\nessentially diffs of a diff of a diff, which I found hard to read. I'd like\nto look through more examples, and use it for a while before submitting the\npatches without the RFC tag.\n\nElijah Newren (2):\n  xdiff: implement a zealous diff3, or \"zdiff3\"\n  update documentation for new zdiff3 conflictStyle\n\n Documentation/config/merge.txt         |  9 ++++-\n Documentation/git-checkout.txt         |  3 +-\n Documentation/git-merge-file.txt       |  3 ++\n Documentation/git-merge.txt            | 32 +++++++++++++---\n Documentation/git-rebase.txt           |  6 +--\n Documentation/git-restore.txt          |  3 +-\n Documentation/git-switch.txt           |  3 +-\n Documentation/technical/rerere.txt     | 10 ++---\n builtin/checkout.c                     |  2 +-\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +--\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 52 ++++++++++++++++++++++++--\n 14 files changed, 107 insertions(+), 27 deletions(-)\n\n\nbase-commit: 670b81a890388c60b7032a4f5b879f2ece8c4558\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1036%2Fnewren%2Fzdiff3-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1036/newren/zdiff3-v1\nPull-Request: https://github.com/git/git/pull/1036\n-- \ngitgitgadget\n"},{"id":"427414","messageId":"b7561a67c192d4bdede47fee5b7b1cb30c44b785.1623734171.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","subject":"[PATCH 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-06-15T05:16:09Z","receivedAt":"2021-06-15T05:16:21Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\n\"zdiff3\" is identical to ordinary diff3 except that it allows compaction\nof common lines on the two sides of history at the beginning or end of\nthe conflict hunk.  For example, the following diff3 conflict:\n\n    1\n    2\n    3\n    4\n    <<<<<<\n    A\n    B\n    C\n    D\n    E\n    ||||||\n    5\n    6\n    ======\n    A\n    X\n    C\n    Y\n    E\n    >>>>>>\n    7\n    8\n    9\n\nhas common lines 'A', 'C', and 'E' on the two sides.  With zdiff3, one\nwould instead get the following conflict:\n\n    1\n    2\n    3\n    4\n    A\n    <<<<<<\n    B\n    C\n    D\n    ||||||\n    5\n    6\n    ======\n    X\n    C\n    Y\n    >>>>>>\n    E\n    7\n    8\n    9\n\nNote that the common lines, 'A', and 'E' were moved outside the\nconflict.  Unlike with the two-way conflicts from the 'merge'\nconflictStyle, the zdiff3 conflict is NOT split into multiple conflict\nregions to allow the common 'C' lines to be shown outside a conflict,\nbecause zdiff3 shows the base version too and the base version cannot be\nreasonably split.\n\nInitial-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  2 +-\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 52 ++++++++++++++++++++++++--\n 5 files changed, 55 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 06a2f90c4875..e695867ee548 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"stdout\", &to_stdout, N_(\"send results to standard output\")),\n \t\tOPT_SET_INT(0, \"diff3\", &xmp.style, N_(\"use a diff3 based merge\"), XDL_MERGE_DIFF3),\n+\t\tOPT_SET_INT(0, \"zdiff3\", &xmp.style, N_(\"use a zealous diff3 based merge\"),\n+\t\t\t\tXDL_MERGE_ZEALOUS_DIFF3),\n \t\tOPT_SET_INT(0, \"ours\", &xmp.favor, N_(\"for conflicts, use our version\"),\n \t\t\t    XDL_MERGE_FAVOR_OURS),\n \t\tOPT_SET_INT(0, \"theirs\", &xmp.favor, N_(\"for conflicts, use their version\"),\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b50c5d0ea386..859455929814 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1566,7 +1566,7 @@ _git_checkout ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin checkout\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 609615db2cd6..9977813a9d37 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -308,6 +308,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n \t\telse if (!strcmp(value, \"merge\"))\n \t\t\tgit_xmerge_style = 0;\n \t\t/*\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 7a046051468f..8629ae287c79 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -65,6 +65,7 @@ extern \"C\" {\n \n /* merge output styles */\n #define XDL_MERGE_DIFF3 1\n+#define XDL_MERGE_ZEALOUS_DIFF3 2\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 1659edb45393..b1dc9df7ea0a 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n \t\t\t      dest ? dest + size : NULL);\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n \t\t/* Shared preimage */\n \t\tif (!dest) {\n \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n@@ -327,7 +327,7 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n  * lines. Try hard to show only these few lines as conflicting.\n  */\n static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n-\t\txpparam_t const *xpp)\n+\t\t\t\txpparam_t const *xpp, int style)\n {\n \tfor (; m; m = m->next) {\n \t\tmmfile_t t1, t2;\n@@ -368,6 +368,42 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t\tcontinue;\n \t\t}\n \t\tx = xscr;\n+\t\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n+\t\t\tint advance1 = xscr->i1, advance2 = xscr->i2;\n+\n+\t\t\t/*\n+\t\t\t * Advance m->i1 and m->i2 so that conflict for sides\n+\t\t\t * 1 and 2 start after common region.  Decrement\n+\t\t\t * m->chg[12] since there are now fewer conflict lines\n+\t\t\t * for those sides.\n+\t\t\t */\n+\t\t\tm->i1 += advance1;\n+\t\t\tm->i2 += advance2;\n+\t\t\tm->chg1 -= advance1;\n+\t\t\tm->chg2 -= advance2;\n+\n+\t\t\t/*\n+\t\t\t * Splitting conflicts due to internal common regions\n+\t\t\t * on the two sides would be inappropriate since we\n+\t\t\t * are also showing the merge base and have no\n+\t\t\t * reasonable way to split the merge base text.\n+\t\t\t */\n+\t\t\twhile (xscr->next)\n+\t\t\t\txscr = xscr->next;\n+\n+\t\t\t/*\n+\t\t\t * Lower the number of conflict lines to not include\n+\t\t\t * the final common lines, if any.  Do this by setting\n+\t\t\t * number of conflict lines to\n+\t\t\t *   (line offset for start of conflict in xscr) +\n+\t\t\t *   (number of lines in the conflict in xscr)\n+\t\t\t */\n+\t\t\tm->chg1 = (xscr->i1 - advance1) + (xscr->chg1);\n+\t\t\tm->chg2 = (xscr->i2 - advance2) + (xscr->chg2);\n+\t\t\txdl_free_env(&xe);\n+\t\t\txdl_free_script(x);\n+\t\t\tcontinue;\n+\t\t}\n \t\tm->i1 = xscr->i1 + i1;\n \t\tm->chg1 = xscr->chg1;\n \t\tm->i2 = xscr->i2 + i2;\n@@ -482,6 +518,16 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tint style = xmp->style;\n \tint favor = xmp->favor;\n \n+\t/*\n+\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n+\t * at common areas of sides 1 & 2, because the base (side 0) does\n+\t * not match and is being shown.\n+\t *\n+\t * XDL_MERGE_ZEALOUS_DIFF3 will attempt to refine conflicts\n+\t * looking for common areas of sides 1 & 2, despite the base\n+\t * not matching and being shown, but will only look for common\n+\t * areas at the beginning or ending of the conflict block.\n+\t */\n \tif (style == XDL_MERGE_DIFF3) {\n \t\t/*\n \t\t * \"diff3 -m\" output does not make sense for anything\n@@ -604,7 +650,7 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \t\tchanges = c;\n \t/* refine conflicts */\n \tif (XDL_MERGE_ZEALOUS <= level &&\n-\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n+\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 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-- \ngitgitgadget\n\n"},{"id":"427415","messageId":"50e82a7a32c1cc5c1d2282f6f5b2b32a8ce7444f.1623734171.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","subject":"[PATCH 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-06-15T05:16:10Z","receivedAt":"2021-06-15T05:16:25Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt         |  9 +++++++-\n Documentation/git-checkout.txt         |  3 +--\n Documentation/git-merge-file.txt       |  3 +++\n Documentation/git-merge.txt            | 32 ++++++++++++++++++++++----\n Documentation/git-rebase.txt           |  6 ++---\n Documentation/git-restore.txt          |  3 +--\n Documentation/git-switch.txt           |  3 +--\n Documentation/technical/rerere.txt     | 10 ++++----\n builtin/checkout.c                     |  2 +-\n contrib/completion/git-completion.bash |  4 ++--\n 10 files changed, 52 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex cb2ed589075b..7ab289f35c7f 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -4,7 +4,14 @@ merge.conflictStyle::\n \tshows a `<<<<<<<` conflict marker, changes made by one side,\n \ta `=======` marker, changes made by the other side, and then\n \ta `>>>>>>>` marker.  An alternate style, \"diff3\", adds a `|||||||`\n-\tmarker and the original text before the `=======` marker.\n+\tmarker and the original text before the `=======` marker.  The\n+\t\"merge\" style tends to produce smaller conflict regions than diff3,\n+\tboth because of the exclusion of the original text, and because\n+\twhen a subset of lines match on the two sides they are just pulled\n+\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n+\tsimilar to diff3 but removes matching lines on the two sides from\n+\tthe conflict region when those matching lines appear near the\n+\tbeginning or ending of a conflict region.\n \n merge.defaultToUpstream::\n \tIf merge is called without any commit argument, merge the upstream\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b1a6fe449973..85c3d3513f74 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -265,8 +265,7 @@ When switching branches with `--merge`, staged changes may be lost.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -p::\n --patch::\ndiff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt\nindex f85603261325..7e9093fab60d 100644\n--- a/Documentation/git-merge-file.txt\n+++ b/Documentation/git-merge-file.txt\n@@ -70,6 +70,9 @@ OPTIONS\n --diff3::\n \tShow conflicts in \"diff3\" style.\n \n+--zdiff3::\n+\tShow conflicts in \"zdiff3\" style.\n+\n --ours::\n --theirs::\n --union::\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 3819fadac1f1..259e1ac2cf0c 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -238,7 +238,8 @@ from the RCS suite to present such a conflicted hunk, like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n <<<<<<< yours:sample.txt\n Conflict resolution is hard;\n let's go shopping.\n@@ -259,16 +260,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the\n other side wants to claim it is easy.\n \n An alternative style can be used by setting the \"merge.conflictStyle\"\n-configuration variable to \"diff3\".  In \"diff3\" style, the above conflict\n-may look like this:\n+configuration variable to either \"diff3\" or \"zdiff3\".  In \"diff3\"\n+style, the above conflict may look like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n <<<<<<< yours:sample.txt\n+or cleanly resolved because both sides changed the same way.\n Conflict resolution is hard;\n let's go shopping.\n-|||||||\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n+Conflict resolution is hard.\n+=======\n+or cleanly resolved because both sides changed the same way.\n+Git makes conflict resolution easy.\n+>>>>>>> theirs:sample.txt\n+And here is another line that is cleanly resolved or unmodified.\n+------------\n+\n+while in \"zdiff3\" style, it may look like this:\n+\n+------------\n+Here are lines that are either unchanged from the common\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n+<<<<<<< yours:sample.txt\n+Conflict resolution is hard;\n+let's go shopping.\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n Conflict resolution is hard.\n =======\n Git makes conflict resolution easy.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 55af6fd24e27..a61742c8f98f 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -740,9 +740,9 @@ information about the rebased commits and their parents (and instead\n generates new fake commits based off limited information in the\n generated patches), those commits cannot be identified; instead it has\n to fall back to a commit summary.  Also, when merge.conflictStyle is\n-set to diff3, the apply backend will use \"constructed merge base\" to\n-label the content from the merge base, and thus provide no information\n-about the merge base commit whatsoever.\n+set to diff3 or zdiff3, the apply backend will use \"constructed merge\n+base\" to label the content from the merge base, and thus provide no\n+information about the merge base commit whatsoever.\n \n The merge backend works with the full commits on both sides of history\n and thus has no such limitations.\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex 55bde91ef9e5..5964810caa41 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values\n-\tare \"merge\" (default) and \"diff3\" (in addition to what is\n-\tshown by \"merge\" style, shows the original contents).\n+\tare \"merge\" (default), \"diff3\", and \"zdiff3\".\n \n --ignore-unmerged::\n \tWhen restoring files on the working tree from the index, do\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex 5c438cd50587..5c90f76fbe35 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -137,8 +137,7 @@ should result in deletion of the path).\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -q::\n --quiet::\ndiff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt\nindex af5f9fc24f93..35d454143399 100644\n--- a/Documentation/technical/rerere.txt\n+++ b/Documentation/technical/rerere.txt\n@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.\n \n Different conflict styles and branch names are normalized by stripping\n the labels from the conflict markers, and removing the common ancestor\n-version from the `diff3` conflict style. Branches that are merged\n-in different order are normalized by sorting the conflict hunks.  More\n-on each of those steps in the following sections.\n+version from the `diff3` or `zdiff3` conflict styles.  Branches that\n+are merged in different order are normalized by sorting the conflict\n+hunks.  More on each of those steps in the following sections.\n \n Once these two normalization operations are applied, a conflict ID is\n calculated based on the normalized conflict, which is later used by\n@@ -42,8 +42,8 @@ get a conflict like the following:\n     >>>>>>> AC\n \n Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB\n-and then merging branch AC2 into it), using the diff3 conflict style,\n-we get a conflict like the following:\n+and then merging branch AC2 into it), using the diff3 or zdiff3\n+conflict style, we get a conflict like the following:\n \n     <<<<<<< HEAD\n     B\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f4cd7747d35d..45606936c328 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1524,7 +1524,7 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\t\t   N_(\"conflict style (merge, diff3, or zdiff3)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 859455929814..8489ca394970 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -2445,7 +2445,7 @@ _git_switch ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin switch\n@@ -2886,7 +2886,7 @@ _git_restore ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--source=*)\n \t\t__git_complete_refs --cur=\"${cur##--source=}\"\n-- \ngitgitgadget\n"},{"id":"427417","messageId":"xmqqv96fven9.fsf@gitster.g","threadId":"55931","inReplyTo":"b7561a67c192d4bdede47fee5b7b1cb30c44b785.1623734171.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-15T06:13:46Z","receivedAt":"2021-06-15T06:13:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Elijah Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n[An example to reduce\n    1234<ABCDE|56=AXCYE>789\nto\n    1234A<BCD|56=XCY>E789\nelided]\n\n> Note that the common lines, 'A', and 'E' were moved outside the\n> conflict.  Unlike with the two-way conflicts from the 'merge'\n> conflictStyle, the zdiff3 conflict is NOT split into multiple conflict\n> regions to allow the common 'C' lines to be shown outside a conflict,\n> because zdiff3 shows the base version too and the base version cannot be\n> reasonably split.\n\nAnother thing that was brought up during the original discussion is\nthat zdiff3 uses different meaning of \"base version\" from diff3.  In\ndiff3 output, if you remove everything enclosed in <<< and |||, and\neverything enclosed in === and >>>, i.e. 123456789 in the original\nexample above, you will get \"the common ancestor\" version, which is\nwhat is shown as the \"base\".  After zdiff3 munges the lines, that is\nnot the case, 1234A56E789 never appeared in anybody's version.  It\nis a \"starting from the common ancestor version, both sides agreed\nto make the same uncontroversial changes, which is already included\nin this base version\" and zdiff3 inserts the conflict markers and\nmaterial unique to each side into it.  Being able to recover the\ncommon ancestor version is not always necessary and that is what\nmakes zdiff3 a plausible solution, but to some workflows and tools\nit may matter, so it would be helpful to mention it in the\ndocumentation.\n\nThanks.\n"},{"id":"427418","messageId":"xmqqr1h3veao.fsf@gitster.g","threadId":"55931","inReplyTo":"50e82a7a32c1cc5c1d2282f6f5b2b32a8ce7444f.1623734171.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-06-15T06:21:19Z","receivedAt":"2021-06-15T06:21:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Elijah Newren via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +\tmarker and the original text before the `=======` marker.  The\n> +\t\"merge\" style tends to produce smaller conflict regions than diff3,\n> +\tboth because of the exclusion of the original text, and because\n> +\twhen a subset of lines match on the two sides they are just pulled\n> +\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n> +\tsimilar to diff3 but removes matching lines on the two sides from\n> +\tthe conflict region when those matching lines appear near the\n> +\tbeginning or ending of a conflict region.\n\nI am not a native speaker, but contrast between \"beginning\" and\n\"ending\" felt funny (I usually see \"beginning\" vs \"end\").\n\n> diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\n> index 3819fadac1f1..259e1ac2cf0c 100644\n> --- a/Documentation/git-merge.txt\n> +++ b/Documentation/git-merge.txt\n> +while in \"zdiff3\" style, it may look like this:\n> +\n> +------------\n> +Here are lines that are either unchanged from the common\n> +ancestor, or cleanly resolved because only one side changed,\n> +or cleanly resolved because both sides changed the same way.\n> +<<<<<<< yours:sample.txt\n> +Conflict resolution is hard;\n> +let's go shopping.\n> +||||||| base:sample.txt\n> +or cleanly resolved because both sides changed identically.\n>  Conflict resolution is hard.\n>  =======\n>  Git makes conflict resolution easy.\n\nOK, the text in the updated example clearly says what it is ;-)  The\nbase is not necessarily \"unchanged from the common ancestor\", but\nnow includes \"both sides changed the same way\".\n\nNicely described.\n\nWill queue.\n\n"},{"id":"427436","messageId":"60c8758c80e13_e633208f7@natae.notmuch","threadId":"55931","inReplyTo":"b7561a67c192d4bdede47fee5b7b1cb30c44b785.1623734171.git.gitgitgadget@gmail.com","subject":"RE: [PATCH 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2021-06-15T09:40:28Z","receivedAt":"2021-06-15T09:40:34Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Elijah Newren via GitGitGadget wrote:\n> From: Elijah Newren <newren@gmail.com>\n> \n> \"zdiff3\" is identical to ordinary diff3 except that it allows compaction\n> of common lines on the two sides of history at the beginning or end of\n> the conflict hunk.\n\nThat was not the main reason behind zdiff3.\n\nThe whole point of zdiff3 was to have something closer to the \"merge\"\nstyle, even if not technically correct.\n\nYour proposal is better than diff3 in that respect, but worse than Uwe's\nzdiff3.\n\nIf you have this:\n\n  l  b  r\n  =  =  =\n  A  A  A\n\n  B     b\n  C     C\n  D     D\n  E     E\n  F     F\n  I     i\n\nmerge will output this:\n\n  A\n\n  <<<<<<< l\n  B\n  =======\n  b\n  >>>>>>> r\n  C\n  D\n  E\n  F\n  <<<<<<< l\n  I\n  =======\n  i\n  >>>>>>> r\n\nThis is simple, and useful.\n\ndiff3 will output this:\n\n  A\n  <<<<<<< l\n\n  B\n  C\n  D\n  E\n  F\n  I\n  ||||||| b\n  =======\n\n  b\n  C\n  D\n  E\n  F\n  i\n  >>>>>>> r\n\nNot very friendly.\n\nYour zdiff3:\n\n  A\n\n  <<<<<<< l\n  B\n  C\n  D\n  E\n  F\n  I\n  ||||||| b\n  =======\n  b\n  C\n  D\n  E\n  F\n  i\n  >>>>>>> r\n\nJust marginally better.\n\nUwe's zdiff3:\n\n  A\n\n  <<<<<<< l\n  B\n  ||||||| b\n  =======\n  b\n  >>>>>>> r\n  C\n  D\n  E\n  F\n  <<<<<<< l\n  I\n  ||||||| b\n  =======\n  i\n  >>>>>>> r\n\nIn my view of all the *diff3's, Uwe's version is the most useful.\n\n-- \nFelipe Contreras\n"},{"id":"427437","messageId":"YMh2M8Ek/RUVjKkL@coredump.intra.peff.net","threadId":"55931","inReplyTo":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-06-15T09:43:15Z","receivedAt":"2021-06-15T09:43:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jun 15, 2021 at 05:16:08AM +0000, Elijah Newren via GitGitGadget wrote:\n\n> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> ordinary diff3 except that it allows compaction of common lines between the\n> two sides of history, if those common lines occur at the beginning or end of\n> a conflict hunk.\n> \n> This is just RFC, because I need to add tests. Also, while I've remerged\n> every merge, revert, or duly marked cherry-pick from both git.git and\n> linux.git with this patch using the new zdiff3 mode, that only shows it\n> doesn't segfault. (Though I also reran 10% of the linux remerges with zdiff3\n> under valgrind without issues.) I looked through some differences between\n> --remerge-diff with diff3 and --remerge-diff with zdiff3, but those are\n> essentially diffs of a diff of a diff, which I found hard to read. I'd like\n> to look through more examples, and use it for a while before submitting the\n> patches without the RFC tag.\n\nI did something similar (but I wasn't smart enough to try your\nremerge-diff, and just re-ran a bunch of merges).\n\nSkimming over the results, I didn't see anything that looked incorrect.\nMany of them are pretty non-exciting, though. A common case seems to be\nones like 01a2a03c56 (Merge branch 'jc/diff-filter-negation',\n2013-09-09), where two sides both add functions in the same place, and\nthe common lines are just the closing \"}\" followed by a blank line.\n\nRemoving those shared lines actually makes things less readable, IMHO,\nbut I don't think it's the wrong thing to do. The usual \"merge\" zealous\nminimization likewise produces the same unreadability. If we want to\naddress that, I think the best way would be by teaching the minimization\nsome heuristics about which lines are trivial.\n\nHere's another interesting one. In 0c52457b7c (Merge branch\n'nd/daemon-informative-errors-typofix', 2014-01-10), the diff3 looks\nlike:\n\n  <<<<<<< ours\n                  if (starts_with(arg, \"--informative-errors\")) {\n  ||||||| base\n                  if (!prefixcmp(arg, \"--informative-errors\")) {\n  =======\n                  if (!strcmp(arg, \"--informative-errors\")) {\n  >>>>>>> theirs\n                          informative_errors = 1;\n                          continue;\n                  }\n  <<<<<<< ours\n                  if (starts_with(arg, \"--no-informative-errors\")) {\n  ||||||| base\n                  if (!prefixcmp(arg, \"--no-informative-errors\")) {\n  =======\n                  if (!strcmp(arg, \"--no-informative-errors\")) {\n  >>>>>>> theirs\n\nA little clunky, but it's easy-ish to see what's going on. With zdiff3,\nthe context between the two hunks is rolled into a single hunk:\n\n  <<<<<<< ours\n                  if (starts_with(arg, \"--informative-errors\")) {\n                          informative_errors = 1;\n                          continue;\n                  }\n                  if (starts_with(arg, \"--no-informative-errors\")) {\n  ||||||| base\n                  if (!prefixcmp(arg, \"--informative-errors\")) {\n  =======\n                  if (!strcmp(arg, \"--informative-errors\")) {\n                          informative_errors = 1;\n                          continue;\n                  }\n                  if (!strcmp(arg, \"--no-informative-errors\")) {\n  >>>>>>> theirs\n\nwhich seems worse. I haven't dug/thought carefully enough into your\nchange yet to know if this is expected, or if there's a bug.\n\n-Peff\n"},{"id":"427517","messageId":"CABPp-BHjXf88MQYX8Fd3WGw2WfbMKAdAD-MEViiB7oTtQbfTyw@mail.gmail.com","threadId":"55931","inReplyTo":"60c8758c80e13_e633208f7@natae.notmuch","subject":"Re: [PATCH 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-06-15T18:12:35Z","receivedAt":"2021-06-15T18:12:49Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jun 15, 2021 at 2:40 AM Felipe Contreras\n<felipe.contreras@gmail.com> wrote:\n>\n> Elijah Newren via GitGitGadget wrote:\n> > From: Elijah Newren <newren@gmail.com>\n> >\n> > \"zdiff3\" is identical to ordinary diff3 except that it allows compaction\n> > of common lines on the two sides of history at the beginning or end of\n> > the conflict hunk.\n>\n> That was not the main reason behind zdiff3.\n>\n> The whole point of zdiff3 was to have something closer to the \"merge\"\n> style, even if not technically correct.\n>\n> Your proposal is better than diff3 in that respect, but worse than Uwe's\n> zdiff3.\n>\n> If you have this:\n>\n>   l  b  r\n>   =  =  =\n>   A  A  A\n>\n>   B     b\n>   C     C\n>   D     D\n>   E     E\n>   F     F\n>   I     i\n>\n> merge will output this:\n>\n>   A\n>\n>   <<<<<<< l\n>   B\n>   =======\n>   b\n>   >>>>>>> r\n>   C\n>   D\n>   E\n>   F\n>   <<<<<<< l\n>   I\n>   =======\n>   i\n>   >>>>>>> r\n>\n> This is simple, and useful.\n>\n> diff3 will output this:\n>\n>   A\n>   <<<<<<< l\n>\n>   B\n>   C\n>   D\n>   E\n>   F\n>   I\n>   ||||||| b\n>   =======\n>\n>   b\n>   C\n>   D\n>   E\n>   F\n>   i\n>   >>>>>>> r\n>\n> Not very friendly.\n>\n> Your zdiff3:\n>\n>   A\n>\n>   <<<<<<< l\n>   B\n>   C\n>   D\n>   E\n>   F\n>   I\n>   ||||||| b\n>   =======\n>   b\n>   C\n>   D\n>   E\n>   F\n>   i\n>   >>>>>>> r\n>\n> Just marginally better.\n\nYour example here is one where diff3 has no original text in the\nconflicted region.  Empty text is trivially easy to split, making it a\nsomewhat uninteresting testcase for zdiff3.  The interesting question\nis what do you do when that region is non-empty?  When it's non-empty,\nit's not going to match the two sides (i.e. it won't have \"C D E F\"\nlines for your example) -- we know that because when the original also\nmatches the two sides, the xdiff code will start with multiple\nseparate conflicts instead of one big one.  So, in such a case, do you\nstill decide to split the conflict regions, and if so, how do you\nsplit the non-matching original text?\n"},{"id":"427519","messageId":"8735tj2c9n.fsf@osv.gnss.ru","threadId":"55931","inReplyTo":"CABPp-BHjXf88MQYX8Fd3WGw2WfbMKAdAD-MEViiB7oTtQbfTyw@mail.gmail.com","subject":"Re: [PATCH 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2021-06-15T18:50:12Z","receivedAt":"2021-06-15T18:50:21Z","isPatch":true,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Tue, Jun 15, 2021 at 2:40 AM Felipe Contreras\n> <felipe.contreras@gmail.com> wrote:\n>>\n>> Elijah Newren via GitGitGadget wrote:\n>> > From: Elijah Newren <newren@gmail.com>\n>> >\n>> > \"zdiff3\" is identical to ordinary diff3 except that it allows compaction\n>> > of common lines on the two sides of history at the beginning or end of\n>> > the conflict hunk.\n>>\n>> That was not the main reason behind zdiff3.\n>>\n>> The whole point of zdiff3 was to have something closer to the \"merge\"\n>> style, even if not technically correct.\n>>\n>> Your proposal is better than diff3 in that respect, but worse than Uwe's\n>> zdiff3.\n>>\n>> If you have this:\n>>\n>>   l  b  r\n>>   =  =  =\n>>   A  A  A\n>>\n>>   B     b\n>>   C     C\n>>   D     D\n>>   E     E\n>>   F     F\n>>   I     i\n>>\n>> merge will output this:\n>>\n>>   A\n>>\n>>   <<<<<<< l\n>>   B\n>>   =======\n>>   b\n>>   >>>>>>> r\n>>   C\n>>   D\n>>   E\n>>   F\n>>   <<<<<<< l\n>>   I\n>>   =======\n>>   i\n>>   >>>>>>> r\n>>\n>> This is simple, and useful.\n>>\n>> diff3 will output this:\n>>\n>>   A\n>>   <<<<<<< l\n>>\n>>   B\n>>   C\n>>   D\n>>   E\n>>   F\n>>   I\n>>   ||||||| b\n>>   =======\n>>\n>>   b\n>>   C\n>>   D\n>>   E\n>>   F\n>>   i\n>>   >>>>>>> r\n>>\n>> Not very friendly.\n\nFor me it's friendly enough. One key-press in Emacs and I get:\n\n--- upper/xx.txt\n+++ lower/xx.txt\n@@ -1,7 +1,7 @@\n \n-B\n+b\n C\n D\n E\n F\n-I\n+i\n\nIn a separate window, that is roughly what you've get in the \"merge\"\noutput in the first place, but even more compact.\n\nMy point is that once Git provides enough context, a good interactive\ntool will do the rest just fine, beneficial to the end-user.\n\n-- Sergey Organov\n"},{"id":"427525","messageId":"CABPp-BE7-E03+x38EK-=AE5mwwdST+d50hiiud2eY2Nsf3rM5g@mail.gmail.com","threadId":"55931","inReplyTo":"YMh2M8Ek/RUVjKkL@coredump.intra.peff.net","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-06-15T19:35:28Z","receivedAt":"2021-06-15T19:35:42Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jun 15, 2021 at 2:43 AM Jeff King <peff@peff.net> wrote:\n>\n> On Tue, Jun 15, 2021 at 05:16:08AM +0000, Elijah Newren via GitGitGadget wrote:\n>\n> > Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> > ordinary diff3 except that it allows compaction of common lines between the\n> > two sides of history, if those common lines occur at the beginning or end of\n> > a conflict hunk.\n> >\n> > This is just RFC, because I need to add tests. Also, while I've remerged\n> > every merge, revert, or duly marked cherry-pick from both git.git and\n> > linux.git with this patch using the new zdiff3 mode, that only shows it\n> > doesn't segfault. (Though I also reran 10% of the linux remerges with zdiff3\n> > under valgrind without issues.) I looked through some differences between\n> > --remerge-diff with diff3 and --remerge-diff with zdiff3, but those are\n> > essentially diffs of a diff of a diff, which I found hard to read. I'd like\n> > to look through more examples, and use it for a while before submitting the\n> > patches without the RFC tag.\n>\n> I did something similar (but I wasn't smart enough to try your\n> remerge-diff, and just re-ran a bunch of merges).\n\nWhat I did was great for testing if there were funny merges that might\ncause segfaults or such with zdiff3, but not so clever for viewing the\ndirect output from zdiff3.  Using remerge-diff in this way does not\nshow the [z]diff3 output directly, but a diff of that output against\nwhat was ultimately recorded in the merge, forcing me to attempt to\nrecreate the original in my head.\n\n(And, of course, I made it even worse by taking the remerge-diff\noutput with diff3, and the remerge-diff output with zdiff3, and then\ndiffing those, resulting in yet another layer of diffs that I'd have\nto undo in my head to attempt to construct the original.)\n\n> Skimming over the results, I didn't see anything that looked incorrect.\n> Many of them are pretty non-exciting, though. A common case seems to be\n> ones like 01a2a03c56 (Merge branch 'jc/diff-filter-negation',\n> 2013-09-09), where two sides both add functions in the same place, and\n> the common lines are just the closing \"}\" followed by a blank line.\n>\n> Removing those shared lines actually makes things less readable, IMHO,\n> but I don't think it's the wrong thing to do. The usual \"merge\" zealous\n> minimization likewise produces the same unreadability. If we want to\n> address that, I think the best way would be by teaching the minimization\n> some heuristics about which lines are trivial.\n>\n> Here's another interesting one. In 0c52457b7c (Merge branch\n> 'nd/daemon-informative-errors-typofix', 2014-01-10), the diff3 looks\n> like:\n>\n>   <<<<<<< ours\n>                   if (starts_with(arg, \"--informative-errors\")) {\n>   ||||||| base\n>                   if (!prefixcmp(arg, \"--informative-errors\")) {\n>   =======\n>                   if (!strcmp(arg, \"--informative-errors\")) {\n>   >>>>>>> theirs\n>                           informative_errors = 1;\n>                           continue;\n>                   }\n>   <<<<<<< ours\n>                   if (starts_with(arg, \"--no-informative-errors\")) {\n>   ||||||| base\n>                   if (!prefixcmp(arg, \"--no-informative-errors\")) {\n>   =======\n>                   if (!strcmp(arg, \"--no-informative-errors\")) {\n>   >>>>>>> theirs\n>\n> A little clunky, but it's easy-ish to see what's going on. With zdiff3,\n> the context between the two hunks is rolled into a single hunk:\n>\n>   <<<<<<< ours\n>                   if (starts_with(arg, \"--informative-errors\")) {\n>                           informative_errors = 1;\n>                           continue;\n>                   }\n>                   if (starts_with(arg, \"--no-informative-errors\")) {\n>   ||||||| base\n>                   if (!prefixcmp(arg, \"--informative-errors\")) {\n>   =======\n>                   if (!strcmp(arg, \"--informative-errors\")) {\n>                           informative_errors = 1;\n>                           continue;\n>                   }\n>                   if (!strcmp(arg, \"--no-informative-errors\")) {\n>   >>>>>>> theirs\n>\n> which seems worse. I haven't dug/thought carefully enough into your\n> change yet to know if this is expected, or if there's a bug.\n\nYeah, I don't know for sure either but that does look buggy to me.\nThanks for digging up these examples.  I'm a bit overdrawn on time for\nthis, so I'll pick it back up in a week or two and investigate this\ncase further.\n"},{"id":"427534","messageId":"55d0eb59-10a0-1dff-f195-4d660c6028e0@kdbg.org","threadId":"55931","inReplyTo":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-06-15T21:36:16Z","receivedAt":"2021-06-15T21:36:20Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.06.21 um 07:16 schrieb Elijah Newren via GitGitGadget:\n> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> ordinary diff3 except that it allows compaction of common lines between the\n> two sides of history, if those common lines occur at the beginning or end of\n> a conflict hunk.\n\nAs a data point, I tried this series (cf9d93e547 en/zdiff3) on my\ncriss-cross merge test case that started this adventure, and it produces\nthe very same output as diff3; cf.\nhttps://lore.kernel.org/git/60883e1b-787f-5ec2-a9af-f2f6757d3c43@kdbg.org/\n\n-- Hannes\n"},{"id":"427536","messageId":"CABPp-BF2KnktDTtTfp=hRS36HN-xYC8=P1eYcqaBhJvAJcTCAw@mail.gmail.com","threadId":"55931","inReplyTo":"55d0eb59-10a0-1dff-f195-4d660c6028e0@kdbg.org","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-06-15T21:45:12Z","receivedAt":"2021-06-15T21:45:26Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jun 15, 2021 at 2:36 PM Johannes Sixt <j6t@kdbg.org> wrote:\n>\n> Am 15.06.21 um 07:16 schrieb Elijah Newren via GitGitGadget:\n> > Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> > ordinary diff3 except that it allows compaction of common lines between the\n> > two sides of history, if those common lines occur at the beginning or end of\n> > a conflict hunk.\n>\n> As a data point, I tried this series (cf9d93e547 en/zdiff3) on my\n> criss-cross merge test case that started this adventure, and it produces\n> the very same output as diff3; cf.\n> https://lore.kernel.org/git/60883e1b-787f-5ec2-a9af-f2f6757d3c43@kdbg.org/\n\nThat's good to hear; your two sides had no common text at the\nbeginning or end of the conflict hunk, so I wouldn't expect zdiff3 to\nchange that particular example.\n\nThe XDL_MERGE_FAVOR_BASE idea (cf.\nhttps://lore.kernel.org/git/20210611190235.1970106-1-newren@gmail.com/),\nthough would I think simplify the diff3 conflict markers in your\nexample to\n\n<<<<<<< HEAD\n    CClustering ComputeSSLClusters(double threshPercent, const\nCDataInfo* scale) const override;\n    void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n        double& minDist, double& maxDist) const;\n    double EstimateNodeDist2() const override;\n    std::vector<double> EstimateNeighborMinDist() const override;\n||||||| merged common ancestors\n    CClustering ComputeClusters(const double* dist, double threshold,\n        const CDataInfo* scale) const override;\n    virtual void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n        double& minDist, double& maxDist);\n    virtual void ComputeUMatrix();\n    virtual void ComputeKNearest(int K, const double*,\n        Neighborhood& result) const;\n=======\n    CClustering ComputeSSLClusters(double threshPercent,\n        const CDoubleArray& compWeights, const CDataInfo* scale) const override;\n    static void ComputeDist(const CNetNodeHolder& vecs, CDoubleArray& dist,\n        double& minDist, double& maxDist);\n>>>>>>> no-compweights-in-cnet\n\nThat seems like it might be nicer, but I don't do many criss-cross\nmerges myself so it'd be nice to get opinions of others like you who\ndo.\n"},{"id":"427580","messageId":"a5267880-09be-a1ed-32bb-3b056b831fb4@kdbg.org","threadId":"55931","inReplyTo":"CABPp-BF2KnktDTtTfp=hRS36HN-xYC8=P1eYcqaBhJvAJcTCAw@mail.gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-06-16T06:16:41Z","receivedAt":"2021-06-16T06:16:44Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 15.06.21 um 23:45 schrieb Elijah Newren:\n> On Tue, Jun 15, 2021 at 2:36 PM Johannes Sixt <j6t@kdbg.org> wrote:\n>>\n>> Am 15.06.21 um 07:16 schrieb Elijah Newren via GitGitGadget:\n>>> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n>>> ordinary diff3 except that it allows compaction of common lines between the\n>>> two sides of history, if those common lines occur at the beginning or end of\n>>> a conflict hunk.\n>>\n>> As a data point, I tried this series (cf9d93e547 en/zdiff3) on my\n>> criss-cross merge test case that started this adventure, and it produces\n>> the very same output as diff3; cf.\n>> https://lore.kernel.org/git/60883e1b-787f-5ec2-a9af-f2f6757d3c43@kdbg.org/\n> \n> That's good to hear; your two sides had no common text at the\n> beginning or end of the conflict hunk, so I wouldn't expect zdiff3 to\n> change that particular example.\n> \n> The XDL_MERGE_FAVOR_BASE idea (cf.\n> https://lore.kernel.org/git/20210611190235.1970106-1-newren@gmail.com/),\n> though would I think simplify the diff3 conflict markers in your\n> example to\n> \n> <<<<<<< HEAD\n>     CClustering ComputeSSLClusters(double threshPercent, const\n> CDataInfo* scale) const override;\n>     void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n>         double& minDist, double& maxDist) const;\n>     double EstimateNodeDist2() const override;\n>     std::vector<double> EstimateNeighborMinDist() const override;\n> ||||||| merged common ancestors\n>     CClustering ComputeClusters(const double* dist, double threshold,\n>         const CDataInfo* scale) const override;\n>     virtual void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n>         double& minDist, double& maxDist);\n>     virtual void ComputeUMatrix();\n>     virtual void ComputeKNearest(int K, const double*,\n>         Neighborhood& result) const;\n> =======\n>     CClustering ComputeSSLClusters(double threshPercent,\n>         const CDoubleArray& compWeights, const CDataInfo* scale) const override;\n>     static void ComputeDist(const CNetNodeHolder& vecs, CDoubleArray& dist,\n>         double& minDist, double& maxDist);\n>>>>>>>> no-compweights-in-cnet\n> \n> That seems like it might be nicer, but I don't do many criss-cross\n> merges myself so it'd be nice to get opinions of others like you who\n> do.\n\nThat *is* nicer as it is just the regular conflict with some base\ncontext. Does that mean that the regular recursive merge is a bit sloppy\nbecause it throws away too many conflicts that occur in virtual\nancestors? Even if that is the case, I couldn't tell whether that is a\ndisadvantage or not, as I've actually never seen nested conflicts in the\npast; the diff3 test was the first time I saw one.\n\nWith the referenced patch applied (after a small tweak around needs_cr\nto make it compile), my testcase does indeed produce the above conflict\nunder zdiff3 mode. The diff3 mode, OTOH, produces an exceedingly large\nnon-nested and obviously incorrect conflict (I'm not going to post it\nhere): our and their side are large and share a lot of text, but the\nbase part is identical to the above and is far too small.\n\n-- Hannes\n"},{"id":"427586","messageId":"CABPp-BFPaHEUzJsBO1dyv+7DteZfJX=Qfi2dLaYS5J30VAVAcA@mail.gmail.com","threadId":"55931","inReplyTo":"a5267880-09be-a1ed-32bb-3b056b831fb4@kdbg.org","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-06-16T08:14:55Z","receivedAt":"2021-06-16T08:15:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Tue, Jun 15, 2021 at 11:16 PM Johannes Sixt <j6t@kdbg.org> wrote:\n>\n> Am 15.06.21 um 23:45 schrieb Elijah Newren:\n> > On Tue, Jun 15, 2021 at 2:36 PM Johannes Sixt <j6t@kdbg.org> wrote:\n> >>\n> >> Am 15.06.21 um 07:16 schrieb Elijah Newren via GitGitGadget:\n> >>> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> >>> ordinary diff3 except that it allows compaction of common lines between the\n> >>> two sides of history, if those common lines occur at the beginning or end of\n> >>> a conflict hunk.\n> >>\n> >> As a data point, I tried this series (cf9d93e547 en/zdiff3) on my\n> >> criss-cross merge test case that started this adventure, and it produces\n> >> the very same output as diff3; cf.\n> >> https://lore.kernel.org/git/60883e1b-787f-5ec2-a9af-f2f6757d3c43@kdbg.org/\n> >\n> > That's good to hear; your two sides had no common text at the\n> > beginning or end of the conflict hunk, so I wouldn't expect zdiff3 to\n> > change that particular example.\n> >\n> > The XDL_MERGE_FAVOR_BASE idea (cf.\n> > https://lore.kernel.org/git/20210611190235.1970106-1-newren@gmail.com/),\n> > though would I think simplify the diff3 conflict markers in your\n> > example to\n> >\n> > <<<<<<< HEAD\n> >     CClustering ComputeSSLClusters(double threshPercent, const\n> > CDataInfo* scale) const override;\n> >     void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n> >         double& minDist, double& maxDist) const;\n> >     double EstimateNodeDist2() const override;\n> >     std::vector<double> EstimateNeighborMinDist() const override;\n> > ||||||| merged common ancestors\n> >     CClustering ComputeClusters(const double* dist, double threshold,\n> >         const CDataInfo* scale) const override;\n> >     virtual void ComputeDist(DistFunc distFunc, CDoubleArray& dist,\n> >         double& minDist, double& maxDist);\n> >     virtual void ComputeUMatrix();\n> >     virtual void ComputeKNearest(int K, const double*,\n> >         Neighborhood& result) const;\n> > =======\n> >     CClustering ComputeSSLClusters(double threshPercent,\n> >         const CDoubleArray& compWeights, const CDataInfo* scale) const override;\n> >     static void ComputeDist(const CNetNodeHolder& vecs, CDoubleArray& dist,\n> >         double& minDist, double& maxDist);\n> >>>>>>>> no-compweights-in-cnet\n> >\n> > That seems like it might be nicer, but I don't do many criss-cross\n> > merges myself so it'd be nice to get opinions of others like you who\n> > do.\n>\n> That *is* nicer as it is just the regular conflict with some base\n> context. Does that mean that the regular recursive merge is a bit sloppy\n> because it throws away too many conflicts that occur in virtual\n> ancestors? Even if that is the case, I couldn't tell whether that is a\n> disadvantage or not, as I've actually never seen nested conflicts in the\n> past; the diff3 test was the first time I saw one.\n>\n> With the referenced patch applied (after a small tweak around needs_cr\n> to make it compile), my testcase does indeed produce the above conflict\n> under zdiff3 mode. The diff3 mode, OTOH, produces an exceedingly large\n> non-nested and obviously incorrect conflict (I'm not going to post it\n> here): our and their side are large and share a lot of text, but the\n> base part is identical to the above and is far too small.\n\nThe thing about XDL_MERGE_FAVOR_BASE in combination with a recursive\nmerge, is that if you have two merge-bases, then XDL_MERGE_FAVOR_BASE\nwill put the text from the merge base of the merge-bases in the\n\"original text\" region.  If your merge bases themselves had multiple\nmerge-bases, and those merge bases in turn had multiple merge-bases,\netc., so that you end up with a deeply nested recursive merge, then\nXDL_MERGE_FAVOR_BASE will cause you to get the \"original text\" filled\nby the text from some ancient ancestor.  The net result is that it\nmakes it look like you have done a three-way merge between the two\nsides you expect with a really ancient merge base.  So, a really small\noriginal text is probably something to be expected from the\nXDL_MERGE_FAVOR_BASE patch.\n\nThe fact that zdiff3 shrinks it down considerably reflects the fact\nthat your two sides had long sections of code that matched at the\nbeginning and end of the conflict hunk.  So it sounds to me like both\npieces were behaving implementationally as designed for your testcase.\n\nWhether using \"ancient original text\" makes sense in general rather\nthan using a \"more recent original text\" (in the form of an attempted\nmerge of the merge-bases complete with conflict markers), feels like\nit's one of those tradeoff things that I figured those who do lots of\ncriss-cross merges should weigh in on rather than me.\n"},{"id":"427591","messageId":"255df678-9a31-bba2-f023-c7d98e5ffc15@gmail.com","threadId":"55931","inReplyTo":"CABPp-BE7-E03+x38EK-=AE5mwwdST+d50hiiud2eY2Nsf3rM5g@mail.gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-06-16T08:57:49Z","receivedAt":"2021-06-16T08:57:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 15/06/2021 20:35, Elijah Newren wrote:\n> On Tue, Jun 15, 2021 at 2:43 AM Jeff King <peff@peff.net> wrote:\n>>\n>> On Tue, Jun 15, 2021 at 05:16:08AM +0000, Elijah Newren via GitGitGadget wrote:\n>>\n>>> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n>>> ordinary diff3 except that it allows compaction of common lines between the\n>>> two sides of history, if those common lines occur at the beginning or end of\n>>> a conflict hunk.\n>>>\n>>> This is just RFC, because I need to add tests. Also, while I've remerged\n>>> every merge, revert, or duly marked cherry-pick from both git.git and\n>>> linux.git with this patch using the new zdiff3 mode, that only shows it\n>>> doesn't segfault. (Though I also reran 10% of the linux remerges with zdiff3\n>>> under valgrind without issues.) I looked through some differences between\n>>> --remerge-diff with diff3 and --remerge-diff with zdiff3, but those are\n>>> essentially diffs of a diff of a diff, which I found hard to read. I'd like\n>>> to look through more examples, and use it for a while before submitting the\n>>> patches without the RFC tag.\n>>\n>> I did something similar (but I wasn't smart enough to try your\n>> remerge-diff, and just re-ran a bunch of merges).\n> \n> What I did was great for testing if there were funny merges that might\n> cause segfaults or such with zdiff3, but not so clever for viewing the\n> direct output from zdiff3.  Using remerge-diff in this way does not\n> show the [z]diff3 output directly, but a diff of that output against\n> what was ultimately recorded in the merge, forcing me to attempt to\n> recreate the original in my head.\n> \n> (And, of course, I made it even worse by taking the remerge-diff\n> output with diff3, and the remerge-diff output with zdiff3, and then\n> diffing those, resulting in yet another layer of diffs that I'd have\n> to undo in my head to attempt to construct the original.)\n> \n>> Skimming over the results, I didn't see anything that looked incorrect.\n>> Many of them are pretty non-exciting, though. A common case seems to be\n>> ones like 01a2a03c56 (Merge branch 'jc/diff-filter-negation',\n>> 2013-09-09), where two sides both add functions in the same place, and\n>> the common lines are just the closing \"}\" followed by a blank line.\n>>\n>> Removing those shared lines actually makes things less readable, IMHO,\n>> but I don't think it's the wrong thing to do. The usual \"merge\" zealous\n>> minimization likewise produces the same unreadability. If we want to\n>> address that, I think the best way would be by teaching the minimization\n>> some heuristics about which lines are trivial.\n>>\n>> Here's another interesting one. In 0c52457b7c (Merge branch\n>> 'nd/daemon-informative-errors-typofix', 2014-01-10), the diff3 looks\n>> like:\n>>\n>>    <<<<<<< ours\n>>                    if (starts_with(arg, \"--informative-errors\")) {\n>>    ||||||| base\n>>                    if (!prefixcmp(arg, \"--informative-errors\")) {\n>>    =======\n>>                    if (!strcmp(arg, \"--informative-errors\")) {\n>>    >>>>>>> theirs\n>>                            informative_errors = 1;\n>>                            continue;\n>>                    }\n>>    <<<<<<< ours\n>>                    if (starts_with(arg, \"--no-informative-errors\")) {\n>>    ||||||| base\n>>                    if (!prefixcmp(arg, \"--no-informative-errors\")) {\n>>    =======\n>>                    if (!strcmp(arg, \"--no-informative-errors\")) {\n>>    >>>>>>> theirs\n>>\n>> A little clunky, but it's easy-ish to see what's going on. With zdiff3,\n>> the context between the two hunks is rolled into a single hunk:\n>>\n>>    <<<<<<< ours\n>>                    if (starts_with(arg, \"--informative-errors\")) {\n>>                            informative_errors = 1;\n>>                            continue;\n>>                    }\n>>                    if (starts_with(arg, \"--no-informative-errors\")) {\n>>    ||||||| base\n>>                    if (!prefixcmp(arg, \"--informative-errors\")) {\n>>    =======\n>>                    if (!strcmp(arg, \"--informative-errors\")) {\n>>                            informative_errors = 1;\n>>                            continue;\n>>                    }\n>>                    if (!strcmp(arg, \"--no-informative-errors\")) {\n>>    >>>>>>> theirs\n>>\n>> which seems worse. I haven't dug/thought carefully enough into your\n>> change yet to know if this is expected, or if there's a bug.\n\nXDL_MERGE_ZEALOUS coalesces adjacent conflicts that are separated by \nfewer than four lines. Unfortunately the existing code in \nxdl_merge_two_conflicts() only coalesces 'ours' and 'theirs', not \n'base'. Applying\n\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex b1dc9df7ea..5f76957169 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -455,6 +455,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i, \nint chg)\n  static void xdl_merge_two_conflicts(xdmerge_t *m)\n  {\n         xdmerge_t *next_m = m->next;\n+       m->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n         m->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n         m->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n         m->next = next_m->next;\n\nand running\n     git checkout 0c52457b7c^ &&\n     bin-wrappers/git -c merge.conflictstyle=zdiff3 merge 0c52457b7c^2\ngives\n\n<<<<<<< HEAD\n\t\tif (starts_with(arg, \"--informative-errors\")) {\n\t\t\tinformative_errors = 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (starts_with(arg, \"--no-informative-errors\")) {\n||||||| 2f93541d88\n\t\tif (!prefixcmp(arg, \"--informative-errors\")) {\n\t\t\tinformative_errors = 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!prefixcmp(arg, \"--no-informative-errors\")) {\n=======\n\t\tif (!strcmp(arg, \"--informative-errors\")) {\n\t\t\tinformative_errors = 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!strcmp(arg, \"--no-informative-errors\")) {\n >>>>>>> 0c52457b7c^2\n\nWhich I think is correct. Whether combining single line conflicts in \nthis way is useful is a different question (and is independent of your \npatch). I can see that with larger conflicts it is worth it but here we \nend up with conflicts where 60% of the lines are from the base version. \nOne the other hand there are fewer conflicts to resolve - I'm not sure \nwhich I prefer.\n\nBest Wishes\n\nPhillip\n\n> Yeah, I don't know for sure either but that does look buggy to me.\n> Thanks for digging up these examples.  I'm a bit overdrawn on time for\n> this, so I'll pick it back up in a week or two and investigate this\n> case further.\n> \n"},{"id":"427599","messageId":"YMnS+2DFYiswc75z@coredump.intra.peff.net","threadId":"55931","inReplyTo":"255df678-9a31-bba2-f023-c7d98e5ffc15@gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-06-16T10:31:23Z","receivedAt":"2021-06-16T10:31:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 16, 2021 at 09:57:49AM +0100, Phillip Wood wrote:\n\n> > > which seems worse. I haven't dug/thought carefully enough into your\n> > > change yet to know if this is expected, or if there's a bug.\n> \n> XDL_MERGE_ZEALOUS coalesces adjacent conflicts that are separated by fewer\n> than four lines. Unfortunately the existing code in\n> xdl_merge_two_conflicts() only coalesces 'ours' and 'theirs', not 'base'.\n> Applying\n> [...]\n> gives\n> \n> <<<<<<< HEAD\n> \t\tif (starts_with(arg, \"--informative-errors\")) {\n> \t\t\tinformative_errors = 1;\n> \t\t\tcontinue;\n> \t\t}\n> \t\tif (starts_with(arg, \"--no-informative-errors\")) {\n> ||||||| 2f93541d88\n> \t\tif (!prefixcmp(arg, \"--informative-errors\")) {\n> \t\t\tinformative_errors = 1;\n> \t\t\tcontinue;\n> \t\t}\n> \t\tif (!prefixcmp(arg, \"--no-informative-errors\")) {\n> =======\n> \t\tif (!strcmp(arg, \"--informative-errors\")) {\n> \t\t\tinformative_errors = 1;\n> \t\t\tcontinue;\n> \t\t}\n> \t\tif (!strcmp(arg, \"--no-informative-errors\")) {\n> >>>>>>> 0c52457b7c^2\n> \n> Which I think is correct. Whether combining single line conflicts in this\n> way is useful is a different question (and is independent of your patch). I\n> can see that with larger conflicts it is worth it but here we end up with\n> conflicts where 60% of the lines are from the base version. One the other\n> hand there are fewer conflicts to resolve - I'm not sure which I prefer.\n\nThanks for figuring that out. I agree that the output after the patch\nyou showed is correct, in the sense that the common lines show up in the\nbase now. It does feel like it's working against the point of zdiff3,\nthough, which is to reduce the number of common lines shown in the\n\"ours\" and \"theirs\" hunks.\n\nLikewise, I think this coalescing makes things worse even for \"merge\",\nwhere you get:\n\n  <<<<<<< ours\n                  if (starts_with(arg, \"--informative-errors\")) {\n                          informative_errors = 1;\n                          continue;\n                  }\n                  if (starts_with(arg, \"--no-informative-errors\")) {\n  =======\n                  if (!strcmp(arg, \"--informative-errors\")) {\n                          informative_errors = 1;\n                          continue;\n                  }\n                  if (!strcmp(arg, \"--no-informative-errors\")) {\n  >>>>>>> theirs\n\nand have to figure out manually that those interior lines are common.\nBut I imagine there are cases where you have a large number of\nuninteresting lines (blank lines, \"}\", etc) that create a lot of noise\nin the output by breaking up the actual changed lines into tiny hunks\nthat are hard to digest on their own.\n\n-Peff\n"},{"id":"427662","messageId":"CABPp-BG-vO6Kf8=oyGYOShvtyZE6AajcRuGf3pCMKkpr9X-eYA@mail.gmail.com","threadId":"55931","inReplyTo":"255df678-9a31-bba2-f023-c7d98e5ffc15@gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-06-17T05:03:56Z","receivedAt":"2021-06-17T05:04:11Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Jun 16, 2021 at 1:57 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 15/06/2021 20:35, Elijah Newren wrote:\n> > On Tue, Jun 15, 2021 at 2:43 AM Jeff King <peff@peff.net> wrote:\n> >>\n> >> On Tue, Jun 15, 2021 at 05:16:08AM +0000, Elijah Newren via GitGitGadget wrote:\n> >>\n> >>> Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\n> >>> ordinary diff3 except that it allows compaction of common lines between the\n> >>> two sides of history, if those common lines occur at the beginning or end of\n> >>> a conflict hunk.\n> >>>\n> >>> This is just RFC, because I need to add tests. Also, while I've remerged\n> >>> every merge, revert, or duly marked cherry-pick from both git.git and\n> >>> linux.git with this patch using the new zdiff3 mode, that only shows it\n> >>> doesn't segfault. (Though I also reran 10% of the linux remerges with zdiff3\n> >>> under valgrind without issues.) I looked through some differences between\n> >>> --remerge-diff with diff3 and --remerge-diff with zdiff3, but those are\n> >>> essentially diffs of a diff of a diff, which I found hard to read. I'd like\n> >>> to look through more examples, and use it for a while before submitting the\n> >>> patches without the RFC tag.\n> >>\n> >> I did something similar (but I wasn't smart enough to try your\n> >> remerge-diff, and just re-ran a bunch of merges).\n> >\n> > What I did was great for testing if there were funny merges that might\n> > cause segfaults or such with zdiff3, but not so clever for viewing the\n> > direct output from zdiff3.  Using remerge-diff in this way does not\n> > show the [z]diff3 output directly, but a diff of that output against\n> > what was ultimately recorded in the merge, forcing me to attempt to\n> > recreate the original in my head.\n> >\n> > (And, of course, I made it even worse by taking the remerge-diff\n> > output with diff3, and the remerge-diff output with zdiff3, and then\n> > diffing those, resulting in yet another layer of diffs that I'd have\n> > to undo in my head to attempt to construct the original.)\n> >\n> >> Skimming over the results, I didn't see anything that looked incorrect.\n> >> Many of them are pretty non-exciting, though. A common case seems to be\n> >> ones like 01a2a03c56 (Merge branch 'jc/diff-filter-negation',\n> >> 2013-09-09), where two sides both add functions in the same place, and\n> >> the common lines are just the closing \"}\" followed by a blank line.\n> >>\n> >> Removing those shared lines actually makes things less readable, IMHO,\n> >> but I don't think it's the wrong thing to do. The usual \"merge\" zealous\n> >> minimization likewise produces the same unreadability. If we want to\n> >> address that, I think the best way would be by teaching the minimization\n> >> some heuristics about which lines are trivial.\n> >>\n> >> Here's another interesting one. In 0c52457b7c (Merge branch\n> >> 'nd/daemon-informative-errors-typofix', 2014-01-10), the diff3 looks\n> >> like:\n> >>\n> >>    <<<<<<< ours\n> >>                    if (starts_with(arg, \"--informative-errors\")) {\n> >>    ||||||| base\n> >>                    if (!prefixcmp(arg, \"--informative-errors\")) {\n> >>    =======\n> >>                    if (!strcmp(arg, \"--informative-errors\")) {\n> >>    >>>>>>> theirs\n> >>                            informative_errors = 1;\n> >>                            continue;\n> >>                    }\n> >>    <<<<<<< ours\n> >>                    if (starts_with(arg, \"--no-informative-errors\")) {\n> >>    ||||||| base\n> >>                    if (!prefixcmp(arg, \"--no-informative-errors\")) {\n> >>    =======\n> >>                    if (!strcmp(arg, \"--no-informative-errors\")) {\n> >>    >>>>>>> theirs\n> >>\n> >> A little clunky, but it's easy-ish to see what's going on. With zdiff3,\n> >> the context between the two hunks is rolled into a single hunk:\n> >>\n> >>    <<<<<<< ours\n> >>                    if (starts_with(arg, \"--informative-errors\")) {\n> >>                            informative_errors = 1;\n> >>                            continue;\n> >>                    }\n> >>                    if (starts_with(arg, \"--no-informative-errors\")) {\n> >>    ||||||| base\n> >>                    if (!prefixcmp(arg, \"--informative-errors\")) {\n> >>    =======\n> >>                    if (!strcmp(arg, \"--informative-errors\")) {\n> >>                            informative_errors = 1;\n> >>                            continue;\n> >>                    }\n> >>                    if (!strcmp(arg, \"--no-informative-errors\")) {\n> >>    >>>>>>> theirs\n> >>\n> >> which seems worse. I haven't dug/thought carefully enough into your\n> >> change yet to know if this is expected, or if there's a bug.\n>\n> XDL_MERGE_ZEALOUS coalesces adjacent conflicts that are separated by\n> fewer than four lines. Unfortunately the existing code in\n> xdl_merge_two_conflicts() only coalesces 'ours' and 'theirs', not\n> 'base'. Applying\n>\n> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\n> index b1dc9df7ea..5f76957169 100644\n> --- a/xdiff/xmerge.c\n> +++ b/xdiff/xmerge.c\n> @@ -455,6 +455,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i,\n> int chg)\n>   static void xdl_merge_two_conflicts(xdmerge_t *m)\n>   {\n>          xdmerge_t *next_m = m->next;\n> +       m->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n>          m->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n>          m->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n>          m->next = next_m->next;\n>\n> and running\n>      git checkout 0c52457b7c^ &&\n>      bin-wrappers/git -c merge.conflictstyle=zdiff3 merge 0c52457b7c^2\n> gives\n>\n> <<<<<<< HEAD\n>                 if (starts_with(arg, \"--informative-errors\")) {\n>                         informative_errors = 1;\n>                         continue;\n>                 }\n>                 if (starts_with(arg, \"--no-informative-errors\")) {\n> ||||||| 2f93541d88\n>                 if (!prefixcmp(arg, \"--informative-errors\")) {\n>                         informative_errors = 1;\n>                         continue;\n>                 }\n>                 if (!prefixcmp(arg, \"--no-informative-errors\")) {\n> =======\n>                 if (!strcmp(arg, \"--informative-errors\")) {\n>                         informative_errors = 1;\n>                         continue;\n>                 }\n>                 if (!strcmp(arg, \"--no-informative-errors\")) {\n>  >>>>>>> 0c52457b7c^2\n>\n> Which I think is correct. Whether combining single line conflicts in\n> this way is useful is a different question (and is independent of your\n> patch). I can see that with larger conflicts it is worth it but here we\n> end up with conflicts where 60% of the lines are from the base version.\n> One the other hand there are fewer conflicts to resolve - I'm not sure\n> which I prefer.\n\nOh, sweet, thanks for tracking this down!  I'll try to find some time\nto play with it some more.\n"},{"id":"428267","messageId":"f76c79d6-f280-3011-d88d-6de146977626@gmail.com","threadId":"55931","inReplyTo":"YMnS+2DFYiswc75z@coredump.intra.peff.net","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-06-23T09:53:25Z","receivedAt":"2021-06-23T09:53:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 16/06/2021 11:31, Jeff King wrote:\n> On Wed, Jun 16, 2021 at 09:57:49AM +0100, Phillip Wood wrote:\n> \n>>>> which seems worse. I haven't dug/thought carefully enough into your\n>>>> change yet to know if this is expected, or if there's a bug.\n>>\n>> XDL_MERGE_ZEALOUS coalesces adjacent conflicts that are separated by fewer\n>> than four lines. Unfortunately the existing code in\n>> xdl_merge_two_conflicts() only coalesces 'ours' and 'theirs', not 'base'.\n>> Applying\n>> [...]\n>> gives\n>>\n>> <<<<<<< HEAD\n>> \t\tif (starts_with(arg, \"--informative-errors\")) {\n>> \t\t\tinformative_errors = 1;\n>> \t\t\tcontinue;\n>> \t\t}\n>> \t\tif (starts_with(arg, \"--no-informative-errors\")) {\n>> ||||||| 2f93541d88\n>> \t\tif (!prefixcmp(arg, \"--informative-errors\")) {\n>> \t\t\tinformative_errors = 1;\n>> \t\t\tcontinue;\n>> \t\t}\n>> \t\tif (!prefixcmp(arg, \"--no-informative-errors\")) {\n>> =======\n>> \t\tif (!strcmp(arg, \"--informative-errors\")) {\n>> \t\t\tinformative_errors = 1;\n>> \t\t\tcontinue;\n>> \t\t}\n>> \t\tif (!strcmp(arg, \"--no-informative-errors\")) {\n>>>>>>>>> 0c52457b7c^2\n>>\n>> Which I think is correct. Whether combining single line conflicts in this\n>> way is useful is a different question (and is independent of your patch). I\n>> can see that with larger conflicts it is worth it but here we end up with\n>> conflicts where 60% of the lines are from the base version. One the other\n>> hand there are fewer conflicts to resolve - I'm not sure which I prefer.\n> \n> Thanks for figuring that out. I agree that the output after the patch\n> you showed is correct, in the sense that the common lines show up in the\n> base now. It does feel like it's working against the point of zdiff3,\n> though, which is to reduce the number of common lines shown in the\n> \"ours\" and \"theirs\" hunks.\n\nI agree - the output is longer rather than shorter. As we only want to \ntrim the common prefix and suffix from the conflicts I wonder if it \nwould be better to special case zdiff3 rather than piggy backing on the \nexisting XDL_MERGE_ZEALOUS implementation. We can trim the common lines \nby looping over the begging and end of the hunk comparing the lines \nwith xdl_recmatch() without going to the trouble of diffing them as \nXDL_MERGE_ZEALOUS does. I don't think we need to worry about coalescing \nadjacent conflicts for zdiff3. It makes sense to coalesce in the \nXDL_MERGE_ZEALOUS case as it can potentially split a  N line conflict \nhunk into N/2 single line conflict hunks but zdiff3 does not split \nconflict hunks.\n\n> Likewise, I think this coalescing makes things worse even for \"merge\",\n> where you get:\n> \n>    <<<<<<< ours\n>                    if (starts_with(arg, \"--informative-errors\")) {\n>                            informative_errors = 1;\n>                            continue;\n>                    }\n>                    if (starts_with(arg, \"--no-informative-errors\")) {\n>    =======\n>                    if (!strcmp(arg, \"--informative-errors\")) {\n>                            informative_errors = 1;\n>                            continue;\n>                    }\n>                    if (!strcmp(arg, \"--no-informative-errors\")) {\n>    >>>>>>> theirs\n> \n> and have to figure out manually that those interior lines are common.\n> But I imagine there are cases where you have a large number of\n> uninteresting lines (blank lines, \"}\", etc that create a lot of noise > in the output by breaking up the actual changed lines into tiny hunks\n> that are hard to digest on their own.\n\nYes, I think the heuristic for coalescing conflict hunks could be \nimproved. It would be fairly simple to only join two hunks if the \nconflicts are longer that the context between them and the existing \nXDL_MERGE_ZEALOUS_ALNUM logic allows conflicts with more context between \nthem to be coalesced if the context lines are uninteresting. I think \nXDL_MERGE_ZEALOUS_ALNUM is only used by `git merge-file` at the moment, \nwith everything else going through ll_merge() which uses XDL_MERGE_ZEALOUS\n\nBest Wishes\n\nPhillip\n\n> \n> -Peff\n> \n\n"},{"id":"428337","messageId":"YNO1p93gHN4RPZaV@coredump.intra.peff.net","threadId":"55931","inReplyTo":"f76c79d6-f280-3011-d88d-6de146977626@gmail.com","subject":"Re: [PATCH 0/2] RFC: implement new zdiff3 conflict style","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-06-23T22:28:55Z","receivedAt":"2021-06-23T22:28:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 23, 2021 at 10:53:25AM +0100, Phillip Wood wrote:\n\n> > Thanks for figuring that out. I agree that the output after the patch\n> > you showed is correct, in the sense that the common lines show up in the\n> > base now. It does feel like it's working against the point of zdiff3,\n> > though, which is to reduce the number of common lines shown in the\n> > \"ours\" and \"theirs\" hunks.\n> \n> I agree - the output is longer rather than shorter. As we only want to trim\n> the common prefix and suffix from the conflicts I wonder if it would be\n> better to special case zdiff3 rather than piggy backing on the existing\n> XDL_MERGE_ZEALOUS implementation. We can trim the common lines by looping\n> over the begging and end of the hunk comparing the lines with xdl_recmatch()\n> without going to the trouble of diffing them as XDL_MERGE_ZEALOUS does. I\n> don't think we need to worry about coalescing adjacent conflicts for zdiff3.\n> It makes sense to coalesce in the XDL_MERGE_ZEALOUS case as it can\n> potentially split a  N line conflict hunk into N/2 single line conflict\n> hunks but zdiff3 does not split conflict hunks.\n\nThat matches my intuition of a reasonable path forward (but I confess to\nnot being too well-versed in the details of the XDL_MERGE internals).\n\n> Yes, I think the heuristic for coalescing conflict hunks could be improved.\n> It would be fairly simple to only join two hunks if the conflicts are longer\n> that the context between them and the existing XDL_MERGE_ZEALOUS_ALNUM logic\n> allows conflicts with more context between them to be coalesced if the\n> context lines are uninteresting. I think XDL_MERGE_ZEALOUS_ALNUM is only\n> used by `git merge-file` at the moment, with everything else going through\n> ll_merge() which uses XDL_MERGE_ZEALOUS\n\nI don't recall much discussion around using ALNUM versus not, nor could\nI find much in the archive. It looks like merge-file was converted to\nshow off ALNUM when it was added in ee95ec5d58 (xdl_merge(): introduce\nXDL_MERGE_ZEALOUS_ALNUM, 2008-02-17), and it never really progressed\nfrom there.\n\nIt might be an interesting exercise to re-run a bunch of merges and see\nif ALNUM produces better (or worse) results on the whole.\n\n-Peff\n"},{"id":"435571","messageId":"pull.1036.v2.git.git.1631379829.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.git.git.1623734171.gitgitgadget@gmail.com","subject":"[PATCH v2 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-11T17:03:47Z","receivedAt":"2021-09-11T17:03:53Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\nordinary diff3 except that it allows compaction of common lines between the\ntwo sides of history, if those common lines occur at the beginning or end of\na conflict hunk.\n\nThis is still just RFC, because one of the testcases I added fails and\nreally should be fixed before this series is ready to merge. Also, while\ntesting I noted one suboptimal merge that I thought I'd be able to easily\nreproduce later, but which I wasn't able to reproduce again and for which\nI've lost the details. I'd like to keep testing and find it again. Sorry for\nthe long delay here; been concentrating mostly on merge-ort and sparse-index\nand untracked/cwd cleanup things instead.\n\nNOTE: You may want to just eject this topic from seen. It needs more fixes\nand while I do plan to eventually get back to it, I'm concentrating more on\nother topics.\n\nChanges since v1:\n\n * Included fixes from Phillip (thanks!)\n * Added some testcases\n\nElijah Newren (2):\n  xdiff: implement a zealous diff3, or \"zdiff3\"\n  update documentation for new zdiff3 conflictStyle\n\n Documentation/config/merge.txt         |  9 +++-\n Documentation/git-checkout.txt         |  3 +-\n Documentation/git-merge-file.txt       |  3 ++\n Documentation/git-merge.txt            | 32 ++++++++++--\n Documentation/git-rebase.txt           |  6 +--\n Documentation/git-restore.txt          |  3 +-\n Documentation/git-switch.txt           |  3 +-\n Documentation/technical/rerere.txt     | 10 ++--\n builtin/checkout.c                     |  2 +-\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +--\n t/t6427-diff3-conflict-markers.sh      | 56 +++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 68 +++++++++++++++++++++++---\n 15 files changed, 176 insertions(+), 30 deletions(-)\n\n\nbase-commit: 670b81a890388c60b7032a4f5b879f2ece8c4558\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1036%2Fnewren%2Fzdiff3-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1036/newren/zdiff3-v2\nPull-Request: https://github.com/git/git/pull/1036\n\nRange-diff vs v1:\n\n 1:  b7561a67c19 ! 1:  06e04c88dea xdiff: implement a zealous diff3, or \"zdiff3\"\n     @@ Commit message\n          because zdiff3 shows the base version too and the base version cannot be\n          reasonably split.\n      \n     -    Initial-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\n     +    Based-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\n     +    Helped-by: Phillip Wood <phillip.wood123@gmail.com>\n          Signed-off-by: Elijah Newren <newren@gmail.com>\n      \n       ## builtin/merge-file.c ##\n     @@ contrib/completion/git-completion.bash: _git_checkout ()\n       \t\t;;\n       \t--*)\n       \t\t__gitcomp_builtin checkout\n     +@@ contrib/completion/git-completion.bash: _git_switch ()\n     + \n     + \tcase \"$cur\" in\n     + \t--conflict=*)\n     +-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n     ++\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n     + \t\t;;\n     + \t--*)\n     + \t\t__gitcomp_builtin switch\n     +@@ contrib/completion/git-completion.bash: _git_restore ()\n     + \n     + \tcase \"$cur\" in\n     + \t--conflict=*)\n     +-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n     ++\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n     + \t\t;;\n     + \t--source=*)\n     + \t\t__git_complete_refs --cur=\"${cur##--source=}\"\n     +\n     + ## t/t6427-diff3-conflict-markers.sh ##\n     +@@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes fake ancestor base' '\n     + \t)\n     + '\n     + \n     ++test_setup_zdiff3 () {\n     ++\ttest_create_repo zdiff3 &&\n     ++\t(\n     ++\t\tcd zdiff3 &&\n     ++\n     ++\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n     ++\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n     ++\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n     ++\n     ++\t\tgit add basic middle-common &&\n     ++\t\tgit commit -m base &&\n     ++\n     ++\t\tgit branch left &&\n     ++\t\tgit branch right &&\n     ++\n     ++\t\tgit checkout left &&\n     ++\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n     ++\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n     ++\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n     ++\t\tgit add -u &&\n     ++\t\tgit commit -m letters &&\n     ++\n     ++\t\tgit checkout right &&\n     ++\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n     ++\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n     ++\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n     ++\t\tgit add -u &&\n     ++\t\tgit commit -m permuted\n     ++\t)\n     ++}\n     ++\n     ++test_expect_failure 'check zdiff3 markers' '\n     ++\ttest_setup_zdiff3 &&\n     ++\t(\n     ++\t\tcd zdiff3 &&\n     ++\n     ++\t\tgit checkout left^0 &&\n     ++\n     ++\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n     ++\n     ++\t\ttest_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n     ++\t\ttest_cmp expect basic &&\n     ++\n     ++\t\ttest_write_lines 1 2 3 \"<<<<<<< HEAD\" CC \"||||||| $(git rev-parse --short HEAD^1)\" AA ======= EE \">>>>>>> right^0\" 4 5 \"<<<<<<< HEAD\" DD \"||||||| $(git rev-parse --short HEAD^1)\" BB ======= FF \">>>>>>> right^0\" 6 7 8 >expect &&\n     ++\t\ttest_cmp expect middle-common &&\n     ++\n     ++\t\t# Not passing this one yet.  For some reason, after extracting\n     ++\t\t# the common lines \"A B C\" and \"G H I J\", the remaining part\n     ++\t\t# is comparing \"5 6\" in the base to \"5 6\" on the left and\n     ++\t\t# \"D E F\" on the right.  And zdiff3 currently picks the side\n     ++\t\t# that matches the base as the merge result.  Weird.\n     ++\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >expect &&\n     ++\t\ttest_cmp expect interesting\n     ++\t)\n     ++'\n     ++\n     + test_done\n      \n       ## xdiff-interface.c ##\n      @@ xdiff-interface.c: int git_xmerge_config(const char *var, const char *value, void *cb)\n     @@ xdiff/xmerge.c: static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xd\n       \t\tm->i1 = xscr->i1 + i1;\n       \t\tm->chg1 = xscr->chg1;\n       \t\tm->i2 = xscr->i2 + i2;\n     +@@ xdiff/xmerge.c: static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n     + static void xdl_merge_two_conflicts(xdmerge_t *m)\n     + {\n     + \txdmerge_t *next_m = m->next;\n     ++\tm->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n     + \tm->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n     + \tm->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n     + \tm->next = next_m->next;\n     +@@ xdiff/xmerge.c: static void xdl_merge_two_conflicts(xdmerge_t *m)\n     +  * it appears simpler -- because it takes up less (or as many) lines --\n     +  * if the lines are moved into the conflicts.\n     +  */\n     +-static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n     ++static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int style,\n     + \t\t\t\t      int simplify_if_no_alnum)\n     + {\n     + \tint result = 0;\n     + \n     +-\tif (!m)\n     ++\tif (!m || style == XDL_MERGE_ZEALOUS_DIFF3)\n     + \t\treturn result;\n     + \tfor (;;) {\n     + \t\txdmerge_t *next_m = m->next;\n      @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n       \tint style = xmp->style;\n       \tint favor = xmp->favor;\n     @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n      +\t/*\n      +\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n      +\t * at common areas of sides 1 & 2, because the base (side 0) does\n     -+\t * not match and is being shown.\n     ++\t * not match and is being shown.  Similarly, simplification of\n     ++\t * non-conflicts is also skipped due to the skipping of conflict\n     ++\t * refinement.\n     ++\t *\n     ++\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n     ++\t * refine conflicts looking for common areas of sides 1 & 2.\n     ++\t * However, since the base is being shown and does not match,\n     ++\t * it will only look for common areas at the beginning or end\n     ++\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n     ++\t * conflict refinement is much more limited in this fashion, the\n     ++\t * conflict simplification will be skipped.\n      +\t *\n     -+\t * XDL_MERGE_ZEALOUS_DIFF3 will attempt to refine conflicts\n     -+\t * looking for common areas of sides 1 & 2, despite the base\n     -+\t * not matching and being shown, but will only look for common\n     -+\t * areas at the beginning or ending of the conflict block.\n     ++\t * See xdl_refine_conflicts() and xdl_simplify_non_conflicts()\n     ++\t * for more details, particularly looking for\n     ++\t * XDL_MERGE_ZEALOUS_DIFF3.\n      +\t */\n       \tif (style == XDL_MERGE_DIFF3) {\n       \t\t/*\n     @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n       \t/* refine conflicts */\n       \tif (XDL_MERGE_ZEALOUS <= level &&\n      -\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n     +-\t     xdl_simplify_non_conflicts(xe1, changes,\n      +\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 0 ||\n     - \t     xdl_simplify_non_conflicts(xe1, changes,\n     ++\t     xdl_simplify_non_conflicts(xe1, changes, style,\n       \t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n       \t\txdl_cleanup_merge(changes);\n     + \t\treturn -1;\n 2:  50e82a7a32c ! 2:  9ce7246c0e9 update documentation for new zdiff3 conflictStyle\n     @@ Documentation/config/merge.txt: merge.conflictStyle::\n      +\twhen a subset of lines match on the two sides they are just pulled\n      +\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n      +\tsimilar to diff3 but removes matching lines on the two sides from\n     -+\tthe conflict region when those matching lines appear near the\n     -+\tbeginning or ending of a conflict region.\n     ++\tthe conflict region when those matching lines appear near either\n     ++\tthe beginning or end of a conflict region.\n       \n       merge.defaultToUpstream::\n       \tIf merge is called without any commit argument, merge the upstream\n     @@ builtin/checkout.c: static struct option *add_common_options(struct checkout_opt\n       \t\tOPT_END()\n       \t};\n       \tstruct option *newopts = parse_options_concat(prevopts, options);\n     -\n     - ## contrib/completion/git-completion.bash ##\n     -@@ contrib/completion/git-completion.bash: _git_switch ()\n     - \n     - \tcase \"$cur\" in\n     - \t--conflict=*)\n     --\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n     -+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n     - \t\t;;\n     - \t--*)\n     - \t\t__gitcomp_builtin switch\n     -@@ contrib/completion/git-completion.bash: _git_restore ()\n     - \n     - \tcase \"$cur\" in\n     - \t--conflict=*)\n     --\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n     -+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n     - \t\t;;\n     - \t--source=*)\n     - \t\t__git_complete_refs --cur=\"${cur##--source=}\"\n\n-- \ngitgitgadget\n"},{"id":"435572","messageId":"06e04c88dea3e15a90f0a11795b7a8eea3533bc8.1631379829.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v2.git.git.1631379829.gitgitgadget@gmail.com","subject":"[PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-11T17:03:48Z","receivedAt":"2021-09-11T17:03:56Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\n\"zdiff3\" is identical to ordinary diff3 except that it allows compaction\nof common lines on the two sides of history at the beginning or end of\nthe conflict hunk.  For example, the following diff3 conflict:\n\n    1\n    2\n    3\n    4\n    <<<<<<\n    A\n    B\n    C\n    D\n    E\n    ||||||\n    5\n    6\n    ======\n    A\n    X\n    C\n    Y\n    E\n    >>>>>>\n    7\n    8\n    9\n\nhas common lines 'A', 'C', and 'E' on the two sides.  With zdiff3, one\nwould instead get the following conflict:\n\n    1\n    2\n    3\n    4\n    A\n    <<<<<<\n    B\n    C\n    D\n    ||||||\n    5\n    6\n    ======\n    X\n    C\n    Y\n    >>>>>>\n    E\n    7\n    8\n    9\n\nNote that the common lines, 'A', and 'E' were moved outside the\nconflict.  Unlike with the two-way conflicts from the 'merge'\nconflictStyle, the zdiff3 conflict is NOT split into multiple conflict\nregions to allow the common 'C' lines to be shown outside a conflict,\nbecause zdiff3 shows the base version too and the base version cannot be\nreasonably split.\n\nBased-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\nHelped-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +--\n t/t6427-diff3-conflict-markers.sh      | 56 +++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 68 +++++++++++++++++++++++---\n 6 files changed, 126 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 06a2f90c487..e695867ee54 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"stdout\", &to_stdout, N_(\"send results to standard output\")),\n \t\tOPT_SET_INT(0, \"diff3\", &xmp.style, N_(\"use a diff3 based merge\"), XDL_MERGE_DIFF3),\n+\t\tOPT_SET_INT(0, \"zdiff3\", &xmp.style, N_(\"use a zealous diff3 based merge\"),\n+\t\t\t\tXDL_MERGE_ZEALOUS_DIFF3),\n \t\tOPT_SET_INT(0, \"ours\", &xmp.favor, N_(\"for conflicts, use our version\"),\n \t\t\t    XDL_MERGE_FAVOR_OURS),\n \t\tOPT_SET_INT(0, \"theirs\", &xmp.favor, N_(\"for conflicts, use their version\"),\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex b50c5d0ea38..8489ca39497 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1566,7 +1566,7 @@ _git_checkout ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin checkout\n@@ -2445,7 +2445,7 @@ _git_switch ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin switch\n@@ -2886,7 +2886,7 @@ _git_restore ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--source=*)\n \t\t__git_complete_refs --cur=\"${cur##--source=}\"\ndiff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\nindex 25c4b720e72..de9c6190b9c 100755\n--- a/t/t6427-diff3-conflict-markers.sh\n+++ b/t/t6427-diff3-conflict-markers.sh\n@@ -211,4 +211,60 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n \t)\n '\n \n+test_setup_zdiff3 () {\n+\ttest_create_repo zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n+\n+\t\tgit add basic middle-common &&\n+\t\tgit commit -m base &&\n+\n+\t\tgit branch left &&\n+\t\tgit branch right &&\n+\n+\t\tgit checkout left &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n+\t\tgit add -u &&\n+\t\tgit commit -m letters &&\n+\n+\t\tgit checkout right &&\n+\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n+\t\tgit add -u &&\n+\t\tgit commit -m permuted\n+\t)\n+}\n+\n+test_expect_failure 'check zdiff3 markers' '\n+\ttest_setup_zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\tgit checkout left^0 &&\n+\n+\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n+\n+\t\ttest_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n+\t\ttest_cmp expect basic &&\n+\n+\t\ttest_write_lines 1 2 3 \"<<<<<<< HEAD\" CC \"||||||| $(git rev-parse --short HEAD^1)\" AA ======= EE \">>>>>>> right^0\" 4 5 \"<<<<<<< HEAD\" DD \"||||||| $(git rev-parse --short HEAD^1)\" BB ======= FF \">>>>>>> right^0\" 6 7 8 >expect &&\n+\t\ttest_cmp expect middle-common &&\n+\n+\t\t# Not passing this one yet.  For some reason, after extracting\n+\t\t# the common lines \"A B C\" and \"G H I J\", the remaining part\n+\t\t# is comparing \"5 6\" in the base to \"5 6\" on the left and\n+\t\t# \"D E F\" on the right.  And zdiff3 currently picks the side\n+\t\t# that matches the base as the merge result.  Weird.\n+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >expect &&\n+\t\ttest_cmp expect interesting\n+\t)\n+'\n+\n test_done\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 609615db2cd..9977813a9d3 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -308,6 +308,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n \t\telse if (!strcmp(value, \"merge\"))\n \t\t\tgit_xmerge_style = 0;\n \t\t/*\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex 7a046051468..8629ae287c7 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -65,6 +65,7 @@ extern \"C\" {\n \n /* merge output styles */\n #define XDL_MERGE_DIFF3 1\n+#define XDL_MERGE_ZEALOUS_DIFF3 2\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 1659edb4539..df0c6041778 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n \t\t\t      dest ? dest + size : NULL);\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n \t\t/* Shared preimage */\n \t\tif (!dest) {\n \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n@@ -327,7 +327,7 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n  * lines. Try hard to show only these few lines as conflicting.\n  */\n static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n-\t\txpparam_t const *xpp)\n+\t\t\t\txpparam_t const *xpp, int style)\n {\n \tfor (; m; m = m->next) {\n \t\tmmfile_t t1, t2;\n@@ -368,6 +368,42 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t\tcontinue;\n \t\t}\n \t\tx = xscr;\n+\t\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n+\t\t\tint advance1 = xscr->i1, advance2 = xscr->i2;\n+\n+\t\t\t/*\n+\t\t\t * Advance m->i1 and m->i2 so that conflict for sides\n+\t\t\t * 1 and 2 start after common region.  Decrement\n+\t\t\t * m->chg[12] since there are now fewer conflict lines\n+\t\t\t * for those sides.\n+\t\t\t */\n+\t\t\tm->i1 += advance1;\n+\t\t\tm->i2 += advance2;\n+\t\t\tm->chg1 -= advance1;\n+\t\t\tm->chg2 -= advance2;\n+\n+\t\t\t/*\n+\t\t\t * Splitting conflicts due to internal common regions\n+\t\t\t * on the two sides would be inappropriate since we\n+\t\t\t * are also showing the merge base and have no\n+\t\t\t * reasonable way to split the merge base text.\n+\t\t\t */\n+\t\t\twhile (xscr->next)\n+\t\t\t\txscr = xscr->next;\n+\n+\t\t\t/*\n+\t\t\t * Lower the number of conflict lines to not include\n+\t\t\t * the final common lines, if any.  Do this by setting\n+\t\t\t * number of conflict lines to\n+\t\t\t *   (line offset for start of conflict in xscr) +\n+\t\t\t *   (number of lines in the conflict in xscr)\n+\t\t\t */\n+\t\t\tm->chg1 = (xscr->i1 - advance1) + (xscr->chg1);\n+\t\t\tm->chg2 = (xscr->i2 - advance2) + (xscr->chg2);\n+\t\t\txdl_free_env(&xe);\n+\t\t\txdl_free_script(x);\n+\t\t\tcontinue;\n+\t\t}\n \t\tm->i1 = xscr->i1 + i1;\n \t\tm->chg1 = xscr->chg1;\n \t\tm->i2 = xscr->i2 + i2;\n@@ -419,6 +455,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n static void xdl_merge_two_conflicts(xdmerge_t *m)\n {\n \txdmerge_t *next_m = m->next;\n+\tm->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n \tm->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n \tm->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n \tm->next = next_m->next;\n@@ -430,12 +467,12 @@ static void xdl_merge_two_conflicts(xdmerge_t *m)\n  * it appears simpler -- because it takes up less (or as many) lines --\n  * if the lines are moved into the conflicts.\n  */\n-static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int style,\n \t\t\t\t      int simplify_if_no_alnum)\n {\n \tint result = 0;\n \n-\tif (!m)\n+\tif (!m || style == XDL_MERGE_ZEALOUS_DIFF3)\n \t\treturn result;\n \tfor (;;) {\n \t\txdmerge_t *next_m = m->next;\n@@ -482,6 +519,25 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tint style = xmp->style;\n \tint favor = xmp->favor;\n \n+\t/*\n+\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n+\t * at common areas of sides 1 & 2, because the base (side 0) does\n+\t * not match and is being shown.  Similarly, simplification of\n+\t * non-conflicts is also skipped due to the skipping of conflict\n+\t * refinement.\n+\t *\n+\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n+\t * refine conflicts looking for common areas of sides 1 & 2.\n+\t * However, since the base is being shown and does not match,\n+\t * it will only look for common areas at the beginning or end\n+\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n+\t * conflict refinement is much more limited in this fashion, the\n+\t * conflict simplification will be skipped.\n+\t *\n+\t * See xdl_refine_conflicts() and xdl_simplify_non_conflicts()\n+\t * for more details, particularly looking for\n+\t * XDL_MERGE_ZEALOUS_DIFF3.\n+\t */\n \tif (style == XDL_MERGE_DIFF3) {\n \t\t/*\n \t\t * \"diff3 -m\" output does not make sense for anything\n@@ -604,8 +660,8 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \t\tchanges = c;\n \t/* refine conflicts */\n \tif (XDL_MERGE_ZEALOUS <= level &&\n-\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n-\t     xdl_simplify_non_conflicts(xe1, changes,\n+\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 0 ||\n+\t     xdl_simplify_non_conflicts(xe1, changes, style,\n \t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n \t\txdl_cleanup_merge(changes);\n \t\treturn -1;\n-- \ngitgitgadget\n\n"},{"id":"435573","messageId":"9ce7246c0e94ab8e73a4e87c11bdc2968d1e56b2.1631379829.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v2.git.git.1631379829.gitgitgadget@gmail.com","subject":"[PATCH v2 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-11T17:03:49Z","receivedAt":"2021-09-11T17:04:00Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt     |  9 ++++++++-\n Documentation/git-checkout.txt     |  3 +--\n Documentation/git-merge-file.txt   |  3 +++\n Documentation/git-merge.txt        | 32 +++++++++++++++++++++++++-----\n Documentation/git-rebase.txt       |  6 +++---\n Documentation/git-restore.txt      |  3 +--\n Documentation/git-switch.txt       |  3 +--\n Documentation/technical/rerere.txt | 10 +++++-----\n builtin/checkout.c                 |  2 +-\n 9 files changed, 50 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex cb2ed589075..52d79117ef8 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -4,7 +4,14 @@ merge.conflictStyle::\n \tshows a `<<<<<<<` conflict marker, changes made by one side,\n \ta `=======` marker, changes made by the other side, and then\n \ta `>>>>>>>` marker.  An alternate style, \"diff3\", adds a `|||||||`\n-\tmarker and the original text before the `=======` marker.\n+\tmarker and the original text before the `=======` marker.  The\n+\t\"merge\" style tends to produce smaller conflict regions than diff3,\n+\tboth because of the exclusion of the original text, and because\n+\twhen a subset of lines match on the two sides they are just pulled\n+\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n+\tsimilar to diff3 but removes matching lines on the two sides from\n+\tthe conflict region when those matching lines appear near either\n+\tthe beginning or end of a conflict region.\n \n merge.defaultToUpstream::\n \tIf merge is called without any commit argument, merge the upstream\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b1a6fe44997..85c3d3513f7 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -265,8 +265,7 @@ When switching branches with `--merge`, staged changes may be lost.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -p::\n --patch::\ndiff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt\nindex f8560326132..7e9093fab60 100644\n--- a/Documentation/git-merge-file.txt\n+++ b/Documentation/git-merge-file.txt\n@@ -70,6 +70,9 @@ OPTIONS\n --diff3::\n \tShow conflicts in \"diff3\" style.\n \n+--zdiff3::\n+\tShow conflicts in \"zdiff3\" style.\n+\n --ours::\n --theirs::\n --union::\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 3819fadac1f..259e1ac2cf0 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -238,7 +238,8 @@ from the RCS suite to present such a conflicted hunk, like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n <<<<<<< yours:sample.txt\n Conflict resolution is hard;\n let's go shopping.\n@@ -259,16 +260,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the\n other side wants to claim it is easy.\n \n An alternative style can be used by setting the \"merge.conflictStyle\"\n-configuration variable to \"diff3\".  In \"diff3\" style, the above conflict\n-may look like this:\n+configuration variable to either \"diff3\" or \"zdiff3\".  In \"diff3\"\n+style, the above conflict may look like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n <<<<<<< yours:sample.txt\n+or cleanly resolved because both sides changed the same way.\n Conflict resolution is hard;\n let's go shopping.\n-|||||||\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n+Conflict resolution is hard.\n+=======\n+or cleanly resolved because both sides changed the same way.\n+Git makes conflict resolution easy.\n+>>>>>>> theirs:sample.txt\n+And here is another line that is cleanly resolved or unmodified.\n+------------\n+\n+while in \"zdiff3\" style, it may look like this:\n+\n+------------\n+Here are lines that are either unchanged from the common\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n+<<<<<<< yours:sample.txt\n+Conflict resolution is hard;\n+let's go shopping.\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n Conflict resolution is hard.\n =======\n Git makes conflict resolution easy.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 55af6fd24e2..a61742c8f98 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -740,9 +740,9 @@ information about the rebased commits and their parents (and instead\n generates new fake commits based off limited information in the\n generated patches), those commits cannot be identified; instead it has\n to fall back to a commit summary.  Also, when merge.conflictStyle is\n-set to diff3, the apply backend will use \"constructed merge base\" to\n-label the content from the merge base, and thus provide no information\n-about the merge base commit whatsoever.\n+set to diff3 or zdiff3, the apply backend will use \"constructed merge\n+base\" to label the content from the merge base, and thus provide no\n+information about the merge base commit whatsoever.\n \n The merge backend works with the full commits on both sides of history\n and thus has no such limitations.\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex 55bde91ef9e..5964810caa4 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values\n-\tare \"merge\" (default) and \"diff3\" (in addition to what is\n-\tshown by \"merge\" style, shows the original contents).\n+\tare \"merge\" (default), \"diff3\", and \"zdiff3\".\n \n --ignore-unmerged::\n \tWhen restoring files on the working tree from the index, do\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex 5c438cd5058..5c90f76fbe3 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -137,8 +137,7 @@ should result in deletion of the path).\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -q::\n --quiet::\ndiff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt\nindex af5f9fc24f9..35d45414339 100644\n--- a/Documentation/technical/rerere.txt\n+++ b/Documentation/technical/rerere.txt\n@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.\n \n Different conflict styles and branch names are normalized by stripping\n the labels from the conflict markers, and removing the common ancestor\n-version from the `diff3` conflict style. Branches that are merged\n-in different order are normalized by sorting the conflict hunks.  More\n-on each of those steps in the following sections.\n+version from the `diff3` or `zdiff3` conflict styles.  Branches that\n+are merged in different order are normalized by sorting the conflict\n+hunks.  More on each of those steps in the following sections.\n \n Once these two normalization operations are applied, a conflict ID is\n calculated based on the normalized conflict, which is later used by\n@@ -42,8 +42,8 @@ get a conflict like the following:\n     >>>>>>> AC\n \n Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB\n-and then merging branch AC2 into it), using the diff3 conflict style,\n-we get a conflict like the following:\n+and then merging branch AC2 into it), using the diff3 or zdiff3\n+conflict style, we get a conflict like the following:\n \n     <<<<<<< HEAD\n     B\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex f4cd7747d35..45606936c32 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1524,7 +1524,7 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\t\t   N_(\"conflict style (merge, diff3, or zdiff3)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n-- \ngitgitgadget\n"},{"id":"435975","messageId":"b6818661-ac6e-fbde-2cab-429c5550a0da@gmail.com","threadId":"55931","inReplyTo":"06e04c88dea3e15a90f0a11795b7a8eea3533bc8.1631379829.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-09-15T10:25:12Z","receivedAt":"2021-09-15T10:25:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nOn 11/09/2021 18:03, Elijah Newren via GitGitGadget wrote:\n> From: Elijah Newren <newren@gmail.com>\n> [...] \n> diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\n> index 25c4b720e72..de9c6190b9c 100755\n> --- a/t/t6427-diff3-conflict-markers.sh\n> +++ b/t/t6427-diff3-conflict-markers.sh\n> @@ -211,4 +211,60 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n>   \t)\n>   '\n>   \n> +test_setup_zdiff3 () {\n> +\ttest_create_repo zdiff3 &&\n> +\t(\n> +\t\tcd zdiff3 &&\n> +\n> +\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n> +\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n> +\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n> +\n> +\t\tgit add basic middle-common &&\n\ninteresting does not get committed\n\n> +\t\tgit commit -m base &&\n\nadding \"base=$(git rev-parse --short HEAD^1)\" here ...\n\n> +\n> +\t\tgit branch left &&\n> +\t\tgit branch right &&\n> +\n> +\t\tgit checkout left &&\n> +\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n> +\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n> +\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n> +\t\tgit add -u &&\n> +\t\tgit commit -m letters &&\n> +\n> +\t\tgit checkout right &&\n> +\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n> +\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n> +\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n> +\t\tgit add -u &&\n> +\t\tgit commit -m permuted\n> +\t)\n> +}\n> +\n> +test_expect_failure 'check zdiff3 markers' '\n> +\ttest_setup_zdiff3 &&\n> +\t(\n> +\t\tcd zdiff3 &&\n> +\n> +\t\tgit checkout left^0 &&\n> +\n> +\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n> +\n> +\t\ttest_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n\n... and then using $base rather than $(git rev-parse ...) would shorten \nthese lines. It might be clearer if they were split up something like \nthis as well\n\t\n\ttest_write_lines \\\n\t\t1 2 3 4 A \\\n\t\t\"<<<<<<< HEAD\" B C D \\\n\t\t\"||||||| $base\" 5 6 ======= \\\n\t\tX C Y \">>>>>>> right^0\"\\\n\t\t E 7 8 9 >expect &&\n\n> +\t\ttest_cmp expect basic &&\n> +\n> +\t\ttest_write_lines 1 2 3 \"<<<<<<< HEAD\" CC \"||||||| $(git rev-parse --short HEAD^1)\" AA ======= EE \">>>>>>> right^0\" 4 5 \"<<<<<<< HEAD\" DD \"||||||| $(git rev-parse --short HEAD^1)\" BB ======= FF \">>>>>>> right^0\" 6 7 8 >expect &&\n> +\t\ttest_cmp expect middle-common &&\n> +\n> +\t\t# Not passing this one yet.  For some reason, after extracting\n> +\t\t# the common lines \"A B C\" and \"G H I J\", the remaining part\n> +\t\t# is comparing \"5 6\" in the base to \"5 6\" on the left and\n> +\t\t# \"D E F\" on the right.  And zdiff3 currently picks the side\n> +\t\t# that matches the base as the merge result.  Weird.\n> +\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >expect &&\n\nI might be about to make a fool of myself but I don't think this is \nright for expect. 5 and 6 are deleted on the left so the two sides \nshould conflict. Manually comparing the result of merging with diff3 and \nzdiff3 the zdiff3 result looked correct to me.\n\nI do wonder (though a brief try failed to trigger it) if there are cases \nwhere the diff algorithm does something \"clever\" which means it does not \ntreat a common prefix or suffix as unchanged (see d2f82950a9 \n(\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We \ncould just trim the common prefix and suffix from the two sides \nourselves using xdl_recmatch().\n\nBest Wishes\n\nPhillip\n\n> +\t\ttest_cmp expect interesting\n> +\t)\n> +'\n> +\n>   test_done\n> diff --git a/xdiff-interface.c b/xdiff-interface.c\n> index 609615db2cd..9977813a9d3 100644\n> --- a/xdiff-interface.c\n> +++ b/xdiff-interface.c\n> @@ -308,6 +308,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n> +\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n>   \t\telse if (!strcmp(value, \"merge\"))\n>   \t\t\tgit_xmerge_style = 0;\n>   \t\t/*\n> diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\n> index 7a046051468..8629ae287c7 100644\n> --- a/xdiff/xdiff.h\n> +++ b/xdiff/xdiff.h\n> @@ -65,6 +65,7 @@ extern \"C\" {\n>   \n>   /* merge output styles */\n>   #define XDL_MERGE_DIFF3 1\n> +#define XDL_MERGE_ZEALOUS_DIFF3 2\n>   \n>   typedef struct s_mmfile {\n>   \tchar *ptr;\n> diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\n> index 1659edb4539..df0c6041778 100644\n> --- a/xdiff/xmerge.c\n> +++ b/xdiff/xmerge.c\n> @@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n>   \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n>   \t\t\t      dest ? dest + size : NULL);\n>   \n> -\tif (style == XDL_MERGE_DIFF3) {\n> +\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n>   \t\t/* Shared preimage */\n>   \t\tif (!dest) {\n>   \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n> @@ -327,7 +327,7 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n>    * lines. Try hard to show only these few lines as conflicting.\n>    */\n>   static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n> -\t\txpparam_t const *xpp)\n> +\t\t\t\txpparam_t const *xpp, int style)\n>   {\n>   \tfor (; m; m = m->next) {\n>   \t\tmmfile_t t1, t2;\n> @@ -368,6 +368,42 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n>   \t\t\tcontinue;\n>   \t\t}\n>   \t\tx = xscr;\n> +\t\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n> +\t\t\tint advance1 = xscr->i1, advance2 = xscr->i2;\n> +\n> +\t\t\t/*\n> +\t\t\t * Advance m->i1 and m->i2 so that conflict for sides\n> +\t\t\t * 1 and 2 start after common region.  Decrement\n> +\t\t\t * m->chg[12] since there are now fewer conflict lines\n> +\t\t\t * for those sides.\n> +\t\t\t */\n> +\t\t\tm->i1 += advance1;\n> +\t\t\tm->i2 += advance2;\n> +\t\t\tm->chg1 -= advance1;\n> +\t\t\tm->chg2 -= advance2;\n> +\n> +\t\t\t/*\n> +\t\t\t * Splitting conflicts due to internal common regions\n> +\t\t\t * on the two sides would be inappropriate since we\n> +\t\t\t * are also showing the merge base and have no\n> +\t\t\t * reasonable way to split the merge base text.\n> +\t\t\t */\n> +\t\t\twhile (xscr->next)\n> +\t\t\t\txscr = xscr->next;\n> +\n> +\t\t\t/*\n> +\t\t\t * Lower the number of conflict lines to not include\n> +\t\t\t * the final common lines, if any.  Do this by setting\n> +\t\t\t * number of conflict lines to\n> +\t\t\t *   (line offset for start of conflict in xscr) +\n> +\t\t\t *   (number of lines in the conflict in xscr)\n> +\t\t\t */\n> +\t\t\tm->chg1 = (xscr->i1 - advance1) + (xscr->chg1);\n> +\t\t\tm->chg2 = (xscr->i2 - advance2) + (xscr->chg2);\n> +\t\t\txdl_free_env(&xe);\n> +\t\t\txdl_free_script(x);\n> +\t\t\tcontinue;\n> +\t\t}\n>   \t\tm->i1 = xscr->i1 + i1;\n>   \t\tm->chg1 = xscr->chg1;\n>   \t\tm->i2 = xscr->i2 + i2;\n> @@ -419,6 +455,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n>   static void xdl_merge_two_conflicts(xdmerge_t *m)\n>   {\n>   \txdmerge_t *next_m = m->next;\n> +\tm->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n>   \tm->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n>   \tm->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n>   \tm->next = next_m->next;\n> @@ -430,12 +467,12 @@ static void xdl_merge_two_conflicts(xdmerge_t *m)\n>    * it appears simpler -- because it takes up less (or as many) lines --\n>    * if the lines are moved into the conflicts.\n>    */\n> -static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n> +static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int style,\n>   \t\t\t\t      int simplify_if_no_alnum)\n>   {\n>   \tint result = 0;\n>   \n> -\tif (!m)\n> +\tif (!m || style == XDL_MERGE_ZEALOUS_DIFF3)\n>   \t\treturn result;\n>   \tfor (;;) {\n>   \t\txdmerge_t *next_m = m->next;\n> @@ -482,6 +519,25 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n>   \tint style = xmp->style;\n>   \tint favor = xmp->favor;\n>   \n> +\t/*\n> +\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n> +\t * at common areas of sides 1 & 2, because the base (side 0) does\n> +\t * not match and is being shown.  Similarly, simplification of\n> +\t * non-conflicts is also skipped due to the skipping of conflict\n> +\t * refinement.\n> +\t *\n> +\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n> +\t * refine conflicts looking for common areas of sides 1 & 2.\n> +\t * However, since the base is being shown and does not match,\n> +\t * it will only look for common areas at the beginning or end\n> +\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n> +\t * conflict refinement is much more limited in this fashion, the\n> +\t * conflict simplification will be skipped.\n> +\t *\n> +\t * See xdl_refine_conflicts() and xdl_simplify_non_conflicts()\n> +\t * for more details, particularly looking for\n> +\t * XDL_MERGE_ZEALOUS_DIFF3.\n> +\t */\n>   \tif (style == XDL_MERGE_DIFF3) {\n>   \t\t/*\n>   \t\t * \"diff3 -m\" output does not make sense for anything\n> @@ -604,8 +660,8 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n>   \t\tchanges = c;\n>   \t/* refine conflicts */\n>   \tif (XDL_MERGE_ZEALOUS <= level &&\n> -\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n> -\t     xdl_simplify_non_conflicts(xe1, changes,\n> +\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 0 ||\n> +\t     xdl_simplify_non_conflicts(xe1, changes, style,\n>   \t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n>   \t\txdl_cleanup_merge(changes);\n>   \t\treturn -1;\n> \n\n"},{"id":"435977","messageId":"2290b657-3c67-0ab8-5c25-cc57feac301c@gmail.com","threadId":"55931","inReplyTo":"b6818661-ac6e-fbde-2cab-429c5550a0da@gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-09-15T11:21:59Z","receivedAt":"2021-09-15T11:22:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 15/09/2021 11:25, Phillip Wood wrote:\n> I do wonder (though a brief try failed to trigger it) if there are cases \n> where the diff algorithm does something \"clever\" which means it does not \n> treat a common prefix or suffix as unchanged (see d2f82950a9 \n> (\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We \n> could just trim the common prefix and suffix from the two sides \n> ourselves using xdl_recmatch().\n\nHere is an evil test case that shows this problem (diff on top of your patch)\n\n\ndiff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\nindex de9c6190b9..836843c6b0 100755\n--- a/t/t6427-diff3-conflict-markers.sh\n+++ b/t/t6427-diff3-conflict-markers.sh\n@@ -219,8 +219,9 @@ test_setup_zdiff3 () {\n                 test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n                 test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n                 test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n+               test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n  \n-               git add basic middle-common &&\n+               git add basic middle-common interesting evil &&\n                 git commit -m base &&\n  \n                 git branch left &&\n@@ -230,19 +231,21 @@ test_setup_zdiff3 () {\n                 test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n                 test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n                 test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n+               test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n                 git add -u &&\n                 git commit -m letters &&\n  \n                 git checkout right &&\n                 test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n                 test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n                 test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n+               test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n                 git add -u &&\n                 git commit -m permuted\n         )\n  }\n  \n-test_expect_failure 'check zdiff3 markers' '\n+test_expect_success 'check zdiff3 markers' '\n         test_setup_zdiff3 &&\n         (\n                 cd zdiff3 &&\n@@ -251,6 +254,14 @@ test_expect_failure 'check zdiff3 markers' '\n  \n                 test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n  \n+               test_write_lines \\\n+                       1 2 3 4 \\\n+                       \"<<<<<<< HEAD\" X A \\\n+                       \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= \\\n+                       Y A B C \">>>>>>> right^0\" \\\n+                       B C 7 8 9 >expect &&\n+               test_cmp expect evil &&\n+\n                 test_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n                 test_cmp expect basic &&\n  \n"},{"id":"436321","messageId":"CABPp-BFQ8wDinW9MuOU6FuGq_h5W=4nv2hVhO0R6bbCxzv_Vyg@mail.gmail.com","threadId":"55931","inReplyTo":"b6818661-ac6e-fbde-2cab-429c5550a0da@gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-18T22:04:28Z","receivedAt":"2021-09-18T22:04:48Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Phillip,\n\nOn Wed, Sep 15, 2021 at 3:25 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> Hi Elijah\n>\n> On 11/09/2021 18:03, Elijah Newren via GitGitGadget wrote:\n> > From: Elijah Newren <newren@gmail.com>\n> > [...]\n> > diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\n> > index 25c4b720e72..de9c6190b9c 100755\n> > --- a/t/t6427-diff3-conflict-markers.sh\n> > +++ b/t/t6427-diff3-conflict-markers.sh\n> > @@ -211,4 +211,60 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n> >       )\n> >   '\n> >\n> > +test_setup_zdiff3 () {\n> > +     test_create_repo zdiff3 &&\n> > +     (\n> > +             cd zdiff3 &&\n> > +\n> > +             test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n> > +             test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n> > +             test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n> > +\n> > +             git add basic middle-common &&\n>\n> interesting does not get committed\n\nWell, that's embarrassing.  It also explains a lot too; I was\nattempting to replicate a weird case I had seen and was surprised I\nwasn't able to get it and saw some new really weird behavior instead.\nTurns out that new weird behavior was just the fact that zdiff3 wasn't\nkicking in because the file wasn't even tracked.  If I had added it,\nit would have duplicated the original case I saw...\n\n> > +             git commit -m base &&\n>\n> adding \"base=$(git rev-parse --short HEAD^1)\" here ...\n\nDon't you mean to add this below inside the next test_expect block,\nwhere it is used?  But yeah, good idea.\n\n>\n> > +\n> > +             git branch left &&\n> > +             git branch right &&\n> > +\n> > +             git checkout left &&\n> > +             test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n> > +             test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n> > +             test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n> > +             git add -u &&\n> > +             git commit -m letters &&\n> > +\n> > +             git checkout right &&\n> > +             test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n> > +             test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n> > +             test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n> > +             git add -u &&\n> > +             git commit -m permuted\n> > +     )\n> > +}\n> > +\n> > +test_expect_failure 'check zdiff3 markers' '\n> > +     test_setup_zdiff3 &&\n> > +     (\n> > +             cd zdiff3 &&\n> > +\n> > +             git checkout left^0 &&\n> > +\n> > +             test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n> > +\n> > +             test_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n>\n> ... and then using $base rather than $(git rev-parse ...) would shorten\n> these lines. It might be clearer if they were split up something like\n> this as well\n>\n>         test_write_lines \\\n>                 1 2 3 4 A \\\n>                 \"<<<<<<< HEAD\" B C D \\\n>                 \"||||||| $base\" 5 6 ======= \\\n>                 X C Y \">>>>>>> right^0\"\\\n>                  E 7 8 9 >expect &&\n\nYeah, that looks a lot better.\n\n> > +             test_cmp expect basic &&\n> > +\n> > +             test_write_lines 1 2 3 \"<<<<<<< HEAD\" CC \"||||||| $(git rev-parse --short HEAD^1)\" AA ======= EE \">>>>>>> right^0\" 4 5 \"<<<<<<< HEAD\" DD \"||||||| $(git rev-parse --short HEAD^1)\" BB ======= FF \">>>>>>> right^0\" 6 7 8 >expect &&\n> > +             test_cmp expect middle-common &&\n> > +\n> > +             # Not passing this one yet.  For some reason, after extracting\n> > +             # the common lines \"A B C\" and \"G H I J\", the remaining part\n> > +             # is comparing \"5 6\" in the base to \"5 6\" on the left and\n> > +             # \"D E F\" on the right.  And zdiff3 currently picks the side\n> > +             # that matches the base as the merge result.  Weird.\n> > +             test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >expect &&\n>\n> I might be about to make a fool of myself but I don't think this is\n> right for expect. 5 and 6 are deleted on the left so the two sides\n> should conflict. Manually comparing the result of merging with diff3 and\n> zdiff3 the zdiff3 result looked correct to me.\n\nYou are right.  Had I managed to add and thus track 'interesting', I\nwould have seen this correct zdiff3 conflict for it, and I hope I\nwould have figured out why I got the 'expect' file wrong here.  But\neither way, thanks for catching and fixing both my bugs in the\ntestsuite.\n\n> I do wonder (though a brief try failed to trigger it) if there are cases\n> where the diff algorithm does something \"clever\" which means it does not\n> treat a common prefix or suffix as unchanged (see d2f82950a9\n> (\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We\n> could just trim the common prefix and suffix from the two sides\n> ourselves using xdl_recmatch().\n\nYou seem to understand the xdl stuff much better than I.  I'm not sure\nhow xdl_recmatch() would be called or where.  Would you like to take\nover the patches?\n"},{"id":"436323","messageId":"CABPp-BHXUNTuYPdCzfKhkvr23W3PODzti84bed6uEP5q+sj0TQ@mail.gmail.com","threadId":"55931","inReplyTo":"2290b657-3c67-0ab8-5c25-cc57feac301c@gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2021-09-18T22:06:35Z","receivedAt":"2021-09-18T22:06:49Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Sep 15, 2021 at 4:22 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>\n> On 15/09/2021 11:25, Phillip Wood wrote:\n> > I do wonder (though a brief try failed to trigger it) if there are cases\n> > where the diff algorithm does something \"clever\" which means it does not\n> > treat a common prefix or suffix as unchanged (see d2f82950a9\n> > (\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We\n> > could just trim the common prefix and suffix from the two sides\n> > ourselves using xdl_recmatch().\n>\n> Here is an evil test case that shows this problem (diff on top of your patch)\n>\n>\n> diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\n> index de9c6190b9..836843c6b0 100755\n> --- a/t/t6427-diff3-conflict-markers.sh\n> +++ b/t/t6427-diff3-conflict-markers.sh\n> @@ -219,8 +219,9 @@ test_setup_zdiff3 () {\n>                  test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n>                  test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n>                  test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n> +               test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n>\n> -               git add basic middle-common &&\n> +               git add basic middle-common interesting evil &&\n>                  git commit -m base &&\n>\n>                  git branch left &&\n> @@ -230,19 +231,21 @@ test_setup_zdiff3 () {\n>                  test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n>                  test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n>                  test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n> +               test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n>                  git add -u &&\n>                  git commit -m letters &&\n>\n>                  git checkout right &&\n>                  test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n>                  test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n>                  test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n> +               test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n>                  git add -u &&\n>                  git commit -m permuted\n>          )\n>   }\n>\n> -test_expect_failure 'check zdiff3 markers' '\n> +test_expect_success 'check zdiff3 markers' '\n\n...except your new testcase makes it fail.\n\n>          test_setup_zdiff3 &&\n>          (\n>                  cd zdiff3 &&\n> @@ -251,6 +254,14 @@ test_expect_failure 'check zdiff3 markers' '\n>\n>                  test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n>\n> +               test_write_lines \\\n> +                       1 2 3 4 \\\n> +                       \"<<<<<<< HEAD\" X A \\\n> +                       \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= \\\n> +                       Y A B C \">>>>>>> right^0\" \\\n> +                       B C 7 8 9 >expect &&\n> +               test_cmp expect evil &&\n> +\n\nYeah, this is an interesting testcase, and I agree with the 'expect'\nchoice you wrote, but the current code doesn't produce it.  I'll\nupdate the patches and send out another round, and then do you want to\ntry your xdl_recmatch() magic to fix this testcase?\n"},{"id":"436325","messageId":"pull.1036.v3.git.git.1632006164.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v2.git.git.1631379829.gitgitgadget@gmail.com","subject":"[PATCH v3 0/2] RFC: implement new zdiff3 conflict style","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-18T23:02:42Z","receivedAt":"2021-09-18T23:06:15Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\nordinary diff3 except that it allows compaction of common lines between the\ntwo sides of history, if those common lines occur at the beginning or end of\na conflict hunk.\n\nThis is still just RFC, because a testcase provided by Phillip fails. I\nthink we should get it working first.\n\nChanges since v2:\n\n * Included more fixes from Phillip, and a new testcase\n\nChanges since v1:\n\n * Included fixes from Phillip (thanks!)\n * Added some testcases\n\nElijah Newren (2):\n  xdiff: implement a zealous diff3, or \"zdiff3\"\n  update documentation for new zdiff3 conflictStyle\n\n Documentation/config/merge.txt         |  9 ++-\n Documentation/git-checkout.txt         |  3 +-\n Documentation/git-merge-file.txt       |  3 +\n Documentation/git-merge.txt            | 32 +++++++--\n Documentation/git-rebase.txt           |  6 +-\n Documentation/git-restore.txt          |  3 +-\n Documentation/git-switch.txt           |  3 +-\n Documentation/technical/rerere.txt     | 10 +--\n builtin/checkout.c                     |  2 +-\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 68 +++++++++++++++++--\n 15 files changed, 210 insertions(+), 30 deletions(-)\n\n\nbase-commit: 4c719308ce59dc70e606f910f40801f2c6051b24\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1036%2Fnewren%2Fzdiff3-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1036/newren/zdiff3-v3\nPull-Request: https://github.com/git/git/pull/1036\n\nRange-diff vs v2:\n\n 1:  06e04c88dea ! 1:  798aefbb40a xdiff: implement a zealous diff3, or \"zdiff3\"\n     @@ Commit message\n          because zdiff3 shows the base version too and the base version cannot be\n          reasonably split.\n      \n     +    Note also that the removing of lines common to the two sides might make\n     +    the remaining text inside the conflict region match the base text inside\n     +    the conflict region (for example, if the diff3 conflict had '5 6 E' on\n     +    the right side of the conflict, then the common line 'E' would be moved\n     +    outside and both the base and right side's remaining conflict text would\n     +    be the lines '5' and '6').  This has the potential to surprise users and\n     +    make them think there should not have been a conflict, but there\n     +    definitely was a conflict and it should remain.\n     +\n          Based-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\n     -    Helped-by: Phillip Wood <phillip.wood123@gmail.com>\n     +    Co-authored-by: Phillip Wood <phillip.wood123@gmail.com>\n          Signed-off-by: Elijah Newren <newren@gmail.com>\n      \n       ## builtin/merge-file.c ##\n     @@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes\n      +\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n      +\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n      +\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n     ++\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n      +\n     -+\t\tgit add basic middle-common &&\n     ++\t\tgit add basic middle-common interesting evil &&\n      +\t\tgit commit -m base &&\n      +\n      +\t\tgit branch left &&\n     @@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes\n      +\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n      +\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n      +\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n     ++\t\ttest_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n      +\t\tgit add -u &&\n      +\t\tgit commit -m letters &&\n      +\n     @@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes\n      +\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n      +\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n      +\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n     ++\t\ttest_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n      +\t\tgit add -u &&\n      +\t\tgit commit -m permuted\n      +\t)\n     @@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes\n      +\n      +\t\tgit checkout left^0 &&\n      +\n     ++\t\tbase=$(git rev-parse --short HEAD^1) &&\n      +\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n      +\n     -+\t\ttest_write_lines 1 2 3 4 A \"<<<<<<< HEAD\" B C D \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= X C Y \">>>>>>> right^0\" E 7 8 9 >expect &&\n     ++\t\ttest_write_lines 1 2 3 4 A \\\n     ++\t\t\t\t \"<<<<<<< HEAD\" B C D \\\n     ++\t\t\t\t \"||||||| $base\" 5 6 \\\n     ++\t\t\t\t ======= X C Y \\\n     ++\t\t\t\t \">>>>>>> right^0\" \\\n     ++\t\t\t\t E 7 8 9 \\\n     ++\t\t\t\t >expect &&\n      +\t\ttest_cmp expect basic &&\n      +\n     -+\t\ttest_write_lines 1 2 3 \"<<<<<<< HEAD\" CC \"||||||| $(git rev-parse --short HEAD^1)\" AA ======= EE \">>>>>>> right^0\" 4 5 \"<<<<<<< HEAD\" DD \"||||||| $(git rev-parse --short HEAD^1)\" BB ======= FF \">>>>>>> right^0\" 6 7 8 >expect &&\n     ++\t\ttest_write_lines 1 2 3 \\\n     ++\t\t\t\t \"<<<<<<< HEAD\" CC \\\n     ++\t\t\t\t \"||||||| $base\" AA \\\n     ++\t\t\t\t ======= EE \\\n     ++\t\t\t\t \">>>>>>> right^0\" \\\n     ++\t\t\t\t 4 5 \\\n     ++\t\t\t\t \"<<<<<<< HEAD\" DD \\\n     ++\t\t\t\t \"||||||| $base\" BB \\\n     ++\t\t\t\t ======= FF \\\n     ++\t\t\t\t \">>>>>>> right^0\" \\\n     ++\t\t\t\t 6 7 8 \\\n     ++\t\t\t\t >expect &&\n      +\t\ttest_cmp expect middle-common &&\n      +\n     -+\t\t# Not passing this one yet.  For some reason, after extracting\n     -+\t\t# the common lines \"A B C\" and \"G H I J\", the remaining part\n     -+\t\t# is comparing \"5 6\" in the base to \"5 6\" on the left and\n     -+\t\t# \"D E F\" on the right.  And zdiff3 currently picks the side\n     -+\t\t# that matches the base as the merge result.  Weird.\n     -+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >expect &&\n     -+\t\ttest_cmp expect interesting\n     ++\t\ttest_write_lines 1 2 3 4 A B C \\\n     ++\t\t\t\t \"<<<<<<< HEAD\" D E F \\\n     ++\t\t\t\t \"||||||| $base\" 5 6 \\\n     ++\t\t\t\t ======= 5 6 \\\n     ++\t\t\t\t \">>>>>>> right^0\" \\\n     ++\t\t\t\t G H I J 7 8 9 \\\n     ++\t\t\t\t >expect &&\n     ++\t\ttest_cmp expect interesting &&\n     ++\n     ++\t\t# Not passing this one yet; the common \"B C\" lines is still\n     ++\t\t# being left in the conflict blocks on the left and right\n     ++\t\t# sides.\n     ++\t\ttest_write_lines 1 2 3 4 \\\n     ++\t\t\t\t \"<<<<<<< HEAD\" X A \\\n     ++\t\t\t\t \"||||||| $base\" 5 6 \\\n     ++\t\t\t\t ======= Y A B C \\\n     ++\t\t\t\t \">>>>>>> right^0\" \\\n     ++\t\t\t\t B C 7 8 9 \\\n     ++\t\t\t\t >expect &&\n     ++\t\ttest_cmp expect evil\n      +\t)\n      +'\n      +\n 2:  9ce7246c0e9 = 2:  90aee68e14a update documentation for new zdiff3 conflictStyle\n\n-- \ngitgitgadget\n"},{"id":"436326","messageId":"798aefbb40ae680f0f9533964425c4c5f1563860.1632006164.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v3.git.git.1632006164.gitgitgadget@gmail.com","subject":"[PATCH v3 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-18T23:02:43Z","receivedAt":"2021-09-18T23:06:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\n\"zdiff3\" is identical to ordinary diff3 except that it allows compaction\nof common lines on the two sides of history at the beginning or end of\nthe conflict hunk.  For example, the following diff3 conflict:\n\n    1\n    2\n    3\n    4\n    <<<<<<\n    A\n    B\n    C\n    D\n    E\n    ||||||\n    5\n    6\n    ======\n    A\n    X\n    C\n    Y\n    E\n    >>>>>>\n    7\n    8\n    9\n\nhas common lines 'A', 'C', and 'E' on the two sides.  With zdiff3, one\nwould instead get the following conflict:\n\n    1\n    2\n    3\n    4\n    A\n    <<<<<<\n    B\n    C\n    D\n    ||||||\n    5\n    6\n    ======\n    X\n    C\n    Y\n    >>>>>>\n    E\n    7\n    8\n    9\n\nNote that the common lines, 'A', and 'E' were moved outside the\nconflict.  Unlike with the two-way conflicts from the 'merge'\nconflictStyle, the zdiff3 conflict is NOT split into multiple conflict\nregions to allow the common 'C' lines to be shown outside a conflict,\nbecause zdiff3 shows the base version too and the base version cannot be\nreasonably split.\n\nNote also that the removing of lines common to the two sides might make\nthe remaining text inside the conflict region match the base text inside\nthe conflict region (for example, if the diff3 conflict had '5 6 E' on\nthe right side of the conflict, then the common line 'E' would be moved\noutside and both the base and right side's remaining conflict text would\nbe the lines '5' and '6').  This has the potential to surprise users and\nmake them think there should not have been a conflict, but there\ndefinitely was a conflict and it should remain.\n\nBased-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\nCo-authored-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 68 +++++++++++++++++--\n 6 files changed, 160 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 06a2f90c487..e695867ee54 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"stdout\", &to_stdout, N_(\"send results to standard output\")),\n \t\tOPT_SET_INT(0, \"diff3\", &xmp.style, N_(\"use a diff3 based merge\"), XDL_MERGE_DIFF3),\n+\t\tOPT_SET_INT(0, \"zdiff3\", &xmp.style, N_(\"use a zealous diff3 based merge\"),\n+\t\t\t\tXDL_MERGE_ZEALOUS_DIFF3),\n \t\tOPT_SET_INT(0, \"ours\", &xmp.favor, N_(\"for conflicts, use our version\"),\n \t\t\t    XDL_MERGE_FAVOR_OURS),\n \t\tOPT_SET_INT(0, \"theirs\", &xmp.favor, N_(\"for conflicts, use their version\"),\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 8108eda1e86..a7fb14e9a40 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1566,7 +1566,7 @@ _git_checkout ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin checkout\n@@ -2446,7 +2446,7 @@ _git_switch ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin switch\n@@ -2887,7 +2887,7 @@ _git_restore ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--source=*)\n \t\t__git_complete_refs --cur=\"${cur##--source=}\"\ndiff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\nindex 25c4b720e72..dc740757187 100755\n--- a/t/t6427-diff3-conflict-markers.sh\n+++ b/t/t6427-diff3-conflict-markers.sh\n@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n \t)\n '\n \n+test_setup_zdiff3 () {\n+\ttest_create_repo zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n+\n+\t\tgit add basic middle-common interesting evil &&\n+\t\tgit commit -m base &&\n+\n+\t\tgit branch left &&\n+\t\tgit branch right &&\n+\n+\t\tgit checkout left &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m letters &&\n+\n+\t\tgit checkout right &&\n+\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m permuted\n+\t)\n+}\n+\n+test_expect_failure 'check zdiff3 markers' '\n+\ttest_setup_zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\tgit checkout left^0 &&\n+\n+\t\tbase=$(git rev-parse --short HEAD^1) &&\n+\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n+\n+\t\ttest_write_lines 1 2 3 4 A \\\n+\t\t\t\t \"<<<<<<< HEAD\" B C D \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= X C Y \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t E 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect basic &&\n+\n+\t\ttest_write_lines 1 2 3 \\\n+\t\t\t\t \"<<<<<<< HEAD\" CC \\\n+\t\t\t\t \"||||||| $base\" AA \\\n+\t\t\t\t ======= EE \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 4 5 \\\n+\t\t\t\t \"<<<<<<< HEAD\" DD \\\n+\t\t\t\t \"||||||| $base\" BB \\\n+\t\t\t\t ======= FF \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 6 7 8 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect middle-common &&\n+\n+\t\ttest_write_lines 1 2 3 4 A B C \\\n+\t\t\t\t \"<<<<<<< HEAD\" D E F \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= 5 6 \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t G H I J 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect interesting &&\n+\n+\t\t# Not passing this one yet; the common \"B C\" lines is still\n+\t\t# being left in the conflict blocks on the left and right\n+\t\t# sides.\n+\t\ttest_write_lines 1 2 3 4 \\\n+\t\t\t\t \"<<<<<<< HEAD\" X A \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= Y A B C \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t B C 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect evil\n+\t)\n+'\n+\n test_done\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 75b32aef51d..2e3a5a2943e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -313,6 +313,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n \t\telse if (!strcmp(value, \"merge\"))\n \t\t\tgit_xmerge_style = 0;\n \t\t/*\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex b29deca5de8..72e25a9ffa5 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -66,6 +66,7 @@ extern \"C\" {\n \n /* merge output styles */\n #define XDL_MERGE_DIFF3 1\n+#define XDL_MERGE_ZEALOUS_DIFF3 2\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 1659edb4539..df0c6041778 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n \t\t\t      dest ? dest + size : NULL);\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n \t\t/* Shared preimage */\n \t\tif (!dest) {\n \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n@@ -327,7 +327,7 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n  * lines. Try hard to show only these few lines as conflicting.\n  */\n static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n-\t\txpparam_t const *xpp)\n+\t\t\t\txpparam_t const *xpp, int style)\n {\n \tfor (; m; m = m->next) {\n \t\tmmfile_t t1, t2;\n@@ -368,6 +368,42 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n \t\t\tcontinue;\n \t\t}\n \t\tx = xscr;\n+\t\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n+\t\t\tint advance1 = xscr->i1, advance2 = xscr->i2;\n+\n+\t\t\t/*\n+\t\t\t * Advance m->i1 and m->i2 so that conflict for sides\n+\t\t\t * 1 and 2 start after common region.  Decrement\n+\t\t\t * m->chg[12] since there are now fewer conflict lines\n+\t\t\t * for those sides.\n+\t\t\t */\n+\t\t\tm->i1 += advance1;\n+\t\t\tm->i2 += advance2;\n+\t\t\tm->chg1 -= advance1;\n+\t\t\tm->chg2 -= advance2;\n+\n+\t\t\t/*\n+\t\t\t * Splitting conflicts due to internal common regions\n+\t\t\t * on the two sides would be inappropriate since we\n+\t\t\t * are also showing the merge base and have no\n+\t\t\t * reasonable way to split the merge base text.\n+\t\t\t */\n+\t\t\twhile (xscr->next)\n+\t\t\t\txscr = xscr->next;\n+\n+\t\t\t/*\n+\t\t\t * Lower the number of conflict lines to not include\n+\t\t\t * the final common lines, if any.  Do this by setting\n+\t\t\t * number of conflict lines to\n+\t\t\t *   (line offset for start of conflict in xscr) +\n+\t\t\t *   (number of lines in the conflict in xscr)\n+\t\t\t */\n+\t\t\tm->chg1 = (xscr->i1 - advance1) + (xscr->chg1);\n+\t\t\tm->chg2 = (xscr->i2 - advance2) + (xscr->chg2);\n+\t\t\txdl_free_env(&xe);\n+\t\t\txdl_free_script(x);\n+\t\t\tcontinue;\n+\t\t}\n \t\tm->i1 = xscr->i1 + i1;\n \t\tm->chg1 = xscr->chg1;\n \t\tm->i2 = xscr->i2 + i2;\n@@ -419,6 +455,7 @@ static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n static void xdl_merge_two_conflicts(xdmerge_t *m)\n {\n \txdmerge_t *next_m = m->next;\n+\tm->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n \tm->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n \tm->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n \tm->next = next_m->next;\n@@ -430,12 +467,12 @@ static void xdl_merge_two_conflicts(xdmerge_t *m)\n  * it appears simpler -- because it takes up less (or as many) lines --\n  * if the lines are moved into the conflicts.\n  */\n-static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int style,\n \t\t\t\t      int simplify_if_no_alnum)\n {\n \tint result = 0;\n \n-\tif (!m)\n+\tif (!m || style == XDL_MERGE_ZEALOUS_DIFF3)\n \t\treturn result;\n \tfor (;;) {\n \t\txdmerge_t *next_m = m->next;\n@@ -482,6 +519,25 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tint style = xmp->style;\n \tint favor = xmp->favor;\n \n+\t/*\n+\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n+\t * at common areas of sides 1 & 2, because the base (side 0) does\n+\t * not match and is being shown.  Similarly, simplification of\n+\t * non-conflicts is also skipped due to the skipping of conflict\n+\t * refinement.\n+\t *\n+\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n+\t * refine conflicts looking for common areas of sides 1 & 2.\n+\t * However, since the base is being shown and does not match,\n+\t * it will only look for common areas at the beginning or end\n+\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n+\t * conflict refinement is much more limited in this fashion, the\n+\t * conflict simplification will be skipped.\n+\t *\n+\t * See xdl_refine_conflicts() and xdl_simplify_non_conflicts()\n+\t * for more details, particularly looking for\n+\t * XDL_MERGE_ZEALOUS_DIFF3.\n+\t */\n \tif (style == XDL_MERGE_DIFF3) {\n \t\t/*\n \t\t * \"diff3 -m\" output does not make sense for anything\n@@ -604,8 +660,8 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \t\tchanges = c;\n \t/* refine conflicts */\n \tif (XDL_MERGE_ZEALOUS <= level &&\n-\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n-\t     xdl_simplify_non_conflicts(xe1, changes,\n+\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 0 ||\n+\t     xdl_simplify_non_conflicts(xe1, changes, style,\n \t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n \t\txdl_cleanup_merge(changes);\n \t\treturn -1;\n-- \ngitgitgadget\n\n"},{"id":"436327","messageId":"90aee68e14adb503c41760265b619a2d70720c2a.1632006164.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v3.git.git.1632006164.gitgitgadget@gmail.com","subject":"[PATCH v3 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-09-18T23:02:44Z","receivedAt":"2021-09-18T23:06:16Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt     |  9 ++++++++-\n Documentation/git-checkout.txt     |  3 +--\n Documentation/git-merge-file.txt   |  3 +++\n Documentation/git-merge.txt        | 32 +++++++++++++++++++++++++-----\n Documentation/git-rebase.txt       |  6 +++---\n Documentation/git-restore.txt      |  3 +--\n Documentation/git-switch.txt       |  3 +--\n Documentation/technical/rerere.txt | 10 +++++-----\n builtin/checkout.c                 |  2 +-\n 9 files changed, 50 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex e27cc639447..99e83dd36e5 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -4,7 +4,14 @@ merge.conflictStyle::\n \tshows a `<<<<<<<` conflict marker, changes made by one side,\n \ta `=======` marker, changes made by the other side, and then\n \ta `>>>>>>>` marker.  An alternate style, \"diff3\", adds a `|||||||`\n-\tmarker and the original text before the `=======` marker.\n+\tmarker and the original text before the `=======` marker.  The\n+\t\"merge\" style tends to produce smaller conflict regions than diff3,\n+\tboth because of the exclusion of the original text, and because\n+\twhen a subset of lines match on the two sides they are just pulled\n+\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n+\tsimilar to diff3 but removes matching lines on the two sides from\n+\tthe conflict region when those matching lines appear near either\n+\tthe beginning or end of a conflict region.\n \n merge.defaultToUpstream::\n \tIf merge is called without any commit argument, merge the upstream\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b1a6fe44997..85c3d3513f7 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -265,8 +265,7 @@ When switching branches with `--merge`, staged changes may be lost.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -p::\n --patch::\ndiff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt\nindex f8560326132..7e9093fab60 100644\n--- a/Documentation/git-merge-file.txt\n+++ b/Documentation/git-merge-file.txt\n@@ -70,6 +70,9 @@ OPTIONS\n --diff3::\n \tShow conflicts in \"diff3\" style.\n \n+--zdiff3::\n+\tShow conflicts in \"zdiff3\" style.\n+\n --ours::\n --theirs::\n --union::\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex e4f3352eb58..e8cecf5a51d 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -240,7 +240,8 @@ from the RCS suite to present such a conflicted hunk, like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n <<<<<<< yours:sample.txt\n Conflict resolution is hard;\n let's go shopping.\n@@ -261,16 +262,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the\n other side wants to claim it is easy.\n \n An alternative style can be used by setting the \"merge.conflictStyle\"\n-configuration variable to \"diff3\".  In \"diff3\" style, the above conflict\n-may look like this:\n+configuration variable to either \"diff3\" or \"zdiff3\".  In \"diff3\"\n+style, the above conflict may look like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n <<<<<<< yours:sample.txt\n+or cleanly resolved because both sides changed the same way.\n Conflict resolution is hard;\n let's go shopping.\n-|||||||\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n+Conflict resolution is hard.\n+=======\n+or cleanly resolved because both sides changed the same way.\n+Git makes conflict resolution easy.\n+>>>>>>> theirs:sample.txt\n+And here is another line that is cleanly resolved or unmodified.\n+------------\n+\n+while in \"zdiff3\" style, it may look like this:\n+\n+------------\n+Here are lines that are either unchanged from the common\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n+<<<<<<< yours:sample.txt\n+Conflict resolution is hard;\n+let's go shopping.\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n Conflict resolution is hard.\n =======\n Git makes conflict resolution easy.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 506345cb0ed..dd9830dc5f8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -741,9 +741,9 @@ information about the rebased commits and their parents (and instead\n generates new fake commits based off limited information in the\n generated patches), those commits cannot be identified; instead it has\n to fall back to a commit summary.  Also, when merge.conflictStyle is\n-set to diff3, the apply backend will use \"constructed merge base\" to\n-label the content from the merge base, and thus provide no information\n-about the merge base commit whatsoever.\n+set to diff3 or zdiff3, the apply backend will use \"constructed merge\n+base\" to label the content from the merge base, and thus provide no\n+information about the merge base commit whatsoever.\n \n The merge backend works with the full commits on both sides of history\n and thus has no such limitations.\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex 55bde91ef9e..5964810caa4 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values\n-\tare \"merge\" (default) and \"diff3\" (in addition to what is\n-\tshown by \"merge\" style, shows the original contents).\n+\tare \"merge\" (default), \"diff3\", and \"zdiff3\".\n \n --ignore-unmerged::\n \tWhen restoring files on the working tree from the index, do\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex 5c438cd5058..5c90f76fbe3 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -137,8 +137,7 @@ should result in deletion of the path).\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -q::\n --quiet::\ndiff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt\nindex af5f9fc24f9..35d45414339 100644\n--- a/Documentation/technical/rerere.txt\n+++ b/Documentation/technical/rerere.txt\n@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.\n \n Different conflict styles and branch names are normalized by stripping\n the labels from the conflict markers, and removing the common ancestor\n-version from the `diff3` conflict style. Branches that are merged\n-in different order are normalized by sorting the conflict hunks.  More\n-on each of those steps in the following sections.\n+version from the `diff3` or `zdiff3` conflict styles.  Branches that\n+are merged in different order are normalized by sorting the conflict\n+hunks.  More on each of those steps in the following sections.\n \n Once these two normalization operations are applied, a conflict ID is\n calculated based on the normalized conflict, which is later used by\n@@ -42,8 +42,8 @@ get a conflict like the following:\n     >>>>>>> AC\n \n Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB\n-and then merging branch AC2 into it), using the diff3 conflict style,\n-we get a conflict like the following:\n+and then merging branch AC2 into it), using the diff3 or zdiff3\n+conflict style, we get a conflict like the following:\n \n     <<<<<<< HEAD\n     B\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c69dcdf72a..a0b0fac6a68 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1519,7 +1519,7 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\t\t   N_(\"conflict style (merge, diff3, or zdiff3)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n-- \ngitgitgadget\n"},{"id":"436937","messageId":"69635d07-7b4e-e94e-db98-0b8f4ea566d7@gmail.com","threadId":"55931","inReplyTo":"CABPp-BHXUNTuYPdCzfKhkvr23W3PODzti84bed6uEP5q+sj0TQ@mail.gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-09-24T10:09:21Z","receivedAt":"2021-09-24T10:09:27Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 18/09/2021 23:06, Elijah Newren wrote:\n> On Wed, Sep 15, 2021 at 4:22 AM Phillip Wood <phillip.wood123@gmail.com> wrote:\n>>\n>> On 15/09/2021 11:25, Phillip Wood wrote:\n>>> I do wonder (though a brief try failed to trigger it) if there are cases\n>>> where the diff algorithm does something \"clever\" which means it does not\n>>> treat a common prefix or suffix as unchanged (see d2f82950a9\n>>> (\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We\n>>> could just trim the common prefix and suffix from the two sides\n>>> ourselves using xdl_recmatch().\n>>\n>> Here is an evil test case that shows this problem (diff on top of your patch)\n>>\n>>\n>> diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\n>> index de9c6190b9..836843c6b0 100755\n>> --- a/t/t6427-diff3-conflict-markers.sh\n>> +++ b/t/t6427-diff3-conflict-markers.sh\n>> @@ -219,8 +219,9 @@ test_setup_zdiff3 () {\n>>                   test_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n>>                   test_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n>>                   test_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n>> +               test_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n>>\n>> -               git add basic middle-common &&\n>> +               git add basic middle-common interesting evil &&\n>>                   git commit -m base &&\n>>\n>>                   git branch left &&\n>> @@ -230,19 +231,21 @@ test_setup_zdiff3 () {\n>>                   test_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n>>                   test_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n>>                   test_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n>> +               test_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n>>                   git add -u &&\n>>                   git commit -m letters &&\n>>\n>>                   git checkout right &&\n>>                   test_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n>>                   test_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n>>                   test_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n>> +               test_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n>>                   git add -u &&\n>>                   git commit -m permuted\n>>           )\n>>    }\n>>\n>> -test_expect_failure 'check zdiff3 markers' '\n>> +test_expect_success 'check zdiff3 markers' '\n> \n> ...except your new testcase makes it fail.\n\nSorry I meant to remove that, I'd changed it to test_expect_success so I \ncould poke about in the test repo when it failed.\n\nBest Wishes\n\nPhillip\n\n> \n>>           test_setup_zdiff3 &&\n>>           (\n>>                   cd zdiff3 &&\n>> @@ -251,6 +254,14 @@ test_expect_failure 'check zdiff3 markers' '\n>>\n>>                   test_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n>>\n>> +               test_write_lines \\\n>> +                       1 2 3 4 \\\n>> +                       \"<<<<<<< HEAD\" X A \\\n>> +                       \"||||||| $(git rev-parse --short HEAD^1)\" 5 6 ======= \\\n>> +                       Y A B C \">>>>>>> right^0\" \\\n>> +                       B C 7 8 9 >expect &&\n>> +               test_cmp expect evil &&\n>> +\n> \n> Yeah, this is an interesting testcase, and I agree with the 'expect'\n> choice you wrote, but the current code doesn't produce it.  I'll\n> update the patches and send out another round, and then do you want to\n> try your xdl_recmatch() magic to fix this testcase?\n> \n\n"},{"id":"436938","messageId":"3f5e1569-4ccd-8494-ea18-1e00bdc73017@gmail.com","threadId":"55931","inReplyTo":"CABPp-BFQ8wDinW9MuOU6FuGq_h5W=4nv2hVhO0R6bbCxzv_Vyg@mail.gmail.com","subject":"Re: [PATCH v2 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2021-09-24T10:16:23Z","receivedAt":"2021-09-24T10:16:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Elijah\n\nOn 18/09/2021 23:04, Elijah Newren wrote:\n> Hi Phillip,\n> [...]\n>> I do wonder (though a brief try failed to trigger it) if there are cases\n>> where the diff algorithm does something \"clever\" which means it does not\n>> treat a common prefix or suffix as unchanged (see d2f82950a9\n>> (\"Re(-re)*fix trim_common_tail()\", 2007-12-20) for a related issue). We\n>> could just trim the common prefix and suffix from the two sides\n>> ourselves using xdl_recmatch().\n> \n> You seem to understand the xdl stuff much better than I.  I'm not sure\n> how xdl_recmatch() would be called or where.  Would you like to take\n> over the patches?\n\nI could do if you really want but I wont have time to do anything for at \nleast a couple of weeks (I really need to get on top of my current \nseries before starting anything new). I outlined the rough idea in [1]. \ntldr; instead of diffing the two sides of the conflict just walk the \nstart and the end of the two sides until we find a line that does not match.\n\nBest Wishes\n\nPhillip\n\n[1] \nhttps://lore.kernel.org/git/f76c79d6-f280-3011-d88d-6de146977626@gmail.com/\n\n"},{"id":"441242","messageId":"473fae82da160459189a6ef11b909ee074464184.1637028785.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v4.git.git.1637028785.gitgitgadget@gmail.com","subject":"[PATCH v4 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-16T02:13:04Z","receivedAt":"2021-11-16T05:10:53Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\n\"zdiff3\" is identical to ordinary diff3 except that it allows compaction\nof common lines on the two sides of history at the beginning or end of\nthe conflict hunk.  For example, the following diff3 conflict:\n\n    1\n    2\n    3\n    4\n    <<<<<<\n    A\n    B\n    C\n    D\n    E\n    ||||||\n    5\n    6\n    ======\n    A\n    X\n    C\n    Y\n    E\n    >>>>>>\n    7\n    8\n    9\n\nhas common lines 'A', 'C', and 'E' on the two sides.  With zdiff3, one\nwould instead get the following conflict:\n\n    1\n    2\n    3\n    4\n    A\n    <<<<<<\n    B\n    C\n    D\n    ||||||\n    5\n    6\n    ======\n    X\n    C\n    Y\n    >>>>>>\n    E\n    7\n    8\n    9\n\nNote that the common lines, 'A', and 'E' were moved outside the\nconflict.  Unlike with the two-way conflicts from the 'merge'\nconflictStyle, the zdiff3 conflict is NOT split into multiple conflict\nregions to allow the common 'C' lines to be shown outside a conflict,\nbecause zdiff3 shows the base version too and the base version cannot be\nreasonably split.\n\nNote also that the removing of lines common to the two sides might make\nthe remaining text inside the conflict region match the base text inside\nthe conflict region (for example, if the diff3 conflict had '5 6 E' on\nthe right side of the conflict, then the common line 'E' would be moved\noutside and both the base and right side's remaining conflict text would\nbe the lines '5' and '6').  This has the potential to surprise users and\nmake them think there should not have been a conflict, but there\ndefinitely was a conflict and it should remain.\n\nBased-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\nCo-authored-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 63 ++++++++++++++++--\n 6 files changed, 155 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 06a2f90c487..e695867ee54 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"stdout\", &to_stdout, N_(\"send results to standard output\")),\n \t\tOPT_SET_INT(0, \"diff3\", &xmp.style, N_(\"use a diff3 based merge\"), XDL_MERGE_DIFF3),\n+\t\tOPT_SET_INT(0, \"zdiff3\", &xmp.style, N_(\"use a zealous diff3 based merge\"),\n+\t\t\t\tXDL_MERGE_ZEALOUS_DIFF3),\n \t\tOPT_SET_INT(0, \"ours\", &xmp.favor, N_(\"for conflicts, use our version\"),\n \t\t\t    XDL_MERGE_FAVOR_OURS),\n \t\tOPT_SET_INT(0, \"theirs\", &xmp.favor, N_(\"for conflicts, use their version\"),\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 8108eda1e86..a7fb14e9a40 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1566,7 +1566,7 @@ _git_checkout ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin checkout\n@@ -2446,7 +2446,7 @@ _git_switch ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin switch\n@@ -2887,7 +2887,7 @@ _git_restore ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--source=*)\n \t\t__git_complete_refs --cur=\"${cur##--source=}\"\ndiff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\nindex 25c4b720e72..a9ee4cb207a 100755\n--- a/t/t6427-diff3-conflict-markers.sh\n+++ b/t/t6427-diff3-conflict-markers.sh\n@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n \t)\n '\n \n+test_setup_zdiff3 () {\n+\ttest_create_repo zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n+\n+\t\tgit add basic middle-common interesting evil &&\n+\t\tgit commit -m base &&\n+\n+\t\tgit branch left &&\n+\t\tgit branch right &&\n+\n+\t\tgit checkout left &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m letters &&\n+\n+\t\tgit checkout right &&\n+\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m permuted\n+\t)\n+}\n+\n+test_expect_success 'check zdiff3 markers' '\n+\ttest_setup_zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\tgit checkout left^0 &&\n+\n+\t\tbase=$(git rev-parse --short HEAD^1) &&\n+\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n+\n+\t\ttest_write_lines 1 2 3 4 A \\\n+\t\t\t\t \"<<<<<<< HEAD\" B C D \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= X C Y \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t E 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect basic &&\n+\n+\t\ttest_write_lines 1 2 3 \\\n+\t\t\t\t \"<<<<<<< HEAD\" CC \\\n+\t\t\t\t \"||||||| $base\" AA \\\n+\t\t\t\t ======= EE \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 4 5 \\\n+\t\t\t\t \"<<<<<<< HEAD\" DD \\\n+\t\t\t\t \"||||||| $base\" BB \\\n+\t\t\t\t ======= FF \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 6 7 8 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect middle-common &&\n+\n+\t\ttest_write_lines 1 2 3 4 A B C \\\n+\t\t\t\t \"<<<<<<< HEAD\" D E F \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= 5 6 \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t G H I J 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect interesting &&\n+\n+\t\t# Not passing this one yet; the common \"B C\" lines is still\n+\t\t# being left in the conflict blocks on the left and right\n+\t\t# sides.\n+\t\ttest_write_lines 1 2 3 4 \\\n+\t\t\t\t \"<<<<<<< HEAD\" X A \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= Y A B C \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t B C 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect evil\n+\t)\n+'\n+\n test_done\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 75b32aef51d..2e3a5a2943e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -313,6 +313,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n \t\telse if (!strcmp(value, \"merge\"))\n \t\t\tgit_xmerge_style = 0;\n \t\t/*\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex b29deca5de8..72e25a9ffa5 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -66,6 +66,7 @@ extern \"C\" {\n \n /* merge output styles */\n #define XDL_MERGE_DIFF3 1\n+#define XDL_MERGE_ZEALOUS_DIFF3 2\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 1659edb4539..fff0b594f9a 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n \t\t\t      dest ? dest + size : NULL);\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n \t\t/* Shared preimage */\n \t\tif (!dest) {\n \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n@@ -322,6 +322,40 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n \treturn size;\n }\n \n+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n+{\n+\treturn xdl_recmatch(rec1->ptr, rec1->size,\n+\t\t\t    rec2->ptr, rec2->size, flags);\n+}\n+\n+/*\n+ * Remove any common lines from the beginning and end of the conflicted region.\n+ */\n+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n+\t\txpparam_t const *xpp)\n+{\n+\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\tfor (; m; m = m->next) {\n+\t\t/* let's handle just the conflicts */\n+\t\tif (m->mode)\n+\t\t\tcontinue;\n+\n+\t\twhile(m->chg1 && m->chg2 &&\n+\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t\tm->chg1--;\n+\t\t\tm->chg2--;\n+\t\t\tm->i1++;\n+\t\t\tm->i2++;\n+\t\t}\n+\t\twhile (m->chg1 && m->chg2 &&\n+\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t\tm->chg1--;\n+\t\t\tm->chg2--;\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Sometimes, changes are not quite identical, but differ in only a few\n  * lines. Try hard to show only these few lines as conflicting.\n@@ -482,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tint style = xmp->style;\n \tint favor = xmp->favor;\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\t/*\n+\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n+\t * at common areas of sides 1 & 2, because the base (side 0) does\n+\t * not match and is being shown.  Similarly, simplification of\n+\t * non-conflicts is also skipped due to the skipping of conflict\n+\t * refinement.\n+\t *\n+\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n+\t * refine conflicts looking for common areas of sides 1 & 2.\n+\t * However, since the base is being shown and does not match,\n+\t * it will only look for common areas at the beginning or end\n+\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n+\t * conflict refinement is much more limited in this fashion, the\n+\t * conflict simplification will be skipped.\n+\t */\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_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@@ -603,10 +652,12 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tif (!changes)\n \t\tchanges = c;\n \t/* refine conflicts */\n-\tif (XDL_MERGE_ZEALOUS <= level &&\n-\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n-\t     xdl_simplify_non_conflicts(xe1, changes,\n-\t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n+\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n+\t\txdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);\n+\t} else if (XDL_MERGE_ZEALOUS <= level &&\n+\t\t   (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n+\t\t    xdl_simplify_non_conflicts(xe1, changes,\n+\t\t\t\t\t       XDL_MERGE_ZEALOUS < level) < 0)) {\n \t\txdl_cleanup_merge(changes);\n \t\treturn -1;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"441243","messageId":"pull.1036.v4.git.git.1637028785.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v3.git.git.1632006164.gitgitgadget@gmail.com","subject":"[PATCH v4 0/2] Implement new zdiff3 conflict style","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-16T02:13:03Z","receivedAt":"2021-11-16T05:10:59Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\nordinary diff3 except that it allows compaction of common lines between the\ntwo sides of history, if those common lines occur at the beginning or end of\na conflict hunk.\n\nChanges since v3:\n\n * More fixes from Phillip.\n * Marked Phillip as the author of the first commit because he's written\n   most the code now; gave myself a co-authored-by trailer on that commit.\n * Removed the RFC label since it's now passing our tests.\n\nChanges since v2:\n\n * Included more fixes from Phillip, and a new testcase\n\nChanges since v1:\n\n * Included fixes from Phillip (thanks!)\n * Added some testcases\n\nElijah Newren (1):\n  update documentation for new zdiff3 conflictStyle\n\nPhillip Wood (1):\n  xdiff: implement a zealous diff3, or \"zdiff3\"\n\n Documentation/config/merge.txt         |  9 ++-\n Documentation/git-checkout.txt         |  3 +-\n Documentation/git-merge-file.txt       |  3 +\n Documentation/git-merge.txt            | 32 +++++++--\n Documentation/git-rebase.txt           |  6 +-\n Documentation/git-restore.txt          |  3 +-\n Documentation/git-switch.txt           |  3 +-\n Documentation/technical/rerere.txt     | 10 +--\n builtin/checkout.c                     |  2 +-\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 63 ++++++++++++++++--\n 15 files changed, 205 insertions(+), 30 deletions(-)\n\n\nbase-commit: 4c719308ce59dc70e606f910f40801f2c6051b24\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1036%2Fnewren%2Fzdiff3-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1036/newren/zdiff3-v4\nPull-Request: https://github.com/git/git/pull/1036\n\nRange-diff vs v3:\n\n 1:  798aefbb40a ! 1:  473fae82da1 xdiff: implement a zealous diff3, or \"zdiff3\"\n     @@\n       ## Metadata ##\n     -Author: Elijah Newren <newren@gmail.com>\n     +Author: Phillip Wood <phillip.wood@dunelm.org.uk>\n      \n       ## Commit message ##\n          xdiff: implement a zealous diff3, or \"zdiff3\"\n     @@ Commit message\n          definitely was a conflict and it should remain.\n      \n          Based-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\n     -    Co-authored-by: Phillip Wood <phillip.wood123@gmail.com>\n     +    Co-authored-by: Elijah Newren <newren@gmail.com>\n     +    Signed-off-by: Phillip Wood <phillip.wood123@gmail.com>\n          Signed-off-by: Elijah Newren <newren@gmail.com>\n      \n       ## builtin/merge-file.c ##\n     @@ t/t6427-diff3-conflict-markers.sh: test_expect_success 'rebase --apply describes\n      +\t)\n      +}\n      +\n     -+test_expect_failure 'check zdiff3 markers' '\n     ++test_expect_success 'check zdiff3 markers' '\n      +\ttest_setup_zdiff3 &&\n      +\t(\n      +\t\tcd zdiff3 &&\n     @@ xdiff/xmerge.c: static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n       \t\tif (!dest) {\n       \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n      @@ xdiff/xmerge.c: static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n     -  * lines. Try hard to show only these few lines as conflicting.\n     -  */\n     - static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n     --\t\txpparam_t const *xpp)\n     -+\t\t\t\txpparam_t const *xpp, int style)\n     - {\n     - \tfor (; m; m = m->next) {\n     - \t\tmmfile_t t1, t2;\n     -@@ xdiff/xmerge.c: static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n     - \t\t\tcontinue;\n     - \t\t}\n     - \t\tx = xscr;\n     -+\t\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n     -+\t\t\tint advance1 = xscr->i1, advance2 = xscr->i2;\n     -+\n     -+\t\t\t/*\n     -+\t\t\t * Advance m->i1 and m->i2 so that conflict for sides\n     -+\t\t\t * 1 and 2 start after common region.  Decrement\n     -+\t\t\t * m->chg[12] since there are now fewer conflict lines\n     -+\t\t\t * for those sides.\n     -+\t\t\t */\n     -+\t\t\tm->i1 += advance1;\n     -+\t\t\tm->i2 += advance2;\n     -+\t\t\tm->chg1 -= advance1;\n     -+\t\t\tm->chg2 -= advance2;\n     -+\n     -+\t\t\t/*\n     -+\t\t\t * Splitting conflicts due to internal common regions\n     -+\t\t\t * on the two sides would be inappropriate since we\n     -+\t\t\t * are also showing the merge base and have no\n     -+\t\t\t * reasonable way to split the merge base text.\n     -+\t\t\t */\n     -+\t\t\twhile (xscr->next)\n     -+\t\t\t\txscr = xscr->next;\n     + \treturn size;\n     + }\n     + \n     ++static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n     ++{\n     ++\treturn xdl_recmatch(rec1->ptr, rec1->size,\n     ++\t\t\t    rec2->ptr, rec2->size, flags);\n     ++}\n      +\n     -+\t\t\t/*\n     -+\t\t\t * Lower the number of conflict lines to not include\n     -+\t\t\t * the final common lines, if any.  Do this by setting\n     -+\t\t\t * number of conflict lines to\n     -+\t\t\t *   (line offset for start of conflict in xscr) +\n     -+\t\t\t *   (number of lines in the conflict in xscr)\n     -+\t\t\t */\n     -+\t\t\tm->chg1 = (xscr->i1 - advance1) + (xscr->chg1);\n     -+\t\t\tm->chg2 = (xscr->i2 - advance2) + (xscr->chg2);\n     -+\t\t\txdl_free_env(&xe);\n     -+\t\t\txdl_free_script(x);\n     ++/*\n     ++ * Remove any common lines from the beginning and end of the conflicted region.\n     ++ */\n     ++static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n     ++\t\txpparam_t const *xpp)\n     ++{\n     ++\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n     ++\tfor (; m; m = m->next) {\n     ++\t\t/* let's handle just the conflicts */\n     ++\t\tif (m->mode)\n      +\t\t\tcontinue;\n     ++\n     ++\t\twhile(m->chg1 && m->chg2 &&\n     ++\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n     ++\t\t\tm->chg1--;\n     ++\t\t\tm->chg2--;\n     ++\t\t\tm->i1++;\n     ++\t\t\tm->i2++;\n      +\t\t}\n     - \t\tm->i1 = xscr->i1 + i1;\n     - \t\tm->chg1 = xscr->chg1;\n     - \t\tm->i2 = xscr->i2 + i2;\n     -@@ xdiff/xmerge.c: static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)\n     - static void xdl_merge_two_conflicts(xdmerge_t *m)\n     - {\n     - \txdmerge_t *next_m = m->next;\n     -+\tm->chg0 = next_m->i0 + next_m->chg0 - m->i0;\n     - \tm->chg1 = next_m->i1 + next_m->chg1 - m->i1;\n     - \tm->chg2 = next_m->i2 + next_m->chg2 - m->i2;\n     - \tm->next = next_m->next;\n     -@@ xdiff/xmerge.c: static void xdl_merge_two_conflicts(xdmerge_t *m)\n     -  * it appears simpler -- because it takes up less (or as many) lines --\n     -  * if the lines are moved into the conflicts.\n     -  */\n     --static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,\n     -+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m, int style,\n     - \t\t\t\t      int simplify_if_no_alnum)\n     - {\n     - \tint result = 0;\n     - \n     --\tif (!m)\n     -+\tif (!m || style == XDL_MERGE_ZEALOUS_DIFF3)\n     - \t\treturn result;\n     - \tfor (;;) {\n     - \t\txdmerge_t *next_m = m->next;\n     ++\t\twhile (m->chg1 && m->chg2 &&\n     ++\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n     ++\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n     ++\t\t\tm->chg1--;\n     ++\t\t\tm->chg2--;\n     ++\t\t}\n     ++\t}\n     ++}\n     ++\n     + /*\n     +  * Sometimes, changes are not quite identical, but differ in only a few\n     +  * lines. Try hard to show only these few lines as conflicting.\n      @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n       \tint style = xmp->style;\n       \tint favor = xmp->favor;\n       \n     +-\tif (style == XDL_MERGE_DIFF3) {\n      +\t/*\n      +\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n      +\t * at common areas of sides 1 & 2, because the base (side 0) does\n     @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n      +\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n      +\t * conflict refinement is much more limited in this fashion, the\n      +\t * conflict simplification will be skipped.\n     -+\t *\n     -+\t * See xdl_refine_conflicts() and xdl_simplify_non_conflicts()\n     -+\t * for more details, particularly looking for\n     -+\t * XDL_MERGE_ZEALOUS_DIFF3.\n      +\t */\n     - \tif (style == XDL_MERGE_DIFF3) {\n     ++\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_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      @@ xdiff/xmerge.c: static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n     + \tif (!changes)\n       \t\tchanges = c;\n       \t/* refine conflicts */\n     - \tif (XDL_MERGE_ZEALOUS <= level &&\n     +-\tif (XDL_MERGE_ZEALOUS <= level &&\n      -\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n      -\t     xdl_simplify_non_conflicts(xe1, changes,\n     -+\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp, style) < 0 ||\n     -+\t     xdl_simplify_non_conflicts(xe1, changes, style,\n     - \t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n     +-\t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n     ++\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n     ++\t\txdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);\n     ++\t} else if (XDL_MERGE_ZEALOUS <= level &&\n     ++\t\t   (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n     ++\t\t    xdl_simplify_non_conflicts(xe1, changes,\n     ++\t\t\t\t\t       XDL_MERGE_ZEALOUS < level) < 0)) {\n       \t\txdl_cleanup_merge(changes);\n       \t\treturn -1;\n     + \t}\n 2:  90aee68e14a = 2:  69f20a702b4 update documentation for new zdiff3 conflictStyle\n\n-- \ngitgitgadget\n"},{"id":"441244","messageId":"69f20a702b41bd2ccf86f14f0abe3b141c0eeb3a.1637028785.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v4.git.git.1637028785.gitgitgadget@gmail.com","subject":"[PATCH v4 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-11-16T02:13:05Z","receivedAt":"2021-11-16T05:11:09Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt     |  9 ++++++++-\n Documentation/git-checkout.txt     |  3 +--\n Documentation/git-merge-file.txt   |  3 +++\n Documentation/git-merge.txt        | 32 +++++++++++++++++++++++++-----\n Documentation/git-rebase.txt       |  6 +++---\n Documentation/git-restore.txt      |  3 +--\n Documentation/git-switch.txt       |  3 +--\n Documentation/technical/rerere.txt | 10 +++++-----\n builtin/checkout.c                 |  2 +-\n 9 files changed, 50 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex e27cc639447..99e83dd36e5 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -4,7 +4,14 @@ merge.conflictStyle::\n \tshows a `<<<<<<<` conflict marker, changes made by one side,\n \ta `=======` marker, changes made by the other side, and then\n \ta `>>>>>>>` marker.  An alternate style, \"diff3\", adds a `|||||||`\n-\tmarker and the original text before the `=======` marker.\n+\tmarker and the original text before the `=======` marker.  The\n+\t\"merge\" style tends to produce smaller conflict regions than diff3,\n+\tboth because of the exclusion of the original text, and because\n+\twhen a subset of lines match on the two sides they are just pulled\n+\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n+\tsimilar to diff3 but removes matching lines on the two sides from\n+\tthe conflict region when those matching lines appear near either\n+\tthe beginning or end of a conflict region.\n \n merge.defaultToUpstream::\n \tIf merge is called without any commit argument, merge the upstream\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b1a6fe44997..85c3d3513f7 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -265,8 +265,7 @@ When switching branches with `--merge`, staged changes may be lost.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -p::\n --patch::\ndiff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt\nindex f8560326132..7e9093fab60 100644\n--- a/Documentation/git-merge-file.txt\n+++ b/Documentation/git-merge-file.txt\n@@ -70,6 +70,9 @@ OPTIONS\n --diff3::\n \tShow conflicts in \"diff3\" style.\n \n+--zdiff3::\n+\tShow conflicts in \"zdiff3\" style.\n+\n --ours::\n --theirs::\n --union::\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex e4f3352eb58..e8cecf5a51d 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -240,7 +240,8 @@ from the RCS suite to present such a conflicted hunk, like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n <<<<<<< yours:sample.txt\n Conflict resolution is hard;\n let's go shopping.\n@@ -261,16 +262,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the\n other side wants to claim it is easy.\n \n An alternative style can be used by setting the \"merge.conflictStyle\"\n-configuration variable to \"diff3\".  In \"diff3\" style, the above conflict\n-may look like this:\n+configuration variable to either \"diff3\" or \"zdiff3\".  In \"diff3\"\n+style, the above conflict may look like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n <<<<<<< yours:sample.txt\n+or cleanly resolved because both sides changed the same way.\n Conflict resolution is hard;\n let's go shopping.\n-|||||||\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n+Conflict resolution is hard.\n+=======\n+or cleanly resolved because both sides changed the same way.\n+Git makes conflict resolution easy.\n+>>>>>>> theirs:sample.txt\n+And here is another line that is cleanly resolved or unmodified.\n+------------\n+\n+while in \"zdiff3\" style, it may look like this:\n+\n+------------\n+Here are lines that are either unchanged from the common\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n+<<<<<<< yours:sample.txt\n+Conflict resolution is hard;\n+let's go shopping.\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n Conflict resolution is hard.\n =======\n Git makes conflict resolution easy.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 506345cb0ed..dd9830dc5f8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -741,9 +741,9 @@ information about the rebased commits and their parents (and instead\n generates new fake commits based off limited information in the\n generated patches), those commits cannot be identified; instead it has\n to fall back to a commit summary.  Also, when merge.conflictStyle is\n-set to diff3, the apply backend will use \"constructed merge base\" to\n-label the content from the merge base, and thus provide no information\n-about the merge base commit whatsoever.\n+set to diff3 or zdiff3, the apply backend will use \"constructed merge\n+base\" to label the content from the merge base, and thus provide no\n+information about the merge base commit whatsoever.\n \n The merge backend works with the full commits on both sides of history\n and thus has no such limitations.\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex 55bde91ef9e..5964810caa4 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values\n-\tare \"merge\" (default) and \"diff3\" (in addition to what is\n-\tshown by \"merge\" style, shows the original contents).\n+\tare \"merge\" (default), \"diff3\", and \"zdiff3\".\n \n --ignore-unmerged::\n \tWhen restoring files on the working tree from the index, do\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex 5c438cd5058..5c90f76fbe3 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -137,8 +137,7 @@ should result in deletion of the path).\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -q::\n --quiet::\ndiff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt\nindex af5f9fc24f9..35d45414339 100644\n--- a/Documentation/technical/rerere.txt\n+++ b/Documentation/technical/rerere.txt\n@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.\n \n Different conflict styles and branch names are normalized by stripping\n the labels from the conflict markers, and removing the common ancestor\n-version from the `diff3` conflict style. Branches that are merged\n-in different order are normalized by sorting the conflict hunks.  More\n-on each of those steps in the following sections.\n+version from the `diff3` or `zdiff3` conflict styles.  Branches that\n+are merged in different order are normalized by sorting the conflict\n+hunks.  More on each of those steps in the following sections.\n \n Once these two normalization operations are applied, a conflict ID is\n calculated based on the normalized conflict, which is later used by\n@@ -42,8 +42,8 @@ get a conflict like the following:\n     >>>>>>> AC\n \n Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB\n-and then merging branch AC2 into it), using the diff3 conflict style,\n-we get a conflict like the following:\n+and then merging branch AC2 into it), using the diff3 or zdiff3\n+conflict style, we get a conflict like the following:\n \n     <<<<<<< HEAD\n     B\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c69dcdf72a..a0b0fac6a68 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1519,7 +1519,7 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\t\t   N_(\"conflict style (merge, diff3, or zdiff3)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n-- \ngitgitgadget\n"},{"id":"442724","messageId":"pull.1036.v5.git.git.1638317108.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v4.git.git.1637028785.gitgitgadget@gmail.com","subject":"[PATCH v5 0/2] Implement new zdiff3 conflict style","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-01T00:05:05Z","receivedAt":"2021-12-01T00:05:14Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Implement a zealous diff3, or \"zdiff3\". This new mode is identical to\nordinary diff3 except that it allows compaction of common lines between the\ntwo sides of history, if those common lines occur at the beginning or end of\na conflict hunk.\n\nChange since v4:\n\n * None; just resending because v3 wasn't picked up.\n\nChanges since v3:\n\n * More fixes from Phillip.\n * Marked Phillip as the author of the first commit because he's written\n   most the code now; gave myself a co-authored-by trailer on that commit.\n * Removed the RFC label since it's now passing our tests.\n\nChanges since v2:\n\n * Included more fixes from Phillip, and a new testcase\n\nChanges since v1:\n\n * Included fixes from Phillip (thanks!)\n * Added some testcases\n\nElijah Newren (1):\n  update documentation for new zdiff3 conflictStyle\n\nPhillip Wood (1):\n  xdiff: implement a zealous diff3, or \"zdiff3\"\n\n Documentation/config/merge.txt         |  9 ++-\n Documentation/git-checkout.txt         |  3 +-\n Documentation/git-merge-file.txt       |  3 +\n Documentation/git-merge.txt            | 32 +++++++--\n Documentation/git-rebase.txt           |  6 +-\n Documentation/git-restore.txt          |  3 +-\n Documentation/git-switch.txt           |  3 +-\n Documentation/technical/rerere.txt     | 10 +--\n builtin/checkout.c                     |  2 +-\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 63 ++++++++++++++++--\n 15 files changed, 205 insertions(+), 30 deletions(-)\n\n\nbase-commit: 4c719308ce59dc70e606f910f40801f2c6051b24\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1036%2Fnewren%2Fzdiff3-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1036/newren/zdiff3-v5\nPull-Request: https://github.com/git/git/pull/1036\n\nRange-diff vs v4:\n\n 1:  473fae82da1 = 1:  473fae82da1 xdiff: implement a zealous diff3, or \"zdiff3\"\n 2:  69f20a702b4 = 2:  409ae2bbd85 update documentation for new zdiff3 conflictStyle\n\n-- \ngitgitgadget\n"},{"id":"442725","messageId":"473fae82da160459189a6ef11b909ee074464184.1638317108.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v5.git.git.1638317108.gitgitgadget@gmail.com","subject":"[PATCH v5 1/2] xdiff: implement a zealous diff3, or \"zdiff3\"","fromName":"Phillip Wood via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-01T00:05:06Z","receivedAt":"2021-12-01T00:05:16Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"From: Phillip Wood <phillip.wood@dunelm.org.uk>\n\n\"zdiff3\" is identical to ordinary diff3 except that it allows compaction\nof common lines on the two sides of history at the beginning or end of\nthe conflict hunk.  For example, the following diff3 conflict:\n\n    1\n    2\n    3\n    4\n    <<<<<<\n    A\n    B\n    C\n    D\n    E\n    ||||||\n    5\n    6\n    ======\n    A\n    X\n    C\n    Y\n    E\n    >>>>>>\n    7\n    8\n    9\n\nhas common lines 'A', 'C', and 'E' on the two sides.  With zdiff3, one\nwould instead get the following conflict:\n\n    1\n    2\n    3\n    4\n    A\n    <<<<<<\n    B\n    C\n    D\n    ||||||\n    5\n    6\n    ======\n    X\n    C\n    Y\n    >>>>>>\n    E\n    7\n    8\n    9\n\nNote that the common lines, 'A', and 'E' were moved outside the\nconflict.  Unlike with the two-way conflicts from the 'merge'\nconflictStyle, the zdiff3 conflict is NOT split into multiple conflict\nregions to allow the common 'C' lines to be shown outside a conflict,\nbecause zdiff3 shows the base version too and the base version cannot be\nreasonably split.\n\nNote also that the removing of lines common to the two sides might make\nthe remaining text inside the conflict region match the base text inside\nthe conflict region (for example, if the diff3 conflict had '5 6 E' on\nthe right side of the conflict, then the common line 'E' would be moved\noutside and both the base and right side's remaining conflict text would\nbe the lines '5' and '6').  This has the potential to surprise users and\nmake them think there should not have been a conflict, but there\ndefinitely was a conflict and it should remain.\n\nBased-on-patch-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>\nCo-authored-by: Elijah Newren <newren@gmail.com>\nSigned-off-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-file.c                   |  2 +\n contrib/completion/git-completion.bash |  6 +-\n t/t6427-diff3-conflict-markers.sh      | 90 ++++++++++++++++++++++++++\n xdiff-interface.c                      |  2 +\n xdiff/xdiff.h                          |  1 +\n xdiff/xmerge.c                         | 63 ++++++++++++++++--\n 6 files changed, 155 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/merge-file.c b/builtin/merge-file.c\nindex 06a2f90c487..e695867ee54 100644\n--- a/builtin/merge-file.c\n+++ b/builtin/merge-file.c\n@@ -34,6 +34,8 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)\n \tstruct option options[] = {\n \t\tOPT_BOOL('p', \"stdout\", &to_stdout, N_(\"send results to standard output\")),\n \t\tOPT_SET_INT(0, \"diff3\", &xmp.style, N_(\"use a diff3 based merge\"), XDL_MERGE_DIFF3),\n+\t\tOPT_SET_INT(0, \"zdiff3\", &xmp.style, N_(\"use a zealous diff3 based merge\"),\n+\t\t\t\tXDL_MERGE_ZEALOUS_DIFF3),\n \t\tOPT_SET_INT(0, \"ours\", &xmp.favor, N_(\"for conflicts, use our version\"),\n \t\t\t    XDL_MERGE_FAVOR_OURS),\n \t\tOPT_SET_INT(0, \"theirs\", &xmp.favor, N_(\"for conflicts, use their version\"),\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 8108eda1e86..a7fb14e9a40 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1566,7 +1566,7 @@ _git_checkout ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin checkout\n@@ -2446,7 +2446,7 @@ _git_switch ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--*)\n \t\t__gitcomp_builtin switch\n@@ -2887,7 +2887,7 @@ _git_restore ()\n \n \tcase \"$cur\" in\n \t--conflict=*)\n-\t\t__gitcomp \"diff3 merge\" \"\" \"${cur##--conflict=}\"\n+\t\t__gitcomp \"diff3 merge zdiff3\" \"\" \"${cur##--conflict=}\"\n \t\t;;\n \t--source=*)\n \t\t__git_complete_refs --cur=\"${cur##--source=}\"\ndiff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh\nindex 25c4b720e72..a9ee4cb207a 100755\n--- a/t/t6427-diff3-conflict-markers.sh\n+++ b/t/t6427-diff3-conflict-markers.sh\n@@ -211,4 +211,94 @@ test_expect_success 'rebase --apply describes fake ancestor base' '\n \t)\n '\n \n+test_setup_zdiff3 () {\n+\ttest_create_repo zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 AA 4 5 BB 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 5 6 7 8 9 >evil &&\n+\n+\t\tgit add basic middle-common interesting evil &&\n+\t\tgit commit -m base &&\n+\n+\t\tgit branch left &&\n+\t\tgit branch right &&\n+\n+\t\tgit checkout left &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 CC 4 5 DD 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C D E F G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 X A B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m letters &&\n+\n+\t\tgit checkout right &&\n+\t\ttest_write_lines 1 2 3 4 A X C Y E 7 8 9 >basic &&\n+\t\ttest_write_lines 1 2 3 EE 4 5 FF 6 7 8 >middle-common &&\n+\t\ttest_write_lines 1 2 3 4 A B C 5 6 G H I J 7 8 9 >interesting &&\n+\t\ttest_write_lines 1 2 3 4 Y A B C B C 7 8 9 >evil &&\n+\t\tgit add -u &&\n+\t\tgit commit -m permuted\n+\t)\n+}\n+\n+test_expect_success 'check zdiff3 markers' '\n+\ttest_setup_zdiff3 &&\n+\t(\n+\t\tcd zdiff3 &&\n+\n+\t\tgit checkout left^0 &&\n+\n+\t\tbase=$(git rev-parse --short HEAD^1) &&\n+\t\ttest_must_fail git -c merge.conflictstyle=zdiff3 merge -s recursive right^0 &&\n+\n+\t\ttest_write_lines 1 2 3 4 A \\\n+\t\t\t\t \"<<<<<<< HEAD\" B C D \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= X C Y \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t E 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect basic &&\n+\n+\t\ttest_write_lines 1 2 3 \\\n+\t\t\t\t \"<<<<<<< HEAD\" CC \\\n+\t\t\t\t \"||||||| $base\" AA \\\n+\t\t\t\t ======= EE \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 4 5 \\\n+\t\t\t\t \"<<<<<<< HEAD\" DD \\\n+\t\t\t\t \"||||||| $base\" BB \\\n+\t\t\t\t ======= FF \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t 6 7 8 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect middle-common &&\n+\n+\t\ttest_write_lines 1 2 3 4 A B C \\\n+\t\t\t\t \"<<<<<<< HEAD\" D E F \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= 5 6 \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t G H I J 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect interesting &&\n+\n+\t\t# Not passing this one yet; the common \"B C\" lines is still\n+\t\t# being left in the conflict blocks on the left and right\n+\t\t# sides.\n+\t\ttest_write_lines 1 2 3 4 \\\n+\t\t\t\t \"<<<<<<< HEAD\" X A \\\n+\t\t\t\t \"||||||| $base\" 5 6 \\\n+\t\t\t\t ======= Y A B C \\\n+\t\t\t\t \">>>>>>> right^0\" \\\n+\t\t\t\t B C 7 8 9 \\\n+\t\t\t\t >expect &&\n+\t\ttest_cmp expect evil\n+\t)\n+'\n+\n test_done\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 75b32aef51d..2e3a5a2943e 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -313,6 +313,8 @@ int git_xmerge_config(const char *var, const char *value, void *cb)\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, \"zdiff3\"))\n+\t\t\tgit_xmerge_style = XDL_MERGE_ZEALOUS_DIFF3;\n \t\telse if (!strcmp(value, \"merge\"))\n \t\t\tgit_xmerge_style = 0;\n \t\t/*\ndiff --git a/xdiff/xdiff.h b/xdiff/xdiff.h\nindex b29deca5de8..72e25a9ffa5 100644\n--- a/xdiff/xdiff.h\n+++ b/xdiff/xdiff.h\n@@ -66,6 +66,7 @@ extern \"C\" {\n \n /* merge output styles */\n #define XDL_MERGE_DIFF3 1\n+#define XDL_MERGE_ZEALOUS_DIFF3 2\n \n typedef struct s_mmfile {\n \tchar *ptr;\ndiff --git a/xdiff/xmerge.c b/xdiff/xmerge.c\nindex 1659edb4539..fff0b594f9a 100644\n--- a/xdiff/xmerge.c\n+++ b/xdiff/xmerge.c\n@@ -230,7 +230,7 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,\n \tsize += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,\n \t\t\t      dest ? dest + size : NULL);\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {\n \t\t/* Shared preimage */\n \t\tif (!dest) {\n \t\t\tsize += marker_size + 1 + needs_cr + marker3_size;\n@@ -322,6 +322,40 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,\n \treturn size;\n }\n \n+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)\n+{\n+\treturn xdl_recmatch(rec1->ptr, rec1->size,\n+\t\t\t    rec2->ptr, rec2->size, flags);\n+}\n+\n+/*\n+ * Remove any common lines from the beginning and end of the conflicted region.\n+ */\n+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,\n+\t\txpparam_t const *xpp)\n+{\n+\txrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;\n+\tfor (; m; m = m->next) {\n+\t\t/* let's handle just the conflicts */\n+\t\tif (m->mode)\n+\t\t\tcontinue;\n+\n+\t\twhile(m->chg1 && m->chg2 &&\n+\t\t      recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {\n+\t\t\tm->chg1--;\n+\t\t\tm->chg2--;\n+\t\t\tm->i1++;\n+\t\t\tm->i2++;\n+\t\t}\n+\t\twhile (m->chg1 && m->chg2 &&\n+\t\t       recmatch(rec1[m->i1 + m->chg1 - 1],\n+\t\t\t\trec2[m->i2 + m->chg2 - 1], xpp->flags)) {\n+\t\t\tm->chg1--;\n+\t\t\tm->chg2--;\n+\t\t}\n+\t}\n+}\n+\n /*\n  * Sometimes, changes are not quite identical, but differ in only a few\n  * lines. Try hard to show only these few lines as conflicting.\n@@ -482,7 +516,22 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tint style = xmp->style;\n \tint favor = xmp->favor;\n \n-\tif (style == XDL_MERGE_DIFF3) {\n+\t/*\n+\t * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking\n+\t * at common areas of sides 1 & 2, because the base (side 0) does\n+\t * not match and is being shown.  Similarly, simplification of\n+\t * non-conflicts is also skipped due to the skipping of conflict\n+\t * refinement.\n+\t *\n+\t * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to\n+\t * refine conflicts looking for common areas of sides 1 & 2.\n+\t * However, since the base is being shown and does not match,\n+\t * it will only look for common areas at the beginning or end\n+\t * of the conflict block.  Since XDL_MERGE_ZEALOUS_DIFF3's\n+\t * conflict refinement is much more limited in this fashion, the\n+\t * conflict simplification will be skipped.\n+\t */\n+\tif (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_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@@ -603,10 +652,12 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,\n \tif (!changes)\n \t\tchanges = c;\n \t/* refine conflicts */\n-\tif (XDL_MERGE_ZEALOUS <= level &&\n-\t    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n-\t     xdl_simplify_non_conflicts(xe1, changes,\n-\t\t\t\t\tXDL_MERGE_ZEALOUS < level) < 0)) {\n+\tif (style == XDL_MERGE_ZEALOUS_DIFF3) {\n+\t\txdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);\n+\t} else if (XDL_MERGE_ZEALOUS <= level &&\n+\t\t   (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||\n+\t\t    xdl_simplify_non_conflicts(xe1, changes,\n+\t\t\t\t\t       XDL_MERGE_ZEALOUS < level) < 0)) {\n \t\txdl_cleanup_merge(changes);\n \t\treturn -1;\n \t}\n-- \ngitgitgadget\n\n"},{"id":"442726","messageId":"409ae2bbd85c20a88347a4e1b8afdddcb7c82e5d.1638317108.git.gitgitgadget@gmail.com","threadId":"55931","inReplyTo":"pull.1036.v5.git.git.1638317108.gitgitgadget@gmail.com","subject":"[PATCH v5 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Elijah Newren via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-12-01T00:05:07Z","receivedAt":"2021-12-01T00:05:26Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@gmail.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/config/merge.txt     |  9 ++++++++-\n Documentation/git-checkout.txt     |  3 +--\n Documentation/git-merge-file.txt   |  3 +++\n Documentation/git-merge.txt        | 32 +++++++++++++++++++++++++-----\n Documentation/git-rebase.txt       |  6 +++---\n Documentation/git-restore.txt      |  3 +--\n Documentation/git-switch.txt       |  3 +--\n Documentation/technical/rerere.txt | 10 +++++-----\n builtin/checkout.c                 |  2 +-\n 9 files changed, 50 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex e27cc639447..99e83dd36e5 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -4,7 +4,14 @@ merge.conflictStyle::\n \tshows a `<<<<<<<` conflict marker, changes made by one side,\n \ta `=======` marker, changes made by the other side, and then\n \ta `>>>>>>>` marker.  An alternate style, \"diff3\", adds a `|||||||`\n-\tmarker and the original text before the `=======` marker.\n+\tmarker and the original text before the `=======` marker.  The\n+\t\"merge\" style tends to produce smaller conflict regions than diff3,\n+\tboth because of the exclusion of the original text, and because\n+\twhen a subset of lines match on the two sides they are just pulled\n+\tout of the conflict region.  Another alternate style, \"zdiff3\", is\n+\tsimilar to diff3 but removes matching lines on the two sides from\n+\tthe conflict region when those matching lines appear near either\n+\tthe beginning or end of a conflict region.\n \n merge.defaultToUpstream::\n \tIf merge is called without any commit argument, merge the upstream\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex b1a6fe44997..85c3d3513f7 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -265,8 +265,7 @@ When switching branches with `--merge`, staged changes may be lost.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -p::\n --patch::\ndiff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt\nindex f8560326132..7e9093fab60 100644\n--- a/Documentation/git-merge-file.txt\n+++ b/Documentation/git-merge-file.txt\n@@ -70,6 +70,9 @@ OPTIONS\n --diff3::\n \tShow conflicts in \"diff3\" style.\n \n+--zdiff3::\n+\tShow conflicts in \"zdiff3\" style.\n+\n --ours::\n --theirs::\n --union::\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex e4f3352eb58..e8cecf5a51d 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -240,7 +240,8 @@ from the RCS suite to present such a conflicted hunk, like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n <<<<<<< yours:sample.txt\n Conflict resolution is hard;\n let's go shopping.\n@@ -261,16 +262,37 @@ side wants to say it is hard and you'd prefer to go shopping, while the\n other side wants to claim it is easy.\n \n An alternative style can be used by setting the \"merge.conflictStyle\"\n-configuration variable to \"diff3\".  In \"diff3\" style, the above conflict\n-may look like this:\n+configuration variable to either \"diff3\" or \"zdiff3\".  In \"diff3\"\n+style, the above conflict may look like this:\n \n ------------\n Here are lines that are either unchanged from the common\n-ancestor, or cleanly resolved because only one side changed.\n+ancestor, or cleanly resolved because only one side changed,\n <<<<<<< yours:sample.txt\n+or cleanly resolved because both sides changed the same way.\n Conflict resolution is hard;\n let's go shopping.\n-|||||||\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n+Conflict resolution is hard.\n+=======\n+or cleanly resolved because both sides changed the same way.\n+Git makes conflict resolution easy.\n+>>>>>>> theirs:sample.txt\n+And here is another line that is cleanly resolved or unmodified.\n+------------\n+\n+while in \"zdiff3\" style, it may look like this:\n+\n+------------\n+Here are lines that are either unchanged from the common\n+ancestor, or cleanly resolved because only one side changed,\n+or cleanly resolved because both sides changed the same way.\n+<<<<<<< yours:sample.txt\n+Conflict resolution is hard;\n+let's go shopping.\n+||||||| base:sample.txt\n+or cleanly resolved because both sides changed identically.\n Conflict resolution is hard.\n =======\n Git makes conflict resolution easy.\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 506345cb0ed..dd9830dc5f8 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -741,9 +741,9 @@ information about the rebased commits and their parents (and instead\n generates new fake commits based off limited information in the\n generated patches), those commits cannot be identified; instead it has\n to fall back to a commit summary.  Also, when merge.conflictStyle is\n-set to diff3, the apply backend will use \"constructed merge base\" to\n-label the content from the merge base, and thus provide no information\n-about the merge base commit whatsoever.\n+set to diff3 or zdiff3, the apply backend will use \"constructed merge\n+base\" to label the content from the merge base, and thus provide no\n+information about the merge base commit whatsoever.\n \n The merge backend works with the full commits on both sides of history\n and thus has no such limitations.\ndiff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt\nindex 55bde91ef9e..5964810caa4 100644\n--- a/Documentation/git-restore.txt\n+++ b/Documentation/git-restore.txt\n@@ -92,8 +92,7 @@ in linkgit:git-checkout[1] for details.\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values\n-\tare \"merge\" (default) and \"diff3\" (in addition to what is\n-\tshown by \"merge\" style, shows the original contents).\n+\tare \"merge\" (default), \"diff3\", and \"zdiff3\".\n \n --ignore-unmerged::\n \tWhen restoring files on the working tree from the index, do\ndiff --git a/Documentation/git-switch.txt b/Documentation/git-switch.txt\nindex 5c438cd5058..5c90f76fbe3 100644\n--- a/Documentation/git-switch.txt\n+++ b/Documentation/git-switch.txt\n@@ -137,8 +137,7 @@ should result in deletion of the path).\n \tThe same as `--merge` option above, but changes the way the\n \tconflicting hunks are presented, overriding the\n \t`merge.conflictStyle` configuration variable.  Possible values are\n-\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n-\t\"merge\" style, shows the original contents).\n+\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n \n -q::\n --quiet::\ndiff --git a/Documentation/technical/rerere.txt b/Documentation/technical/rerere.txt\nindex af5f9fc24f9..35d45414339 100644\n--- a/Documentation/technical/rerere.txt\n+++ b/Documentation/technical/rerere.txt\n@@ -14,9 +14,9 @@ conflicts before writing them to the rerere database.\n \n Different conflict styles and branch names are normalized by stripping\n the labels from the conflict markers, and removing the common ancestor\n-version from the `diff3` conflict style. Branches that are merged\n-in different order are normalized by sorting the conflict hunks.  More\n-on each of those steps in the following sections.\n+version from the `diff3` or `zdiff3` conflict styles.  Branches that\n+are merged in different order are normalized by sorting the conflict\n+hunks.  More on each of those steps in the following sections.\n \n Once these two normalization operations are applied, a conflict ID is\n calculated based on the normalized conflict, which is later used by\n@@ -42,8 +42,8 @@ get a conflict like the following:\n     >>>>>>> AC\n \n Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB\n-and then merging branch AC2 into it), using the diff3 conflict style,\n-we get a conflict like the following:\n+and then merging branch AC2 into it), using the diff3 or zdiff3\n+conflict style, we get a conflict like the following:\n \n     <<<<<<< HEAD\n     B\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c69dcdf72a..a0b0fac6a68 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1519,7 +1519,7 @@ static struct option *add_common_options(struct checkout_opts *opts,\n \t\tOPT_BOOL(0, \"progress\", &opts->show_progress, N_(\"force progress reporting\")),\n \t\tOPT_BOOL('m', \"merge\", &opts->merge, N_(\"perform a 3-way merge with the new branch\")),\n \t\tOPT_STRING(0, \"conflict\", &opts->conflict_style, N_(\"style\"),\n-\t\t\t   N_(\"conflict style (merge or diff3)\")),\n+\t\t\t   N_(\"conflict style (merge, diff3, or zdiff3)\")),\n \t\tOPT_END()\n \t};\n \tstruct option *newopts = parse_options_concat(prevopts, options);\n-- \ngitgitgadget\n"},{"id":"442859","messageId":"df70e408-9355-aff7-c6fe-540b5c687865@gmail.com","threadId":"55931","inReplyTo":"409ae2bbd85c20a88347a4e1b8afdddcb7c82e5d.1638317108.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-12-02T08:42:42Z","receivedAt":"2021-12-02T08:42:49Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 01/12/21 07.05, Elijah Newren via GitGitGadget wrote:\n>   \tThe same as `--merge` option above, but changes the way the\n>   \tconflicting hunks are presented, overriding the\n>   \t`merge.conflictStyle` configuration variable.  Possible values are\n> -\t\"merge\" (default) and \"diff3\" (in addition to what is shown by\n> -\t\"merge\" style, shows the original contents).\n> +\t\"merge\" (default), \"diff3\", and \"zdiff3\".\n>   \n\nMissing '... (the latter both also shows the original contents)'.\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"442864","messageId":"CAPig+cRh6BLTqCFRkfspZt+7DnDt4is5=w1AUa6UiBtOYKnBBA@mail.gmail.com","threadId":"55931","inReplyTo":"df70e408-9355-aff7-c6fe-540b5c687865@gmail.com","subject":"Re: [PATCH v5 2/2] update documentation for new zdiff3 conflictStyle","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2021-12-02T13:28:32Z","receivedAt":"2021-12-02T13:28:53Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Dec 2, 2021 at 3:42 AM Bagas Sanjaya <bagasdotme@gmail.com> wrote:\n> On 01/12/21 07.05, Elijah Newren via GitGitGadget wrote:\n> >       The same as `--merge` option above, but changes the way the\n> >       conflicting hunks are presented, overriding the\n> >       `merge.conflictStyle` configuration variable.  Possible values are\n> > -     \"merge\" (default) and \"diff3\" (in addition to what is shown by\n> > -     \"merge\" style, shows the original contents).\n> > +     \"merge\" (default), \"diff3\", and \"zdiff3\".\n>\n> Missing '... (the latter both also shows the original contents)'.\n\nThe omission is clearly intentional. The additional text added to the\nhunk just above this one (which you snipped), does a better job of\nexplaining the differences between the styles than this little blurb\ndid. Moreover, there is insufficient context at this location in the\ndocumentation for a reader to fully understand the difference between\n\"merge\" and \"diff3\", and the problem of lack of context becomes worse\nwith the addition of \"zdiff3\". So, Elijah's choice to drop this blurb\n-- leaving just an enumeration of choices -- and elsewhere provide\ncomprehensive detail about the differences is quite sensible. The\nrevised text is improved by the loss of the blurb.\n"}]}