[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)