{"thread":{"id":"34638","subject":"[Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","startedAt":"2013-08-08T07:07:23Z","lastAt":"2013-08-16T00:05:17Z","messageCount":5,"participants":["Luke San Antonio","Phil Hord"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"224792","messageId":"CAP+CFTZWzoAg=ni6t1aif-85y6W4-2JUT4wooapTvD0oGH+HRw@mail.gmail.com","threadId":"34638","inReplyTo":null,"subject":"[Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","fromName":"Luke San Antonio","fromEmail":"lukesanantonio@gmail.com","sentAt":"2013-08-08T07:07:23Z","receivedAt":"2013-08-08T07:07:23Z","isPatch":false,"sender":{"key":"lukesanantonio@gmail.com","avatar":"https://gravatar.com/avatar/15116120eb7dfaad53c5c03b5c1091770866a3bdce4f7ac6657400c1d03cf3ce?d=mp&s=160"},"body":"Hi, my name's Luke!\n\nToday, I had a problem merging a stash after immediately creating it.\nThis is exactly what I did!\n\ngit stash save --keep-index\ngit stash pop\n\nAnd BAM! Merge conflict! This was especially weird because my file had\nthis in it (taken directly from my code!)\n\n<<<<<<< Updated upstream\n     *\n     * It should be used and passed along to member objects by GameStates!\n     *\n=======\n     *\n     * It should be used and passed along to member objects by GameStates!\n     *\n>>>>>>> Stashed changes\n\nThey are exactly the same!\n\nOh, by the way, I should mention that I did not edit any hunks to get\nthe index the way I wanted it, I have read that doing that causes\nmerge conflicts similar to this!\n\nThen I got a hunch! I realized that git will refrain from applying a\nhunk if it finds it already was applied exactly (that's correct\nright?)... So I thought, maybe the patches are similar (represent the\nsame changes) but aren't *exactly* the same.\n\nI was right!\n\nAfter saving the stash I take a look at the diff: (git stash show -p)\n\n     /*!\n-     * \\brief The default font renderer, global to all who have a pointer to\n-     * the Game class.\n+     * \\brief The font renderer implementation, obtained from the config file.\n+     *\n+     * It should be used and passed along to member objects by GameStates!\n      *\n-     * It need not be used at all!\n+     * \\note It can be cached, but not between GameStates, meaning it should be\n+     * cached again every time a new GameState is constructed!\n      */\n\nAfter that, I take a look at the diff in my index: (git diff --staged)\n\n     /*!\n-     * \\brief The default font renderer, global to all who have a pointer to\n-     * the Game class.\n+     * \\brief The font renderer implementation, obtained from the config file.\n      *\n-     * It need not be used at all!\n+     * It should be used and passed along to member objects by GameStates!\n+     *\n+     * \\note It can be cached, but not between GameStates, meaning it should be\n+     * cached again every time a new GameState is constructed!\n      */\n\nAha! A difference, a difference so tiny it went unnoticed by me, but not by git!\n\nNow the housekeeping:\n\nWhat I wanted to do:\n\nApply a stash on top of a 'kept' index.\n\nWhat I did:\n\ngit stash save --keep-index\ngit stash pop\n\nWhat I saw happen:\n\nA merge conflict between the same changes (see above).\n\nWhat I expected to see:\n\nNo merge conflict.\n\nHow are these different:\n\nThe conflict, which shouldn't happen since the changes introduced were the same!\n\n-------------------------------------------------\n\nIt seems to me like the stash command is using a slightly different\ndiff algorithm...\nCan anyone explain to me what's going on under the hood so I can\nunderstand this subtle difference? Does anyone know?\n\nThanks in advance, I'm sure you all will be very helpful!\n- Luke\n"},{"id":"224853","messageId":"CABURp0oATJ58POmgYLjSmy-y84LTjkP0PTh4=3M-vc04_AoMdg@mail.gmail.com","threadId":"34638","inReplyTo":"CAP+CFTZWzoAg=ni6t1aif-85y6W4-2JUT4wooapTvD0oGH+HRw@mail.gmail.com","subject":"Re: [Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2013-08-08T20:54:59Z","receivedAt":"2013-08-08T20:54:59Z","isPatch":false,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On Thu, Aug 8, 2013 at 3:07 AM, Luke San Antonio\n<lukesanantonio@gmail.com> wrote:\n>\n> Hi, my name's Luke!\n>\n> Today, I had a problem merging a stash after immediately creating it.\n> This is exactly what I did!\n>\n> git stash save --keep-index\n> git stash pop\n>\n> And BAM! Merge conflict! This was especially weird because my file had\n> this in it (taken directly from my code!)\n>\n\nLuke,\n\nI think the issue is that your working directory receives your cached\nfile when you say 'git stash --keep-index'.  When you restore the\nstash, your previous working directory now conflicts with your new\nworking directory, but neither is the same as HEAD.\n\nHere's a test script to demonstrate the issue, I think.  Did I get\nthis right, Luke?\n\n # cd /tmp && rm -rf foo\n git init foo && cd foo\n echo \"foo\" > bar &&  git add bar && git commit -mfoo\n echo \"bar\" > bar &&  git add bar\n echo \"baz\" > bar\n echo \"Before stash  bar: $(cat bar)\"\n git stash --keep-index\n echo \"After stash  bar: $(cat bar)\"\n git stash apply\n\n\nThe output looks like this:\n\n$  git init foo && cd foo\nInitialized empty Git repository in /tmp/foo/.git/\n$ git commit --allow-empty -mInitialCommit\n[master (root-commit) b5ecc7e] InitialCommit\n$ echo \"Bar\" > bar &&  git add bar && git commit -mBar\n[master 16d708b] Bar\n 1 file changed, 1 insertion(+)\n create mode 100644 bar\n$ echo \"bar\" > bar &&  git add bar\n$  echo \"baz\" > bar\n$  echo \"Before stash  bar: $(cat bar)\"\nBefore stash  bar: baz\n$  git stash --keep-index\nSaved working directory and index state WIP on master: 16d708b Bar\nHEAD is now at 16d708b Bar\n$  echo \"After stash  bar: $(cat bar)\"\nAfter stash  bar: bar\n$  git stash apply\nAuto-merging bar\nCONFLICT (content): Merge conflict in bar\nRecorded preimage for 'bar'\n$ cat bar\n<<<<<<< Updated upstream\nbar\n=======\nbaz\n>>>>>>> Stashed changes\n\n\n\nPhil\n"},{"id":"225070","messageId":"520872CC.3040507@gmail.com","threadId":"34638","inReplyTo":"CABURp0oATJ58POmgYLjSmy-y84LTjkP0PTh4=3M-vc04_AoMdg@mail.gmail.com","subject":"Re: [Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","fromName":"Luke San Antonio","fromEmail":"lukesanantonio@gmail.com","sentAt":"2013-08-12T05:29:48Z","receivedAt":"2013-08-12T05:29:48Z","isPatch":false,"sender":{"key":"lukesanantonio@gmail.com","avatar":"https://gravatar.com/avatar/15116120eb7dfaad53c5c03b5c1091770866a3bdce4f7ac6657400c1d03cf3ce?d=mp&s=160"},"body":"On 08/08/20130 04:54 PM, Phil Hord wrote:\n> Luke,\n>\n> I think the issue is that your working directory receives your cached\n> file when you say 'git stash --keep-index'.  When you restore the\n> stash, your previous working directory now conflicts with your new\n> working directory, but neither is the same as HEAD.\n>\n> Here's a test script to demonstrate the issue, I think.  Did I get\n> this right, Luke?\n>\n>   # cd /tmp && rm -rf foo\n>   git init foo && cd foo\n>   echo \"foo\" > bar &&  git add bar && git commit -mfoo\n>   echo \"bar\" > bar &&  git add bar\n>   echo \"baz\" > bar\n>   echo \"Before stash  bar: $(cat bar)\"\n>   git stash --keep-index\n>   echo \"After stash  bar: $(cat bar)\"\n>   git stash apply\nActually no, in your script, the bar file has a modification in the working\ntree which is in the same hunk as a change applied to the index. In my\nproject the changes that were added to the index are not modified further\nin theworking tree.\n\n--------\n\nNot only that, but I found out why git was generated different patches!\nI realized that when I removed a hunk appearing before the merge conflict\nfrom the working tree and index, the merge conflict disappeared! Turns\nout, we can forget about stashing for a minute!\nFirst the hunk in my working tree:\n\n@@ -56,12 +56,14 @@\n      bool running_ = true;\n\n      /*!\n-     * \\brief The default font renderer, global to all who have a \npointer to\n-     * the Game class.\n+     * \\brief The font renderer implementation, obtained from the \nconfig file.\n       *\n-     * It need not be used at all!\n+     * It should be used and passed along to member objects by GameStates!\n+     *\n+     * \\note It can be cached, but not between GameStates, meaning it \nshould be\n+     * cached again every time a new GameState is constructed!\n       */\n-    std::unique_ptr<FontRenderer> font_renderer_ = nullptr;\n+    FontRenderer* font_renderer_ = nullptr;\n\n      int run(int argc, char* argv[]);\n\nMost of this is unimportant, but notice the line number spec:@@ -56,12 \n+56,14 @@\nThe line number of this hunk doesn't change! Then I addeda few lines *above*\nthis hunk, (around line 30 I think). Here is the diff again:\n\n@@ -56,12 +58,14 @@\n      bool running_ = true;\n\n      /*!\n-     * \\brief The default font renderer, global to all who have a \npointer to\n-     * the Game class.\n+     * \\brief The font renderer implementation, obtained from the \nconfig file.\n+     *\n+     * It should be used and passed along to member objects by GameStates!\n       *\n-     * It need not be used at all!\n+     * \\note It can be cached, but not between GameStates, meaning it \nshould be\n+     * cached again every time a new GameState is constructed!\n       */\n-    std::unique_ptr<FontRenderer> font_renderer_ = nullptr;\n+    FontRenderer* font_renderer_ = nullptr;\n\n      int run(int argc, char* argv[]);\n\nNotice the new line number spec:@@ -56,12 +58,14 @@\n\nIt moves two lines down, because I added those two lines before it, \nmakes sense!\nBut also notice that the patches are different, just because of the two \nlines\nabove it!\n\nI thought I might be able to fix this problem by changing the new \ndiff.algorithm\nconfig option to 'patience', but it seems to only affect how patches \nlook, not\nhow they are stored internally... Same problem!\n\nAlso, I'm wondering why that line was picked up by git if the patches \ndon't match,\nshouldn't git give me a conflict with the whole hunk, or is the system \nsmarter\nthan that?\n\nWhat if merging suppressed the conflict if both possibilities are the \nsame? Isn't\nthat reasonable, or is there some 1% where this could cause (silent but \ndeadly)\ndata loss.\n\nSorry, I'm just rambling...\n\nAnyway, thanks for your help Phil!\n- Luke\n"},{"id":"225129","messageId":"5209C4CD.8040607@gmail.com","threadId":"34638","inReplyTo":"CABURp0rWMAs9vT791vp4BEYS-Y9Jmzjmt4bbuB+po8=vkiqUWQ@mail.gmail.com","subject":"Re: [Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","fromName":"Luke San Antonio","fromEmail":"lukesanantonio@gmail.com","sentAt":"2013-08-13T05:31:57Z","receivedAt":"2013-08-13T05:31:57Z","isPatch":false,"sender":{"key":"lukesanantonio@gmail.com","avatar":"https://gravatar.com/avatar/15116120eb7dfaad53c5c03b5c1091770866a3bdce4f7ac6657400c1d03cf3ce?d=mp&s=160"},"body":"On 08/12/2013 12:05 PM, Phil Hord wrote:\n> On Mon, Aug 12, 2013 at 1:29 AM, Luke San Antonio\n> <lukesanantonio@gmail.com> wrote:\n>> On 08/08/20130 04:54 PM, Phil Hord wrote:\n>>> Luke,\n>>>\n>>> I think the issue is that your working directory receives your cached\n>>> file when you say 'git stash --keep-index'.  When you restore the\n>>> stash, your previous working directory now conflicts with your new\n>>> working directory, but neither is the same as HEAD.\n>>>\n>>> Here's a test script to demonstrate the issue, I think.  Did I get\n>>> this right, Luke?\n>>>\n>>>    # cd /tmp && rm -rf foo\n>>>    git init foo && cd foo\n>>>    echo \"foo\" > bar &&  git add bar && git commit -mfoo\n>>>    echo \"bar\" > bar &&  git add bar\n>>>    echo \"baz\" > bar\n>>>    echo \"Before stash  bar: $(cat bar)\"\n>>>    git stash --keep-index\n>>>    echo \"After stash  bar: $(cat bar)\"\n>>>    git stash apply\n>> Actually no, in your script, the bar file has a modification in the working\n>> tree which is in the same hunk as a change applied to the index. In my\n>> project the changes that were added to the index are not modified further\n>> in theworking tree.\n>>\n>> --------\n>>\n>> Not only that, but I found out why git was generated different patches!\n>> I realized that when I removed a hunk appearing before the merge conflict\n>> from the working tree and index, the merge conflict disappeared! Turns\n>> out, we can forget about stashing for a minute!\n>> First the hunk in my working tree:\n>>\n>> @@ -56,12 +56,14 @@\n>>       bool running_ = true;\n>>\n>>\n>>       /*!\n>> -     * \\brief The default font renderer, global to all who have a pointer\n>> to\n>> -     * the Game class.\n>> +     * \\brief The font renderer implementation, obtained from the config\n>> file.\n>>        *\n>> -     * It need not be used at all!\n>> +     * It should be used and passed along to member objects by GameStates!\n>> +     *\n>> +     * \\note It can be cached, but not between GameStates, meaning it\n>> should be\n>> +     * cached again every time a new GameState is constructed!\n>>        */\n>> -    std::unique_ptr<FontRenderer> font_renderer_ = nullptr;\n>> +    FontRenderer* font_renderer_ = nullptr;\n>>\n>>       int run(int argc, char* argv[]);\n>>\n>> Most of this is unimportant, but notice the line number spec:@@ -56,12\n>> +56,14 @@\n>> The line number of this hunk doesn't change! Then I addeda few lines *above*\n>> this hunk, (around line 30 I think). Here is the diff again:\n>>\n>> @@ -56,12 +58,14 @@\n>>       bool running_ = true;\n>>\n>>\n>>       /*!\n>> -     * \\brief The default font renderer, global to all who have a pointer\n>> to\n>> -     * the Game class.\n>> +     * \\brief The font renderer implementation, obtained from the config\n>> file.\n>> +     *\n>> +     * It should be used and passed along to member objects by GameStates!\n>>        *\n>> -     * It need not be used at all!\n>> +     * \\note It can be cached, but not between GameStates, meaning it\n>> should be\n>> +     * cached again every time a new GameState is constructed!\n>>        */\n>> -    std::unique_ptr<FontRenderer> font_renderer_ = nullptr;\n>> +    FontRenderer* font_renderer_ = nullptr;\n>>\n>>       int run(int argc, char* argv[]);\n>>\n>> Notice the new line number spec:@@ -56,12 +58,14 @@\n>>\n>> It moves two lines down, because I added those two lines before it, makes\n>> sense!\n>> But also notice that the patches are different, just because of the two\n>> lines\n>> above it!\n>>\n>> I thought I might be able to fix this problem by changing the new\n>> diff.algorithm\n>> config option to 'patience', but it seems to only affect how patches look,\n>> not\n>> how they are stored internally... Same problem!\n>>\n>> Also, I'm wondering why that line was picked up by git if the patches don't\n>> match,\n>> shouldn't git give me a conflict with the whole hunk, or is the system\n>> smarter\n>> than that?\n> Git does not store patches.  Git stores the entire file.  I do not\n> think the diff algorithm you choose will have any effect on the\n> results of the merge.  But I am pretty clueless about the merge\n> engine, so I could be off-base on this last part.\n>\n>> What if merging suppressed the conflict if both possibilities are the same?\n>> Isn't\n>> that reasonable, or is there some 1% where this could cause (silent but\n>> deadly)\n>> data loss.\n> I think that is what Git is meant to do.  But I am confused now about\n> where the failure is occurring for you.  Can you demonstrate the\n> problem by modifying my test script?\n>\n> Is this more like it?\n>\n>    cd /tmp && rm -rf foo\n>    git init foo && cd foo\n>    printf \"1\\n2 foo\\n3\\n4\\n5\\n6\\n7\\n8 foo\\n9\\n10\\n\" > bar &&  git add bar\n>    git commit -mfoo\n>    printf \"1\\n2 XXX\\n3\\n4\\n5\\n6\\n7\\n8 foo\\n9\\n10\\n\" > bar &&  git add bar\n>    printf \"1\\n2 XXX\\n3\\n4\\n5\\n6\\n7\\n8 XXX\\n9\\n10\\n\" > bar\n>    echo \"Before stash  bar: $(cat bar)\"\n>    git stash --keep-index\n>    echo \"After stash  bar: $(cat bar)\"\n>    git stash apply\n>\n> Phil\nSo I found an isolated case, it's very strange...\n\nHere's a script!\n\n   cd /tmp && rm -rf foo;\n   git init foo && cd foo;\n   git config --local diff.algorithm myers\n   printf \"\\n\\n-----------------\\n\\n\\n    /*\"'!'\"\\\n\\n     * ---------\\n     * ^^^^^^^^^\\n     *\\n     \\\n* =========\\n     */\\n    |-----------|\\n\" > foo;\n   git add foo && git commit -m \"foo\";\n   printf \"\\n-----------------\\n\\n\\n    /*\"'!'\"\\\n\\n     * #########\\n     *\\n     * !!!!!!!!!\\n     \\\n*\\n     * @@@@@@@@@\\n     * &&&&&&&&&\\n     */\\n    \\\n|===========|\\n\" > foo\n   printf \"s\\nn\\ny\\ny\\ny\\n\" | git add foo --patch > /dev/null\n   git stash save --keep-index\n\nLet me start off by apologizing for that! =D\n\n... Copy and paste that into a terminal and you should have a recreated \nversion of my repository there! Now that the file is partly stashed and \npartly in the index, check out the difference in diffs:\n\ntry:\n   git diff --staged\nthen try:\n   git stash show -p\n\nYou see the difference? Then pop the stash and you'll see a very \nobfuscated and verbose sample of what I am talking about!\n\nAlso, sorry about the typos in my last message, I guess I looked past \nthem...\n\nThanks Phil, for you know, helping me out!\n- Luke\n"},{"id":"225302","messageId":"CABURp0qFnvW4X9MLhB=Q45S6xmV1HJ1-8NGRD7UyOLrLVxapvQ@mail.gmail.com","threadId":"34638","inReplyTo":"5209C4CD.8040607@gmail.com","subject":"Re: [Bug] git stash generates a different diff then other commands (diff, add, etc) resulting in merge conflicts!","fromName":"Phil Hord","fromEmail":"phil.hord@gmail.com","sentAt":"2013-08-16T00:05:17Z","receivedAt":"2013-08-16T00:05:17Z","isPatch":false,"sender":{"key":"phil.hord@gmail.com","avatar":"https://avatars.githubusercontent.com/u/123908?v=4"},"body":"On Tue, Aug 13, 2013 at 1:31 AM, Luke San Antonio\n<lukesanantonio@gmail.com> wrote:\n>\n> So I found an isolated case, it's very strange...\n>\n> Here's a script!\n>\n>\n<deleted>\n\nThanks for that.  It was hard to read, but it demonstrates the problem well.\n\n\n> ... Copy and paste that into a terminal and you should have a recreated\n> version of my repository there! Now that the file is partly stashed and\n> partly in the index, check out the difference in diffs:\n>\n> try:\n>   git diff --staged\n> then try:\n>   git stash show -p\n\nThis one is pretty sneaky.  It is not limited to git-stash.  I can\ndemonstrate the problem now using 'git merge-file'.\n\nBut I can only make this problem show itself when:\n   1. The collisions are separated by just one line of common code, and\n   2. One of the lines of common code is duplicated in one of the\ncollisions, and\n   3. The first two lines of the file are duplicated, and\n   4. One of the first two lines is deleted on one side but not the other.\n\nI have managed to boil the test down to this script:\n\n    #-----------------------------\n    cat >base <<base\n        1 duplicate\n        1 duplicate\n        3 unchanged\n        4 will change\n        5 gets deleted\n        7 duplicated\n        8 will change\nbase\n\n    cat >left <<left\n        1 duplicate\n        1 duplicate\n        3 unchanged\n        4 changed\n        7 duplicated\n        6 new line\n        7 duplicated\n        8 changed\nleft\n\n    sed -e 1d left > right\n\n    git merge-file -p left base right\n\n    #-----------------------------\n\n\nThe result looks like this, showing the duplicate \"collision\":\n\n  1 duplicate\n  3 unchanged\n  4 changed\n  <<<<<<< left\n  7 duplicated\n  6 new line\n  7 duplicated\n  =======\n  7 duplicated\n  6 new line\n  7 duplicated\n  >>>>>>> right\n  8 changed\n\n\nBut it should look like this instead:\n\n  1 duplicate\n  3 unchanged\n  4 changed\n  7 duplicated\n  6 new line\n  7 duplicated\n  8 changed\n\n\nA similar (but different) stupidity shows up if you remove line \"3\"\nfrom all three files.\n\nI tested this in 1.6.5 and the same thing occurs there, so this is NOT\nrecent regression.\n\nPhil\n"}]}