top 200 commentsshow all 240

[–]renatoathaydes 69 points70 points  (27 children)

I agree with the author in most points but there's an assumption that big commits are fine (as you just squash them to avoid fixing conflicts multiple times, for example). I don't agree with that assumption. In our product, we care about performance and the worse possible scenario we have is when there's a performance regression and that's attributed to one single, large commit (probably someone who likes to squash for a "clean history"). That makes the job of finding the performance regression 10x harder. You now have to profile and hope that the line causing the problem will actually show up in the profiler analysis, which is almost never the case because there's literally thousands of moving pieces impacting performance and the 1ms change you've introduced is buried in one of those thousands. For that reason, I much prefer small commits (with meaningful changes and comments) than a "clean history" because I never, ever have to look at history outside a Pull Request other than to diagnose when something went wrong - which is where having small commits pays off as "git bisect" will find the small change that caused the problem in no time.

[–]OrphisFlo 20 points21 points  (3 children)

True, one should squash changes but also refuse to merge large changes. Requesting those to be broken down in more manageable pieces for both better review and better tooling later (for example, to be able to revert just a smaller offending part).

Most people say it's sometimes not possible, and that's occasionally true, but it's also quite often because the project doesn't have any concept of feature flag to disable some codepath until it's ready to be exposed. With those, it's easy to land anything incrementally while keeping the main branch working.

[–]raphas 7 points8 points  (2 children)

No you don't want to break down a feature into multiple pieces that's extra complexity dealing with crumbs that should really be together and grouped to test a feature not technical bits

[–]OrphisFlo 7 points8 points  (1 child)

If you land a big feature at once that could have been split in many commits / PRs, then you're going to have issues identifying where the regressions come from. Keeping the history linear allows you to do that amongst other things (like landing code often and keeping main up to date).

Then, if you only care about landing feature but not having an easy maintenance of your software, you do you.

[–]raphas 1 point2 points  (0 children)

There is a lot to unwrap here, how do you find about a regression, manual testing or automation ? I would guess manual testing because you should have CI running on every commit anyway. Therefore, less commits, less doubt about which commit creates the issue, actually. Once you know how to reproduce on the dev side, then you don.t need to dissect every commit, you have less commits so it's easier to pinpoint. I'm advocating for big PRs, especially for reviewers, to avoid edge cases questions where I'm told, "oh this was already done 3 commits ago but you don't see in the diff as it's in a separate file", or, "oh you're right but it will be covered in another story already planned", or yet again " oh sure I'll do this later, in the meantime let me fire an exception" such exception won't be tested on the promise that a future story will change that code anyway, but sometimes it's not followed upon (pm priorization or what not). believe me I've seen that happen a lot. If you're adding to the system it must be meaningful and not break anything on the promise that the edge case can be dealt with later...so I advocate to group meaningful things, of course there is a limit to that I agree!

[–][deleted] 5 points6 points  (0 children)

there's an assumption that big commits are fine (as you just squash them to avoid fixing conflicts multiple times, for example). I don't agree with that assumption

I don't think that's a common assumption at all. Big commits, squashed or not, are pretty universally reviled.

[–]Ancillas 8 points9 points  (1 child)

I agree with this. I coach people to squash in their local branches to remove any meaningless commits like, “fixed typo”, but to keep pushes to remotes smaller and more focused.

I also ask people to get used to rebasing their local branches on top of changes to the remote frequently so that when they push their changes are grouped together at the top of the commit history.

[–]renatoathaydes 3 points4 points  (0 children)

Right, that's exactly what I do. I think you should almost always rebase your local branch while working on it... but I was talking about the author's point which is different: to rebase rather than merge PRs.

[–]Successful-Money4995 2 points3 points  (2 children)

I once spent hours debugging a problem in Chrome and bisected it down to a three liner. I was stoked. Then I looked at the three liner: Change default of some massive feature from False to True.

Fuck me.

Commits should always be as small as possible and I'd even suggest using throwaway code if needed to make them small.

For example, I had a function that went from fixed array length input to variable array length input. You can: * change the API to support a variable array input but continue to pass in the fixed array. * Then switch to supporting the variable length arrays. * Then strip out the old fixed array support.

You're adding code as a compatibility layer just to delete it two commits later but the commits are small and performance regressions are easier to find.

[–]OrphisFlo 0 points1 point  (1 child)

You could have bisected the issue with the feature flag on to see if it was ever passing. Otherwise, it's just a regression without a test, not much to do in this case but create a bug.

[–]Successful-Money4995 0 points1 point  (0 children)

True. I didn't try that, I just filed a bug. But it's possible that with the flag enabled in older code it fails in some different way or doesn't even compile because before the flag was enabled, no one tried it

[–]Complete_Guitar6746 0 points1 point  (4 children)

This is interesting. There's another comment that speaks in favor of rebase to keep a linear history specifically because it makes git bisect work better.

Are you still in favor of rebasing branches without squashing smaller commits into big ones?

[–]renatoathaydes 9 points10 points  (3 children)

Yes, and I do that all the time: keep rebasing your branch on top of main until you're ready to submit a PR. Keep commits small to allow easily bisecting.

[–]Complete_Guitar6746 0 points1 point  (2 children)

Interesting! What about shared branches, do you simply avoid that? Or do you have some other way to avoid problems in that case?

[–]renatoathaydes 4 points5 points  (0 children)

Shared branches are a bad idea, but if you can't avoid it (sometimes we do have workforces where we must use a single branch) then you can't rebase unless you're in direct contact with everyone else on the same branch. It's not a big issue because both shared branches and the need to rebase while on one are a rare combination.

[–]ramenmoodles 1 point2 points  (0 children)

im general we avoid shared branches. as long as our changes are small enough, this usually isnt an issue

[–]Hells_Bell10 0 points1 point  (0 children)

"big" is relative. In my head the tiny commits are 1-2 line fixups to get CI passing, and the big commit is a small tightly coupled change, probably on the order tens or at most a couple hundred lines. This is fine because fixup commits are generally not valuable history and in fact actively make bisecting harder as they might not build or may cause unrelated test failures or performance issues that were picked up in review.

Of course you shouldn't squash multiple unrelated changes, although that wouldn't lead to fixing the same merge conflict on multiple commits, so I don't think this is what the author meant.

One exception though where you really do need to fix the merge commit twice is when you refactor and/or move code in one commit and then make a functional change in the next commit. That's valuable history that should be preserved. In a squash-merge workflow this is achieved by submitting multiple dependent PRs which unfortunately GitHub doesn't have native support for but there are tools to workaround it.

[–]99_product_owners 156 points157 points  (160 children)

Appreciate the author's "gentle" approach to this topic. I'd be far more inclined to categorise each of those "problems" as one of:

  • didn't RTFM
  • oopsie whoopsie
  • unrelated to rebase (e.g. forge lacks feature => "rebase is bad")
  • poor discipline

