{"thread":{"id":"47908","subject":"RFC: git squash","startedAt":"2018-02-23T03:41:15Z","lastAt":"2018-02-23T20:31:10Z","messageCount":4,"participants":["Julius Musseau","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"339993","messageId":"CAA7Zk=tMMPE_Ov6cSJuzgYwWmTuMTSd_909c5JY3a2k5KFzHgQ@mail.gmail.com","threadId":"47908","inReplyTo":null,"subject":"RFC: git squash","fromName":"Julius Musseau","fromEmail":"julius@mergebase.com","sentAt":"2018-02-23T03:41:06Z","receivedAt":"2018-02-23T03:41:15Z","isPatch":false,"sender":{"key":"julius@mergebase.com","avatar":null},"body":"Hi, Git Developers,\n\nThanks for your help regarding my earlier email (trying to break git\npull --rebase).\n\nI just wanted to warn you all that my first attempt at a patch is\nimminent.  I'm working on a \"git squash\" command.  Here's a quick\nsummary:\n\n------\ngit squash [<commit>]\n\nSquashes <commit>..HEAD into a single commit. Replaces HEAD with the\nresult.  If not specified, <commit> defaults to the current branch's\nupstream (a.k.a. @{upstream}).\n\nRationale:\n\nThis command provides an intuitive mechanism for in-place squash that\ndoesn't drop dirty merge results.\n\nWe call this an in-place squash because the state of all files and\ndirectories at HEAD does not change. Only the ancestory of HEAD\nchanges: its (only) parent becomes the merge-base of <commit> and\nHEAD, removing all intermediate commits.\n\nAlternatives:\n\n- \"git merge --squash master\" correctly preserves dirty merge results,\nbut it's tricky to achieve an in-place squash with this command, since\nit requires the following sequence of commands (these assume the\ncurrent branch is \"feature\" and we want to squash it relative to\n\"master\"):\n\n    git checkout $(git merge-base HEAD master)\n    git merge --squash feature\n    git commit\n    git branch -f feature\n    git checkout feature\n\n- \"git rebase --interactive HEAD~N\" with commits set to \"squash\" (or\n\"fixup\") is very popular in industry.  But it has some tricky edge\ncases: drops dirty merge results, can run into conflicts, and can be\nconfusing if HEAD~N spans any merges (including clean merges).\n-----\n\n\nI expect I'll have the patch finished this weekend, and ready for\neveryone to look at by Monday (Feb 26th).\n\nNote:  I'm not 100% sure \"git rebase --interactive\" would drop dirty\nmerge results (e.g., the dirty parts from conflict-resolving merges).\nThat's speculation on my part.  I'll confirm this before I finish and\nsubmit the patch.\n\n\nyours sincerely,\n\nJulius Musseau\n"},{"id":"339996","messageId":"xmqqfu5sutrc.fsf@gitster-ct.c.googlers.com","threadId":"47908","inReplyTo":"CAA7Zk=tMMPE_Ov6cSJuzgYwWmTuMTSd_909c5JY3a2k5KFzHgQ@mail.gmail.com","subject":"Re: RFC: git squash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-23T06:04:07Z","receivedAt":"2018-02-23T06:04:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Julius Musseau <julius@mergebase.com> writes:\n\n> git squash [<commit>]\n>\n> Squashes <commit>..HEAD into a single commit. Replaces HEAD with the\n> result.  If not specified, <commit> defaults to the current branch's\n> upstream (a.k.a. @{upstream}).\n>\n> Rationale:\n>\n> This command provides an intuitive mechanism for in-place squash that\n> doesn't drop dirty merge results.\n>\n> We call this an in-place squash because the state of all files and\n> directories at HEAD does not change. Only the ancestory of HEAD\n> changes: its (only) parent becomes the merge-base of <commit> and\n> HEAD, removing all intermediate commits.\n\nSo is it essentially the same as\n\n    git reset --soft $(git merge-base $commit HEAD)\n    git commit\n\nwith some icing for coming up with a default log message?  The above\nwon't touch the working tree at all.\n\n\n"},{"id":"340042","messageId":"CAA7Zk=smFOZ5+ThOZ9bfr8+UB_tD1371Oiy501sR=J4xjKdhsQ@mail.gmail.com","threadId":"47908","inReplyTo":"xmqqfu5sutrc.fsf@gitster-ct.c.googlers.com","subject":"Re: RFC: git squash","fromName":"Julius Musseau","fromEmail":"julius@mergebase.com","sentAt":"2018-02-23T17:03:30Z","receivedAt":"2018-02-23T17:03:37Z","isPatch":false,"sender":{"key":"julius@mergebase.com","avatar":null},"body":"On Thu, Feb 22, 2018 at 10:04 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Julius Musseau <julius@mergebase.com> writes:\n>\n>> git squash [<commit>]\n>>\n>> Squashes <commit>..HEAD into a single commit. Replaces HEAD with the\n>> result.  If not specified, <commit> defaults to the current branch's\n>> upstream (a.k.a. @{upstream}).\n>>\n>> Rationale:\n>>\n>> This command provides an intuitive mechanism for in-place squash that\n>> doesn't drop dirty merge results.\n>>\n>> We call this an in-place squash because the state of all files and\n>> directories at HEAD does not change. Only the ancestory of HEAD\n>> changes: its (only) parent becomes the merge-base of <commit> and\n>> HEAD, removing all intermediate commits.\n>\n> So is it essentially the same as\n>\n>     git reset --soft $(git merge-base $commit HEAD)\n>     git commit\n>\n> with some icing for coming up with a default log message?  The above\n> won't touch the working tree at all.\n>\n>\n\nYes!  I had no idea about this approach.  Thanks!  My implementation\nuses \"git commit-tree\".\n\nYes, some icing to build the squashed log message, as well as some\nprotection to ensure the working tree is clean before doing the squash\n(just like \"git rebase\" does).\n\nI'm also working on two more pieces of icing:\n\n- Adding an \"--oldest-author\" flag to help adjust the author for the\nsquashed commit (using the author value from the oldest commit in the\nsquash).  By \"oldest\" I mean whatever \"git log --reverse --no-merges\n-1 <commit>..HEAD\" spits out.  (I don't consider timestamps.)\n\n- Ability to arbitrarily squash any branch by taking a 2nd \"branch\"\nargument.  (And making the command work with bare repos.)\n\nI'm embarrassed to realise your approach matches the top-voted\nstack-overflow answer on the subject:\nhttps://stackoverflow.com/a/5201642\n\nNonetheless, the top-voted stack-overflow comment to that answer says:\n\n> Ha! I like this method. It is the one closes to the spirit of the problem. It's a pity that it requires so much voodoo. Something like this should be added to one of the basic commands.\n\nShould I proceed?\n"},{"id":"340068","messageId":"xmqq371rtpmj.fsf@gitster-ct.c.googlers.com","threadId":"47908","inReplyTo":"CAA7Zk=smFOZ5+ThOZ9bfr8+UB_tD1371Oiy501sR=J4xjKdhsQ@mail.gmail.com","subject":"Re: RFC: git squash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-02-23T20:31:00Z","receivedAt":"2018-02-23T20:31:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Julius Musseau <julius@mergebase.com> writes:\n\n> I'm embarrassed to realise your approach matches the top-voted\n> stack-overflow answer on the subject:\n> https://stackoverflow.com/a/5201642\n\nI personally do not visit stack-overflow, but I am happy to see that\nthere are people who remember \"right\" way to do this among the folks\nwho do.\n\n\"reset --soft\" was invented exactly for this use case, and you can\nuse it even with dirty working tree and dirty index (they are left\nintact).\n"}]}