{"thread":{"id":"54925","subject":"git pull/rebase bug: when \"onto\" branch has rebasing branch's commits in reflog","startedAt":"2021-01-03T22:03:32Z","lastAt":"2021-01-03T22:45:19Z","messageCount":3,"participants":["Andrew Oates","Johannes Sixt"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"413349","messageId":"CAAVLcG5Z0UnKVyBYyvPXdPWU-Q0-jEaUC=f3gDxZoaKqSUKN3g@mail.gmail.com","threadId":"54925","inReplyTo":null,"subject":"git pull/rebase bug: when \"onto\" branch has rebasing branch's commits in reflog","fromName":"Andrew Oates","fromEmail":"andrew@andrewoates.com","sentAt":"2021-01-03T22:02:37Z","receivedAt":"2021-01-03T22:03:32Z","isPatch":false,"sender":{"key":"andrew@andrewoates.com","avatar":null},"body":"I've poked at the source code but haven't found exactly what causes\nthe issue --- but if you do a 'git pull --rebase' or 'git rebase' onto\na tracking branch that has previously pointed to a commit that the\nrebasing branch includes, the rebase will be a noop.\n\nIn practice I've hit this a few times lately when splitting a topic\nbranch into two branches after the fact.\n\nHere is a short repro:\n```\ngit init\ntouch file1\ngit add file1\ngit commit -a -m \"first commit\"\ntouch file2\ngit add file2\ngit commit -a -m \"second commit\"\ntouch file3\ngit add file3\ngit commit -a -m \"third commit\"\ngit checkout -b branch\ngit branch --set-upstream-to=master\ngit checkout master\ngit reset --hard 'HEAD^1'\n\ntouch file2.5\ngit add file2.5\ngit commit -a -m \"second-and-a-half commit\"\ngit --no-pager log --oneline --all --graph\n\n#rm .git/logs/refs/heads/master\n\ngit checkout branch\ngit pull -v --rebase\ngit --no-pager log --oneline --all --graph\n```\n\nThis outputs,\n* 58432a7 (branch) third commit\n| * 0e4f775 (HEAD -> master) second-and-a-half commit\n|/\n* 37b2e3f second commit\n* 5e9f0b7 first commit\n...\nSuccessfully rebased and updated refs/heads/branch.\n* 0e4f775 (HEAD -> branch, master) second-and-a-half commit\n* 37b2e3f second commit\n* 5e9f0b7 first commit\n\nshowing that \"third commit\" is lost.  If you execute the \"rm ...\"\nline, then the sequence works as expected, and the final state is,\n\nSuccessfully rebased and updated refs/heads/branch.\n* b636309 (HEAD -> branch) third commit\n* 410a5dc (master) second-and-a-half commit\n* 41981d0 second commit\n* 286398d first commit\n\nMy best guess is that there's something odd happening in get_fork_point().\n\nCheers,\nAndrew\n"},{"id":"413350","messageId":"4e508192-c3a9-4cac-3255-1324aba347d4@kdbg.org","threadId":"54925","inReplyTo":"CAAVLcG5Z0UnKVyBYyvPXdPWU-Q0-jEaUC=f3gDxZoaKqSUKN3g@mail.gmail.com","subject":"Re: git pull/rebase bug: when \"onto\" branch has rebasing branch's commits in reflog","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2021-01-03T22:33:52Z","receivedAt":"2021-01-03T22:36:49Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 03.01.21 um 23:02 schrieb Andrew Oates:\n> I've poked at the source code but haven't found exactly what causes\n> the issue --- but if you do a 'git pull --rebase' or 'git rebase' onto\n> a tracking branch that has previously pointed to a commit that the\n> rebasing branch includes, the rebase will be a noop.\n> \n> In practice I've hit this a few times lately when splitting a topic\n> branch into two branches after the fact.\n> \n> Here is a short repro:\n> ```\n> git init\n> touch file1\n> git add file1\n> git commit -a -m \"first commit\"\n> touch file2\n> git add file2\n> git commit -a -m \"second commit\"\n> touch file3\n> git add file3\n> git commit -a -m \"third commit\"\n> git checkout -b branch\n> git branch --set-upstream-to=master\n> git checkout master\n> git reset --hard 'HEAD^1'\n> \n> touch file2.5\n> git add file2.5\n> git commit -a -m \"second-and-a-half commit\"\n> git --no-pager log --oneline --all --graph\n> \n> #rm .git/logs/refs/heads/master\n> \n> git checkout branch\n> git pull -v --rebase\n> git --no-pager log --oneline --all --graph\n> ```\n> \n> This outputs,\n> * 58432a7 (branch) third commit\n> | * 0e4f775 (HEAD -> master) second-and-a-half commit\n> |/\n> * 37b2e3f second commit\n> * 5e9f0b7 first commit\n> ...\n> Successfully rebased and updated refs/heads/branch.\n> * 0e4f775 (HEAD -> branch, master) second-and-a-half commit\n> * 37b2e3f second commit\n> * 5e9f0b7 first commit\n> \n> showing that \"third commit\" is lost.  If you execute the \"rm ...\"\n> line, then the sequence works as expected, and the final state is,\n> \n> Successfully rebased and updated refs/heads/branch.\n> * b636309 (HEAD -> branch) third commit\n> * 410a5dc (master) second-and-a-half commit\n> * 41981d0 second commit\n> * 286398d first commit\n> \n> My best guess is that there's something odd happening in get_fork_point().\n\nTo me, the outcome looks reasonable:\n\nBy the time when the branch was created, it had no commits on top of\nmaster. Then master was rewound and grew in a different direction, while\nthe branch didn't do anything. When you rebase it to its upstream, you\nshould expect that no commits are rebased.\n\n-- Hannes\n"},{"id":"413352","messageId":"CAAVLcG69j8MHDEbEBWrFjbApm1jSzU3WciP_VHCu-N3Mw4BJzw@mail.gmail.com","threadId":"54925","inReplyTo":"4e508192-c3a9-4cac-3255-1324aba347d4@kdbg.org","subject":"Re: git pull/rebase bug: when \"onto\" branch has rebasing branch's commits in reflog","fromName":"Andrew Oates","fromEmail":"andrew@andrewoates.com","sentAt":"2021-01-03T22:44:09Z","receivedAt":"2021-01-03T22:45:19Z","isPatch":false,"sender":{"key":"andrew@andrewoates.com","avatar":null},"body":"On Sun, Jan 3, 2021 at 5:33 PM Johannes Sixt <j6t@kdbg.org> wrote:\n>\n> Am 03.01.21 um 23:02 schrieb Andrew Oates:\n> > I've poked at the source code but haven't found exactly what causes\n> > the issue --- but if you do a 'git pull --rebase' or 'git rebase' onto\n> > a tracking branch that has previously pointed to a commit that the\n> > rebasing branch includes, the rebase will be a noop.\n> >\n> > In practice I've hit this a few times lately when splitting a topic\n> > branch into two branches after the fact.\n> >\n> > Here is a short repro:\n> > ```\n> > git init\n> > touch file1\n> > git add file1\n> > git commit -a -m \"first commit\"\n> > touch file2\n> > git add file2\n> > git commit -a -m \"second commit\"\n> > touch file3\n> > git add file3\n> > git commit -a -m \"third commit\"\n> > git checkout -b branch\n> > git branch --set-upstream-to=master\n> > git checkout master\n> > git reset --hard 'HEAD^1'\n> >\n> > touch file2.5\n> > git add file2.5\n> > git commit -a -m \"second-and-a-half commit\"\n> > git --no-pager log --oneline --all --graph\n> >\n> > #rm .git/logs/refs/heads/master\n> >\n> > git checkout branch\n> > git pull -v --rebase\n> > git --no-pager log --oneline --all --graph\n> > ```\n> >\n> > This outputs,\n> > * 58432a7 (branch) third commit\n> > | * 0e4f775 (HEAD -> master) second-and-a-half commit\n> > |/\n> > * 37b2e3f second commit\n> > * 5e9f0b7 first commit\n> > ...\n> > Successfully rebased and updated refs/heads/branch.\n> > * 0e4f775 (HEAD -> branch, master) second-and-a-half commit\n> > * 37b2e3f second commit\n> > * 5e9f0b7 first commit\n> >\n> > showing that \"third commit\" is lost.  If you execute the \"rm ...\"\n> > line, then the sequence works as expected, and the final state is,\n> >\n> > Successfully rebased and updated refs/heads/branch.\n> > * b636309 (HEAD -> branch) third commit\n> > * 410a5dc (master) second-and-a-half commit\n> > * 41981d0 second commit\n> > * 286398d first commit\n> >\n> > My best guess is that there's something odd happening in get_fork_point().\n\nYeah, I guess this is actually WAI, though the outcome was surprising\nto me (git silently dropped my commits).  Of course, after poking for\na while, as soon as I sent this I found some clear documentation for\nthe behavior :)  Under the git rebase docs:\n\"If your branch was based on <upstream> but <upstream> was rewound and\nyour branch contains commits which were dropped, this option can be\nused with --keep-base in order to drop those commits from your\nbranch.\"\n\nIn that case, maybe this is a UX suggestion rather than a bug report.\nI experienced this via \"git pull\" rather than \"git rebase\", which I\ngenerally consider a pretty safe thing to run --- but in this case,\ngit silently dropped my commits (which luckily I noticed).\n\nThoughts on how this would be less surprising to me as a user,\n 1) somehow make it just DTRT (though per the rebase --fork-point\ndocs, and your point, this may not be possible/desirable)\n 2) document these commits out when checking out the branch[1]\n 3) after a pull, log how many such commits were \"dropped\" for this reason\n\nAs it currently stands, the behavior is surprising --- the commits\nthat get rebased don't match either what is printed when checking out\nthe branch, or by the summary in the docs (\"This is the same set of\ncommits that would be shown by git log <upstream>..HEAD\"), and the\ncommits are dropped silently.  The fact that the commits are somehow\ntied to the old branch via the reflog breaks my mental model of branch\nhistory not being semantically important, or the fact that \"git pull\"\nin two repositories that are otherwise identical would have a\ndifferent outcome based on the individual history.\n\n[1] it currently says something along the lines of \"...and have X and\nY different commits each, respectively.\n  (use \"git pull\" to merge the remote branch into yours)\", which I've\nalways assumed would mean that (barring stuff like duplicate commits),\nI would end up with X+Y commits after pulling.\n>\n> To me, the outcome looks reasonable:\n>\n> By the time when the branch was created, it had no commits on top of\n> master. Then master was rewound and grew in a different direction, while\n> the branch didn't do anything. When you rebase it to its upstream, you\n> should expect that no commits are rebased.\n\nFWIW in the cases where I've actually hit this, I had additional\ncommits on top of the new branch, so only the commits at the \"bottom\"\nwere dropped (which is much harder to notice).  I think the difference\nis whether you consider the dropped commits part of the original\nbranch (now discarded), or the new branch --- my mental model was the\nlatter, in part because I didn't realize that branch history was\nactually semantically important.\n\n>\n> -- Hannes\n"}]}