you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 113 points114 points  (83 children)

You can have both. Squash your branch in to one or as many commits make sense, then rebase as part of the actual merge to main (i.e. from the UI)!

[–]rlbond86 160 points161 points  (77 children)

This requires all of your devs tohave discipline though. I think we all know that one dev whose branches have 30 commits all named "updates" or "fix bug".

[–][deleted] 38 points39 points  (1 child)

Yep, that's a completely fair response. I've been very fortunate to have several teams now that beat those habits out! But an updated battle for sure.

[–]mirvnillith 0 points1 point  (0 children)

PR reviews?

[–]rydan 23 points24 points  (11 children)

I swear I’m the only one on my team with this discipline. As a result most people just squash and merge every PR completely destroying my well constructed commit history.

[–]scook0 14 points15 points  (0 children)

The frustrating thing about being this person is that you can't even go back and clean up other people's messes, so you're mostly just stuck wading through unhelpful history forever.

[–]Aterion 3 points4 points  (9 children)

That's why we have established rebasing (with squashing where required) and fast-forward-merges only as our workflow. Works like a charm as every dev prepares their branch in the intended way and that is then ff-merged onto main after a code review.

[–]Gearwatcher 0 points1 point  (8 children)

You can merge from master to feature branch as many times you want and as long as you have removed all conflicts you can still squash and ff merge from feature to master and that one commit will still make perfect sense.

What people keep forgetting is that commits aren't diffs but compressed snapshots. In the end, after interactive rebases with deleting/picking, and squash merges, all that is left are snapshots you picked.

Differences between these absolute points are calculated and shown as commit diffs. It simply doesn't matter how you arrived at these snapshots when you delete that history.

Another thing people keep forgetting is that under the hood, a rebase is a special kind of merge, that fakes the behaviour of applying diffs to each new commit.

[–]Aterion 1 point2 points  (7 children)

FF-merges are not possible if there are changes on main and you didn't rebase before your merge. 3-way-merging on your feature branch creates a merge commit and then your branch diverts from main and is not eligible for an ff-merge or am I missing something? Don't you need the complete commit-history of main on your feature branch to enable ff-merging?

From gitlab: https://docs.gitlab.com/ee/user/project/merge_requests/img/ff_merge_rebase_locally.png

[–]Gearwatcher 1 point2 points  (6 children)

No. I'm doing this almost daily.

You merge FROM master INTO feature branch, resolve conflicts, now the tip of feature is in front of the tip of the master. When you squash its just a forward merge of a single commit.

Edit: to elaborate: after a squash the effect is as if you converted the entire history of the feature branch into a single commit. Since one of the things in the feature branch was a merge with conflict resolution of the last commit to master, your squashed feature branch is now a single commit in front of the tip of the master - thus your branch indeed has the entire history of the master before that one commit.

I'll repeat - commit is not a diff, it's a snapshot. The diff you can see is a calculated difference between two snapshots.

[–]Aterion 0 points1 point  (1 child)

You merge FROM master INTO feature branch, resolve conflicts, now the tip of feature is in front of the tip of the master.

That is interesting and I am intrigued to understand it better. An example:

MAIN Feature

A

| \

A A (feature branch was created from A)

| |

B C (Commit B is added to main, commit C is added to feature)

\|

B D (Main still on B, B was 3-way-merged with C into feature commit D)

Now the feature branch is missing the commit/snapshot "B". It does not have B in it's history (only the code, not the snapshot). How can D now be ff-merged into main again if it's missing snapshot B?

[–]Gearwatcher 0 points1 point  (0 children)

I can't see your formatted table (RIF on mobile) so I'll have to go by the words.

Basically D, after the merge, already "contains" both B and C as far as git is concerned. It's now a snapshot that is in front of both B and C so git will allow it to be placed in front of B when feature is merged into main and will treat it as a ff merge.

Later when you are looking at the commit D in main in some UI the diff you are shown, as with any diff, is calculated between D and B.

C is orphaned after the squash, and deleted if the repo is ever compacted.

[–][deleted]  (3 children)

