{"thread":{"id":"58289","subject":"Fwd: Idea: add --squash to cherry-pick","startedAt":"2022-08-11T08:53:32Z","lastAt":"2022-08-12T08:37:18Z","messageCount":4,"participants":["Noam Yorav-Raphael","Johannes Sixt","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"461065","messageId":"CAO8o=D7QC71mnyKSceMpYJzVO4=POva=mJQ1bi0teUHPB9xesA@mail.gmail.com","threadId":"58289","inReplyTo":"CAO8o=D6p+aFOeRdTDHwPa1E+V45Uqh-TuSZbz0WU=rE-ksGj_w@mail.gmail.com","subject":"Fwd: Idea: add --squash to cherry-pick","fromName":"Noam Yorav-Raphael","fromEmail":"noamraph@gmail.com","sentAt":"2022-08-11T08:53:11Z","receivedAt":"2022-08-11T08:53:32Z","isPatch":false,"sender":{"key":"noamraph@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1553464?v=4"},"body":"Hi,\n\nHere's something I need regularly, which I haven't found an easy\nsolution for yet. If there's support, I may try and implement it.\n\nThe need is described in this Stack Overflow question:\nhttps://stackoverflow.com/q/35123108. It's fairly popular (107 votes\nfor the question, 154 for the best answer), but I find the suggested\nsolution lacking.\n\nBasically, I would like to add a flag --squash to the cherry-pick\ncommand, that would apply the diff between the start and end of the\nspecified range in one operation.\n\nThe use case is that there's a feature branch which I would like to\napply on another branch as one commit.\n\nI could use the `-n` flag to apply all the commits from the source\nbranch without committing them. However, if there are conflicts, I\nwould have to deal with them on every commit applied. Instead, what I\nwant is to just apply the diff between the first and last commit, and\nthen deal with the conflicts.\n\nI find this to be a very natural operation. Usual cherry-pick applies\nthe difference between commit A^ and commit A over HEAD. The suggested\n`git cherry-pick --squash A..B` would apply the difference between\ncommit A and commit B over HEAD.\n\nWhat do you think?\n\nThanks,\nNoam\n"},{"id":"461078","messageId":"a4c937ed-ea89-0564-db64-f0634fe7eeee@kdbg.org","threadId":"58289","inReplyTo":"CAO8o=D7QC71mnyKSceMpYJzVO4=POva=mJQ1bi0teUHPB9xesA@mail.gmail.com","subject":"Re: Fwd: Idea: add --squash to cherry-pick","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-08-11T15:42:15Z","receivedAt":"2022-08-11T15:51:12Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 11.08.22 um 10:53 schrieb Noam Yorav-Raphael:\n> The need is described in this Stack Overflow question:\n> https://stackoverflow.com/q/35123108. It's fairly popular (107 votes\n> for the question, 154 for the best answer), but I find the suggested\n> solution lacking.\n> \n> Basically, I would like to add a flag --squash to the cherry-pick\n> command, that would apply the diff between the start and end of the\n> specified range in one operation.\n> \n> The use case is that there's a feature branch which I would like to\n> apply on another branch as one commit.\n> \n> I could use the `-n` flag to apply all the commits from the source\n> branch without committing them. However, if there are conflicts, I\n> would have to deal with them on every commit applied. Instead, what I\n> want is to just apply the diff between the first and last commit, and\n> then deal with the conflicts.\n> \n> I find this to be a very natural operation. Usual cherry-pick applies\n> the difference between commit A^ and commit A over HEAD. The suggested\n> `git cherry-pick --squash A..B` would apply the difference between\n> commit A and commit B over HEAD.\n\nThat question on Stackoverflow asks \"how to do X with Y\". But Y (git\ncherry-pick) is the wrong tool to do X (apply commits from a branch to\nsomewhere else in squashed form).\n\n   git diff A...B | git apply --3way\n\nwould do what you want. You would have to come up with a new commit\nmessage anyway, so cherry-pick would be of little use there.\n\n-- Hannes\n"},{"id":"461085","messageId":"xmqqzggag28x.fsf@gitster.g","threadId":"58289","inReplyTo":"a4c937ed-ea89-0564-db64-f0634fe7eeee@kdbg.org","subject":"Re: Fwd: Idea: add --squash to cherry-pick","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-08-11T16:43:10Z","receivedAt":"2022-08-11T17:10:31Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n> That question on Stackoverflow asks \"how to do X with Y\". But Y (git\n> cherry-pick) is the wrong tool to do X (apply commits from a branch to\n> somewhere else in squashed form).\n>\n>    git diff A...B | git apply --3way\n>\n> would do what you want. You would have to come up with a new commit\n> message anyway, so cherry-pick would be of little use there.\n\nI do agree that cherry-pick is not the best solution for that XY\nproblem, but in a typical undisciplined development work, it is not\nentirely implausible that pieces from original commit message may\nstill be of some use, exactly the same way as people would want to\nuse \"squash\" while \"rebase -i\".  Most commits may have completely\nunusable \"oops fix\", \"worked more\", etc. messages, but some of them\nmay summarize what the past handful steps with such garbage messages\nhave done, which may be worth salvaging.\n\nIf I were solving the XY problem, I would probably use \"rebase -i\".\n\nTo transplant the range A..B of the history on top of HEAD, for\nexample, I'd start with (notice ^0 after B, as I do not trust myself\nso I'd leave the true branch B untouched as I may make mistakes\nwhile running rebase):\n\n    $ git checkout --detach HEAD\t;# this is only to use @{-1} later\n    $ git rebase -i --onto HEAD A B^0\n\nThen if my goal is to squash everything down into a single commit,\nthen replace all 'pick', except for the first one, to 'squash'.\nThat will give me one single chance to edit a single commit message,\nbut the editor buffer starts with the log message from all of the\noriginal, so I can pick good bits from them while writing new stuf.\n\nI'll have the result on detached HEAD.  If I like the result, I may\nupdate the branch I was originally on with it.\n\n    $ git checkout -B @{-1}\n\nOr, if I don't, perhaps because I made mistakes, then I can just\ndiscard it and go back to the original branch.\n\n    $ git checkout @{-1}\n\n"},{"id":"461113","messageId":"CAO8o=D7Zf3W2BwYwx91SK8STgi+xRbvEnZtdWhLahj6e_t0ZUw@mail.gmail.com","threadId":"58289","inReplyTo":"xmqqzggag28x.fsf@gitster.g","subject":"Re: Fwd: Idea: add --squash to cherry-pick","fromName":"Noam Yorav-Raphael","fromEmail":"noamraph@gmail.com","sentAt":"2022-08-12T08:37:02Z","receivedAt":"2022-08-12T08:37:18Z","isPatch":false,"sender":{"key":"noamraph@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1553464?v=4"},"body":"Thank Hannes and Junio for your suggestions!\n\nAnother issue with using \"git diff A...B | git apply --3way\", besides\nit not showing the commit messages, is that I wouldn't be able to use\na graphical merge tool that shows the entire source file. When I use\n\"git cherry-pick\" and there are conflicts, I see the change in the\ncontext of the entire file.\n\n(I'm using this modification I made of the meld diff tool, which shows\n4 panes, and helps me understand the cause of the conflict:\nhttps://github.com/noamraph/meld)\n\nMy main problem with using \"rebase -i\" is that it would require me to\nfix merge conflicts one by one, on each commit in which they appear,\ninstead of fixing all conflicts at once, treating the change from A to\nB as one. It also requires manual editing for every commit between A\nand B.\n\nI think that the best way to do what I want using the existing commands is:\n\ngit checkout A\ngit merge --squash B\ngit commit --no-edit\ngit checkout @{2}  # Go back to where we were at the beginning. This\nis not exact, as you're in detached HEAD state.\ngit cherry-pick --edit @{1}  # cherry-pick the squashed commit A..B\n\nThis allows you to fix the merge conflicts in one go, shows the entire\nfiles causing the conflicts, and allows you to edit the commit\nmessage, starting with the descriptions of all the squashed commits.\n\nI think this also gives a pretty good explanation of what \"cherry-pick\n--squash\" will do: it really is the analog of the \"merge --squash\",\nbut for cherry-pick.\n\nWhat do you think? Did I explain my need to \"cherry-pick --squash\" and\nexplain its behavior?\n\nIf I do an experiment and try to implement this, would this help?\n\nThanks,\nNoam\n\nOn Thu, Aug 11, 2022 at 7:43 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Johannes Sixt <j6t@kdbg.org> writes:\n>\n> > That question on Stackoverflow asks \"how to do X with Y\". But Y (git\n> > cherry-pick) is the wrong tool to do X (apply commits from a branch to\n> > somewhere else in squashed form).\n> >\n> >    git diff A...B | git apply --3way\n> >\n> > would do what you want. You would have to come up with a new commit\n> > message anyway, so cherry-pick would be of little use there.\n>\n> I do agree that cherry-pick is not the best solution for that XY\n> problem, but in a typical undisciplined development work, it is not\n> entirely implausible that pieces from original commit message may\n> still be of some use, exactly the same way as people would want to\n> use \"squash\" while \"rebase -i\".  Most commits may have completely\n> unusable \"oops fix\", \"worked more\", etc. messages, but some of them\n> may summarize what the past handful steps with such garbage messages\n> have done, which may be worth salvaging.\n>\n> If I were solving the XY problem, I would probably use \"rebase -i\".\n>\n> To transplant the range A..B of the history on top of HEAD, for\n> example, I'd start with (notice ^0 after B, as I do not trust myself\n> so I'd leave the true branch B untouched as I may make mistakes\n> while running rebase):\n>\n>     $ git checkout --detach HEAD        ;# this is only to use @{-1} later\n>     $ git rebase -i --onto HEAD A B^0\n>\n> Then if my goal is to squash everything down into a single commit,\n> then replace all 'pick', except for the first one, to 'squash'.\n> That will give me one single chance to edit a single commit message,\n> but the editor buffer starts with the log message from all of the\n> original, so I can pick good bits from them while writing new stuf.\n>\n> I'll have the result on detached HEAD.  If I like the result, I may\n> update the branch I was originally on with it.\n>\n>     $ git checkout -B @{-1}\n>\n> Or, if I don't, perhaps because I made mistakes, then I can just\n> discard it and go back to the original branch.\n>\n>     $ git checkout @{-1}\n>\n"}]}