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