{"thread":{"id":"37515","subject":"Please help provide clarity on git rebase internals","startedAt":"2014-09-08T11:25:25Z","lastAt":"2014-09-19T13:12:34Z","messageCount":3,"participants":["Colin Yates","Johannes Sixt","Fabian Ruch"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"249030","messageId":"CAE2xRkHgnXK84u5zeLyVZqAnvu3u+0gSgaB+smFXu6Y7pkY1kQ@mail.gmail.com","threadId":"37515","inReplyTo":null,"subject":"Please help provide clarity on git rebase internals","fromName":"Colin Yates","fromEmail":"colin.yates@gmail.com","sentAt":"2014-09-08T11:25:25Z","receivedAt":"2014-09-08T11:25:25Z","isPatch":false,"sender":{"key":"colin.yates@gmail.com","avatar":null},"body":"Hi all,\n\nTLDR; I am seeing merge conflicts when rebasing even though applying\nthem to HEAD of target branch should work. Can you please upgrade my\nunderstanding so I understand.\n\nMy understanding is that rebasing branch B onto branch A unrolls all\nof branch B's commits and then \"reduces\" them onto the HEAD of branch\nA.\n\nFor example, I took featureA branch from develop three days ago.\ndevelop subsequently had commits #d1, #d2 and #d3. featureA also had\n#f1 and #f2 and in terms of time they are all intermingled.\n\nMy understanding of rebase is that after issuing \"git fetch; git\nrebase origin/develop\" in featureA branch a git log should show #f2,\n#f1, #d3, #d2, #d1.\n\nI am seeing this, but sometimes I see something I can't explain and\nthat is a merge conflict as if git was doing a merge rather than a\nrebase.\n\nFor example, let's imagine that #f1 removed fileA, some time later #d1\nadded a line to that file. If I was doing a merge then of course this\nshould be a conflict, however applying #f1 to develop HEAD should work\neven if fileA has changed (i.e. #f1 removes the updated fileA).\n\nAs it is I am frequently running into merge conflicts in this manner\nwhen it *appears* git is applying a patch from featureA onto develop\n_as it was then the patch was made_.\n\nI am also seeing merge conflicts that occur between commits in the\ndevelop branch itself as well, which I assumed would be effectively\nread-only.\n\nIn terms of functional programming I thought rebase was a pure reduce\nof a sequence of patches from featureA branch onto HEAD.\n\nI have no idea what git is doing internally, and if I am confident of\nanything it is almost certainly that the bug is in my understanding\n:).\n\nWhat am I missing?\n\nThanks,\n\nCol\n"},{"id":"249051","messageId":"540DF064.5010907@kdbg.org","threadId":"37515","inReplyTo":"CAE2xRkHgnXK84u5zeLyVZqAnvu3u+0gSgaB+smFXu6Y7pkY1kQ@mail.gmail.com","subject":"Re: Please help provide clarity on git rebase internals","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2014-09-08T18:07:32Z","receivedAt":"2014-09-08T18:07:32Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 08.09.2014 13:25, schrieb Colin Yates:\n> For example, let's imagine that #f1 removed fileA, some time later #d1\n\nAssumption: #d1 is in the branch you call \"develop HEAD\".\n\n> added a line to that file. If I was doing a merge then of course this\n> should be a conflict, however applying #f1 to develop HEAD should work\n> even if fileA has changed (i.e. #f1 removes the updated fileA).\n\nNo. You should get the very same conflict, because the content that #f1\nremoved is not identical to the content on develop HEAD anymore.\n\nWith rebase you generally get the same conflicts as if you did a merge.\nBut since rebase applies changes only piece-wise, you get the conflicts\nalso only piece-wise. (Sometimes you can be lucky that you get no\nconflicts due to the nature of changes, sometimes you can also have bad\nluck and see more conflicts.)\n\n-- Hannes\n"},{"id":"249656","messageId":"541C2BC2.3050205@gmail.com","threadId":"37515","inReplyTo":"CAE2xRkHgnXK84u5zeLyVZqAnvu3u+0gSgaB+smFXu6Y7pkY1kQ@mail.gmail.com","subject":"Re: Please help provide clarity on git rebase internals","fromName":"Fabian Ruch","fromEmail":"bafain@gmail.com","sentAt":"2014-09-19T13:12:34Z","receivedAt":"2014-09-19T13:12:34Z","isPatch":false,"sender":{"key":"bafain@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1150972?v=4"},"body":"Hi Colin,\n\nOn 09/08/2014 01:25 PM, Colin Yates wrote:\n> My understanding is that rebasing branch B onto branch A unrolls all\n> of branch B's commits and then \"reduces\" them onto the HEAD of branch\n> A.\n> \n> For example, I took featureA branch from develop three days ago.\n> develop subsequently had commits #d1, #d2 and #d3. featureA also had\n> #f1 and #f2 and in terms of time they are all intermingled.\n> \n> My understanding of rebase is that after issuing \"git fetch; git\n> rebase origin/develop\" in featureA branch a git log should show #f2,\n> #f1, #d3, #d2, #d1.\n\nAlmost, it will show #f2', #f1', #d3, #d2, #d1. The commits #f1 and #f2\nmust be recreated because the changes they introduce are being applied\nto a different base, that is a different tree. The result of rebasing\n#f1 and #f2 will be a tree different from the one at the tip of branch\n'featureA'.\n\n> I am seeing this, but sometimes I see something I can't explain and\n> that is a merge conflict as if git was doing a merge rather than a\n> rebase.\n\nA rebase is a series of patch applications to a base different from the\none they were created in relation to. If a patch context is different in\nthe new base, the patch cannot be applied by simply replacing '-' lines\nwith '+' lines and a merge of changes is required. That merge can fail\nitself and we see merge conflicts. It's no contradiction that a merge\n(of changes) is happening even though git is not doing a merge (of\nbranches).\n\n> For example, let's imagine that #f1 removed fileA, some time later #d1\n> added a line to that file. If I was doing a merge then of course this\n> should be a conflict, however applying #f1 to develop HEAD should work\n> even if fileA has changed (i.e. #f1 removes the updated fileA).\n\nThe commit #f1 does not just record the deletion of the file named\n'fileA' but also a patch that removes every single line in that file.\nAnother way to view the behaviour of 'git rm' is that the command does\nnot remove names from the tree but objects that are given by both a name\nand a content. The replay of #f1 on top of #d3 conflicts because the\npatch cannot be applied, the content does not match respectively.\n\n> As it is I am frequently running into merge conflicts in this manner\n> when it *appears* git is applying a patch from featureA onto develop\n> _as it was then the patch was made_.\n\nI'm not sure if I'm understanding correctly, but I'd say it doesn't just\nappear that way. First, git-rebase takes the patch that represents the\nchanges between develop@{3 days ago} and #f1 and applies it to #d3. The\nresult is commit #f1'. Then it applies the differences between #f1 and\n#f2 to #f1', which in turn results in #f2'.\n\n> I am also seeing merge conflicts that occur between commits in the\n> develop branch itself as well, which I assumed would be effectively\n> read-only.\n\nYou're right, the branch 'develop' shouldn't be touched at all if you\nrun 'git rebase develop' on branch 'featureA'. Do you mean \"between\ncommits in the *featureA* branch itself\" instead, i.e. it is unexpected\nif the replay of #f2 fails after the replay of #f1 succeeded?\n\n> In terms of functional programming I thought rebase was a pure reduce\n> of a sequence of patches from featureA branch onto HEAD.\n\nI like how you're viewing 'rebase' as, I guess, a right fold with the\nbase as the initial element and 'apply'/'cherry-pick' as the operator,\nbut I'm not sure what we can learn from this representation. Is it true\nthat there is an emphasis on \"pure\" here suggesting that this is where\nthe functional interpretation fails?\n\nCheers,\n   Fabian\n"}]}