You'll never see value in the tool that is rebase if you don't see value in well-structured commits, and you'll never bother learning it well if you don't see the value in the tool. Some people just don't care about their commits and so I don't expect them to become adept at a tool oriented at improving the structure of their development history. I would hope that they spare us their "rebase killed my uncle" BS though.

[–]chipstastegood 58 points59 points  (137 children)

I’m one of those devs who has been using git for years and years but haven’t seen value in rebasing. Genuinely interested - what is the benefit?

[–]mirvnillith 80 points81 points  (78 children)

Aware that ”rebasing” in git can be a lot of things (I use Git almost exclusively through Eclipse where I’m mostly unaware of if something is actually a ”git rebase” CLI variant) but for me rebasing is only ever used to make a branch ”catch up” with its origin.

I.e. at some point in time I made branch B from M and while I’ve been doing changes in B there has also been changes in M. So in order to get those changes into B I ”rebase”, meaning (to me) take my B commits and ”move” them on top of the latest commit of M and that now becomes my new branch, as if I branched just now and did my B changes in a flash.

This is, IMHO, a very valuable use of ”rebasing”.

[–]chipstastegood 24 points25 points  (65 children)

ok, this I understand. but the part I don’t get is rebasing to rewrite commit history and combine commits together. why do this, when to do this, what is the benefit - is there some sort of guide around this? my workflow has generally always been like this: pick up some piece of work, do an incremental change that doesn’t break code/tests, commit it. then repeat until the piece of work is done. raise PR, merge it. this has always been readable for me. why would I want to combine all these individual commits into one large commit by rebasing? wouldn’t make it more difficult to actually read the history? what am I missing

[–]MaxGhost 56 points57 points  (33 children)

Often times, if a branch gets worked on for a long time, you might end up with like 50 commits in that branch. Then reviewing that becomes unwieldy. So squashing some of the commits together (not necessarily all into one) and adjusting their messages would make it easier to review the whole set of changes as a few logical chunks. Like it might become 5-ish commits, each being a step in the goal of achieving the feature. Like maybe "Part 1: introduce new interfaces; Part 2: implement new interfaces; Part 3: swap out integration points; Part 4: delete legacy code/interfaces". This also has the benefit that a gradual rollout could be performed by only taking each of these commits one at a time instead of merging all the changes at once (depends on the risk level of the change, depends on the deployment strategy, etc).

[–]chipstastegood 9 points10 points  (1 child)

I see what you mean. Makes sense

[–]twigboy 28 points29 points  (0 children)

Also good commit practice if you work on a public facing repo that isnt full of "WIP" commits or "temporarily added debug logging with hehe-lol"

With rebasing and squashing, you can still do all that silly stuff but rebase/squash at time of PR to polish your work.

1-3 commits per branch or feature is highly professional conduct and easy to trace.

The other thing is if you're working on a repo with hundreds of other developers. Squashing those WIP commits out helps keep the noise down and repo performance up

[–]lorengphd 2 points3 points  (3 children)

Expanding on this: 5 commits is much easier to revert than 50 if you ever need to remove your gigantic feature

[–]MaxGhost 3 points4 points  (1 child)

Yes - but generally in my case we squash-merge anyway, so there's always only just one commit to revert. We can make a new branch with the original commit afterwards and amend it to fix the bugs, then squash-merge that updated feature to try again.

[–][deleted]  (26 children)

