{"push_id":2955490233,"size":13,"distinct_size":13,"ref":"refs/heads/merge-file-collision-consistency","head":"99c77dcb796ac10df8c66d4fbae3797369935cd3","before":"28b69c0aa98c1dff62c76c012d138720e0609162","commits":[{"sha":"1625150bd84c626f2e46fb06f870445902161521","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"t6036: add testcase where virtual merge base contains nested conflicts\n\nWhen merges involve content conflicts, merge.conflictstyle=diff3 can be\nused to also show the content for the file from the base version.  If\nthere was more than one merge base, then the \"base version\" is\ndetermined by merging the merge bases to create a virtual merge base.\nThis means that the \"base version\" of the file can itself already have\nconflict markers.  In the past, this could have created a situation\nwhere users have difficulty distinguishing between the inner and outer\nconflict markers.  However, commit d694a17986a2 (\"ll-merge: use a longer\nconflict marker for internal merge\", 2016-04-14) addressed this issue by\nmaking conflict markers two characters longer for a virtual merge base.\n\nUnfortunately, this solution was incomplete.  The recursive merge\nalgorithm is, as you'd expect from it's name, recursive in nature.  In\nparticular, this means that the virtual merge base itself could have had\na virtual merge base of its own that contained conflict markers.  In\nother words, we can have three (or more) levels of conflict markers when\nusing merge.conflictstyle=diff3.  Thus, what we need is for conflict\nmarker length to increase with the depth of recursion.  Add a testcase\ndemonstrating the problem and testing for marker length increasing with\nincreasing recursion depth.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/1625150bd84c626f2e46fb06f870445902161521"},{"sha":"2cfaeca9659d6034cb4193768ea54af7f544b4f4","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: increase marker length with depth of recursion\n\nWhen using merge.conflictstyle=diff3 to have the \"base version\" be shown\nin conflicts, there is the possibility that the base version itself has\nconflicts in it.  This comes about when there are more than one merge\nbase, and the merging of those merge bases produces a conflict.\nSince this process applies recursively, it is possible to have conflict\nmarkers nested at an arbitrary depth; to be able to differentiate the\nconflict markers from different nestings, we make them all of different\nlengths.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/2cfaeca9659d6034cb4193768ea54af7f544b4f4"},{"sha":"ee06b1cb3452d3e1c5177270689dfb32ec340629","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: improve auto-merging messages with path collisions\n\nEach individual file involved in a rename could have also been modified\non both sides of history, meaning it may need to have content merges.\nIf two such files are renamed into the same location, then on top of the\ntwo natural auto-merging messages we also have to two-way merge the\nresult, giving us messages that look like\n\n  Auto-merging somefile.c (was somecase.c)\n  Auto-merging somefile.c (was somefolder.c)\n  Auto-merging somefile.c\n\nHowever, despite the fact that I was the one who put the \"(was %s)\"\nportions into the messages (and just a few months ago), I was still\ninitially confused when running into a rename/rename(2to1) case and\nwondered if somefile.c had been merged three times.  Update this to\ninstead be:\n\n  Auto-merging version of somefile.c from somecase.c\n  Auto-merging version of somefile.c from someportfolio.c\n  Auto-merging somefile.c\n\nThis is an admittedly long set of messages for a single path, but you\nonly get all three messages when dealing with the rare case of a\nrename/rename(2to1) conflict where both sides of both original files\nwere also modified, in conflicting ways.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/ee06b1cb3452d3e1c5177270689dfb32ec340629"},{"sha":"9e4dfcab430e687ff4e57c25bac0a6af25e3318c","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: Avoid showing conflicts with merge branch before HEAD\n\nWe want to load unmerged entries from HEAD into the index at stage 2 and\nfrom MERGE_HEAD into stage 3.  Similarly, folks expect merge conflicts\nto look like\n\n<<<<<<<< HEAD\ncontent from our side\n========\ncontent from their side\n>>>>>>>> MERGE_HEAD\n\nnot\n\n<<<<<<<< MERGE_HEAD\ncontent from their side\n========\ncontent from our side\n>>>>>>>> HEAD\n\nThe correct order usually comes naturally and for free, but with renames\nwe often have data in the form {rename_branch, other_branch}, and\nworking relative to the rename first (e.g. for rename/add) is more\nconvenient elsewhere in the code.  Address the slight impedance\nmismatch by having some functions re-call themselves with flipped\narguments when the branch order is reversed.\n\nNote that setup_rename_conflict_info() has one asymmetry in it, in\nsetting dst_entry1->processed=0 but not doing similarly for\ndst_entry2->processed.  When dealing with rename/rename and similar\nconflicts, we do not want the processing to happen twice, so the\ndesire to only set one of the entries to unprocessed is intentional.\nSo, while this change modifies which branch's entry will be marked as\nunprocessed, that dovetails nicely with putting HEAD first so that we\nget the index stage entries and conflict markers in the right order.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/9e4dfcab430e687ff4e57c25bac0a6af25e3318c"},{"sha":"76d90189fd0095dba7e9efb7329d23ed86b48942","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"Add testcases for consistency in file collision conflict handling\n\nAdd testcases dealing with file collisions for the following types of\nconflicts:\n  * add/add\n  * rename/add\n  * rename/rename(2to1)\n\nAll these conflict types simplify down to two files \"colliding\"\nand should thus be handled similarly.  This means that rename/add and\nrename/rename(2to1) conflicts need to be modified to behave the same as\nadd/add conflicts currently do: the colliding files should be two-way\nmerged (instead of the current behavior of writing the two colliding\nfiles out to separate temporary unique pathnames).  Add testcases which\ncheck this; subsequent commits will fix the conflict handling to make\nthese tests pass.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/76d90189fd0095dba7e9efb7329d23ed86b48942"},{"sha":"867471c639285ae8bca319c70a844d1bfff1c0dd","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"t6036, t6042: testcases for rename collision of already conflicting files\n\nWhen a single file is renamed, it can also be modified, yielding the\npossibility of that renamed file having content conflicts.  If two\ndifferent such files are renamed into the same location, then two-way\nmerging those files may result in nested conflicts.  Add a testcase that\nmakes sure we get this case correct, and uses different lengths of\nconflict markers to differentiate between the different nestings.\n\nAlso add another case with an extra (i.e. third) level of conflict\nmarkers due to using merge.conflictstyle=diff3 and the virtual merge\nbase also having conflicts present.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/867471c639285ae8bca319c70a844d1bfff1c0dd"},{"sha":"ef78f37e257fb7d9db24ca48340f9e0bd8701ac8","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: new function for better colliding conflict resolutions\n\nThere are three conflict types that represent two (possibly entirely\nunrelated) files colliding at the same location:\n  * add/add\n  * rename/add\n  * rename/rename(2to1)\n\nThese three conflict types already share more similarity than might be\nimmediately apparent from their description: (1) the handling of the\nrename variants already involves removing any entries from the index\ncorresponding to the original file names[*], thus only leaving entries\nin the index for the colliding path; (2) likewise, any trace of the\noriginal file name in the working tree is also removed.  So, in all\nthree cases we're left with how to represent two colliding files in both\nthe index and the working copy.\n\n[*] Technically, this isn't quite true because rename/rename(2to1)\nconflicts in the recursive (o->call_depth > 0) case do an \"unrename\"\nsince about seven years ago.  But even in that case, Junio felt\ncompelled to explain that my decision to \"unrename\" wasn't necessarily\nthe only or right answer -- search for \"Comment from Junio\" in t6036 for\ndetails.\n\nMy initial motivation for looking at these three conflict types was that\nif the handling of these three conflict types is the same, at least in\nthe limited set of cases where a renamed file is unmodified on the side\nof history where the file is not renamed, then a significant performance\nimprovement for rename detection during merges is possible.  However,\nwhile that served as motivation to look at these three types of\nconflicts, the actual goal of this new function is to try to improve the\nhandling for all three cases, not to merely make them the same as each\nother in that special circumstance.\n\n=== Handling the working tree ===\n\nThe previous behavior for these conflict types in regards to the\nworking tree (assuming the file collision occurs at 'foo') was:\n  * add/add does a two-way merge of the two files and records it as 'foo'.\n  * rename/rename(2to1) records the two different files into two new\n    uniquely named files (foo~HEAD and foo~$MERGE), while removing 'foo'\n    from the working tree.\n  * rename/add records the two different files into two different\n    locations, recording the add at foo~$SIDE and, oddly, recording\n    the rename at foo (why is the rename more important than the add?)\n\nSo, the question for what to write to the working tree boils down to\nwhether the two colliding files should be two-way merged and recorded in\nplace, or recorded into separate files.  As per discussion on the git\nmailing lit, two-way merging was deemed to always be preferred, as that\nmakes these cases all more like content conflicts that users can handle\nfrom within their favorite editor, IDE, or merge tool.  Note that since\nrenames already involve a content merge, rename/add and\nrename/rename(2to1) conflicts could result in nested conflict markers.\n\n=== Handling of the index ===\n\nFor a typical rename, unpack_trees() would set up the index in the\nfollowing fashion:\n           old_path  new_path\n   stage1: 5ca1ab1e  00000000\n   stage2: f005ba11  00000000\n   stage3: 00000000  b0a710ad\nAnd merge-recursive would rewrite this to\n           new_path\n   stage1: 5ca1ab1e\n   stage2: f005ba11\n   stage3: b0a710ad\nRemoving old_path from the index means the user won't have to `git rm\nold_path` manually every time a renamed path has a content conflict.\nIt also means they can use `git checkout [--ours|--theirs|--conflict|-m]\nnew_path`, `git diff [--ours|--theirs]` and various other commands that\nwould be difficult otherwise.\n\nThis strategy becomes a problem when we have a rename/add or\nrename/rename(2to1) conflict, however, because then we have only three\nslots to store blob sha1s and we need either four or six.  Previously,\nthis was handled by continuing to delete old_path from the index, and\njust outright ignoring any blob shas from old_path.  That had the\ndownside of deleting any trace of changes made to old_path on the other\nside of history.  This function instead does a three-way content merge of\nthe renamed file, and stores the blob sha1 for that at either stage2 or\nstage3 for new_path (depending on which side the rename came from).  That\nhas the advantage of bringing information about changes on both sides and\nstill allows for easy resolution (no need to git rm old_path, etc.), but\ndoes have the downside that if the content merge had conflict markers,\nthen what we store in the index is the sha1 of a blob with conflict\nmarkers.  While that is a downside, it seems less problematic than the\ndownsides of any obvious alternatives, and certainly makes more sense\nthan the previous handling.  Further, it has a precedent in that when we\ndo recursive merges, we may accept a file with conflict markers as the\nresolution for the merge of the merge-bases, which will then show up in\nthe index of the outer merge at stage 1 if a conflict exists at the outer\nlevel.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/ef78f37e257fb7d9db24ca48340f9e0bd8701ac8"},{"sha":"91afe4868192f10dddf23ef5846b738076e59302","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: SQUASHME -- remove conflicts-already-present flag","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/91afe4868192f10dddf23ef5846b738076e59302"},{"sha":"692b50601d8b3240b25a3b71a0b67c17ee7d11fc","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: fix rename/add conflict handling\n\nThis makes the rename/add conflict handling make use of the new\nhandle_file_collision() function, which fixes several bugs and improves\nthings for the rename/add case significantly.  Previously, rename/add\nwould:\n\n  * Not leave any higher order stage entries in the index, making it\n    appear as if there were no conflict.\n  * Would place the rename file at the colliding path, and move the\n    added file elsewhere, which combined with the lack of higher order\n    stage entries felt really odd.  It's not clear to me why the\n    rename should take precedence over the add; if one should be moved\n    out of the way, they both probably should.\n  * In the recursive case, it would do a two way merge of the added\n    file and the version of the renamed file on the renamed side,\n    completely excluding modifications to the renamed file on the\n    unrenamed side of history.\n\nUsing the new handle_file_collision() fixes all of these issues, and\nadds smarts to allow two-way merge OR move colliding files to separate\npaths depending on the similarity of the colliding files.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/692b50601d8b3240b25a3b71a0b67c17ee7d11fc"},{"sha":"4295fc93f5282f95d12556745e4120c1d2d2dec4","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: improve handling for rename/rename(2to1) conflicts\n\nThis makes the rename/rename(2to1) conflicts use the new\nhandle_file_collision() function.  Since that function was based\noriginally on the rename/rename(2to1) handling code, the main\ndifferences here are in what was added.  In particular:\n\n  * Instead of storing files at collide_path~HEAD and collide_path~MERGE,\n    the files are two-way merged and recorded at collide_path.\n\n  * Instead of recording the version of the renamed file that existed\n    on the renamed side in the index (thus ignoring any changes that\n    were made to the file on the side of history without the rename),\n    we do a three-way content merge on the renamed path, then store\n    that at either stage 2 or stage 3.\n\n  * Note that since the content merge for each rename may have conflicts,\n    and then we have to merge the two renamed files, we can end up with\n    nested conflict markers.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/4295fc93f5282f95d12556745e4120c1d2d2dec4"},{"sha":"5985374a94cc548c5db57ccbc898470d90ffdc8e","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: use handle_file_collision for add/add conflicts\n\nThis results in no-net change of behavior, it simply ensures that all\nfile-collision conflict handling types are being handled the same by\ncalling the same function.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/5985374a94cc548c5db57ccbc898470d90ffdc8e"},{"sha":"8020db6ca44878dbfb8c3f7fb6b7efe4fba9f71b","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"merge-recursive: improve rename/rename(1to2)/add[/add] handling\n\nFIXME: For sides that are a normal rename, they could be involved in\na D/F conflict (tracked or untracked) or have dirty changes; logic needs\nto be smarter here.  (Can I re-use merge_content() somehow?)\n\nWhen we have a rename/rename(1to2) conflict, each of the renames can\ncollide with a file addition.  Each of these rename/add conflicts suffered\nfrom the same kinds of problems that normal rename/add suffered from.\nMake the code use handle_file_conflicts() as well so that we get all the\nsame fixes and consistent behavior between the different conflict types.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/8020db6ca44878dbfb8c3f7fb6b7efe4fba9f71b"},{"sha":"99c77dcb796ac10df8c66d4fbae3797369935cd3","author":{"name":"Elijah Newren","email":"955405675e5ddb1a110d200661c528aa223ef60f@gmail.com"},"message":"t6036: Testcase representing tectonic inundation of conflicts\n\nFIXME:\nHowever, the whole 'undo renames for recursive case' kind of destroys the\nwhole setup.  Remove this test?","distinct":true,"url":"https://api.github.com/repos/newren/git/commits/99c77dcb796ac10df8c66d4fbae3797369935cd3"}]}
{"actor":{"display_login":"newren"}}