Rebase private commits, merge public commits. Never change public history, do whatever you want with private history. By public, I mean 2+ developers depend on it.
Yeah, I rebase the private commits on my working branch. I will often commit on my working branch every so often for safety. However, when I do the merge request I don't want there to be a dozen different commits in the history.
@@dorbie I've not been that diligent, and wiped progress several times! If you're unsure, that's the way to go. My worst one was two weeks of local commits gone, after trying to put it on Github with a different name. Completely erased the git folder trying to sync up the empty remote with my local.
@@neonraytracer8846 You're not an expert git user unless you've blown away work from your working files or local repo. This is why we have a love hate relationship with git, even the masochists who pretend it's all love.
“We rebased you out of the chat.” what a way to end a video. pure gold. Appreciate this video, Prime. Things like this always add to my limited perspective
I've just started using fixups and my commit history looks so much better. I've also been a fan of rebasing on master/main before opening a PR so that I'm up to date with the repo. Squash merges and fast forwards are great too, one commit per feature makes things pretty readable Imo.
Rebasing and Merging is two different tools for different situations though. Refusing either seems wrong. Rebasing keeps things clean before you merge something into the base. You get a clean history that is nice to work with and reason about. Rebase for your local/private branches to keep up to date with exactly what things look like upstream and to clean up messy commits. Merge to combine branches. Torvald's/Linux' policy is not to rebase anything public, and not to rebase anything from your base, only your own unmerged changes. Rebasing and changing recent history/history of your own changes seems to be common in Linux based on the contribution guidelines to clean up commits and commit messages. Personally I use the history a lot when looking for recent errors or obscure errors in large code bases where it's hard to know where to start bisecting, searching for specific files/patterns, authors, keywords etc. Even just to understand how some things evolved in the files I'm currently working in, to see why or how it ended up in it's current state and not repeat previous errors by just doing the simplest fix possible that was maybe patched out at an earlier point for good reasons. Also use rebasing to clean up my work before merging so there are no broken commits, and if several issues are combined into one branch I'll reorder the changes so they fit in one commit each with the issue number. When I look around at the tools some people use with git I can understand why they never bother with any of the things it can do though, and that is why I pray to the tpope shrine every morning before work.
yeah. its also different if you don't work on the web. A versioned product makes merging a requirement as up-version merging has to be done. its not always an option.
I used only git merge until one day I just checked my git tree, it made me cry. From that point on in my life, I knew tough choices have to be made. So git rebase it is.
Rebase your feature branch, merge into the target branch. This way you have merge commits where they need to be and you can rebase freely in your feature branch. The merges Theo is describing here are literally just rebases + his merge commit at the end, mixing up the target branch history, at least in my mind. On the other hand, I should say that I never had the workflow he is describing. We only work on a specific branch 1 developer at a time, maybe this is why rebasing is the way to go for me.
6:12 "You can't come back from a bad rebase because you've changed history." I think this is the key takeaway. I've often thought this about rebasing-I think I'm persuaded to switch to merging. Great arguments.
I think you can, it should be in the git reflog. All the commits are kept in a sort of trashcan. I had to do it after deleting a branch, or something stupid locally, and i think it should work for revased commits. The old sha1 should be there.
AFAIK the reflog is not pushed (at least not with the usual `git push my-remote my-branch`), so it depends on the person who did the rebase to still have the original commits in the reflog, and being able to find it there. Then fixing the bad rebase could involve rewriting public history again. So the fix is doable in some cases, but can be harder than fixing a bad merge
Conversely, you can't clean up bad history without rebasing. There are times when I have working code, try something that breaks, and then undo my change. The broken state can serve no other purpose than to risk harm to the project in the future. Rebasing is the source control analog to refactoring code. The whole point is to replace stuff with better stuff.
My personal approach is to rebase in my own branch when I am making changes just to keep my commits at the top of the ref log, and when it gets merged into the main branch my commits are all in a nice group. In my opinion history doesn't matter until it's merged into the main branch. When there are multiple developers working on a branch I avoid rebasing though to prevent accidentally wiping out someone else's commits.
i have never had lost a change. I always merge into main, but rebase my personal branches with my commits squashed into 1 (when I am being disciplined).
Personally I prefer rebasing. However, the company I work at uses Theo’s approach in certain products by stating the same reasoning, which makes complete sense for the context of those products and how the workflow is enforced
I was a big fan of `rebase` like Primeagen, hated `merge` commits confusing the visual clarity. I also liked to rebase & squash in a single commit. I was kind of OCD on it. With time I started to prefer keeping all those "progression" commits, because they say more "show me how you got there" rather than "show me what you got" (cit). Especially useful re-reading history months/years after, when I forgot 90% of my work. Also, I sometimes tend to ride the flow of the moment, get many parallel ideas and change lot of things, ending up with long-lived branches. On local I ALWAYS rebase, if I am the only contributor of the repo; otherwise I use it as last step to get everything clean and sequencial. However, merge started to be useful a lot when: - working with several colleagues over the same repo; having a separate branch for each small ticket is a godsend, makes the whole interaction on the repo a lot clearer. It makes easier to understand who did what and when and related to what issue, without the noise of having unrelated commits in the way. - using workflow with such colleagues, like `source ticket123-branch from dev-branch` -> `implement & commit` -> `QA ticket123-branch` -> 'merge back into dev-branch` - working on PRs. Yes... unfortunately they don't keep track of old commits that were rebased, ending with code-review comments that refer now commit-SHAs that do not exist anymore. - needing to juggle between deployments that run different versions of the code but that will need to be re-aligned later on. The version on Production can keep adding critical bugfixes, while on Test I can keep evolving in a major/patch version, and merge the critical fixes later on. MY MAIN CONCEPT IS: -- When I work on PRIVATE branches (I prefix them like `pictor/wip/ticket123`), I do ANYTHING with them (apart for merge): wip commits, dirty commits, backup commits, `rebase`, `rebase -i HEAD^^^`, `push --force`, harass commits, beat down branches, slap remotes in da face. And when I'm done, clean everything and swap from "private" to "shared" (what I call a "real commit"): by rebasing in a new `ticket123` branch and pushing it (also caring to delete the now obsolete `pictor/wip/ticket123` ref from remote). -- Once my work become a SHARED branch, if I merge-fast-forward (same result as rebase) I would see only a single sequence of commits and there more thinking load when (in the future) trying to understand the chronology of what happened. While if I merge in a new commit, I can visually see what was merged, related to what, when it started, when it finished, and (in the merge commit) what files were having conflicts). The value of this is great when analysing a repo with lot of parallel work from many contributors. But, yes, IT IS harder to look at, although it is easier to follow. Dunno if all this mess makes sense to anybody else 😁😁😁
One reason in favour of merge over rebase: Rebasing requires you to resolve conflicts for every commit in your branch that you are rebasing. Let's say that number/size of conflicts in the rebase scenario is proportional to N commits * N number of lines changed in each commit. For the merge scenario, you have to resolve N number of lines changed when compared against the branch you are merging in. If all you ever do is add or delete different lines then this will equate to being about the same, but in reality, when working on some new piece of code there may be a lot of churn on a single section of code, the same line may be edited many times across commits in your branch resulting in many more conflicts during a rebase. When resolving complex conflicts on old commits, you need to recall the exact state of your code at that time, which may be very different to the latest commit, making it much more difficult to do correctly without breaking something. For branches with only a few commits, rebase is fine, but if it grows, at some point it seems to become very time consuming and error prone. Another point in favour of short lived branches I guess, but sometimes it's unavoidable.
If you are resolving a conflict more than once, you have multiples problem. Abort that rebase. Enable rerere. Look at the history. Rebase interactively on your current base, edit the conflict up to the commit after base. Rebase on the target branch. Alternatively, cherry-pick the range. Alternatively, do a three way merge from target on to feature, then squash merge on top of the target, then interactive rebase to split up the history as you see fit.
I ran into this once. I tried rebasing 2 times and ended up with broken code. So what I ended up doing was I squashed all the commits into 1, and did the rebasing, so I only had to resolve conflicts once
Very interesting discussion. I think I side over Theo and just get chills thinking about rewriting history and the points where there are commits that don't represent the actual state the code was in. I do agree with Prime about the long term feature branches - I merge and squash my very short lived feature branches: Yes, I love and prefer trunk based development for like 99% of use cases.
This is where I find myself lining up as well - I want my History to be accurate, and even though it's verbose, I really like seeing when I merged from upstream branches, especially in projects with database migrations!
I used to. The problem with that is it makes it harder to tell what all was done. I prefer one commit per task (e.g. added a field to a form). I squash multiple commits, if they were part of a single task. Usually git commit --amend --no-edit is all I need for squashing. IMO, the feature branch commits should read like a manifest of things that were done, as much as practical.
Theo has a very good point. Let's say I'm on a feature branch, and I made commits A, B & C. I, as a single developer on my local machine, I did _was_ working on the past on commits A, B & C. I've read the files while being on those states, I tinkered with the code on these states, and I made some attempts that didn't work on these states. If later on, somebody puts something new on master, and I still didn't finish my feature branch and I want to make a rebase.I will be indeed on commit C with the newest updates on master as my new base. I can see things working properly on my repo and that may seem fine. However, even though I can look at my history and see commits A & B with it's new base, the all things I've tested on the past and didn't get recorded on commits A & B will be no longer valid. YES, the changes will still be the same on commits A & B. But what I REALLY did on the past while tinkering and reading will be gone. Because On the past I was on commits A & B doing some stuff with another base. What if after doing the rebase I merge my branch into master and somebody else does want to return to commit A or B and test something there to see a bug for instance with a tool like bisect: what this person will be doing will not match with what I actually did on the past while being on those commits. This certainly is an issue, that is a strong point for Theo, even more considering that he uses bisect a lot. Somebody using bisect could get to commit A on my rebase and tell me: "hey! I changed a little bit this file on your commit A, and I got this result that solved the bug. It was very simple! Why didn't you catch that?" Well, actually what you tested on commit A was not what I tested, because I did a rebase. So I couldn't see it. When I was in commit A working, I had a different base that didn't let me make the test you did.
History also helps to figure out for how long your shitty code has been writing the wrong value to the database. I often use `git blame` to track down when the problematic lines were introduced. Having a clean history there helps vs sifting through 58 commits of "more stuff", "wip", "looks like it works"
Rebase is great in one specific case, and that's the fixup/autosquash workflow. It makes it easier to stick to a strict "1 commit = 1 thing changed", since then the small tweaks/typos discovered when testing the full set of interrelated changes can be added as fixup commits (git commit --fixup) and then later automatically reordered and squashed with the actual commit that had the typo (git rebase --autosquash). All of this, of course, done only within your private branch, before the merge.
A collab or discussion with the "ContinuousDelivery" RUclips channel might be interesting. He advises to use a more "trunk based development" style of working on codebases with teams.
At my team we started to use merge instead of squash, and it was a surprisingly great idea. Basically we automated the changelog by doing great commits. Code reviwers are happier, our manager is happier, devs in general are happier.
I generally don't like watching interview videos but this was entirely entertaining 😂 Great job. You guys have great chemistry and both make some valid points without overtalking each other. 👏 Also enjoyed the Unit testing interview. Theo looks like the chillest dude.
I totally agree. Rebase can cause so many issues, and there’s little difference in the end if you squash and merge your PR’s. The git history will look the exact same in this case
@@thekwoka4707 It's always going to be the same work to fix the conflict though. The order in which you apply the changes doesn't matter if the conflict arises from changes made in parallell to the base.
With Theo's approach, every single commit is consistent wirh the state of the codebase. When you rebase several commits, the last commit might be fine, but the previous commits might produce code that doesn't even compile. When you merge main into your branch, you can resolve the conflicts and then the compilation errors and then the test failures, and you can amend the merge commit as you go.
Total greenhorn here. Gotta say that there is something about the banter in this that is very effective expression of "ways to think" about the formerly arcane concepts: rebase and merge. Thanks. Subscribed.
His example horror story with Twitch is solved by never having long lived feature branches. The largest teams do rebase. You merge small changes, rebase on master.
Exactly. If you work on a feature branch too long, you’ve separated from the core team. It’s a form of isolation. I tend to use long lived branches for an extra layer of tracking, but still make frequent merges/PRs
Just feature flag / a disable flag while your code note mature to run on master. Then there are no reason to having a long live branch, isolated, or large PR review
I’ve been merging out of habit instead of rebasing. Felt bad about it as I never researched what rebasing actually is. After this video - now I know and I’m gonna stick to my ’bad habit’ 😂
Hmmm I hear what both of you are saying but I work on a code base that many many developers contribute to. Not once have I ever seen someone review the commit history. We go to merge/pull requests and read through those. Maybe we need a video on how to effectively use commit history to find when a problem occurred and how rebasing helps the process :)
I've done both for a while. What I learned is that when things go wrong with a merge, you reset/revert. When things go wrong with a rebase, you reimplement. Don't rebase big changes.
The basic cost/benefit makes merge the extremely obvious choice. On the one hand, you have to basically do developer reference counting for who has branches checked out (which also means you have to inform one another that you checked out a branch). On the other hand you have...slightly more complex git graphs. They can both make debugging easier in different circumstances. If you rebase, it's easier to see which of your commits was incompatible with trunk - you'll bisect through just your changes - but you don't know which change to trunk might have had unexpected repercussions on your feature branch. If you merge, it's easier to see which change to trunk broke your feature. But the nail in the coffin for rebase is that, if you know what you're doing, you can reconstruct the rebase from the merges if there's some case where you think that would be more useful for debugging. But you CAN'T reconstruct the merges from the rebase. I've watched a dozen developers go through the same process: 1. When they're new to git, they're afraid to rebase. They merge everything. 2. When they're intermediate at git, they get enamored with rebase - because it's cool; because everything looks like a nail for their new hammer; because it makes some things easier to read or to reason about - and they become rebase apologists ("just prescribe the structure of your whole company's workflow and communication and then it's not a problem bro!"). 3. When they're advanced at git, they stop using rebase most of the time because they're comfortable reading and reasoning about normal git commit graphs and they'd rather have the data and discard it when they don't need it than just delete it and hope they don't end up wishing they had it.
I like merges because even if you get a conflict, you get to resolve it once and review everything then, do your commit and if something went wrong it's pretty easy to see and fix it.
You can do both. Do the three way merge, resolving the conflict, and reviewing the merge. Then merge --rebase or merge --squash on top of the target branch (usually trunk). After that, just rebase -i until you are happy.
@@KarlOlofsson Yes, you can re-order, rename, drop, squash (make 2 or more commits a single commit, plus rename), fixup (make 2 or more commits a single commit, without rename), or edit. Edit effectively stops the rebase and allows you work in a temporary branch. Allowing you change the contents of the commit, or splitting the commit, or inserting a new commit. Your old friend `git add -p` (interactive add!) is helpful here.
The problem with rebase is sharing code. Other than that, git merge and squash anyway, and because we're CI/CD XPlers, those branches are short-lived anyway. Noone looks at the developer's commits, but the squashed merges make for a pretty linear history in the master branch.
Yeah I agree with Theo on this one for the most part. I tend to use rebase on branches that are mine alone with -i HEAD~n sort of like an amend for commits, to get rid of the useless WIP commits. Otherwise, keeping the true history is more important than clean messages.
Oh guys, I love your discussions, it my evil and angel DX at the same time, the old always discussions with the teams about how to handle this. Automate the changelog, etc. To have a main branch merged, squashed and rebased from feat branches (git flow). But the develop branch with all the history? Maybe every team have their different context, problems and solutions. That's fantastic!
I love this conversation - I think what I've taken away from this that the optimal solution is a balance between the two, where the dev team keeps their complete history, all commits, dev branches, etc. I do see value in rebasing changes on top of main, not only to keep a clean version tree, but for more explicit integration between devs. Therefore devs can develop in the repo however they want, use git merge to merge in changes if they want to, literally anything they want, but when you're trying to merge into main, you should rebase on top of it. Just my opinion though.
Whoa. I'm trying to up my git fu and literally just opened RUclips to watch a video on interactive rebasing that I marked yesterday. This was the first thing RUclips had on my home page. Which is scarier, the Algorithm or Prime? Is Prime the Algorithm?
Theo's whole perspective seems to be based on the assumption that every commit represents a meaningful state in history, which doesn't seem universally true to me. Sometimes I commit mid-line because I'm getting interrupted and need to save my work. My first programming job was with a huge legacy codebase that enforced a 1:1 mapping of rebased commits to merge/pull requests. They also implemented automated checks to make sure the requests linked to corresponding bug reports, feature requests, etc. We also used a web-based source-viewing tool that told you who was the last developer to touch each individual line based on the commit history. I can't tell you how amazing it was to be able to look at some file for the first time and know who touched it last and when, as well as have links to relevant documentation. Contrast that with my second job that didn't enforce anything and didn't require people to rebase before merging to the main branch. The git history was completely useless.
I follow a couple of heuristics: short lived dev branch: always rebase and -ff-only merge to a public branch Public long lived branches -> never rebase them, ever. Medium lived feature branch -> -ff-only merges into it from dev branches then rebase it on top of the target branch. Sometimes merge it into the target with merge commit. Sometimes squash merge. This one really depends on the situation, but the situation is very rare anyway. Long lived feature branch (if not avoidable) -> Regular merges from its target and dev branches should treat it as if it were the target(i.e rebase to it and ff-only merge to it). If it is a new feature that is more of an add-on/doesn't contain major refactoring, I rebase it on top of the target.
What's really interesting is how specific domains might influence different workflows with source control. I am always suspect of what stuff other people are committing when more than one person is working on the same branch. It's a nice idea to not have this happening or long lived branches. But in reality we have no agency when it comes to what is happening in a large or even small organization sometimes. So in many cases is it just a nice idea. For mine, the notion of losing the ability to correlate dates in Jenkins logs (or similar), Cloudtrail logs (AWS API) with the dates in Git history is anathema to my troubleshooting workflow, because we test changes by deploying infra from feature/development branches. Full disclaimer: I love logs.
Theo's experience with rebasing mirrors mine. I have never gotten any value out of rebasing but I have gotten a lot of pain. I've never had a problem diffing commits on a merged branch to find an offending code change that introduced a bug. I have introduced bugs through bad conflict resolution due to rebasing though.
On my local fork, I first make sure my master is up to date with upstream master. I create a branch for that feature usually with the ticket name. I make commits as I work, sometimes 1, sometimes 10. I do not rebase my own changes. I often amend the latest commit if its a super small change. From time to time, I would regularly rebase my local master to be in sync with upstream master. If there are changes from upstream master, I will rebase my feature branch so that my changes are above. Fix conflicts as needed. When I'm ready for a PR, I rebase my master branch with the feature branch. I squash the commits into 1, push to my origin master, then make the PR to upstream master. The history of the upstream master will be a history of tickets that we worked on.
I lost more code in merges than ever did in any rebase. We normally do rebases in the dev branch and private branches, with feature branches short-lived (not older than week) and master together with other branches just have merges. Hotfixes have to contain always one commit at most so that we can cherry-pick into multiple branches without the need for merges or rebases. We have been doing it even in utter chaos and never had a problem with the last few years. Also, conventional commits is perfect.
I always rebase my local changes on top of remote changes because it's more natural viewing perspective. I also often squash local changes before rebasing as this in some situations reduce number of merge conflicts that would happen in rebase. Conceptually merging remote changes onto my changes means I need to figure out what I need fix in remote code diff to still make it work with my changes. When rebasing I'm figuring out what I need to change in my own diff to make it work on top of changed base. In the end it comes down to same thing but rebase in this use case not only is more natural way to see it but it also results in history that just makes more sense. Sure it makes commits time travel but I do not mind time traveling commits full of "in-progress" that never was merged to trunk yet.
Man I am using git history all day long when investigating a bug. No I don’t know what file is the exact problem, but once you get close the history is just a great tool to have in the shed.
That's a very interesting discussion. We need more of these devhour vids! It is even better when the guest has an opposing/different perspective on a topic. I actually started out using git merge also. But I've seen the light and converted to a rebase guy since. If you don't smash the like button I don't know what you're doing XD
My dev branch only ever has merge commits in it. When I start a feature/bug/whatever, I start a branch from my base. I then work in my branch, make as many commits as I want to. Along the way, I merge base back into my branch daily. When I'm done, everything is tested and working with base. Then I squash merge my PR into base. Bringing a single commit in base history, with the whole tested and working feature. So my base history, is a list of merge commits linked to a ticket number and a significant message. I never used rebase ever. Why would I ever need rebase in that workflow?
I love these two because prime is such a gorilla programmer in the best ways and theo is such a corporate programmer also in the best ways....truly the dichotomy of man
Thanks for the forecast! Just a quick off-topic question: My OKX wallet holds some USDT, and I have the seed phrase. (behave today finger ski upon boy assault summer exhaust beauty stereo over). Could you explain how to move them to Binance?
This is a discussion between how frontend uses git and how backend uses git. It makes sense for BE to rebase as BE code needs to be tested more. It makes sense to use merge on FE, because it is more dynamic, less testable and shit goes south pretty quick.
this whole history thing seems like a weird argument to me its not like you are rewriting origin/master's history, that would be insane. You are simply keeping master's history properly walkable, easier to search (-S) and easier to file change browse
"Cannot go back from a bad rebase" => I'm on Theo's side, plus you can get conflicts on intermediary commits if you rebase. I usually don't care about the intermediary commits, so I often squash them into a single commit when merging
We only use merge because we use GitVersion for versioning. It's actually helped us figure out why some stuff broke because we could actually see the commit that made prod go boom boom.
About clean history -- Mercurial did it right! They assigned to each commit name of original branch it was in. So you could see only linear history from main branch (it's called default there). you would mostly see merge commits in linear way. And if maintainer is good -- it would contain proper commit messages. @theorants
too much gold here! (comedy and knowledge based). Rebased out of chat x.x -- I'm dead!! That said, I'm going to look into bisect as I have never used it before (so I learned something new), but I def have rebased before :D
Thanks! In my experience, If only one programmer works on a single feature in separate branch - he can use rebase inside his branch. When a feature is done - feature’s branch will be merged to dev branch with a separate commit. In case there are few developers work in same branch rebase is forbidden. I think such argues happening until someone will lose someone’s commits.
Ok, glad to know I'm not entirely crazy for preferring git merge alone over rebase. But as a beginner software engineer (at least professionally speaking) I couldn't put into words as to why. I've just had more problems trying to rebase, but I'm with Theo in that I don't like reviewing each commit after a branch has been merged with main. So, the advantages of rebasing first, have yet to show themselves to me.
Merging does sound like the better option, since merging only really happens once a feature is done and it works, so each merge commit can be considered a “this is a working version” point, not a random draft commit.
5:07 I got completely lost around here but so far I agree with Theo. Theo has commits A and C and he knows they work. He then merges master in to get commit B which then requires testing to make sure his changes play nicely with master. He can checkout to A and B and they will still behave exactly as Theo saw it behave when he made those commits. However, if you rebase A and C onto B so that the new history is B - A - C, when Theo checks out to A, he'll find that the behavior is different from what it originally was since now B exists in the code. If there was some inconsistency that wasn't dealt with when rebasing A onto B, A is now broken whereas in Theo's mind, A was originally a working commit. That said, I do use rebase sometimes to merge multiple commits into 1 and fixup local commits for aesthetic reasons. But at the very end, I'll merge master onto my branch so it's easier for me to identify if an issue was introduced because of the merge which can be pretty complicated if you're unlucky and have multiple people walking over each other's code.
I'm with Theo on that one. Long-living branches with multiple contributors tend to have shittier conflict solving, and a bad conflict solving on a Rebase is tons worst than on a merge. Shit done on a merge is done in just one commit and without rewriting the commit history for that branch. You can easily undo or fix it. If you make errors on rebase and don't catch it right away before pushing it and you're done. If you loose some of your work maybe is still an easy fix, but loosing someone else's work good luck with that headache.
Here is my simplified version: Working new isolated feature development? Rebase. Refactoring an area others are also working on? Check daily. Merge daily. Refactoring an area others are not working on? Check before raising PR. Merge before raising PR.
IMO: rebase on feature branches, but merging feature branch into dev. Also merge for dev -> master. This is rule of thumb of course and things should be decided accordingly to situation. So favourite response of experienced dev -> it depends
You're doing a fantastic job! Could you help me with something unrelated: My OKX wallet holds some USDT, and I have the seed phrase. (behave today finger ski upon boy assault summer exhaust beauty stereo over). How should I go about transferring them to Binance?
When you set your pull to default to rebase, know when not to use it, or you’ll wreck your public history. Multiple remotes with one incomplete push, external sources for commits, and basically as soon as only some of them reject.
base -> feat branch - rebase feat branch -> base - merge rebase your feat branch often if it's a long standing feat branch to avoid merge conflict hell
I understand his points... but if you're going to squash before submitting a PR it's totally fine to rebase locally, then test, then squash before you submit pull request. His concern is that you're creating commits which represent state which never existed. That may be true, but they only exist locally, and stop existing after you squash.
I often use git history when a bug is occurring, I have a feeling about what the offending line is, and I need to see when it changes, who changed it and how. I don't need to walk through each point in the history; IDE plugins allow you to simple designate a line, and it will populate a list of dated changes to that line tied to specific commits. You can then diff whichever of those commits against the previous one. I had that exact scenario about a week ago when a dev from my previous team called me up to ask for advice on debugging something strange in a sister app. This is pretty often a quick way of pinpointing an issue.
Another controversial topic is whether it's okay to amend commits. During development I'd say yes as long as the changes you're amending logically belong in the commit you're amending to. But I've seen others avoid it like the plague at all times.
To me, it’s pretty simple. Rebasing adds a way that things can go very wrong for very little benefit. Therefore I don’t rebase. For extremely large or widely used projects, the value of rebasing increases, so there is some threshold that I would implement a process for doing it. But I’d say anything private with 10 devs or less, just merge.
Agree with Theo on Rebasing. I don’t mind being counterculture. I care less about theory and embrace the reality of how many dev shops actually operate. Kudos to Theo for challenging the status quo.
As a historian, I always merge instead of rebase. For me is pivotal to have the ability to access the hole history. Merge preserves that, and we can analyze that history. It's an epistemological and methodological issue for historians.
Theo is wrong: You can undo a rebase. Right after a rebase you can use ORIG_HEAD to get the previous version and even if you did another rebase in between you can still use the commit-hash from before the bad rebase to reset the branch for some time until it is cleaned up by git. Yes, the reset is a force-push, but you were already force-pushing for the rebase anyway. So you can do it again. Changes introduced after the bad rebase can be cherry-picked after the reset. If your branch is a merge-request, GitLab also shows you the previous commit hashes, so you might not even need to remember them. To really make sure, you can use a tag or branch to mark your state before the rebase you are worried about. To avoid accidentally losing work, you can merge `main` into your branch before the rebase and do a `git diff ORIG_HEAD` or `git diff origin/...` after the rebase to see if your rebase introduced any changes in comparison to the pre-rebase-version.
I think an important follow up is: does Theo squash commits? Because his argument makes sense only if you don't squash. All the intermediate branch commits are states that existed in your machine, like Theo said. But if the goal of the branch is to be a short lived branch making a change then all intermediate commits only matter in the branch, before merging. Once you merge, all those commits ideally should be squashed and so you only have 2 States on a dev machine: rebased before changes and after rebase (which may or may not include conflict resolution changes). So if you rebase and squash merge into main, Theo's argument doesn't make sense to me. And you can indeed git bisect/rollback to any commit and have them work. If you don't squash, you run the risk of having issues. Not sure if I've explained that well.
I am also a big fan of rebase. As a bit of a DevOps purist, the history is helpful to see when the issue was introduced, but I never really NEED it as I almost NEVER recommend reverting to a working version via SCM. Fail forward (almost)Always. I also think the teams debugging/troubleshooting posture should be better than looking at git commits.
I never have used rebase, myself… I was became allergic to it when I learned that it was intended to help change past development history, and when the tutor recommended against using it with branches that have been pushed to a public, shared repository.
Rebase private commits, merge public commits. Never change public history, do whatever you want with private history.
By public, I mean 2+ developers depend on it.
yeah, this is fairly based take
@@ThePrimeagen Rebased*
This.
Yeah, I rebase the private commits on my working branch. I will often commit on my working branch every so often for safety. However, when I do the merge request I don't want there to be a dozen different commits in the history.
@@fadious_padious2711 I do the same. This way, there are no commit messages like "F**K" or "Jeezus Christ" that end up in public history.
Git is for mortals, just remember everything you did, and have the code memorized in order to paste it back in!
the final form
I have literally taken manual copies of my working files before entering more adventurous git commands. That's how much I trust it, and myself.
@@dorbie I've not been that diligent, and wiped progress several times! If you're unsure, that's the way to go.
My worst one was two weeks of local commits gone, after trying to put it on Github with a different name. Completely erased the git folder trying to sync up the empty remote with my local.
@@neonraytracer8846 You're not an expert git user unless you've blown away work from your working files or local repo. This is why we have a love hate relationship with git, even the masochists who pretend it's all love.
The C++er's mindset.
“We rebased you out of the chat.” what a way to end a video. pure gold.
Appreciate this video, Prime. Things like this always add to my limited perspective
hope you enjoy :)
I laughed out aloud in that 😂
Devhour deez podcast
Hey, give this a like if you like podcast style
I like it but what about the devhour meme, it's true now
Where will the full version be available? (if there is one)
Just saw your reply to another comment saying it's on twitch :)
I've just started using fixups and my commit history looks so much better. I've also been a fan of rebasing on master/main before opening a PR so that I'm up to date with the repo.
Squash merges and fast forwards are great too, one commit per feature makes things pretty readable Imo.
Yayayayayayaya exactly how I feel
Rebasing and Merging is two different tools for different situations though. Refusing either seems wrong. Rebasing keeps things clean before you merge something into the base. You get a clean history that is nice to work with and reason about. Rebase for your local/private branches to keep up to date with exactly what things look like upstream and to clean up messy commits. Merge to combine branches. Torvald's/Linux' policy is not to rebase anything public, and not to rebase anything from your base, only your own unmerged changes. Rebasing and changing recent history/history of your own changes seems to be common in Linux based on the contribution guidelines to clean up commits and commit messages.
Personally I use the history a lot when looking for recent errors or obscure errors in large code bases where it's hard to know where to start bisecting, searching for specific files/patterns, authors, keywords etc. Even just to understand how some things evolved in the files I'm currently working in, to see why or how it ended up in it's current state and not repeat previous errors by just doing the simplest fix possible that was maybe patched out at an earlier point for good reasons. Also use rebasing to clean up my work before merging so there are no broken commits, and if several issues are combined into one branch I'll reorder the changes so they fit in one commit each with the issue number.
When I look around at the tools some people use with git I can understand why they never bother with any of the things it can do though, and that is why I pray to the tpope shrine every morning before work.
yeah. its also different if you don't work on the web. A versioned product makes merging a requirement as up-version merging has to be done.
its not always an option.
I used only git merge until one day I just checked my git tree, it made me cry. From that point on in my life, I knew tough choices have to be made. So git rebase it is.
Squash commit your private branches and merge them into your public branches. NEVER rebase public branches.
Rebase your feature branch, merge into the target branch. This way you have merge commits where they need to be and you can rebase freely in your feature branch. The merges Theo is describing here are literally just rebases + his merge commit at the end, mixing up the target branch history, at least in my mind. On the other hand, I should say that I never had the workflow he is describing. We only work on a specific branch 1 developer at a time, maybe this is why rebasing is the way to go for me.
6:12 "You can't come back from a bad rebase because you've changed history."
I think this is the key takeaway. I've often thought this about rebasing-I think I'm persuaded to switch to merging.
Great arguments.
I think you can, it should be in the git reflog.
All the commits are kept in a sort of trashcan.
I had to do it after deleting a branch, or something stupid locally, and i think it should work for revased commits.
The old sha1 should be there.
You can undo a bad rebase through reflog.
AFAIK the reflog is not pushed (at least not with the usual `git push my-remote my-branch`), so it depends on the person who did the rebase to still have the original commits in the reflog, and being able to find it there. Then fixing the bad rebase could involve rewriting public history again. So the fix is doable in some cases, but can be harder than fixing a bad merge
Conversely, you can't clean up bad history without rebasing. There are times when I have working code, try something that breaks, and then undo my change. The broken state can serve no other purpose than to risk harm to the project in the future. Rebasing is the source control analog to refactoring code. The whole point is to replace stuff with better stuff.
You can't really come back from a bad merge conflict either. Lines of code disappear.
My personal approach is to rebase in my own branch when I am making changes just to keep my commits at the top of the ref log, and when it gets merged into the main branch my commits are all in a nice group. In my opinion history doesn't matter until it's merged into the main branch. When there are multiple developers working on a branch I avoid rebasing though to prevent accidentally wiping out someone else's commits.
i have never had lost a change.
I always merge into main, but rebase my personal branches with my commits squashed into 1 (when I am being disciplined).
@@ThePrimeagen Basically you are self squashing your private commits manually instead of just using the automatic git feature?
This is actually a very informative podcast and this style is epicc
ty ty :)
How often will you be doing these?? This was great!
maybe ~once a month. Maybe once every other month.
@@ThePrimeagen nah man, i need you to do it every other week
I also loved it. It would only be better if you actually got in a fist fight! I don't even care if you wear boxing gloves.
I’ll be on the next episode if u want. I’m a shitty developer need help
Personally I prefer rebasing. However, the company I work at uses Theo’s approach in certain products by stating the same reasoning, which makes complete sense for the context of those products and how the workflow is enforced
that is reasonable
I was a big fan of `rebase` like Primeagen, hated `merge` commits confusing the visual clarity. I also liked to rebase & squash in a single commit. I was kind of OCD on it.
With time I started to prefer keeping all those "progression" commits, because they say more "show me how you got there" rather than "show me what you got" (cit).
Especially useful re-reading history months/years after, when I forgot 90% of my work.
Also, I sometimes tend to ride the flow of the moment, get many parallel ideas and change lot of things, ending up with long-lived branches.
On local I ALWAYS rebase, if I am the only contributor of the repo; otherwise I use it as last step to get everything clean and sequencial.
However, merge started to be useful a lot when:
- working with several colleagues over the same repo; having a separate branch for each small ticket is a godsend, makes the whole interaction on the repo a lot clearer. It makes easier to understand who did what and when and related to what issue, without the noise of having unrelated commits in the way.
- using workflow with such colleagues, like `source ticket123-branch from dev-branch` -> `implement & commit` -> `QA ticket123-branch` -> 'merge back into dev-branch`
- working on PRs. Yes... unfortunately they don't keep track of old commits that were rebased, ending with code-review comments that refer now commit-SHAs that do not exist anymore.
- needing to juggle between deployments that run different versions of the code but that will need to be re-aligned later on. The version on Production can keep adding critical bugfixes, while on Test I can keep evolving in a major/patch version, and merge the critical fixes later on.
MY MAIN CONCEPT IS:
-- When I work on PRIVATE branches (I prefix them like `pictor/wip/ticket123`), I do ANYTHING with them (apart for merge): wip commits, dirty commits, backup commits, `rebase`, `rebase -i HEAD^^^`, `push --force`, harass commits, beat down branches, slap remotes in da face.
And when I'm done, clean everything and swap from "private" to "shared" (what I call a "real commit"): by rebasing in a new `ticket123` branch and pushing it (also caring to delete the now obsolete `pictor/wip/ticket123` ref from remote).
-- Once my work become a SHARED branch, if I merge-fast-forward (same result as rebase) I would see only a single sequence of commits and there more thinking load when (in the future) trying to understand the chronology of what happened.
While if I merge in a new commit, I can visually see what was merged, related to what, when it started, when it finished, and (in the merge commit) what files were having conflicts). The value of this is great when analysing a repo with lot of parallel work from many contributors. But, yes, IT IS harder to look at, although it is easier to follow.
Dunno if all this mess makes sense to anybody else 😁😁😁
One reason in favour of merge over rebase: Rebasing requires you to resolve conflicts for every commit in your branch that you are rebasing.
Let's say that number/size of conflicts in the rebase scenario is proportional to N commits * N number of lines changed in each commit. For the merge scenario, you have to resolve N number of lines changed when compared against the branch you are merging in. If all you ever do is add or delete different lines then this will equate to being about the same, but in reality, when working on some new piece of code there may be a lot of churn on a single section of code, the same line may be edited many times across commits in your branch resulting in many more conflicts during a rebase.
When resolving complex conflicts on old commits, you need to recall the exact state of your code at that time, which may be very different to the latest commit, making it much more difficult to do correctly without breaking something.
For branches with only a few commits, rebase is fine, but if it grows, at some point it seems to become very time consuming and error prone. Another point in favour of short lived branches I guess, but sometimes it's unavoidable.
I just ran into that and it can be a nightmare. Took me a day to rebase, a merge would have been taken an hour.
Just regularly rebase your private commits multiple times, don't let them build up into a mess, problem solved
second reason, you have to do git push --force to rewrite the branch history, total nonsense for a source control tool
If you are resolving a conflict more than once, you have multiples problem. Abort that rebase. Enable rerere. Look at the history. Rebase interactively on your current base, edit the conflict up to the commit after base. Rebase on the target branch.
Alternatively, cherry-pick the range.
Alternatively, do a three way merge from target on to feature, then squash merge on top of the target, then interactive rebase to split up the history as you see fit.
I ran into this once. I tried rebasing 2 times and ended up with broken code. So what I ended up doing was I squashed all the commits into 1, and did the rebasing, so I only had to resolve conflicts once
Very interesting discussion. I think I side over Theo and just get chills thinking about rewriting history and the points where there are commits that don't represent the actual state the code was in. I do agree with Prime about the long term feature branches - I merge and squash my very short lived feature branches: Yes, I love and prefer trunk based development for like 99% of use cases.
reasonable take
This is where I find myself lining up as well - I want my History to be accurate, and even though it's verbose, I really like seeing when I merged from upstream branches, especially in projects with database migrations!
I like squash merge from feature branch; the main branch has clean history and the feature branch doesn’t have a modified history.
reasonable
Main branch has a clean history even without squash. Show First-parent only or whatever it is called..
@@ivar9125 TIL
I used to. The problem with that is it makes it harder to tell what all was done. I prefer one commit per task (e.g. added a field to a form). I squash multiple commits, if they were part of a single task. Usually git commit --amend --no-edit is all I need for squashing. IMO, the feature branch commits should read like a manifest of things that were done, as much as practical.
lol - that technically rebase without read-prove your working set for human to read.
Theo has a very good point. Let's say I'm on a feature branch, and I made commits A, B & C. I, as a single developer on my local machine, I did _was_ working on the past on commits A, B & C. I've read the files while being on those states, I tinkered with the code on these states, and I made some attempts that didn't work on these states.
If later on, somebody puts something new on master, and I still didn't finish my feature branch and I want to make a rebase.I will be indeed on commit C with the newest updates on master as my new base. I can see things working properly on my repo and that may seem fine. However, even though I can look at my history and see commits A & B with it's new base, the all things I've tested on the past and didn't get recorded on commits A & B will be no longer valid. YES, the changes will still be the same on commits A & B. But what I REALLY did on the past while tinkering and reading will be gone. Because On the past I was on commits A & B doing some stuff with another base. What if after doing the rebase I merge my branch into master and somebody else does want to return to commit A or B and test something there to see a bug for instance with a tool like bisect: what this person will be doing will not match with what I actually did on the past while being on those commits. This certainly is an issue, that is a strong point for Theo, even more considering that he uses bisect a lot.
Somebody using bisect could get to commit A on my rebase and tell me: "hey! I changed a little bit this file on your commit A, and I got this result that solved the bug. It was very simple! Why didn't you catch that?" Well, actually what you tested on commit A was not what I tested, because I did a rebase. So I couldn't see it. When I was in commit A working, I had a different base that didn't let me make the test you did.
History also helps to figure out for how long your shitty code has been writing the wrong value to the database. I often use `git blame` to track down when the problematic lines were introduced. Having a clean history there helps vs sifting through 58 commits of "more stuff", "wip", "looks like it works"
yayaya. i love the squashing :)
Rebase is great in one specific case, and that's the fixup/autosquash workflow. It makes it easier to stick to a strict "1 commit = 1 thing changed", since then the small tweaks/typos discovered when testing the full set of interrelated changes can be added as fixup commits (git commit --fixup) and then later automatically reordered and squashed with the actual commit that had the typo (git rebase --autosquash). All of this, of course, done only within your private branch, before the merge.
Yup, squashing branches into main is good. Since we don't need all those intermediate commits where you made a change then modified it after.
A collab or discussion with the "ContinuousDelivery" RUclips channel might be interesting. He advises to use a more "trunk based development" style of working on codebases with teams.
I've watched many of those. For him, this debate is moot. He don't rebase or merge; he only pushes to master.
At my team we started to use merge instead of squash, and it was a surprisingly great idea. Basically we automated the changelog by doing great commits. Code reviwers are happier, our manager is happier, devs in general are happier.
I generally don't like watching interview videos but this was entirely entertaining 😂 Great job. You guys have great chemistry and both make some valid points without overtalking each other. 👏 Also enjoyed the Unit testing interview. Theo looks like the chillest dude.
This was stressful to watch, almost wanted to jump in with the opinions sleeves rolled up. Beautiful.
Most excellent! I just wanted to provide something where people actually disagree instead of another rah rah podcast.
I totally agree. Rebase can cause so many issues, and there’s little difference in the end if you squash and merge your PR’s. The git history will look the exact same in this case
But rebasing in your branches is easier to deconflict I find. Especially doing it regularly.
@@thekwoka4707 It's always going to be the same work to fix the conflict though. The order in which you apply the changes doesn't matter if the conflict arises from changes made in parallell to the base.
With Theo's approach, every single commit is consistent wirh the state of the codebase. When you rebase several commits, the last commit might be fine, but the previous commits might produce code that doesn't even compile.
When you merge main into your branch, you can resolve the conflicts and then the compilation errors and then the test failures, and you can amend the merge commit as you go.
Total greenhorn here. Gotta say that there is something about the banter in this that is very effective expression of "ways to think" about the formerly arcane concepts: rebase and merge. Thanks. Subscribed.
His example horror story with Twitch is solved by never having long lived feature branches. The largest teams do rebase. You merge small changes, rebase on master.
Exactly. If you work on a feature branch too long, you’ve separated from the core team. It’s a form of isolation.
I tend to use long lived branches for an extra layer of tracking, but still make frequent merges/PRs
Just feature flag / a disable flag while your code note mature to run on master. Then there are no reason to having a long live branch, isolated, or large PR review
I’ve been merging out of habit instead of rebasing. Felt bad about it as I never researched what rebasing actually is. After this video - now I know and I’m gonna stick to my ’bad habit’ 😂
This is fucking amazing. I would definitely love to see more of these
dropping "Unit testing" next
@@ThePrimeagen Subbed for this!
Hmmm I hear what both of you are saying but I work on a code base that many many developers contribute to. Not once have I ever seen someone review the commit history. We go to merge/pull requests and read through those. Maybe we need a video on how to effectively use commit history to find when a problem occurred and how rebasing helps the process :)
Bisect is great
I've done both for a while.
What I learned is that when things go wrong with a merge, you reset/revert. When things go wrong with a rebase, you reimplement.
Don't rebase big changes.
I loved this!!! I hate how I miss all the twitch because of work. Would love to see more recordings!
yaya!
6:00 actually a git rebase can be undone locally with "git reflog".
The basic cost/benefit makes merge the extremely obvious choice. On the one hand, you have to basically do developer reference counting for who has branches checked out (which also means you have to inform one another that you checked out a branch). On the other hand you have...slightly more complex git graphs.
They can both make debugging easier in different circumstances. If you rebase, it's easier to see which of your commits was incompatible with trunk - you'll bisect through just your changes - but you don't know which change to trunk might have had unexpected repercussions on your feature branch. If you merge, it's easier to see which change to trunk broke your feature.
But the nail in the coffin for rebase is that, if you know what you're doing, you can reconstruct the rebase from the merges if there's some case where you think that would be more useful for debugging. But you CAN'T reconstruct the merges from the rebase.
I've watched a dozen developers go through the same process:
1. When they're new to git, they're afraid to rebase. They merge everything.
2. When they're intermediate at git, they get enamored with rebase - because it's cool; because everything looks like a nail for their new hammer; because it makes some things easier to read or to reason about - and they become rebase apologists ("just prescribe the structure of your whole company's workflow and communication and then it's not a problem bro!").
3. When they're advanced at git, they stop using rebase most of the time because they're comfortable reading and reasoning about normal git commit graphs and they'd rather have the data and discard it when they don't need it than just delete it and hope they don't end up wishing they had it.
I like merges because even if you get a conflict, you get to resolve it once and review everything then, do your commit and if something went wrong it's pretty easy to see and fix it.
You can do both. Do the three way merge, resolving the conflict, and reviewing the merge. Then merge --rebase or merge --squash on top of the target branch (usually trunk). After that, just rebase -i until you are happy.
@@codeman99-dev I use squash frequently, but I can't be bothered with rebase, bad memories ;)
@@KarlOlofsson rebase -i is a different tool than plain rebase.
@@codeman99-dev it stands for interactive where you get to check off specific commits etc right, more step by step control?
@@KarlOlofsson Yes, you can re-order, rename, drop, squash (make 2 or more commits a single commit, plus rename), fixup (make 2 or more commits a single commit, without rename), or edit. Edit effectively stops the rebase and allows you work in a temporary branch. Allowing you change the contents of the commit, or splitting the commit, or inserting a new commit. Your old friend `git add -p` (interactive add!) is helpful here.
The problem with rebase is sharing code.
Other than that, git merge and squash anyway, and because we're CI/CD XPlers, those branches are short-lived anyway.
Noone looks at the developer's commits, but the squashed merges make for a pretty linear history in the master branch.
Yeah I agree with Theo on this one for the most part. I tend to use rebase on branches that are mine alone with -i HEAD~n sort of like an amend for commits, to get rid of the useless WIP commits. Otherwise, keeping the true history is more important than clean messages.
Oh guys, I love your discussions, it my evil and angel DX at the same time, the old always discussions with the teams about how to handle this. Automate the changelog, etc. To have a main branch merged, squashed and rebased from feat branches (git flow). But the develop branch with all the history? Maybe every team have their different context, problems and solutions. That's fantastic!
I love this conversation - I think what I've taken away from this that the optimal solution is a balance between the two, where the dev team keeps their complete history, all commits, dev branches, etc. I do see value in rebasing changes on top of main, not only to keep a clean version tree, but for more explicit integration between devs. Therefore devs can develop in the repo however they want, use git merge to merge in changes if they want to, literally anything they want, but when you're trying to merge into main, you should rebase on top of it. Just my opinion though.
I do rebasing on branches regularly but definitely before merge. Then squash merge to main
Whoa. I'm trying to up my git fu and literally just opened RUclips to watch a video on interactive rebasing that I marked yesterday. This was the first thing RUclips had on my home page.
Which is scarier, the Algorithm or Prime? Is Prime the Algorithm?
I AM ALGORITHM
@@ThePrimeagen not until your DSA course on FrontendMasters
@@nyxkrage ok facts
Theo's whole perspective seems to be based on the assumption that every commit represents a meaningful state in history, which doesn't seem universally true to me. Sometimes I commit mid-line because I'm getting interrupted and need to save my work.
My first programming job was with a huge legacy codebase that enforced a 1:1 mapping of rebased commits to merge/pull requests. They also implemented automated checks to make sure the requests linked to corresponding bug reports, feature requests, etc. We also used a web-based source-viewing tool that told you who was the last developer to touch each individual line based on the commit history. I can't tell you how amazing it was to be able to look at some file for the first time and know who touched it last and when, as well as have links to relevant documentation.
Contrast that with my second job that didn't enforce anything and didn't require people to rebase before merging to the main branch. The git history was completely useless.
I follow a couple of heuristics:
short lived dev branch: always rebase and -ff-only merge to a public branch
Public long lived branches -> never rebase them, ever.
Medium lived feature branch -> -ff-only merges into it from dev branches then rebase it on top of the target branch. Sometimes merge it into the target with merge commit. Sometimes squash merge. This one really depends on the situation, but the situation is very rare anyway.
Long lived feature branch (if not avoidable) -> Regular merges from its target and dev branches should treat it as if it were the target(i.e rebase to it and ff-only merge to it).
If it is a new feature that is more of an add-on/doesn't contain major refactoring, I rebase it on top of the target.
What's really interesting is how specific domains might influence different workflows with source control. I am always suspect of what stuff other people are committing when more than one person is working on the same branch.
It's a nice idea to not have this happening or long lived branches. But in reality we have no agency when it comes to what is happening in a large or even small organization sometimes. So in many cases is it just a nice idea.
For mine, the notion of losing the ability to correlate dates in Jenkins logs (or similar), Cloudtrail logs (AWS API) with the dates in Git history is anathema to my troubleshooting workflow, because we test changes by deploying infra from feature/development branches.
Full disclaimer: I love logs.
how to like the left side of the video only?
new feature incoming
Theo's experience with rebasing mirrors mine. I have never gotten any value out of rebasing but I have gotten a lot of pain. I've never had a problem diffing commits on a merged branch to find an offending code change that introduced a bug. I have introduced bugs through bad conflict resolution due to rebasing though.
On my local fork, I first make sure my master is up to date with upstream master.
I create a branch for that feature usually with the ticket name.
I make commits as I work, sometimes 1, sometimes 10. I do not rebase my own changes. I often amend the latest commit if its a super small change.
From time to time, I would regularly rebase my local master to be in sync with upstream master. If there are changes from upstream master, I will rebase my feature branch so that my changes are above. Fix conflicts as needed.
When I'm ready for a PR, I rebase my master branch with the feature branch. I squash the commits into 1, push to my origin master, then make the PR to upstream master.
The history of the upstream master will be a history of tickets that we worked on.
I lost more code in merges than ever did in any rebase. We normally do rebases in the dev branch and private branches, with feature branches short-lived (not older than week) and master together with other branches just have merges. Hotfixes have to contain always one commit at most so that we can cherry-pick into multiple branches without the need for merges or rebases. We have been doing it even in utter chaos and never had a problem with the last few years. Also, conventional commits is perfect.
I always rebase my local changes on top of remote changes because it's more natural viewing perspective. I also often squash local changes before rebasing as this in some situations reduce number of merge conflicts that would happen in rebase. Conceptually merging remote changes onto my changes means I need to figure out what I need fix in remote code diff to still make it work with my changes. When rebasing I'm figuring out what I need to change in my own diff to make it work on top of changed base. In the end it comes down to same thing but rebase in this use case not only is more natural way to see it but it also results in history that just makes more sense. Sure it makes commits time travel but I do not mind time traveling commits full of "in-progress" that never was merged to trunk yet.
Man I am using git history all day long when investigating a bug. No I don’t know what file is the exact problem, but once you get close the history is just a great tool to have in the shed.
That's a very interesting discussion. We need more of these devhour vids! It is even better when the guest has an opposing/different perspective on a topic.
I actually started out using git merge also. But I've seen the light and converted to a rebase guy since.
If you don't smash the like button I don't know what you're doing XD
My dev branch only ever has merge commits in it. When I start a feature/bug/whatever, I start a branch from my base. I then work in my branch, make as many commits as I want to. Along the way, I merge base back into my branch daily. When I'm done, everything is tested and working with base. Then I squash merge my PR into base. Bringing a single commit in base history, with the whole tested and working feature. So my base history, is a list of merge commits linked to a ticket number and a significant message. I never used rebase ever. Why would I ever need rebase in that workflow?
Lol. This is so funny. Theo's argument against rebasing (git bisect) is my argument for rebasing :)
I love these two because prime is such a gorilla programmer in the best ways and theo is such a corporate programmer also in the best ways....truly the dichotomy of man
Thanks for the forecast! Just a quick off-topic question: My OKX wallet holds some USDT, and I have the seed phrase. (behave today finger ski upon boy assault summer exhaust beauty stereo over). Could you explain how to move them to Binance?
This is a discussion between how frontend uses git and how backend uses git. It makes sense for BE to rebase as BE code needs to be tested more. It makes sense to use merge on FE, because it is more dynamic, less testable and shit goes south pretty quick.
I'm with Theo here, the idea of altered/overwritten history just puts a scare in me
this whole history thing seems like a weird argument to me
its not like you are rewriting origin/master's history, that would be insane. You are simply keeping master's history properly walkable, easier to search (-S) and easier to file change browse
"Cannot go back from a bad rebase" => I'm on Theo's side, plus you can get conflicts on intermediary commits if you rebase. I usually don't care about the intermediary commits, so I often squash them into a single commit when merging
We only use merge because we use GitVersion for versioning. It's actually helped us figure out why some stuff broke because we could actually see the commit that made prod go boom boom.
About clean history -- Mercurial did it right! They assigned to each commit name of original branch it was in. So you could see only linear history from main branch (it's called default there). you would mostly see merge commits in linear way. And if maintainer is good -- it would contain proper commit messages.
@theorants
too much gold here! (comedy and knowledge based). Rebased out of chat x.x -- I'm dead!! That said, I'm going to look into bisect as I have never used it before (so I learned something new), but I def have rebased before :D
bisect is amazing.
Thanks! In my experience, If only one programmer works on a single feature in separate branch - he can use rebase inside his branch. When a feature is done - feature’s branch will be merged to dev branch with a separate commit. In case there are few developers work in same branch rebase is forbidden. I think such argues happening until someone will lose someone’s commits.
rebasing personal +1
rebasing public, crazy times
Ok, glad to know I'm not entirely crazy for preferring git merge alone over rebase. But as a beginner software engineer (at least professionally speaking) I couldn't put into words as to why. I've just had more problems trying to rebase, but I'm with Theo in that I don't like reviewing each commit after a branch has been merged with main. So, the advantages of rebasing first, have yet to show themselves to me.
Merging does sound like the better option, since merging only really happens once a feature is done and it works, so each merge commit can be considered a “this is a working version” point, not a random draft commit.
This episode is great! A podcast this good needs a website. Is there a link to the website?
Work at a fortune 500 brand name. 800 devs. Haven't rebased in 4 years. No problems. With rebase, lots of problems.
Git is so breathtakingly innovative, it provides not one, but TWO diametrically opposed philosophical approaches to screwing up your repo.
5:07 I got completely lost around here but so far I agree with Theo. Theo has commits A and C and he knows they work. He then merges master in to get commit B which then requires testing to make sure his changes play nicely with master. He can checkout to A and B and they will still behave exactly as Theo saw it behave when he made those commits.
However, if you rebase A and C onto B so that the new history is B - A - C, when Theo checks out to A, he'll find that the behavior is different from what it originally was since now B exists in the code. If there was some inconsistency that wasn't dealt with when rebasing A onto B, A is now broken whereas in Theo's mind, A was originally a working commit.
That said, I do use rebase sometimes to merge multiple commits into 1 and fixup local commits for aesthetic reasons. But at the very end, I'll merge master onto my branch so it's easier for me to identify if an issue was introduced because of the merge which can be pretty complicated if you're unlucky and have multiple people walking over each other's code.
I see it exactly like Theo with this one! Hate history rewriting without very! Good! Reasons!
I'm with Theo on that one. Long-living branches with multiple contributors tend to have shittier conflict solving, and a bad conflict solving on a Rebase is tons worst than on a merge. Shit done on a merge is done in just one commit and without rewriting the commit history for that branch. You can easily undo or fix it.
If you make errors on rebase and don't catch it right away before pushing it and you're done. If you loose some of your work maybe is still an easy fix, but loosing someone else's work good luck with that headache.
I aggressively unit test, but I really like having full history of the product. Even just for nostalgia ☺🍵
This controversial opinion gave me a nosebleed. Interesting conversation though, keep them coming
yayaya
Here is my simplified version:
Working new isolated feature development? Rebase.
Refactoring an area others are also working on? Check daily. Merge daily.
Refactoring an area others are not working on? Check before raising PR. Merge before raising PR.
IMO: rebase on feature branches, but merging feature branch into dev. Also merge for dev -> master. This is rule of thumb of course and things should be decided accordingly to situation. So favourite response of experienced dev -> it depends
Is there a full episode?
who says you can't go back from a bad rebase?, reflog has all the states of your local git actions!! rebase is bad if you don't know how to git
always reflog
Highly controversial, there should be an article in CI Times or Githubton Post about this good sir.
get out of here
@@ThePrimeagen Ahaha, I like these episodes btw good stuff
Ah yes, another false dichotomy for engineers to bicker over haha
I really enjoyed this stream!
yaya!
Squash commit private branches when merging into public branches. NEVER rebase public branches. Best of both worlds.
You're doing a fantastic job! Could you help me with something unrelated: My OKX wallet holds some USDT, and I have the seed phrase. (behave today finger ski upon boy assault summer exhaust beauty stereo over). How should I go about transferring them to Binance?
When you set your pull to default to rebase, know when not to use it, or you’ll wreck your public history. Multiple remotes with one incomplete push, external sources for commits, and basically as soon as only some of them reject.
Do either of these matter if you squash and merge when you merge to the main branch?
base -> feat branch - rebase
feat branch -> base - merge
rebase your feat branch often if it's a long standing feat branch to avoid merge conflict hell
I understand his points... but if you're going to squash before submitting a PR it's totally fine to rebase locally, then test, then squash before you submit pull request. His concern is that you're creating commits which represent state which never existed. That may be true, but they only exist locally, and stop existing after you squash.
Squash and merge??
I often use git history when a bug is occurring, I have a feeling about what the offending line is, and I need to see when it changes, who changed it and how. I don't need to walk through each point in the history; IDE plugins allow you to simple designate a line, and it will populate a list of dated changes to that line tied to specific commits. You can then diff whichever of those commits against the previous one. I had that exact scenario about a week ago when a dev from my previous team called me up to ask for advice on debugging something strange in a sister app. This is pretty often a quick way of pinpointing an issue.
1:34 Bravo! And it's a good advice for time travellers too.
Another controversial topic is whether it's okay to amend commits. During development I'd say yes as long as the changes you're amending logically belong in the commit you're amending to. But I've seen others avoid it like the plague at all times.
i do it to my own commits on my own branch, never on any public branch.
To me, it’s pretty simple. Rebasing adds a way that things can go very wrong for very little benefit. Therefore I don’t rebase.
For extremely large or widely used projects, the value of rebasing increases, so there is some threshold that I would implement a process for doing it. But I’d say anything private with 10 devs or less, just merge.
Agree with Theo on Rebasing. I don’t mind being counterculture. I care less about theory and embrace the reality of how many dev shops actually operate. Kudos to Theo for challenging the status quo.
As a historian, I always merge instead of rebase.
For me is pivotal to have the ability to access the hole history. Merge preserves that, and we can analyze that history.
It's an epistemological and methodological issue for historians.
I love informational videos that is also very entertaining. This one is it 🤩
Theo is wrong: You can undo a rebase. Right after a rebase you can use ORIG_HEAD to get the previous version and even if you did another rebase in between you can still use the commit-hash from before the bad rebase to reset the branch for some time until it is cleaned up by git. Yes, the reset is a force-push, but you were already force-pushing for the rebase anyway. So you can do it again. Changes introduced after the bad rebase can be cherry-picked after the reset.
If your branch is a merge-request, GitLab also shows you the previous commit hashes, so you might not even need to remember them.
To really make sure, you can use a tag or branch to mark your state before the rebase you are worried about.
To avoid accidentally losing work, you can merge `main` into your branch before the rebase and do a `git diff ORIG_HEAD` or `git diff origin/...` after the rebase to see if your rebase introduced any changes in comparison to the pre-rebase-version.
I think an important follow up is: does Theo squash commits? Because his argument makes sense only if you don't squash. All the intermediate branch commits are states that existed in your machine, like Theo said. But if the goal of the branch is to be a short lived branch making a change then all intermediate commits only matter in the branch, before merging. Once you merge, all those commits ideally should be squashed and so you only have 2 States on a dev machine: rebased before changes and after rebase (which may or may not include conflict resolution changes). So if you rebase and squash merge into main, Theo's argument doesn't make sense to me. And you can indeed git bisect/rollback to any commit and have them work. If you don't squash, you run the risk of having issues.
Not sure if I've explained that well.
Great stuff, looking forward for more content like this
yayaya
You can get back data that was "lost" during a rebase (in reasonable time): "reflog"
This is the juicy contreversy I live for. I am team rebase, but I do merge in certain scenarios.
I am also a big fan of rebase. As a bit of a DevOps purist, the history is helpful to see when the issue was introduced, but I never really NEED it as I almost NEVER recommend reverting to a working version via SCM. Fail forward (almost)Always. I also think the teams debugging/troubleshooting posture should be better than looking at git commits.
I don't rebase either.
But my reasoning is a lot more simple:
I have no idea how to do it or what it does
I never have used rebase, myself… I was became allergic to it when I learned that it was intended to help change past development history, and when the tutor recommended against using it with branches that have been pushed to a public, shared repository.
I googled git rebase today on my work laptop, now I get this this video on my personal iPad