[HN Gopher] Things I wish Git had: Commit groups
       ___________________________________________________________________
        
       Things I wish Git had: Commit groups
        
       Author : nathell
       Score  : 132 points
       Date   : 2021-07-03 15:27 UTC (7 hours ago)
        
 (HTM) web link (blog.danieljanus.pl)
 (TXT) w3m dump (blog.danieljanus.pl)
        
       | Quenty wrote:
       | Azure DevOps has this concept, it's called "semi-linear merge"
       | where it will rebase your PR on top of the branch being merged
       | into, but then create a 2 parent merge commit with the PR
       | comments and text to merge the content, letting you easily
       | reconstruct what changes were made in one PR, while also
       | preserving commit history and keeping the history clean overall.
        
         | Jasper_ wrote:
         | This is what GitHub used to do, before they changed it to the
         | squash model.
        
       | theknocker wrote:
       | So like some kind of separate sequence of commits that can have
       | its own name.
        
       | agshew wrote:
       | The idea of commit groups makes me think of Linux kernel style
       | patch series and https://github.com/git-series/git-series
        
       | emodendroket wrote:
       | I started using the convention of prefixing commit messages with
       | the ticket they're addressing, which I think is actually pretty
       | helpful. Makes it way easier to do interactive rebases too.
        
         | cerved wrote:
         | in my personal repos I follow the convention to prefix
         | add/mod/del/fix/sty: to each commit to indicate whether it
         | adds, modifies or deletes the API or whether it fixes or
         | changes the private interface, or if it merely changes
         | stylistic elements (ie no changes to functionality). This helps
         | me quickly understand how each commit affects the whole
        
       | derriz wrote:
       | The author identifies the problem which I think is a fundamental
       | failure of the git model which is that commits aren't associated
       | with branches. But proposes a different solution. I wonder why
       | they didn't decide to attach a branch identifier to each commit.
       | This would solve the problem as I see it as you could truly view
       | a branch history; typically for example you'd be asking for logs
       | of the "master" branch history. The topological view can help but
       | the lack of notion of a branch as anything but a pointer to one
       | commit means you effectively do loose history unless you go for a
       | burdensome tagging scheme.
        
         | cesarb wrote:
         | > This would solve the problem as I see it as you could truly
         | view a branch history; typically for example you'd be asking
         | for logs of the "master" branch history.
         | 
         | Which "master"? Since git is fully distributed, there's no
         | central repository which contains the true "master" branch.
         | It's perfectly valid to have two independent lines of
         | development, both naming its current branch "master" (in
         | separate clones of the repository), and later merge one of them
         | into the other. As another example, consider the branch named
         | "for-linus"; take a look at the Linux kernel git history, and
         | see how many independent branches all named "for-linus" are
         | merged on each release.
        
         | zeroimpl wrote:
         | But commits change branches. None of the commits started on the
         | "master" branch, they started on some developer's branch (which
         | might also be called "master" in a different repo, but is still
         | separate).
        
           | derriz wrote:
           | I'm not sure I understand your point?
           | 
           | Say for example, I'm looking at a freshly cloned repo.
           | There's a first commit and most-recent commit on master - I
           | can identify them with git-log. The problem is that I cannot
           | view the path of commits between these two if I'm only
           | interested in the commits made when the current branch was
           | master (which is generally the case unless I want to drill
           | into a feature branch).
           | 
           | Disallowing merges makes the problem go away but that removes
           | a lot of options in terms of work-flow.
        
             | jhardy54 wrote:
             | Would `git log --merges` solve this? Assuming you use a
             | merge-based workflow this would show only merge commits
             | without any of the details of each individual commit from
             | the merged branch.
        
             | zeroimpl wrote:
             | I don't follow - If you are on a freshly cloned repo, none
             | of the commits were made when the current branch was
             | master, they were made on another user's git repo before
             | your repo existed.
        
         | AtNightWeCode wrote:
         | On the contrary, once people realize that a commit hash is
         | something that spans across all branches the real beauty of GIT
         | emerges. You can than and there make decisions on a branch
         | level or at a commit level. The real failure of GIT in my
         | opinion is that people think that they need branches when
         | commits suffice and thereby making things very complicated with
         | zero benefits. #onlywinersdownvote
         | 
         | Edit: Garbage people downvoting as expected. Are you selling
         | garbage to control the garbage or what is wrong with you?
        
       | lloydatkinson wrote:
       | Yes this would definitely be a way to shut up the "squash merge
       | all the nuances of all the commits for this feature into a single
       | commit called "implement feature XYZ"" crowd.
        
       | cerved wrote:
       | I'm not sure I appreciate the point the author is trying to make
       | because it sounds like what they want is a merge commit
        
       | infogulch wrote:
       | I agree with the author's sentiment. The way this problem is
       | typically framed is as a dichotomy between preserving a "true"
       | history of what really happened on the micro/commit scale VS
       | presenting a "clean" history that makes the story of the change
       | easy to follow on the macro/PR scale.
       | 
       | This is a _false dichotomy_. I 'm greedy, I want BOTH. Give me
       | story mode when I'm just browsing the repo, but offer me the
       | option to switch into commit-by-commit mode when I want more
       | detail.
        
         | hashhar wrote:
         | A pragmatic middle ground is learning fixup and autosquash and
         | making the story mode before merging the code.
         | 
         | Your story mode == commit mode provided you can divide at a
         | good enough granularity. This is generally not a problem
         | though.
        
         | forty wrote:
         | I use the semi linear history function of gitlab: it's like
         | rebase & merge, but doesn't fast forward (ie it always creates
         | a merge commit)
         | 
         | This way to see feature per feature I do git log --merges and
         | to see the commits git log --no-merges
        
         | dec0dedab0de wrote:
         | It's pretty easy though. Leave all the commits. Put meaningful
         | messages in your merge commits, and your clean macro history is
         | just merge commits and the messy one is the rest.
        
         | andy_ppp wrote:
         | I've often thought this too, you shouldn't have to _rewrite_
         | i.e. lie about what happened. Git history ideally should be
         | completely immutable but there should be a view that tidies up
         | what happened for those that like to see individual features
         | /bugs/hotfixes all listed in history in a nice way.
         | 
         | I don't like the name thought, commit groups seems odd, I
         | prefer feature view or something else. The reason is commit
         | groups sounds to me like groups of people who are allowed to
         | commit.
        
           | infogulch wrote:
           | I want to be able to preserve two parallel commit histories:
           | one where the the commits are ordered by time, and another
           | where the commits are ordered by 'story'. Git could
           | cryptographically verify that the end-states of the two
           | histories are identical, and allow me to alter the storied
           | history at will (shifting hunks between commits,
           | splitting/combining commits, reordering commits etc), where
           | during merge _both_ histories are preserved.
           | 
           | I don't really follow your naming critique though. "commit
           | groups" seems like a fine name, they are groups of commits.
           | What you describe I would call "committ _er_ groups ".
        
             | dllthomas wrote:
             | Something like --date-order to view commits chronologically
             | vs --topo-order to view them topologically sorted?
        
           | fragmede wrote:
           | The ugly truth though, is that the process of programming can
           | be shamefully bad. Often times the in-progress branch commits
           | are the opposite of the platonic ideal commit message, for
           | common human reasons. By 'common human reasons' I mean
           | programming is messy, and it's often unclear why something
           | does or does not work until after something functional is
           | arrived at.
           | 
           | The important thing is that your tools, specifically git,
           | works for you, and not the other way around, so 'bad' commit
           | messages like 'before', followed by 'after' are totally fine
           | while working in the branch as long as it gets cleaned up
           | during merge. However, they are the antithesis of useful
           | months or _years_ down the line while playing code historian.
           | (Hilariously, the answer to the question  "what _idiot_ wrote
           | this code ", is sometimes the reader!)
           | 
           | So is it a _lie_ that sometimes after lunch on a Friday, the
           | act of programming is often a series of off-by-one, off-by-
           | two, off-by-one-the-other-direction compile-test-edit-commit
           | loops? And that we 'd prefer to be thought of as a genius
           | that wrote some fundamental-to-the-company code 5-10 years
           | later, with beautiful commit messages that live up to some
           | platonic ideal, rather than "that one dumbass"?
           | 
           | It's a lie the same way that people who wear makeup are
           | 'lying'. It's true under a very specific, _weird_ framing,
           | but it doesn 't really agree with reality.
           | 
           | There are notable high profile exceptions like publicly
           | viewable patch series against the linux kernel, but you're
           | deluded if you think those aren't edited before being
           | released for human consumption.
        
         | cortesoft wrote:
         | Couldn't you get this with squash and merge, along with not
         | deleting merged branches? The main branch would have the story,
         | and you could go into the actual squashed branch to get the
         | actual commit history?
        
           | Izkata wrote:
           | That creates a mess. It's not clear which remote branches are
           | still active, and can be difficult to link the branches back
           | to the merges historically for hunting bugs.
           | 
           | The best way I've seen by far: Prepare a fast-forward merge,
           | then merge it with --no-ff. You end up with a linear history
           | of commits grouped by the merge commits, can see either view
           | in git log using --first-parent or not, and bisect can find
           | the actual commit when needed.
        
             | stormbrew wrote:
             | Does this have some kind of distinct result from rebasing
             | the branch before merging does? I'm not thinking of
             | anything that would be different, so I'm not sure if using
             | the more obscure command (`git merge --no-commit` vs `git
             | rebase`) is for a specific reason.
        
               | Izkata wrote:
               | Did you mean to respond to someone else? I never
               | mentioned --no-commit, I'm talking about rebasing if
               | needed - set up a fast-forward merge, which may or may
               | not need a rebase.
               | 
               | This top-level comment shows what the commit history
               | looks like doing what I said:
               | https://news.ycombinator.com/item?id=27723435
               | 
               | I also didn't just say rebase because that could also
               | mean squashing commits manually and isn't what I'm
               | talking about.
        
         | jhardy54 wrote:
         | I use merge commits as the "clean" history and non-merge
         | commits as the "true" history. Are there problems that this
         | approach doesn't solve? My only gripe is that apps like GitHub
         | don't give me the option to display commits how I want, but
         | that's a problem with inflexible tooling in general.
        
       | tolmasky wrote:
       | I had the similar thought a little while ago (I called them
       | nested commits):
       | https://twitter.com/tolmasky/status/1212452048618131456?s=21
       | 
       | In my version, the nesting can be infinite of course (I guess the
       | author here would call these "groups of groups" -- but that might
       | be complicated with the flat approach of a group being a range of
       | commits).
       | 
       | But basically, I want the UI of an entire "set of commits", but
       | then a disclosure triangle to be able to see the "true history"
       | if it's interesting to me. It is simply the case that sometimes
       | one is more useful than the other, and other times the reverse is
       | true. There simply isn't a one-size-fits-all solution. As far as
       | most people are concerned, you only ever want to, for example,
       | unroll the entire set if a test fails. Or, you want to cherry-
       | pick the entire set to another branch. They serve as one logical
       | unit. But if you for example care about the decision-making
       | process that lead to that final code change, you can see it by
       | revealing the "inner history".
        
       | kibwen wrote:
       | Emphatic agreement, this is something I've wanted for a while.
       | For the purpose of history management/traversal (reverting,
       | bisecting, or just understanding) you want certain code changes
       | to be _atomic_ , implying that they should all be
       | understood/tested/reverted together. But for the purpose of code
       | review you want something more granular and less monolithic, so
       | that a single reviewer can more easily understand a large change,
       | or so that review can be more easily divvied out among certain
       | code owners. Currently getting all all of these properties
       | requires decomposing the commits during the review phase and then
       | squashing before merge, but this is a bad practice because now no
       | reviewer has actually signed off on the code that's getting
       | merged and you're just hoping that nobody's slipping any last-
       | minute or malicious changes in during the squash.
        
         | tomsmeding wrote:
         | Agreed; however, since this is HN, I'd like to suggest that it
         | should be totally doable to improve on the squashing workflow
         | using tooling. Programmatically it's easy to verify that the
         | signed-off commits induce the same diff as the squashed commit;
         | they're either equal or not. Then, if the interface where
         | signoffs are registered (e.g. github PR) enforces that all PR's
         | are signed off while allowing a squashed, un-signed-off commit
         | if it has the same diff as a signed-off commit, then you can
         | squash at will. Optionally you could also require that the
         | signers additionally sign-off the final commit, under the tool-
         | provided guarantee that the diff is identical.
        
       | secondcoming wrote:
       | Is it not standard practice to first merge 'master' into
       | 'feature' before you merge 'feature' into 'master'? If 'master'
       | has changes that are not in 'feature' then 'feature' is out of
       | date, testing probably needs to be redone. It also means any
       | merge conflicts are resolved in the 'feature' branch. If
       | 'feature' is a long lived branch then is should merge 'master' on
       | every release anyway.
       | 
       | If your weakest git user cannot revert easily then you're in
       | trouble. Enjoy being on call 24/7 otherwise. Reverting is more
       | important than committing.
       | 
       | I genuinely don't understand why people care about git history so
       | much. I've never needed to look at it in 8 years of using git.
        
         | NBJack wrote:
         | Git history plays a critical role in code forensics,
         | particularly in large code bases (or places where you may have
         | a large number of potential authors). I. E. 10 commits just
         | went to production from 3 different teams on one service.
         | Something breaks a few hours later; was it one of the 10? Was
         | it a corner case in something much older? Or was it someone's
         | feature flag going live?
         | 
         | If you deal with a service that has been matiained for a few
         | years, this is also an excellent way to figure out what was
         | done why and when. Or figure out if a well meaning rebase
         | accidentally clobbered a critical piece of ancient logic. Or
         | determine who the heck owns something when you realize it's
         | time to split up a larger service.
         | 
         | The list goes on. Note git history plays directly into git
         | blame too; it can be an excellent tool in the right
         | circumstances.
        
         | Noumenon72 wrote:
         | For stuff like "this line doesn't make sense next to this other
         | line. Why was it added?" Sometimes a line was deleted between
         | them, sometimes it was a bad merge, sometimes the rest of the
         | commit tells you "oh, it was added to make the Foobar work."
         | 
         | I have allocated some of the valuable left-hand-only keyboard
         | shortcuts in my IDE to searching Git history. It tells you the
         | "why" when the "what" doesn't make sense, and the "who" when
         | git blame shows some reformatter.
        
       | jefftk wrote:
       | You can do this today in standard git with
       | https://www.davidchudzicki.com/posts/first-parent
       | 
       | Summary: every feature is merged with 'main' as the first parent.
       | Then, whenever interacting with history, you tell git you only
       | want it to consider --first-parent
        
         | Ericson2314 wrote:
         | Yeah just need to make git bisect --first-parent, and then the
         | solution be obvious.
        
           | jefftk wrote:
           | git bisect already supports --first-parent: https://git-
           | scm.com/docs/git-bisect
        
             | Ericson2314 wrote:
             | Oh great! That's new since last I tried.
        
         | exclipy wrote:
         | This. There is no need to introduce a new concept like "commit
         | groups" to permeate through everything. Just use merges and
         | display them nicely.
         | 
         | The problem is getting all the visualization tools on the same
         | page.
         | 
         | Bazaar got this right. Their official UI displays a linear
         | history with the ability to expand any merge commit to show the
         | side branch.
         | 
         | https://commons.wikimedia.org/wiki/File:Bazaar_Explorer_-_Lo...
        
         | kevin_b_er wrote:
         | The author makes the argument that 'first parent' being the
         | original branch is a mere convention.
         | 
         | And he's right. If someone does the merge while checked out on
         | the feature branch, then commits it _as_ the master branch,
         | then the first-parent concept breaks.
        
           | thrashh wrote:
           | But that problem is easy to fix with 0 disadvantages: Don't
           | do that.
           | 
           | Don't do it for the same reason that you have a convention of
           | useful commit messages.
        
             | ianlevesque wrote:
             | I masquerade as proficient in this field and I'm pretty
             | sure I've done that just by accident with git a few times.
             | Assuming any developer on your team is much better than
             | monkeys on typewriters when it comes to git will lead to
             | disappointment.
        
               | exclipy wrote:
               | Make a precommit hook that enforces this. Done.
        
       | roland35 wrote:
       | I will admit I am always afraid to lose my changes if I squash!
       | Maybe I should try making a new temporary branch before squashing
       | to give myself peace of mind.
        
         | sopooneo wrote:
         | I used to have that fear also, but after playing with reflow a
         | bit and seeing how you can get back to almost anything, I'm
         | more comfortable.
        
       | imiric wrote:
       | So groups would be just named commit ranges? I'm not sure I see
       | the appeal if it's already possible.
       | 
       | The article also seems to imply that there's one "right" way to
       | merge branches and that teams should stick to one approach. I
       | disagree. I use all 3 approaches whenever it makes sense: merge
       | commits for large PRs with more than 1 commit where you want to
       | preserve the history of the changes, squash+merge when the
       | history is messy (usually after code review) but the change
       | itself should be atomic, and rebase when the history is clean,
       | though I mostly reserve rebasing for smaller single-commit PRs
       | where a merge commit would just add clutter.
       | 
       | I also disagree that Git is this flawless piece of software we
       | can't improve upon. It regularly fails to do fairly trivial
       | merges automatically, forcing me to manually fix conflicts, or
       | use `rerere`. Ideally my code versioning tool would understand
       | language semantics and be as maintenance-free as possible. Git is
       | nowhere near this and requires quite a lot of familiarity and
       | hand holding to work as the user intended. The amount of time and
       | effort spent understanding and using it properly is difficult to
       | quantify, and it's still a tall hurdle for new developers.
        
         | buck4roo wrote:
         | Emphatically agree.
         | 
         | The git cli UX is terrible, cryptic, and forces one to think
         | way too much. The number of GUI tools that wrap the git cli
         | should tell us all something.
         | 
         | I Mercurial. It - manipulates the identical data structure (the
         | DAG) - interops just fine with git (thanks hg-git plugin!) -
         | its cli manages to use common sense verb names, by default, for
         | all its functions - includes no unnecessary concepts/models
         | (read: git's index)
        
           | zck wrote:
           | > [Mercurial]...includes no unnecessary concepts/models
           | (read: git's index)
           | 
           | As someone who stubbornly uses mercurial for all his code,
           | git's index is the single greatest feature that git has over
           | mercurial (not counting the Magit interface). The index
           | allows me to _incrementally_ build up a commit. I can go back
           | and fix things, add and remove things from the index, and
           | only when I'm ready, commit. In mercurial, I have to hold so
           | much more state in my head about what is ready to commit,
           | what needs to be fixed, and what is code that needs to be
           | reverted.
           | 
           | And `hg commit -i` is nice, but is not a replacement for the
           | index. Git's index allows me to add hunks, then stop, go to
           | lunch, review the status, remove some bits, add more, then
           | commit.
           | 
           | I'm told that Mercurial's queues feature would support this,
           | but I find it incredibly non-ergonomic, and I can't quite
           | figure out how to use it as an index replacement.
        
         | [deleted]
        
       | larusso wrote:
       | I'm one of the people who strongly advocates the squash merge. My
       | PRs are also single commits most of the times with the PR body as
       | the commit message. That is a hidden gem of GitHub, if you open a
       | PR with a single commit then GitHub will use the commit message
       | as the description.
       | 
       | The reason why I prefer squash commits is not only the linear
       | history but also the fact that I'm simply not interested in the
       | sausage making process. If a PR becomes so big that it would need
       | multiple commits than I request smaller patches. But I also see
       | that this heavily depends on the team size and general setup. But
       | I use squash PR ever since it was introduced in GitHub. Before I
       | manually rebased the feature branches to have a clean merge.
        
         | sopooneo wrote:
         | Sincere question: does a "squash merge" implicitly include a
         | rebase as well when the merge target has diverged?
        
           | jbrot wrote:
           | It means using `git merge --squash` which performs a normal
           | merge but then instead of adding a merge commit, it just
           | makes a single regular commit with the changes.
        
           | richardwhiuk wrote:
           | It's equivalent to rebasing, and squashing into a single
           | commit.
           | 
           | It's also equivalent to a merge commit, and then only keeping
           | the diff from the merging in branch.
           | 
           | So it's sort of a half way house, and sort of the worse of
           | the both worlds.
        
             | larusso wrote:
             | Yes it has it up and downs. But it also helps me and my
             | team to enforce smaller patches since they land as one
             | anyways. And other teams in my company follow the open PR
             | and develop the next 2 weeks on it and merge in full. There
             | is so much garbage in all these commits no one ever wants
             | to get back to. Good luck bisecting on of these histories.
        
               | MereInterest wrote:
               | How independent are those commits? If I'm developing a
               | feature that has distinct but dependent changes, then the
               | rebased/squash pattern doesn't work as well. Submitting
               | in a single PR means that those distinct changes all get
               | squashed away. Submitting in a series of PRs keeps the
               | history, but means that the have commits on main have
               | different ids, and so I need to keep using `git rebase
               | -i` to remove the corresponding commit from my dev branch
               | whenever one gets merged in.
        
               | larusso wrote:
               | GitHub is pretty smart nowadays. If you propose a series
               | of PR on top of each other, GitHub will change the base
               | when it the base of the second PR got merged and so on.
               | There is sometimes the need to rebase but I have
               | generally no problem with it. Mind that I don't propose
               | this workflow with teams that also have non tech members
               | like artists because these Workflows bring too much
               | friction. But I keep my changes as independent as
               | possible. All running with tests to cover the
               | added/changed code.
        
         | sfvisser wrote:
         | > that I'm simply not interested in the sausage making process
         | 
         | Sure, no one really cares about they sausage making process,
         | but tracking down regressions is so much easier with a proper
         | history. Bisect is your friend.
        
         | war1025 wrote:
         | > The reason why I prefer squash commits is not only the linear
         | history but also the fact that I'm simply not interested in the
         | sausage making process.
         | 
         | I see this a lot and in my opinion squash on merge is just a
         | very poor version of using rebase to put your commits into a
         | proper state before merging.
         | 
         | A commit should be one logical set of changes. It's great if
         | you can get a piece of work done in one change, but often
         | larger work requires several changes.
         | 
         | Beginners seem to treat pull requests as places to pile commits
         | until you get something reasonable at the end (the sausage
         | making process). A better way is to curate your work and clean
         | up the individual commits as changes are requested in review.
         | Then you have a coherent history with commit messages that
         | might tell you something useful.
        
           | larusso wrote:
           | There is another angle I forgot to mention. That is that I
           | also want that each commit can be compiled (depends on the
           | project obviously) and is tested and won't fail tests. That
           | is super hard to achieve when only testing the last commit of
           | a PR through a push. I mainly want that to help when
           | bisecting. That's what I achieve by smaller PRs. But again I
           | personally prefers this and lay out my projects and tasks in
           | a way that this actually works for me. I'm not Dogmatic and
           | see the reason for different strategies.
        
         | faizshah wrote:
         | I've worked on multiple codebases where nobody on the team had
         | been there when the sausage was first made. The squash commits
         | are some of the worst commits for finding out why a weird
         | design decision was made because all the things that came out
         | in code review or would have had bugfix commit messages in
         | their local branch are all squashed together in one commit. CRs
         | help a lot but frankly I do want to know how the sausage was
         | made when I'm look through the git history or else I would just
         | look at the code directly. Especially when I'm investigating
         | some bug in prod.
        
           | larusso wrote:
           | I don't quite understand what you mean. As I wrote I write my
           | PR message as the commit message. That is one thing that is
           | also very important for me. Write in the commit message why
           | something changed. If the PR gets so damn big that it is so
           | much to explain everything than it's quite frankly too big.
           | From my experience the follow up commits for fixes or code
           | review changes boil down to messages like "fix stupid bug",
           | "adjustments after code review", etc. which are also not
           | helpful when looking for the why. But I honestly understand
           | what you mean. I'm just saying that limiting down the number
           | of commits and the size of changes in them helped me over the
           | years. I also don't do it when I start of from a white paper.
           | Only when the project enters the stage when PRs plus checks
           | help mitigate potential bugs etc.
        
       | [deleted]
        
       | sakisv wrote:
       | I think most of the author's pains could be implicitly solved by
       | using tags on merge.
       | 
       | That way you maintain a linear history, you get clear marks of
       | when each pr was merged and you can see what happened in between.
       | 
       | The obvious downside here is that you'd need to add yet another
       | step in your process and come up with a meaningful system for
       | this.
        
       | nooyurrsdey wrote:
       | I disagree with how difficult merge commits are. It's not
       | something to filter out in your view - it's a representation of
       | an actual change made by combining two different versions of a
       | document/code/etc... They are valuable indicators in history.
       | 
       | Furthermore services like Github add valuable valuable comments
       | like the PR number that was merged.
        
       | raziel2p wrote:
       | I use squash merge and make sure each PR is small enough to
       | justify being just one commit. Big things which are too big to
       | fit in a single PR can be tracked in other ways, such as
       | mentioning a Github issue number or project management issue ID
       | in the commits.
       | 
       | Individual commits on a PR/feature branch are useful to check
       | what's changed since the last review (I silently despise
       | developers who force-push their rebased commits when fixing
       | things, making it basically impossible to re-review).
       | 
       | I just wish it was easier to detect locally whether my
       | development branch has been squash-merged so I could script the
       | deletion of them.
        
       | ufo wrote:
       | In my organization we kind of do this. We use one merge commit
       | per pull request, without squashing. However, we also rebase
       | before merging, which results in a commit graph that looks like a
       | cactus:                     o-o-o   o-o   o-o-o          /     \
       | /   \ /     \         o-------o-----o-------o-->
        
         | tomerv wrote:
         | This is the best option in my opinion. You can also "optimize"
         | by doing the forced merge only for branches that have 2+
         | commits. That way, if most of your branches have a single
         | commit you won't have many useless merges. The branches with
         | multiple commits will still be visually grouped.
        
         | rzimmerman wrote:
         | I also prefer this method - rebase and force a merge commit.
         | It's pretty much the grouping functionality that the author
         | wants. You can tell a short story or break a merge into a
         | couple parts, and bisect + revert work. You can also link each
         | merge with a merge/pull request and a ticket. Also, an
         | occasional quick fix commit to master works fine and makes
         | sense. Sometimes a merge without a rebase makes sense as long
         | as it doesn't make the graph too confusing. Different workflows
         | work for different teams, but I like this one.
        
         | taberiand wrote:
         | The "semi-linear merge" (as Azure DevOps calls it) is our
         | preferred merge approach as well, though reasonable cases can
         | be made for alternative merges depending on circumstance, e.g.,
         | an old version hotfix merge into master is of course done as a
         | regular merge.
         | 
         | In addition, if people are feeling charitable, the branch will
         | be cleaned up prior to merge with an interactive rebase to
         | squash out "Wip" commits and hopefully leave a nice clear set
         | of self-contained commits that provide a logically separated
         | view of the work that went into the change.
        
         | nemetroid wrote:
         | GitLab offers this in the web UI. Even Azure DevOps does.
         | GitHub apparently doesn't, though.
        
           | metaltyphoon wrote:
           | GitHub is weird. Even their rebase workflow works differently
           | than git by creating a new hash :/
        
             | TimWolla wrote:
             | A rebase always creates a new hash, because the commit hash
             | is a hash over the contents of the commit. This contents
             | include, among others, the parent commit(s) and the time of
             | committing (which in case of a rebase will be different
             | than the time of authoring).
        
               | jlokier wrote:
               | I think that's not what the GP means.
               | 
               | "git rebase" doesn't create a new hash if there's no
               | change as a result of the rebase. But GitHub's PR rebase
               | button always creates a new hash even if there is no
               | reason to. (Its PR merge button does not do this; it will
               | merge a one-commit PR without creating a new commit).
               | 
               | To add to the inconsistencies, GitHub doesn't sign the
               | new commit when using the rebase button so it doesn't
               | show up with the green "verified" icon - even if there
               | was no need for a new commit anyway. Yet when using the
               | merge button it does the opposite - if it doesn't need a
               | new commit, your signed PR commit is merged to the main
               | branch and shows as verified, and if it does need a new
               | commit GitHub signs it (if the PR commit was signed) so
               | it still says "verified" (even though it's really
               | GitHub's key that was used, not the author's).
               | 
               | For this reason, when I merge PRs I avoid the GitHub UI,
               | and use "git rebase -S" locally followed by "git push".
               | This does what the PR rebase button should do.
        
         | masklinn wrote:
         | Sure but that's not really helpful, the only thing it does is
         | avoid breaking history visualisation tools which tend to deal
         | _very badly_ with  "wide" histories.
         | 
         | For instance one of the biggest annoyances with git is it's a
         | pain in the ass to find the the merge of a commit into the
         | mainline (aka the next child with more than one ancestor...
         | probably), which can make it difficult to go back from a commit
         | to a PR unless it was a single-commit pull request.
        
           | nemetroid wrote:
           | > Sure but that's not really helpful, the only thing it does
           | 
           | It encodes the necessary information in the commit graph,
           | without introducing a completely new concept (commit groups).
           | It's true that Git doesn't give you the tooling to get that
           | information out of the box, though.
        
           | Hello71 wrote:
           | https://github.com/mhagger/git-when-merged
           | 
           | edit: also https://stackoverflow.com/questions/8475448/find-
           | merge-commi..., and also github shows this (but only for
           | github PRs, not other merges)
        
       | EugeneOZ wrote:
       | I didn't get the last bit: why author can't write meaningful
       | commit messages with the merge strategy?
        
       | stormbrew wrote:
       | > So it tells you that these two parents have been merged
       | together, but it doesn't tell you which one used to be main. You
       | might guess 8, because it's the leftmost one, but you don't know
       | for sure. (Remember, branches in Git are just pointers to
       | commits.) The only way (that I know of) to be sure is to use the
       | reflog, but that is ephemeral: Git occassionally prunes old
       | entries from reflogs.
       | 
       | This feels extremely pedantic to me. There are certainly
       | workflows that produce this confusion, but the most common ones
       | definitely don't.
       | 
       | For the most part, in especially most github-based flows, the
       | 'left-most' (aka `git log --first-parent`) history of the main
       | branch is precisely the history of the main branch, and the
       | "commit groups" are the divergent "right" parents.
       | 
       | Can someone do something that temporarily breaks this? Sure.
       | People `git pull`ing with divergent changes at least used to
       | litter projects' histories with this kind of nonsense. But it's
       | not terribly likely to make it into your main history these days
       | if your upstream repo has a 'protected' main branch, which is so
       | normalized at this point it ought to be considered the default
       | state of affairs.
       | 
       | It seems like maybe the thing the OP really wants is just for the
       | branch name at commit time to be stored as metadata in the
       | commit. That would maybe help with pulling out intentions while
       | looking at history.
       | 
       | Also, Mercurial had a kind of 'hard branch' feature that also
       | might have resembled what's desired here, but as far as I can
       | tell most users of mercurial found it more frustrating than
       | helpful and used plugins that provided looser kinds of branching.
        
       | everyone wrote:
       | I was disappointed when I learned that git doesnt remember which
       | branch a commit was made in.
       | 
       | cus,
       | 
       | Each feature = one branch
       | 
       | Each little change = one commit
       | 
       | It's annoying that they throw out half of that info.
        
         | cerved wrote:
         | git forgets nothing. A branch is just a reference to a commit,
         | that reference goes nowhere unless you delete it
        
           | ghoward wrote:
           | Not GP.
           | 
           | I may be wrong here, but I would argue that git _does_ forget
           | something: it forgets where the branch reference _used_ to
           | point when a new commit is made on that branch. If a commit
           | has more than one parent, that means some information is lost
           | because it has to guess which parent the branch reference
           | came from.
        
       | fiddlerwoaroof wrote:
       | In theory, you could implement commit groups already in one of
       | two ways: objects in a special ref that are just lists of commit
       | hashes (tree-style) or a branch where every commit has, in
       | addition to the last HEAD of the branch, all the other commits in
       | the group as parents.
        
         | fiddlerwoaroof wrote:
         | Here's a demo of this idea:
         | https://github.com/fiddlerwoaroof/git-group-demo
         | 
         | the network graph shows what's going on:
         | https://github.com/fiddlerwoaroof/git-group-demo/network
        
       | neolog wrote:
       | > Under the hood, all the commit really says is:
       | 
       | > Merge: 8 6
       | 
       | > So it tells you that these two parents have been merged
       | together, but it doesn't tell you which one used to be main. You
       | might guess 8, because it's the leftmost one, but you don't know
       | for sure.
       | 
       | Why don't I know for sure it's gotta be the one on the left?
        
         | cesarb wrote:
         | > Why don't I know for sure it's gotta be the one on the left?
         | 
         | Because of fast-forward merges.
         | 
         | It will be the one on the left if you, as expected, were on the
         | master branch and merged the feature branch ("git checkout
         | master; git merge feature"). But if you were on the feature
         | branch, merged the master branch into the feature branch
         | (usually, this is done to resolve conflicts), and then went
         | back to the master branch and merged the resulting feature
         | branch into it ("git checkout feature; git merge master; git
         | checkout master; git merge feature"), it will be a fast-forward
         | merge: no new commit will be created, and master will point to
         | the merge commit which was originally on the feature branch,
         | which is going on the _opposite_ direction as you would expect.
         | 
         | The solution, as others have already mentioned here, is to do
         | all merges to master as non-fast-forward ("git merge --no-ff
         | feature"); that gives a consistent order to all merge commits
         | on the master branch, and the end effect is the most similar to
         | the "grouping" feature OP wants (if everything on master are
         | these non-fast-forward merges, the difference between one merge
         | and the preceding one is that "group").
        
           | neolog wrote:
           | Thanks.
           | 
           | - It's still ok to "git-pull --ff=only", right?
           | 
           | - Is it possible to enforce this at the CI level?
        
             | wizzwizz4 wrote:
             | Yes to both, though I don't know how to enforce it. (A push
             | hook would do.)
        
       | Phlogistique wrote:
       | This is possible by doing this:                 git checkout
       | feature       git rebase main        git checkout main       git
       | merge --no-ff feature
        
         | jlarky2012 wrote:
         | I was going to post exactly the same. Bonus point you can make
         | it work like that in GitLab (but not GitHub)
        
         | svalorzen wrote:
         | This is what I also do. I rebase all my branches and merge them
         | with a no-fast-forward commit, which does not introduce any
         | changes itself, but can be used to document the overall changes
         | of a specific feature.
         | 
         | The best part about this workflow is that history remains
         | linear; it is very easy to track the history of changes (since
         | there is never a "branch" with changes on both sides) while at
         | the same time you keep the ability to visualize where the
         | start/end points for a given feature were.
         | 
         | It also works with nested branches! You simply create a new
         | branch2 from your branch1, and then merge --no-ff branch2 to
         | branch1.
        
           | scooble wrote:
           | You also get to write a nice detailed commit message for the
           | merge commit explaining the purpose of the branch being
           | merged, which may not be apparent from the individual
           | commits. Without the merge commit I'm not sure where this
           | would go.
        
         | larusso wrote:
         | Isn't that what GitHub does when the rebase strategy. Because
         | when the rebase can't be made cleanly GitHub still asks for
         | conflict resolution.
        
           | yxhuvud wrote:
           | No. If you rebase you get no merge commit by default.
        
             | larusso wrote:
             | Ah yes. My bad. You explicitly want the empty merge commit.
        
         | ufo wrote:
         | On Github the workflow we use for this is rebase in the command
         | line and then press the "Create a merge commit" button in the
         | web UI.
        
         | [deleted]
        
         | finnthehuman wrote:
         | I came to the comments to post the same thing. --no-ff is under
         | appreciated.
         | 
         | I like that this poster is at least thinking about what he
         | wants the history to look like, but you'll never get it good
         | with a broad rule. It really takes the exercise of rewriting
         | every change you make from a stream of consciousness set of WIP
         | commits into a logical series of incremental patches with clear
         | commit messages. And doing that every time you want to push
         | anything (especially when it is "just" a WIP or feature
         | branch). After a months practice your sense of taste will kick
         | in and tell you if a --no-ff merge seems appropriate for the
         | current chunk of work.
        
         | koo6 wrote:
         | right, and then git log --first-parent
        
       | eeperson wrote:
       | If you use the default merge messages, can't you tell which was
       | the branch that got merged? The second parent is the branch that
       | got merged and its name will appear in the commit message on the
       | merge commit. This is probably something that you could infer
       | most of the time.
        
       | georgyo wrote:
       | I too wish for this.
       | 
       | Some people are really great at writing commit messages, most are
       | not. But for even for the ones who write the best commit
       | messages, often their is a lot of discussion inside the actual MR
       | which never makes it into git.
       | 
       | GitLab and GitHub write a merge commit that can be used to get
       | back to that discussion, but a git blame or bisect doesn't take
       | you to the merge commit, which means you have to spend a fair
       | amount of effort to get to the merge commit.
       | 
       | Treating merges as a group would be amazing.
       | 
       | Something but addressed in the article is how you deal with
       | groups of groups of groups of merges. IE topic, feature, dev,
       | master branches.
       | 
       | It would be somewhat difficult to make the tooling graceful at
       | ungrouping different levels of groups.
        
       | mitko wrote:
       | The author might enjoy `git rebase -i main`, which allows
       | reordering, renaming, combining or pretty much everything in your
       | own branch before you rebase and merge it to the main branch.
       | 
       | That way even if a commit message is not clear or you added an
       | improvement to an earlier commit, you can reduce the clutter a
       | lot before sending out for code review, while still having
       | individual commits for different parts of the code change
        
         | martijnvds wrote:
         | Doing that will kind of work, but you'll lose the context of
         | the group after merging/fast-forwarding onto the main branch.
        
           | frutiger wrote:
           | If you always create a merge commit, you can see the group as
           | you traverse the history via the two parent commits. One will
           | walk the group and the other will go directly to the state
           | before the group.
        
       | toomanybeersies wrote:
       | In principle, I agree with the author. Although I don't think git
       | needs group commits to achieve this functionality. My preferred
       | workflow for the past couple of years has been to interactively
       | rebase and squash/fixup! the commits so that each commit
       | represents a functioning state of the code, which more or less
       | achieves the same thing as what the author wants.
       | 
       | However, this approach only works if all the developers on a
       | project buy in to this philosophy.
       | 
       | As much as I dislike squash and merge, it's better than the
       | alternative of trawling through a git history with dozens of
       | "WIP" and "Fix tests" commits and janky rebases/merges.
        
       | stolee wrote:
       | That history that looks tangled and awful would look a lot better
       | if the commits were sorted by `--topo-order` instead of `--date-
       | order`. That sort "groups" commits that are in a single line of
       | history.
        
       | thrower123 wrote:
       | Any workflow that alters history is dangerous. Just merge things
       | normally, and don't get fancy.
       | 
       | If you want to futz with stuff and squash commits and rewrite
       | commit messages because you didn't write good ones on a feature
       | branch, fine, whatever makes you happy.
       | 
       | But just merge to master, don't rebase.
        
       ___________________________________________________________________
       (page generated 2021-07-03 23:00 UTC)