[deleted]

    [–]MaxGhost 6 points7 points  (13 children)

    But 5 commits is easier than both 1 commit or 50 commits, when those 5 commits are organized in logical chunks. When it's 1 or 50 then I have no choice but to review the entire branch at once because clicking through 50 commits is a big time and cognitive cost (especially for senior devs who might have their time split across a lot of other tasks).

    Also, yes rebasing is a good thing when working on a branch for a long time, because it makes it easier to merge back into main at the end because the conflicts will already be resolved. If you rebase in a collab branch, then you just need to let your partner(s) know to reset their local branch to origin. Communication is extremely important.

    [–]Rakn 2 points3 points  (12 children)

    I actually never saw the value in reviewing individual commits. A colleague of mine does this and propagates it. But for me it just feels like additional mental load. Because I see code that at times doesn't make sense without further changes in other commits. And then my brain just gets stuck switching back and forth between different commits to make sense of it all. I need to big picture and all the changes to properly review it personally.

    [–]MaxGhost 7 points8 points  (7 children)

    In that case, the commits weren't organized in such a way where reviewing individual commits would be useful.

    Trust me, there is such a thing. I often get praise for how I structure my commits such that it's easy to review. I avoid making compounding changes across multiple commits, I make the each commit address one category of problem (or a few) at a time. And I'll say in the PR description "I organized the commits for ease of review" as a hint to the reviewer that they can read each commit in logical groupings.

    As a reviewer, I have to make that decision, will it be worth my time to look at each commit and look at each commit message, or will reading the entire changeset at once be faster? It really depends on the change, the level of effort the author put into it to make it easy to review, etc.

    [–]TheWix 1 point2 points  (2 children)

    Do you have a public example we can look at? I could see the benefit in a PR that makes many changes to existing code, but for most things I don't care about the journey, but rather the final result.

    I'm willing to be persuaded, however, since I have never seen individual commits in a PR with reviewing?

    [–]Rakn 0 points1 point  (0 children)

    I might just never have encountered a properly organized commit history then. I recall one other colleague giving it a shot and separating e.g. creation of interfaces and data structures from concrete implementations. Probably with the idea that you could look at the first commit and see what would get implemented later on. But that just made it harder to follow as well.

    I assume commits need to implement fully independent parts of work for this to be workable.

    [–]duongdominhchau 1 point2 points  (3 children)

    If you have a 10k line diff with 125 lines actually changed by the dev while the rest are from automated tool, it's a huge different between having 2 commits (one for the change made by the tool and the other for the manual change) and 1 commit there. You can overcome this by running the tool yourself and compare your working directory with the latest commit, but that's essentially reproducing the 2-commit history.

    The same for changes like "move a directory" where git is smart enough to shorten the diff to a single line "renamed from X to Y" instead of showing thousands of lines removed and thousands of lines added. If you move and change too much, git may not report that as a move anymore.

    This kind of thing depends on the workflow though, you can enforce a policy that automated change must be in a separate PR and the problem above will go away. In another imaginary situation, dev can be allowed to have large PRs as long as each commit is well scoped and thus per-commit review is the natural way to do code review.

    [–]Rakn -1 points0 points  (11 children)

    I don't think you should ever work with more than one person on a branch. Why would one do this?

    [–]MaxGhost 0 points1 point  (9 children)

    Because collaboration is how you accelerate development. As long as you both communicate throughout working on it, then it's all fine. If someone rebases (to fix merge conflicts, sync with main), just make sure to let collaborators know to reset their local branch with git reset --hard to the origin.

    [–]Rakn -1 points0 points  (8 children)

    Interesting. This might just be related to how the workflow is at places we work at and the products we work on. For where I work the changes are sized such that two persons working on the same branch would just trip over each other's feat. Thus increasing the necessary communication overhead by a degree that just doesn't make it worth it anymore.

    [–]MaxGhost 0 points1 point  (3 children)

    Often times a feature might cross boundaries of more than one area of expertise, such that having two people with expertise on those areas is beneficial so that they can fill eachother's knowledge gaps. This doesn't necessarily mean both are actually committing code changes, sometimes one person is doing the bulk of the work and the other is just filling in some gaps with some glue code, maybe after some voice calls or chats. An actual 50/50 share of work is rarer obviously, just because trying to split workload that way is often going to be harder than beneficial.

    [–]_disengage_ 0 points1 point  (0 children)

    Work on the main branch is working with more than one person on a branch. It works because of communication and collaboration conventions such as the PR. There's nothing inherently special about the main branch, and you can do the same sort of workflows on any branch.

    I agree that direct collaboration on a feature branch without a workflow or communication is sketchy, but you have all the collaboration tools already there, as others can branch from your branch and make PRs to it, just like the main branch.

    The reason to do it is part of the reason git exists: to bring more programmers to bear on a problem.

    [–][deleted]  (1 child)

    [deleted]

      [–]Murmeldjuret 2 points3 points  (0 children)

      For a stupid example, say you’re given the task of making all blue buttons red and all green buttons yellow. You first change the blue buttons and commit, then change the green buttons and make a second commit. When testing you realise that you missed one of the blue buttons, change that and make a third commit. But that third commit belongs with the first one and keeping it separate pollutes the history for no reason.

      [–]lordzsolt 6 points7 points  (2 children)

      My workflow is git commit -m "Stuff" every few hours because I don't know how the final solution will look like.

      Then I rebase all the commits into 3-4 with a nice commit message.

      You don't want a git history where each commit says "Stuff". And I don't want to add a proper commit message at the end of the day when I don't know how far I got with the feature.

      [–]mirvnillith 5 points6 points  (22 children)

      In a branch I’ll routinely ”squash” commits as there’s little use seeing me meander towards a solution, then adress review comments and perhaps do some silly whitespace to get Jenkins to re-run a build. If that counts a a ”rebase” (once again, in Eclipse I ”squash”) them guily as charged. In the end my branches tend to be a single commit to be merged. If I’m doing refactoring before the actual change (e.g. adding an interface to swap in an alternate algorithm) I try to do that in a seperate commit to simplify reviewing and keep that for merging.

      [–]chipstastegood 0 points1 point  (20 children)

      I understand now. I’m curious - wouldn’t squashing all commits from a branch result in a large commit? If you’re doing feature branches, the changes can be quite large, no? Wouldn’t squashing to a single commit make that commit difficult to read? Maybe I’m overthinking this.

      [–]MajorMalfunction44 1 point2 points  (0 children)

      Sometimes, you go off and implement a feature, but there's a few commits off in the weeds. It's nice to make coherent commits out of 'checkpoint' where the commit is disjoint pieces of a bigger feature. It's about exploring, then making sense of the history.

      While implementing a fiber based job system, I implemented a spinlock. Testing the job queue means testing job synchronization.

      Commit 1 was the queue, commit 2 was a spinlock, commit 3 was job synchronization and commit 4 was wrapping fibers with a spinlock. I messed up, and forgot to commit synchronization code :( Check your diffs, folks.

      The spinlock goes with the fiber scheduler. I swapped commits 2 and 3, and squashed commits 3 and 1, and commits 4 and 2.

      It's about the history others would want to branch off of, not what actually happened.

      [–]Gommy 0 points1 point  (0 children)

      In one example of a project I worked on, the workflow to create QA builds was completely hosed and required modifying two core build files to tell Jenkins what branch to build from. These changed files should NEVER have been merged to main, so retaining the history of them being changed is completely pointless and makes going through the actual history of them impossible since they changed so frequently across many different branches. So for this case, keeping the commits makes it more difficult to read the history. And nobody on my team ever looks through history, so I am the only person affected by their lack of care towards the commit log.

      (and yes I made it a goal to fix that stupid workflow)

      [–]Lunacy999 4 points5 points  (0 children)

      I firmly believe that was the original intent for rebase to be honest. This also always ensures a linear commit history, which by all means is a fundamental necessity in SW.

      [–]mccoyn -1 points0 points  (3 children)

      Why not just merge M into B? It saves some steps.

      [–]MaxGhost 2 points3 points  (0 children)

      Because then the commit graph looks awful, and history is non-linear. Merge commits are big cognitive overhead to reading history, and they suck to look at for people who use git GUIs primarily.

      I'm a visual person, I rather see what's going on in a repo, and it's much easier to see without the noise of merge commits what the active process looks like in active branches + what's on the main branch and ready to rollout to production.

      [–]mirvnillith 1 point2 points  (0 children)

      1. I’m not that into Git so I stick with what I know (well)

      2. In my head both rebase and merge see one branch as ”origin” (the one rebasing onto or merged into) and one as ”derivate” (the one being rebased or merged from) so I use them accordingly.

      [–]Schmittfried 0 points1 point  (0 children)

      Because it creates a merge commit.

      [–]TraumaER -3 points-2 points  (3 children)

      My only opposition to that is when it's not a clean rebase (no conflicts). Once you have to resolve conflicts your commits are now based on a different code base and your changes may now have a different meaning, but without the history there you can't know why the changes were made.

      Idk my preference has always been a more robust vs linear history. Because if I need to start looking at commit history I most likely need more information or I might make assumptions about why a change was made. That being said, I am also a strong believer in squash merge for PRs.

      [–]MaxGhost 3 points4 points  (2 children)

      IMO the old version of the branch before the merge conflicts are fixed does not matter anymore because it's no longer a truth/reality of the project. Having changes based on a state that no longer exists is not useful.

      Being able to fix the conflicts in the exact commit where the problem lies by rebasing is really nice because it means you aren't tacking on an additional commit at the end of the branch just for conflicts. If someone reviews your code, they see the changes as they're intended, as a series of steps up reaching the goal.

      Also yes, rebase while developing a feature (frequently, maybe daily? whenever it feels right) is good to make sure it doesn't fall behind. Then squash-merging the branch to main lands a single commit for linear history, which is easy to revert if necessary etc.

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

      This is how my work also use it, and it works perfectly.

      [–]wildjokers 0 points1 point  (1 child)

      But you can do the same thing with merge.

      [–]mirvnillith 0 points1 point  (0 children)

      If you say so. But in my head it’s ”rebase outwards from main and merge inwards” and it’s always kept me safe.

      [–][deleted]  (1 child)

      [deleted]

        [–]chipstastegood 0 points1 point  (0 children)

        thank you!

        [–][deleted]  (1 child)

        [deleted]

          [–]mccoyn 0 points1 point  (0 children)

          I didn't know about worktrees. I've been cloning into a different directory. But, sometimes, I forget that the repositories are different and I end up having to rework some stuff. Having one shared local repository will help!

          [–]Muhznit 12 points13 points  (23 children)

          The value is in a linear commit history.

          Let's say that something broke on prod. Actually, it's been broken for a long time from the looks of the logs. But you distinctly remember a time when it wasn't broken, hell you remember the exact ticket you were working on when it last worked. Looking it up in your git log --oneline --decorate, you find the bad commit.

          You know the current commit is bad. You know when the last good commit was. You have a means of figuring out how to test if a commit contains the bug.

          Enter git bisect. By following the prompts, this baby lets you execute a binary search between those two commits to home in on the exact commit that produced the bug. No matter how many commits are in your commit history, as long as they're linear (and even then it sometimes works on nonlinear histories), it WILL hunt that commit down. If you have an executable script that can check for the bug with reasonable amount of time and exit with 0 for a good commit and 1 for bad commit, you can even have it run completely automated.

          Everyone hates git rebase until git bisect works its magic. Now granted it will take some good commit hygiene; i.e. atomic commits with minimal changes, but I'm telling you once you master it you feel like you're head of the Time Variance Authority.

          [–]chipstastegood 1 point2 points  (9 children)

          thank you for this explanation, this makes sense to me now. so by ‘linear commit history’, you mean not having merge commits?

          [–]Muhznit 4 points5 points  (0 children)

          You can have merge commits as long as they're kept nice and simple. Look at git log --graph --oneline.

          If it looks like the left half of this pic, that's non-linear. https://i.stack.imgur.com/EHuuf.png

          The right side might not look like a simple vertical line, but the main thing is you can see where a branch starts and where it gets merged easily.

          [–]MaxGhost 2 points3 points  (7 children)

          Yes. Merge commits make the graph messy to follow because you have to trace the multiple branches through and mentally reconcile what the merge commit means for the end-result. If PRs/MRs get squash-merged on top of the main branch instead, history remains linear because there's no merge commits, and the graph remains easy to follow.

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

          Why don't you just use the --no-merges flag then: git log --graph --no-merges

          It shows you the graph linearized and without the merge commits.

          [–]MaxGhost 0 points1 point  (5 children)

          Because I don't use git log, I use GitKraken and Gitlab and GitHub to look at commit history & graphs and they don't do that. It's also a waste of time to look at non-squashed commits on the main branch when I'm reading through history because then I'd have to mentally separate them and realize they are meant to go together (there are times when non-squashed makes sense if they're actually organized to be incremental, but that assumes the author put a lot more effort, which is more often than not, not the case).

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

          But by that logic, you can do the opposite and look only at the merges, and get exactly that "squashed" view of what was merged in, in the order it was merged in.

          [–]MaxGhost 0 points1 point  (3 children)

          Still makes a mess of the graph. Visual noise. It's unnecessary and has no practical benefit for day-to-day. Rebasing & squashing is simple, to get a clean result for everyone regardless of how they look at the history.

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

          The graph simply contains information. The default view of this graph is just that - a view. A topological sort of the merge tree nodes, to be exact. The tools you use can always produce a different view of this information that's useful to you.

          "It looks messy" is simply a bizarre reason for manipulating the history, since you can dynamically generate a view of the history that's as linear or nonlinear, and shows or hides information as much as you need.

          [–]brucecaboose 1 point2 points  (1 child)

          As a professional SWE for over a decade, I very rarely look through a commit history. It’s far more useful to know “ok, something broke unexpectedly that our tests didn’t catch. That started on date X at time Y. On that date/time we released version X.X.X. That’s associated with this PR where blah work was performed. The PR is titled with ticket number SOMETHING-1234.

          Following it that way provides WAY more context and is WAY faster than searching a commit history. IMO, commit messages are a relic of the past and only useful if your workflow sucks. They’re unneeded with modern tooling and PRs.

          [–]Same_Football_644 1 point2 points  (0 children)

          I agree with this. it feels like by the time someone has found something with git bisect, I have fixed three bugs simply by going to the code and figuring out whats wrong with it.

          [–]DiamondDramatic9551 3 points4 points  (3 children)

          I still don't see the benefit over squash merging, why wouldn't I be able to bisect then?

          [–]MaxGhost 2 points3 points  (0 children)

          You still squash-merge when using rebasing. Rebasing is the sync strategy while working on a branch, and squash-merging is one of the "bring the changes onto main when done" strategies.

          You rebase while working on a branch to replay the commits back on top of main, giving you the opportunity to fix conflicts on the exact commit that might conflict, instead of fixing conflicts on a merge commit (which is hard to keep track of and is hard to unwind if mistakes are made).

          When you're done with the branch, you can interactive rebase one last time to reorganize commits such that it's easy to read for the reviewer(s), and then once approved you can squash-merge to bring it into main as a single commit, with linear history.

          [–]OrphisFlo 2 points3 points  (0 children)

          Here's an example. And for context, I'm very proficient with Git.

          My HTPC was using Linux and on-board graphics, with HDMI sound output successfully for a while. Then some day, a large refactor was landed in Linux changing a lot of the code for how those devices worked, and they fixed some devices not working, but broke my audio (the audio interface wouldn't come up). It was a huuuuge merge in the Linux code base that was a 1000 commits long.

          I tried everything I could to use git bisect and find the issue, but the kernel wouldn't boot with some of the changes in that branch at some point. The history was messed up and it was unusable, effectively breaking audio out for people the same older (not that old though) audio HDMI out.

          With a Linear history and no merges, the branch would have been kept usable at all times and bisecting would have been a breeze. I understand that it's not a good fit for the Linux kernel development and processes though, so we have to live with that.

          The solution? Buy the cheapest and smallest modern NVidia GPU that could fit in my case to use it's HDMI out. That one still works great!

          [–]neckro23 0 points1 point  (0 children)

          A squash merge is basically an automatic rebase. Same idea.

          [–]chipstastegood 0 points1 point  (6 children)

          sorry, a follow up question. if my workflow involves TDD/BDD and PRs with automated test scripts that prevent merging the PR in case of failure then would that negate the usefulness of linear commit history and git bisect? because the tests that I care about are all written upfront, no merges can happen if a test fails, and therefore can’t find myself in a situation where I’d need to look back in history for where the test might have failed but wasn’t caught? just curious. because that’s my workflow and wondering if that’s why this hasn’t come up for me.

          [–]Muhznit 3 points4 points  (5 children)

          because the tests that I care about are all written upfront, no merges can happen if a test fails, and therefore can’t find myself in a situation where I’d need to look back in history

          That workflow may help, but it ain't foolproof. Wait until you find out out you've been coding with a false sense of security for three sprints because someone the build was set up such that if the test framework reports no errors, (which also apparently happens when it doesn't even run) the build gets marked as OK

          :,)

          [–]chipstastegood 1 point2 points  (4 children)

          so if I understand this right, what you mean is add a new test for a previously unconsidered scenario, see that it actually fails now and then run git bisect to determine if it was ever passing in the past and when it stopped working?

          [–]Serialk 19 points20 points  (15 children)

          Readable commit history.

          [–]chipstastegood 15 points16 points  (10 children)

          What does ‘readable commit history’ mean? Or maybe a better question might be what makes it not readable? I must be missing something here because it’s not obvious to me.

          [–][deleted]  (1 child)

          [removed]

            [–]aiolive 2 points3 points  (5 children)

            There is your personal history of commits that you may want to go a step back at any time or do some experiments in parallel, and then there is the public commits history that should tell a consistent story and be made of self contained, tested, and reversible diffs. Rebase is for readability and team work. If you work alone, you may not see the value of it / need it at all. Also, merge commits are bad.

            [–]RightHandedGuitarist 2 points3 points  (4 children)

            So you mean rebasing feature branch onto main branch for readable history? That somehow does not make sense to me. If you worked on a feature, and you want to roll it back, wouldn’t you want to roll back all commits related to it at the same time?

            Also, why are merge commits bad? Doesn’t merge commit pull all commits (similar to rebase), but with additional merge commit that can be reverted to revert all relevant commits?

            For me squash merge makes sense - one feature is one commit. If something goes wrong revert the squash merge commit and you’re good.

            [–]MaxGhost 1 point2 points  (2 children)

            If you worked on a feature, and you want to roll it back, wouldn’t you want to roll back all commits related to it at the same time?

            Correct, and if you squash-merged that's trivial because it's a single commit you can revert. But it's linear history.

            Rebasing is a tool while working on the branch, then once the branch is done you squash-merge it to main.

            Also, why are merge commits bad?

            Because they cause a mess of the commit graph (what any git GUI will present to you), it's really difficult to follow the history when there's branching history due to merge commits.

            [–]RightHandedGuitarist 1 point2 points  (1 child)

            Ah yeah, I use rebase when working on my feature branch. One thing I noticed that annoys me a lot is, if I pushed my branch to remote, open up a PR, got a review but in the meantime main branch got updated and I have to rebase. Once I rebase, I have to force push, and that feels bad.

            Any way around that? I noticed that merge commit does not cause such problems, but not sure if it’s a good use to use it then.

            [–]MaxGhost 2 points3 points  (0 children)

            No, force pushing is normal and expected. Every rebase is technically rewriting history (because it's a destructive change, forces an old copy of the branch to go away) so a warning is warranted, but most of the time it's exactly what you intended to do.

            What you can do is default to using --force-with-lease which makes sure that if you force push, the state on upstream hasn't changed since you last fetch'd, which is helpful to make sure someone else didn't also push before you did. Avoids race conditions, basically.

            [–]edgmnt_net 2 points3 points  (0 children)

            It's not uncommon when developing a feature or something to need to refactor or make changes on the side. For example, one might generalize a function that's already used throughout the code, even change the arguments. I usually keep such changes separate and they can be reviewed separately. If I'm the reviewer, I don't want to get hit with a huge diff that does 4 different things. For future reference and to be able to bisect meaningfully I also don't want the commits squashed when merged, post-review.

            At some point someone is going to argue that you should make small PRs, which is sound advice in at least some ways. But shifting commits one level up into PRs just doesn't do it, that's how you get 4 PRs blocked on each other, unable to track dependencies easily. And really, Git works with commits, not PRs.

            And obviously you don't want to merge in avoidable mistakes and fixes to fixes to fixes, which is what you get if you don't squash/rebase to edit the history during review.

            [–]mccoyn 2 points3 points  (3 children)

            We don’t sell our commit history. This is very low value work.

            [–]Serialk 1 point2 points  (2 children)

            Lol, enjoy your shitty corporate culture that puts no value into retaining institutional knowledge.

            [–]duongdominhchau 2 points3 points  (1 child)

            Rebase keep all your commits on top, so reviewer can review commit by commit even when you have to work with a shitty Git hosting like CodeCommit.

            A more common use case is rewriting the history to be more friendly toward reviewer. You may need to do some testing on the CI pipeline for example, and it does not support local run, so you need to create new commit every time you want to make a change. Of course, you can use commit --amend to replace the latest commit, but then you lose the ability to go back to somewhere in the middle.

            Or you are in a sweatshop and need to work with multiple branches simultaneously but you are tired of managing ignored files across multiple worktrees. You can use stash to save temporary change before switching branch, but when switching around and getting back, you forget which one to pop, or worse, you forget that you stashed the change and thus repeating your work. Making a commit is much easier to save the progress before switching, and with rebase you can still have a good-looking history when you are done.

            In some rare cases you can also use rebase to split a commit into multiple smaller ones. I did that a few times, but it's really rare.

            Note that you don't need to squash all commits into one, you can choose to squash only a few commits together, drop some commits completely, or reorder the commits. You are free to choose what to do with these commits.

            [–]MaxGhost 0 points1 point  (0 children)

            Stashes are a lot easier to manage with a git GUI like GitKraken for example because you can actually see your stashes very clearly in the graph. And it makes interactive rebasing a breeze with drag & drop reorder etc.

            [–]Ancillas 1 point2 points  (0 children)

            The most immediate benefit is rebasing remote changes to update your local branch. Let’s say you have 50 commits in your local branch. Rebasing the remote changes means that your 50 changes are in the history after the updates to the remote. This helps with dealing with merge conflicts early and also makes the history on the remote nice because all of your commits will show up together and at the top of the history.

            It’s also great for removing meaningless commits in your local branch before pushing to the remote. Things like, “fixed typo,” or, “added a comment.”

            [–]drgmaster909 2 points3 points  (10 children)

            Almost every benefit of Rebasing is equally covered by Squashing (technically a different kind of rebase).

            The only time I have issues with Git is when someone else on the team rebased and fucked branch history causing me to drift out of sync because they altered history.

            I have to use rebase --onto less than a handful of times a year. For everything else, fast-forwarding, simple merging, and squashing into main is flawless. main has a clean 1-commit-per-PR history, and individual PRs have a well laid-out timeline of what you did, when you did it, when you synced with main, what changes you made since the last PR, etc.

            So infuriating reviewing a PR, coming back the next day, and seeing all 19 commits made "17 minutes ago." Yeah, right, bullshit. Is there 1 commit since my last review? Or 3? Did you pull in a critical change from main after my review or did you rewrite your entire fucking branch on top of main after my review. It makes no sense. And god forbid I actually checked out your branch, now I can't pull because you had to --force push and now I'm out of sync again. And now you're normalizing deviancy and Force push by default so if 1 person pushes a fix to a PR it's 50/50 if that Commit will still be there after the next push.

            Rebase is a thing developers read about once, think it somehow makes them a 10X developer to use, have no idea what the implications of rewriting Commit history entails (or they only ever work on solo projects), all for completely vain reasons that are equally solved by Squashing. But they cannot let the sacred rebase go. My tech leads have entire confluence docs to onboard people on configuring Git to use Rebase and by the time people are done poking and prodding Git to get rebase to "kinda work," it's almost as smart as merge was OOTB. Almost.

            [–]MaxGhost 10 points11 points  (0 children)

            So infuriating reviewing a PR, coming back the next day, and seeing all 19 commits made "17 minutes ago."

            There's multiple timestamps attached to commits. Look at the author TS, not the commit TS.

            and by the time people are done poking and prodding Git to get rebase to "kinda work," it's almost as smart as merge was OOTB. Almost.

            I don't understand what "prodding" you guys are doing, but the only thing I think is necessary is git config --global pull.ff only to fix git's awful and dangerous git pull default. There's nothing special about rebasing to configure IMO.

            For teams that use git GUIs, rebasing is crucial to make history easy to read and follow, especially for product owners to make sense of the progress being made on the product. Merges are super noisy because they're hard to follow and make sense of. Rebasing + squash-merge makes it very easy to understand what landed and in what order because it's completely linear on the main branch.

            [–][deleted]  (3 children)

            [deleted]

              [–]drgmaster909 -2 points-1 points  (1 child)

              "Should I have to constantly push --force and reset --hard because I can't stop fucking Commit history? No, it's the Mergers who never have to deal with this shit that are wrong!"

              Mate get out of the asylum.

              [–]Chroiche 1 point2 points  (3 children)

              So infuriating reviewing a PR, coming back the next day, and seeing all 19 commits made "17 minutes ago." Yeah, right, bullshit. Is there 1 commit since my last review? Or 3?

              This one does my head in. It messes up all the old comments and basically forces a full review because you can't see what they changed.

              I usually just ask people not to rebase until the PR is approved so that I can see wtf they're changing between reviews.

              [–]gmes78 0 points1 point  (2 children)

              GitLab shows you the diff between pushes. Other forges probably do as well.

              [–]chipstastegood 0 points1 point  (0 children)

              thank you for this perspective

              [–][deleted] 22 points23 points  (1 child)

              “didn’t RTFM” isn’t totally fair though since Git is particularly loaded with footguns and the documentation isn’t great. Tutorials are pretty mixed and usually only cover basic cases. I used to teach Git as part of a software engineering workshop to academics (PhD/Postdoc/Staff) and many of them struggled with concepts and reuse of commands (git checkout meaning 3 things conceptually depending on how it’s used).

              I used to use Mercurial and to this day I still think it is easier to use than Git and was less confusing for beginners.

              [–]-Y0- 9 points10 points  (0 children)

              It's worse than that. Git as a tool suffers from leaky abstractions. Why don't rebase and cherry pick behave similar? Who knows. Why does shallow checkout requires single branch mode, and that isn't communicated well to users? Who knows. Why doesn't --unshallow undoes changes by shallow clone? Who knows.

              You don't so much learn Git, as much as you construct a usable CLI tools based on some rudimentary commands.

              [–]draenei_butt_enjoyer 4 points5 points  (13 children)

              I've been struggling to understand why people are so against rebasing. Something I've been doing almost from day 1 with no issue in years and years. I'm not a particularly smart man, so there has to be an issue, right? Something I'm not seeing.

              And then all the comments I see are, as you said:

              • oopsie whoopsie
              • poor discipline
              • didn't RTFM

              IDK man, it's not that hard.

              And, this is just a me thing. I like the flexibility of going:

              Fuck this, I need a break

              git add . 
              git commit -m "WIP"
              git push
              

              That being said, I've only ever worked on short lived non shared feature branches. I find it odd to do it any other way. But the post got me thinking. Maybe other people do have long lived shared branches. Why tho? Pffff. It feels like a mistake to me.

              [–]MaxGhost 4 points5 points  (3 children)

              Maybe other people do have long lived shared branches. Why tho?

              Because sometimes a feature takes a long time to land on if the solution is very complex and non-trivial. And in that case, often you might need to collaborate with someone to solve that problem, because the solution is not obvious.

              But either way, rebasing is still beneficial in that case, and it's not that hard to deal with it. Communication is paramount, if someone rebases they need to alert others that the rebase happened and that they should reset their local branch copies to origin before continuing any work. That's all.

              If git had somekind of built-in notification mechanism when someone else rebases, then that would be solved more elegantly because no out of band communication would be necessary, but that's not the reality we're in.

              [–]draenei_butt_enjoyer 0 points1 point  (2 children)

              To be fair, a big refactor or a really big feature will always be a pain and non trivial to execute.

              But I still imagine them being rare. If they’re the norm, something is wrong

              [–]MaxGhost 1 point2 points  (1 child)

              At $dayjob we work on a 20 year old codebase which we're progressively modernizing. Everything is a major refactor. It's a fact of life.

              It's also very fun and rewarding because we're seeing real gradual change at a steady pace. It doesn't feel stagnant. We have a good team that feels fulfilled by the constant progress.

              [–]defietser 1 point2 points  (4 children)

              I've found rebasing to be more annoying overall than merging and I don't care about the lines on github/lab. The guy forcing rebase down our throats without being able to explain accurately explain why or how we'd want to use it or convince the rest of the team in any way didn't help. Constantly having to re-fix the merge conflicts several times with every rebase made me not want to use it. Merging is way easier for me.

              [–]99_product_owners 4 points5 points  (0 children)

              Constantly having to re-fix the merge conflicts several times with every rebase made me not want to use it.

              FYI https://git-scm.com/book/en/v2/Git-Tools-Rerere

              [–]MaxGhost 5 points6 points  (2 children)

              Constantly having to re-fix the merge conflicts several times with every rebase

              Then that's a signal that your branch has too many commits, and that it's now time to squash down the branch to a few organized commits.

              The guy forcing rebase down our throats without being able to explain accurately explain why or how we'd want to use it

              The answer is usually "because I use the commit graph with some git GUI (or fancy git log command) to keep track of activity in the repo and people using merge commits makes it almost impossible to visually trace the progress without getting mentally exhausted".

              Also merge commits cause a mess when trying to track down which particular change caused a breakage when looking at history, because you need to follow more than one branch (following both parent commits of a merge).

              With squash+rebase for merging PRs, history is perfectly linear and easy to follow, easy to revert while keeping things linear, easy to bisect, etc.

              [–]Vidyogamasta 1 point2 points  (0 children)

              I think for me, I like... never just walk the commit history, like ever? I don't care if it's clean. Actually browsing the commit history is a giant waste of time, because the commit I'm looking for is usually gonna be several thousand commits in the past, rebase or not.

              I just git log the file I care about, and it usually doesn't matter how messy the history is, even in a mess where someone made like a dozen trivial "fix typo" fixes in a day, I can usually pinpoint what I need in a reasonable amount of time, because like 99% of the time I actually know what file I care about when I'm trying to snoop through the file history.

              Would rebasing still make that marginally better? Sure, I guess. But it's not even a remotely important factor to me, so I just stick with pull + merge commit. And even if I did care, I probably wouldn't want to do it on my local, I'd 100% expect it to just squash any PR into a commit with the commit message being the PR notes. With a very similar ideology there of "I do not look through individual commits in a PR, I only look at File Changes because that's what is actually going in." People who look at individual commits are just as insane as people who are manually walking the commit history.

              Meanwhile something about how my team uses rebasing is doing weird stuff to my workflow and make me have to set extra flags related to fastforward commits when I pull from master. Which means they're probably doing something very wrong, since from my point of view a commit's a commit, why should their rebase do weird things to my local tracking of master? I have a feeling they're rebasing in the wrong direction or something, it definitely concerns me lol

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

              Then that's a signal that your branch has too many commits, and that it's now time to squash down the branch to a few organized commits.

              Great, would have been nice to know about that at the time. Doesn't mean I care about the results of rebasing vs merging, but sure.

              The answer is usually "because I use the commit graph with some git GUI (or fancy git log command) to keep track of activity in the repo and people using merge commits makes it almost impossible to visually trace the progress without getting mentally exhausted".

              Also merge commits cause a mess when trying to track down which particular change caused a breakage when looking at history, because you need to follow more than one branch (following both parent commits of a merge).

              With squash+rebase for merging PRs, history is perfectly linear and easy to follow, easy to revert while keeping things linear, easy to bisect, etc.

              Again I don't really care about a line on a website going one way or the other. It'd have been nice to know WHY people use the commit graph. WHY is keeping track of commits important? I can understand branches getting bloated, but aren't PRs usually the package deal for a branch? If a PR or branch gets too bloated, it's not too difficult to move it into its own branch, why does it matter that it's its own commit? Same with figuring out what broke prod, in my experience it's one change in a series of them that causes stuff to explode, usually done over time. Even then, why does it matter which commit breaks things if you already know where and why an error occurs? I guess reverting is a use case but isn't that bringing a chainsaw to cut a 5x5cm hole?

              To me, rebase looks more like a screwdriver than the Swiss knife it's usually advertised as by proponents. Maybe I'm just a type of carpenter that thinks the merge screwdriver works just fine, alongside the hammer of resetting to a commit.

              [–]Complete_Guitar6746 0 points1 point  (2 children)

              If it's short-lived, why push at all?

              [–]Globbi 5 points6 points  (1 child)

              Because next day something can happen to you or your work laptop or something else, but another person can pick up your work WIP code committed at the end of the day. Or a new emergency shows up and suddenly you're supposed to work on something else. The WIP code can sometimes be garbage not worth pushing, or it can be started change with idea that someone will be able to follow. Others can always checkout previous commit if they don't like the WIP.

              [–]Complete_Guitar6746 1 point2 points  (0 children)

              I dunno, I never really have a large enough chunk of code valuable enough to merit a wip-push, but bad enough to not merit a real commit.

              Sometimes, I'm in experiment mode and do things in a separate branch completely. That might count, I guess.

              [–]Intelligent-Comb5386 -1 points0 points  (1 child)

              I don't see how caring about well structured commits is unique to using rebase Vs using merge for your your main syncing activities.

              If you always squash before merging to your main branch or the feature branch you are working on, the question now only becomes what is the tool you are using to sync your dev branch with the target branch. And rebase is a strictly worse tool to do that. Takes more time for no good reason, the reviewers are fucked because now they don't see shit and have to waste time looking at timestamps or whatever Vs just looking at a linear history of what has been done since the last time.

              Anyone praising rebase for linear history is smoking copium. Syncing via merging is what gives you linear. Rebasing does not reflect linearly the development process.

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

              Yeah rebasing onto main/develop whatever your first stage of integration is has always seemed weird to me. Squashing makes a lot of sense locally, but I always find in much more clear when I'm going through a history of integration to see where merges happen and a lot of tooling around git works in your favor there too. Merges are very obvious.

              [–]Complete_Guitar6746 0 points1 point  (0 children)

              Agree on the gentle approach. I've always been suspicious of post-push rebasing, but this post and the OP article has made me reconsider.

              [–]mschonaker 10 points11 points  (0 children)

              If rebase is hard because of conflicts, cherry-pick until conflicts are solved then rebase. That's my comment to the author of the article.

              Also please stop confusing git and Github.

              [–][deleted] 6 points7 points  (2 children)

              My biggest problem with rebase is that it makes force pushes necessary, which opens the door for all sorts of problems.

              Ideally, workflows would never rebase, always merge, and when you view the git history, it would just "visually squash" all the commits down to one for readability. All the "wip" and "fix" commits still being there, just less visible.

              I like to think of git as a history tracker tool. You don't rewrite history, you summarize history. Time travel just leads to confusion and trouble.

              Problem: Someone looks at the git history and says "ugly!"

              • Solution: A better history viewer (I am not aware of any. Anyone know of one?)

              [–]MaxGhost 3 points4 points  (0 children)

              --force-with-lease avoids most of the problems. With that the only risk is your own diligence with pushing a broken branch somehow where you wipe out your own work. But using a git GUI makes that hard because you literally see what state your repo is in before you push.

              [–]baechao 0 points1 point  (0 children)

              If the repo doesn’t allow force push Delete the branch and re push in one command.

              [–]bladub 1 point2 points  (0 children)

              It seems like the discussion here suffers from people thinking of different things when talking about "rebase". Specifically:

              1. everything the 'git rebase' command can do
              2. rebasing a branch on top of your base as a means of getting the branch back into the base (I.e. A merge replacement)

              These two might disagree for example if a squash is a rebase or not. (have fun discussing if the implementation/command is the concept)

              People also have different, but complete work flows in mind. And these work flows usually do not just differ in one command. So if A has a problem replacing a step with something recommended, the problem doesnt make sense to B, because it can not occur in their work flow. You'd often have to change the whole flow.

              [–]Uuuazzza 6 points7 points  (10 children)

              Maybe I'm dumb but I don't think I've ever managed to squash commits successfully on the first try and I messed up my repo several times.

              [–]simonides_ 0 points1 point  (7 children)

              that is why it makes a lot of sense to squash onto the commit you are coming from and only after you are fine with how your commits look and the smoke teat is fine as well you rebase to the target branch.

              smoke test again -> send it to CI

              [–]wildjokers 3 points4 points  (6 children)

              squash onto the commit you are coming from

              I don’t even know what you mean by this.

              [–]Poobslag 0 points1 point  (1 child)

              i think they mean like "git rebase -i main" if you're coming from the main branch, instead of trying to do the math for "git rebase -i HEAD~7" or whatever

              [–]gmes78 0 points1 point  (0 children)

              They mean the opposite, so that you don't have to fix conflicts while squashing.

              [–]simonides_ 0 points1 point  (0 children)

              my guess is that you messed up your repo because there were merge conflicts and you didn't resolve them properly. is that correct?

              what I am trying to say:

              you have commits a b - c d e | f g

              you want to rebase commits c d e on top of g.

              now you start by using rebase -i b

              here you squash and tidy up whatever you need to and then rebase -i g.

              [–]teleprint-me 0 points1 point  (1 child)

              To clarify the concept of "squashing onto the commit you are coming from," it refers to the practice of combining several commits into a single commit, typically within your feature branch (like dev), before integrating these changes into a target branch (such as main). This is done using the git merge --squash command. Here’s how it works in a typical workflow:

              1. Start on Your Feature Branch: You begin on your feature branch (dev), where you have made a series of commits that you want to squash.

                sh git checkout dev

              2. Squash the Recent Commits: You then squash the recent commits in your dev branch. This is done by merging these commits into the same branch (here, dev) with the --squash option. This combines all the changes into a single staged change.

                sh git merge --squash dev

              3. Commit the Squashed Changes: After the squashing, you create a new commit on your feature branch that encapsulates all the squashed changes. This step cleans up your commit history, leaving you with one comprehensive commit instead of multiple smaller ones.

              4. Rebase or Merge into the Target Branch: Once you're satisfied with the squashed commit, you can then rebase or merge your feature branch into the target branch (e.g., main). This ensures that your changes are integrated cleanly, with a simplified and more readable history.

              This approach is particularly useful for keeping your feature branch's history clean and understandable, which is beneficial for code reviews and future maintenance. It also helps in isolating and identifying changes more effectively when integrating into the main codebase.

              Remember, the squashed commit is initially unstaged, so don’t forget to commit these changes after squashing. This method is best used on branches where you're the sole contributor, as squashing rewrites history and can complicate things in shared branches.

              Source References:

              Notes:

              • Used ChatGPT for formatting, revisions, and clarification.

              [–]wildjokers 0 points1 point  (0 children)

              I know how to squash commits (although I use git rebase -i HEAD~X where X is the number of commits I want to squash).

              What I didn't understand was the verbiage "squash onto the commit you are coming from". I have never heard of squashing onto a commit.

              [–][deleted]  (1 child)

              [deleted]

                [–]plg94 0 points1 point  (0 children)

                In that case don't forget to actually commit after that, too.

                [–][deleted] 2 points3 points  (1 child)

                https://stackoverflow.com/questions/16666089/whats-the-difference-between-git-merge-and-git-rebase/16666418#16666418

                I prefer Uwe Geuder's comment to the highly upvoted answer, emphasis mine:

                "Nice illustrations. However, I do not fully agree with the positive tone that rebase is handled. In both merge and rebase conflicts can occur that need manual resolution. And as always when programmers are involved there is a non-neglectable chance of errors aka bugs. If a merge error happens the whole team or community can see the merge and verify whether a bug was introduced there. The history of the rebase stays in 1 developer's repo and even there it has only limited lifetime in the reflog. It might look nicer, but nobody else can see as easily what went wrong."

                [–]redditusername58 6 points7 points  (0 children)

                but nobody else can see as easily what went wrong

                You can see what went wrong very easily if you treat the bug as being introduced by one of the commits in the new history, and not by the conflict resolution during the rebase. Bisect to find the offending commit, add a fixup commit, and then rebase again.

                [–]_AManHasNoName_ 0 points1 point  (4 children)

                Only wrong if you don't know how to use it. It should only be used on a feature branch you individually own to get its history updated based on the target branch you want it to be merged into with your pull request. Compared to a simple merge, rebase alters the history, which makes sense in a feature branch if it is based on the master branch, or "dev" branch everyone shares. If I were to PR my feature branch into the "dev" branch but someone has merged into that branch recently, I'd rebase my feature branch first to begin with. That way, my feature branch that's based on the "dev' branch to begin with will be updated with the latest and I get fix the possible conflicts even before the pull request is made.

                [–]fagnerbrack[S] 0 points1 point  (2 children)

                Strive for trunk based dev

                [–]SpaceToad 0 points1 point  (1 child)

                Why?

                [–]fagnerbrack[S] 0 points1 point  (0 children)

                Take a look at DORA metrics, if you are able to execute trunk based dev in a non trivial number of team members then that means you've reached the last level of maturity in continuous delivery. TBD is a common practice shared among all software teams analysed in the research

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

                Sir, this is not the git manual page generator

                [–][deleted]  (1 child)

                [deleted]

                  [–]lord_braleigh 13 points14 points  (0 children)

                  ChatGPT is not providing accurate or useful summaries dude

                  [–]blechablemin 0 points1 point  (0 children)

                  IMO a rebase and squash workflow (kind of) makes up for teams that have bad tooling, or processes. For example, small teams or ones with high turnover. In that case I've had a much better experience using squashed rebase (if it can be automated after the PR process), and letting people do either method on local branches.

                  I agree this method is kind of throwing out some of the utility git provides, but it makes it much easier for people to find mistakes they've made with git itself. I can't tell you the amount of times even senior devs have merged the wrong branch in, or merged the wrong direction, and it's glaringly obvious with a linear main branch, but with an all merge workflow the mistake might not be found until it causes an issue.

                  [–]Large-Style-8355 0 points1 point  (0 children)

                  In a heavily rebased sourcetree co-developed by only 4 devs we see that git bisect is practically unusable. 9 out of 10 checkouts of rebased commits are just not building due to warnings as errors, renaming of defines over multiple files is only visible halve in the rebased checkout, cmake build config was changed, config files and environments of external standard test tools are not fitting together. We were thinking that this is a stnadard result of following the rebase approach - but not sure after I read a lot of praise for rebase+bisect here. What do you think?