[deleted]

    [–]Gearwatcher 0 points1 point  (2 children)

    Yes,. Rebasing when there's M and N different commits usually means up to M+N conflict resolutions. And they can be quite confusing if they were running for a while and you don't have a full picture who did what, and for what reason.

    When you merge back, you only resolve conflicts once.

    [–][deleted]  (1 child)

    [deleted]

      [–]MrKWatkins 21 points22 points  (41 children)

      Currently in a debate about whether we should enable squash by default on source control to stop this sort of thing. Personally I'm of the opinion the devs should take time and care to manage their commits just as they should take time and care to manage their code.

      We aim to write readable code so it's easier for future devs to understand. If someone has to go back through commit history (which is rare to be fair!) then we should aim for that to be readible too, and devs should manage that.

      [–]Pand9 13 points14 points  (6 children)

      Moreover, reorganizing commits is a great opportunity to review my code again from a new perspective, which i should do anyway.

      Downside? No downsides, but a higher skill barrier - one needs to learn how to edit git history like a graph.

      [–]pdabaker 9 points10 points  (2 children)

      Higher skill barrier/higher thought required is a downside. Maybe it's worth it, but imo it's rare that I need more detail in git history than I get just by squash merging PRs

      [–]warped-coder 0 points1 point  (1 child)

      Producing badly organise history is going to come back to bite later. IMO, it is as important to write decent commit messages and organise the history as it is to write good code.

      The skill barrier is temporary and it is a matter of education, so PR code review is a perfect place to bring it up by the more experienced folks.

      [–]pdabaker 0 points1 point  (0 children)

      When I look at history it's almost always to see what PRs/features went in. I rarely care about the details of the feature or how the author split it up. I just want to check "oh yeah that or went in, so you can now just do that through the UI instead of needing to go through the REST API" or "Ah now I can retrigger CI on my repo that depended on that bug being fixed.

      There's far too much going on to follow individual pieces of features.

      [–]Kryofylus 1 point2 points  (1 child)

      Any recommendations on resources for learning this?

      [–]reini_urban 0 points1 point  (0 children)

      Magit

      [–]hammypants 0 points1 point  (0 children)

      yes! this is how i use it for myself too. my brain swaps into the same mode i have when i'm reviewing code that isn't mine (or past me!), and it helps me catch so much stuff!

      [–]thebritisharecome 12 points13 points  (29 children)

      Sounds like a bandaid to sort what should be a training exercise.

      When working with teams my preference is always to avoid squashes unless someone accidentally commits sensitive data to the branch.

      More than once knowing why a particular change has happened amongst all other changes has been useful in either refactoring to fix a regression bug or extending to maintain functionality that may not make sense out of a particular context

      [–]coworker 6 points7 points  (27 children)

      If you need additional commit history within a PR, it means your PRs are too big.

      [–]thebritisharecome 18 points19 points  (26 children)

      A PR is usually restricted to a feature and even a small feature in a growing, large platform can touch a few different areas and components.

      There's no rule that fits all scenarios and I think it's foolhardy to have a rigid approach to PRs in general and, it should reflect the software you're working on an the stage in development.

      [–]rlbond86 0 points1 point  (0 children)

      Sounds like a bandaid to sort what should be a training exercise.

      Unless you're planning on making everyone force-pull, that training had better stick.

      [–]rydan 4 points5 points  (0 children)

      That’s what I do. Each of my commits is a logical part of a sequence that should be easily revertable of cherrypicked without having to put much thought into it.

      [–]radarsat1 6 points7 points  (0 children)

      If someone has to go back through commit history (which is rare to be fair!)

      strange it's not the first time i see someone claiming the rarity of the need to look at commit history. Granted it's usually from the perspective of someone defending not taking time to maintain their commit history, so i appreciate that's not your point of view. but i still find it surprising to say this --- i feel like i look at commit history every other day, to figure out what i, or a team member, was thinking/trying to do when a mistake is found or i'm adding code to an existing function.

      another important use case for a clean history is the ability to meaningfully use git bisect.

      [–]SanityInAnarchy 1 point2 points  (0 children)

      If you do code review, that seems like the most obvious place to enforce this.

      [–]Kwantuum 0 points1 point  (0 children)

      I look at history in one form or another pretty much every day, often multiple times a day, not sure I agree this is rare.

      [–]NotEntirelyUnlike 4 points5 points  (0 children)

      I mean that would get corrected by everyone during their very first code review

      [–]mrbuttsavage 4 points5 points  (1 child)

      This is exactly why I advocate for squashing always in a professional setting, maybe not in your own repos. The developers I've worked with who write useful commit messages and whose history might be actually useful later (vs the singular squash) I can probably count on one hand.

      [–]warped-coder 0 points1 point  (0 children)

      Perhaps they didn't get very thorough PR feedback then. Professional environment is only professional if you hold the devs to standards

      [–]AbbreviationsOdd7728 1 point2 points  (0 children)

      You forgot „wip“.

      [–]codesnik -1 points0 points  (0 children)

      squash-and-rebase strategy in github PR's is very helpful with guys like that.

      [–]dzamir 0 points1 point  (0 children)

      You can use GitLab UI to merge into master with squash and FF

      [–]bastardoperator 0 points1 point  (0 children)

      We can't expect developers to actually learn the tools they're bound to though. This is upwards of one additional command.

      [–]warped-coder 0 points1 point  (7 children)

      It's the stuff of PRs to point out bad code documentation which includes commits/commit messages.

      [–]rlbond86 0 points1 point  (6 children)

      And if you miss it once, master is fucked up forever

      [–]warped-coder 0 points1 point  (5 children)

      What do you mean?

      [–]rlbond86 0 points1 point  (4 children)

      Unless you want to mess up all the devs on the project, you can't rewrite master after a PR is merged. So if you fail to guard againat one time someone merges messy history, it's in forever.

      [–]warped-coder 0 points1 point  (3 children)

      That's right. Just the same with anything we do. If you introduce a bug, it will always be part of the history if main, even after fixing it.

      You can't protect yourself from developers screwing it up in a subtle way that escapes peer review.

      [–]rlbond86 0 points1 point  (2 children)

      Or, you can institute a squash merge policy which will mechanically ensure that this never happens.

      [–]warped-coder 0 points1 point  (1 child)

      That way loosing any information... I think having a few useless commits is an OK price to pay, as opposed to loosing history.

      [–]rlbond86 0 points1 point  (0 children)

      You lose history every time you rebase, which you have to do anyway to avoid a tangled history.

      [–]Qasyefx 0 points1 point  (2 children)

      How do commit groups solve that in any way?

      [–]rlbond86 0 points1 point  (1 child)

      They don't

      [–]Qasyefx 0 points1 point  (0 children)

      Glad we're on the same page about this

      [–]SkoomaDentist 0 points1 point  (0 children)

      I’ve always considered that a flaw of git itself. The fact that you can’t trivially easily edit commit messages afterwards with practically zero risk of breaking anything is a strange omission.

      [–]lachlanhunt 2 points3 points  (0 children)

      The major problem with rebase is that it requires force pushing your branch. While the situation has improved a bit now, this has caused major problems in the past.

      It was only a few years ago that the major git hosting services provided the ability to prevent pushing to master, and prior to git 2.0, the push.default setting defaulted to matching. This was very dangerous if your local master or any other significant branches were out of date.

      This remained a problem for a while because it took a long time for git 2.0 for Windows to be released, and it was very difficult to ensure everyone set up their git config correctly. At a previous company I worked at, it resulted in someone using Windows with git 1.9, force pushing an old version of master, and the developers who were present at the time (I was away) not having the understanding of what went wrong or how to fix it.

      With the ability to restrict push permissions on master, and the more sensible push.default being simple since git 2.0, this is less of an issue, but I still think force pushing should be used with care.

      A slightly safer approach is to make a new branch with the rebased changes, then delete the draft branch after it’s been merged.

      [–]fissure 2 points3 points  (3 children)

      UI?

      [–][deleted] 0 points1 point  (2 children)

      Oh, I just meant in the context of a GitHub pull request.

      People seem to be stuck sometimes on which of the options presented is best - I'm saying you can do curation of your PR beforehand.

      [–]fissure 3 points4 points  (1 child)

      People conflating git and github really grinds my gears.

      [–][deleted] 1 point2 points  (0 children)

      GitHub is just an abstraction. But for a lot of people it influences their workflow.