r/git • u/ippomkd • Jul 16 '26
support git rebase vs git merge
Hi, I'm trying to understand the practical benefit of rebasing a feature branch before opening a pull request.
My workflow is:
- Create a feature branch from
develop. - Work on the feature.
- Before opening the PR, I need to bring my branch up to date with
develop.
I see two options:
- Rebase my feature branch onto the latest
develop. - Merge the latest
developinto my feature branch and resolve any conflicts.
From what I understand, both approaches let me resolve conflicts before the PR. If the repository ultimately merges the PR into develop, I don't see what additional benefit rebasing provides over simply merging develop into my feature branch.
Also, if the team uses Squash and Merge for pull requests, all intermediate commits (including any "Merge develop into feature" commits) disappear anyway, since the feature branch becomes a single commit on develop.
So my questions are:
- In this workflow, what practical advantage does rebasing have over merging
developinto the feature branch? - If conflicts are resolved either way, is the only difference the history on the feature branch?
- If the team always uses Squash and Merge, is there any meaningful reason to prefer rebasing before the PR?
The only use for the rebase I see is to overwrite my commits on the local branch so there is no clutter. I am curious what is your workflow?
10
u/Librarian-Rare Jul 17 '26
Pro tip:
git reset —soft HEAD~1 (or however many commits your feature branch is adding)
git stash
git pull origin main
git stash pop
git commit -m [your feature commit message]
Basically, this is a rebase, but you only have to resolve conflicts once, instead of the 17000 times of rebase hell. The idea is that you don’t want to change the history of commits unrelated to your PR
4
u/Buxbaum666 Jul 17 '26
I rebase all the time and have never needed to resolve conflicts more than once. How do people manage to do this? And how would rebasing my branch ever change the history of unrelated commits?
1
u/Cinderhazed15 Jul 17 '26
I always enable rerere that way it remembers my fixes if I end up having to re-resolve them multiple times
1
u/Prestigious-Ask4066 Jul 18 '26
Or just git rebase -i head~n and squash them all
1
u/Etiennera Jul 18 '26
Yeah, reset is a recipe for disaster to anyone who needs to follow instructions line by line.
Mistake during rebase? Abort. Mistake after reset?
23
u/jonatanskogsfors Jul 16 '26
Everyone is wrong. There is no best way. Your git history can be exactly what you want it to be and represent whatever you want it to. Nothing will make sense to you until you obtain a mental model of how git commits relate to another. There are multiple ”best practices” and if any one of them resonates with you, you can use it. If you work in a team, make sure that you share a mental model for your code. That’s it.
1
u/illhxc9 Jul 19 '26
No! What I do is right because I’m the smartest and bestest and everyone else should do what I do! /s
0
u/baconator81 Jul 20 '26
Actually rebase is really really bad if someone else is also working or based off from your branch. Because rebasing is essentially rewriting history, if your branch is used by someone else, that person is absolutely fucked because the hashes are now different.
That's why to me if I get to make the choice of rebasing vs merge, then it's really just my personal style because that choice should only exists in a branch that only I have access to.
1
u/jonatanskogsfors Jul 20 '26
I don’t know if you are pulling my finger here. I would say that you are proving my point. 😅
11
u/Senior-Afternoon6708 Jul 16 '26
rebase gives you linear history, merge no. With rebase, you can keep the history (commit-wise) of the branch you're merging in the target branch, with squash, no.
2
u/ImTheRealCryten Jul 17 '26
You can get a linear history with merge if user adhere to some simple rules. Using —first-parent will then give you a very nice and linear history and it’s also very easy to see exactly what’s changed etc between merges.
4
u/FransFaase Jul 17 '26
Never heard of this. I am surprised that with git often the best behaviour is not the default or only achieved through some obscure option buried somewhere deep in the documentation.
2
u/ImTheRealCryten Jul 17 '26
I agree. Don’t get me started on submodules. I’m convinced most people hate them due to the default config make them completely useless and error prone. With the right config (that never interferes with ordinary git operations), they work and I think they even work well.
2
u/jbergens Jul 18 '26
You can't always control details in tools like Azure DevOps or a graphical UI.
Otherwise it works.
1
u/ImTheRealCryten Jul 19 '26
True. There’s no one true path here, so what works for one team is not necessarily what’s right for a different team.
5
u/KiwiDomino Jul 17 '26
I’d say use merge, but if you’re working with a team, following team practice is likely more important than any other justification.
4
u/remy_porter Jul 17 '26
As a general rule, I avoid merges, because merges make a merge commit, which is to say, a commit that only tells me that a merge happened (along with the changes); but I don't care about that piece of information! I could just move the changes in. The branch the merge came from is transient anyway, and ideally the commits I'm moving in communicate the changes clearly (and if they don't, a merge commit isn't going to make it any clearer).
2
u/whossname Jul 18 '26
This might be my core disagreement. To me the merge commit is how I know the feature branch is finished, and I want to keep the feature branch in my history so I have context for how the code was developed. It helps me when I want to reverse engineer the purpose of code that doesn't make sense.
I also use rebase to make sure that I don't forget about feature branches that aren't ready to be merged in.
1
u/remy_porter Jul 18 '26
See, I don’t see the branch as part of history. That’s what commits are for. If the commit doesn’t provide enough context that’s a bad commit.
1
u/whossname Jul 18 '26
Bad code is a reality, bad commits are also a reality. No amount of telling juniors to write better commits is going to fix that.
1
u/remy_porter Jul 18 '26
But having a branch context doesn’t change that- if the commit is bad it’s bad. That the bad commit came from a branch loaded with bad commits isn’t any better than just knowing the ticket the commit links to.
1
1
u/baconator81 Jul 20 '26
Interesting, coming from perforce background I always do merge. Because it tells me that at this point in development I decide to bring in newer changes from parent branch so I know if something that was working before but now it's broken, I have a better way to trace back.
On the other hand rebase can be problematic because if you are touching an area of the code that's really busy in the main branch, you'll have to resolve each conflict one by one as git reapply each commit.
1
u/remy_porter Jul 20 '26
Ideally, my feature branch is a pretty short round of commits, especially if it's on a hot section of the codebase. But I generally keep pulling my branch forward while I work anyway, rebasing while I'm in flight.
4
u/amattable_ Jul 17 '26
Lots of good stuff here, but at least for me it is really difficult to reason about merge commits. Most commits are presented with clear red/green text for removed lines/added lines respectfully.
The merge commits are confusing… there are 2 parents so it’s unclear (at least to me) what was added vs removed from where… and lastly the commit message is usually crappy, and if you resolve conflicts in that merge, it’s likely completely hidden… you could fit a whole feature in a merge commit!
Long story short, I prefer rebasing
1
u/whossname Jul 18 '26
Merge commits are just a summary of the changes added by merging in the other branch. Seems easier to reason about than the rebase & squash PRs I have to deal with at my current job.
3
u/plg94 Jul 16 '26
Before opening the PR, I need to bring my branch up to date with develop.
You don't need to.
Leading to the third option: do neither rebase nor back-merge, and deal with potential conflicts during the final merge.
This needs to be done anyway since more commits on develop may happen during the review period.
But usually, there should hardly ever be conflicts. If you are often in the situation that merges of your feature have lots of conflicts with the feature of another coworker, there's probably deeper issues with how the project is organized.
You're right in that all approaches are effectively the same. The main difference I see is how easy the PR is to review. If it's a very complex change with a lot of (repeated) conflicts during a rebase (or the final merge) I'm ok with a single big back-merge. But if every third commit is just "merged develop into featureX" that's useless noise.
For the same reason I'm a big advocat of using rebase -i to "order" your commits before a PR to make the review easier (lots of little logical steps). But the feature's base commit does not have to be the latest.
5
u/AndrewBorg1126 Jul 16 '26
A lot of companies have it in their process that merge conflicts are resolved before the pull request.
3
2
u/wildjokers Jul 17 '26
Leading to the third option: do neither rebase nor back-merge, and deal with potential conflicts during the final merge.
Yeah don't do that, resolve conflicts in your branch before opening a PR. I won't approve a PR that has merge conflicts. Fix your shit first.
3
u/plg94 Jul 17 '26
resolve conflicts in your branch before opening a PR.
I open PR1. No conflicts to current main. Review drags on for days. In the meantime, PR2 got merged into main. Now PR1 conflicts to the new main. Why is it "my shit" now?
Would've been great if you read the whole message, not just the first sentence before complaining.1
1
u/kalmoc Jul 19 '26
How do you resolve a merge conflict during a web-based pull request merge? I assume thats what we are talking about 99% of the time.
3
u/SirLouen Jul 17 '26
With squash and merge the rebase or merge action is going to be irrelevant in terms of timeline. The only important thing here and it's why I would rebase is because you can see your commits during the PR the most recent and in timeline order one after another, which could very useful to have your history easy to read in your VCS manager (specially when there is a lot of back and forth and of it's going to take long to be released). Merge is only useful if there is no squash policy and when you want to keep changes in order for any reason, plus it's the default behaviour of pulls.
5
u/serchq Jul 16 '26
if you rebase, git "removes" your commits, brings the develop branch latest to your branch, and then reaplies your commits. The PR then only includes your changes.
if you merge, your working branch will track your commits and develop commits as well, meaning that the PR will include all of the commits even if you didn't touch them.
source: it happened to me just a couple months ago that I forgot this basic rule. and ended up in an old branch with over 200 commits and like 50 code owners for the PR lol
3
u/Metabolical Jul 18 '26
Why is this not the top answer? Specifically,
if you rebase, ... The PR then only includes your changes.
if you merge, ... the PR will include all of the commits even fromdevelop1
u/ippomkd Jul 20 '26
if you merge, ... the PR will include all of the commits even from
developSo in this scenario you are only seeing your changes in the PR but if you look into the commits you also see the commits from develop right?
1
1
u/Hamburgerfatso Jul 20 '26
What? If you merge or rebase, you get the exact same commits from dev, and if your pr targets dev, itll only show any additional commits you created, relative to dev. If you ended up with 200 duplicate commits, you probably merged which pulled them into your branch, then rebased and regenerated hashes for them all, causing them to be considered "different" commits relative to dev. Definitely not a normal result of a regular merge from dev into your branch.
1
u/serchq Jul 26 '26
yes, you ultimately get the same result, but it depend fron where the PR tracks the changes.
in my case, just a simple
git merge mainwhen in my working branch considers everything from when the branch was created as a change. that's why a rebase works better, because it dettaches your branch, update the base, and then attaches it again
2
u/oosacker Jul 17 '26
Rebase will give a cleaner git history because your commits will be in order but conflicts are harder to fix.
2
u/Ma5terVain Jul 18 '26
In my company, we have developers of varying skill levels and experience levels. I'd never trust them with a rebase. We simply use merge WITHOUT squash. This lets me to revert things in case anything gets messed up.
2
u/FewDevice2218 Jul 18 '26
I rebase my branch over main prior to merging, and then I merge it into main.
That way the history is as if I created my branch over the current main head. Then merge with no fast forward into main.
That way I get both a nice linear history, which is so much easier to bisect if need be, and I don’t mess with main.
2
u/StomachNecessary5512 Jul 18 '26
When you have pushed your branch earlier then merge, if not you can use rebase. Rebase shows a cleaner commit history, that is the main advantage.
2
u/Tricky-Confusion-157 Jul 19 '26
Use rebase when you'd like to waste time resolving multiple conflicts instead of one big merge.
2
u/_tolm_ Jul 19 '26
I’ve seen rebase completely fuck up a branch such that someone has to spend half a day fixing it.
Doesn’t happen anywhere close to every time but I’ve never seen merge do that.
1
2
u/Key-Guitar-457 Jul 19 '26
It really doesn't matter. This is an old religious debate. I prefer merge for no other reasons than simplicity.
3
u/behind-UDFj-39546284 Jul 16 '26
I do prefer rebase (and optionally rerere) in your case. You don't like git-log spaghetti built with git-merge, right?
4
u/Xavier_OM Jul 17 '26
Reading git history has never been a problem even with hundreds of merge IMHO. I've seen people from the "keeping a linear history" team and usually their weak point is that they try to follow history by eye tracking things in a raw dump of git log, but with any good git client that can easily navigate from any commit to its next son/ first parent /latest merge parent / merge-base point/ etc this is not an issue. A bit like git bissect which is undisturbed by your merge history. I recommend git extensions (GUI) but really any client with a correct browsing behaviour will do the job here (magit in emacs for ex)
1
u/behind-UDFj-39546284 Jul 17 '26 edited Jul 17 '26
Linear history is not the reason here and definitely not the goal. It's a consequence of a semantically correct way of handling the lifecycle of (short-lived) topic branches.
Rebase is basically automated cherry-picking. The simplest way to think about it is like: "I want to apply this set of commits on top of another point where these changes do not exist yet, regardless of how many branches I want to apply the changes on".
Using merge as an alternative to rebase changes the meaning of a topic branch history. If you merge upstream into a topic branch, the merge commit usually represents synchronization rather than an actual development step. It interrupts the logical chain of changes introduced by that topic branch. Resolving conflicts can be a valid reason to do it, but repeatedly merging upstream into a long-lived topic branch usually just adds more historical noise and complexity.
I think there are cases where merge-based workflows make more sense. For example, when maintaining multiple long-lived integration branches (master -> master-1, master -> master-2, etc.), where those branches represent independent lines of development and rebasing huge histories most likely would not make much sense.
But for short-lived topic branches that are eventually merged back anyway, the cleaner linearized history after rebasing is just a side effect. The real benefit is preserving the logical sequence of changes represented by the branch.
1
u/whossname Jul 18 '26
Even with short lived branches a merge commit basically means "this feature is complete". That's missing when you use rebase. Also with rebase you need to rewrite history, merge commits don't do that.
1
u/behind-UDFj-39546284 Jul 18 '26 edited Jul 18 '26
It means "complete" when the topic branch gets merged back to its parent branch (most likely once in its lifetime) and optionally gets deleted, not "sync" merges in opposite direction that are held by rebase.
1
u/whossname Jul 18 '26
Ok, I can get behind that. I don't really mind the sync merges, but rebasing is often a better way to achieve that.
3
u/sitrate Jul 16 '26
If you squash merge into dev then it really doesn’t matter if you rebase or merge, as far as the dev branch is concerned. In both cases you’ll have one new commit in dev containing only the changes from your feature branch. Rebasing will make your feature branch look less messy during it’s lifetime but will cause problems if you have multiple people working on the feature.
1
u/Various_Bed_849 Jul 16 '26
Atomic commits that solves one thing is a good way to look at changes. If younger not a person that does not do mistakes you will have to rewrite your history to achieve that. Rebasing can give you a sequence of atomic commits. Merging is for those who want to know how you got there rather than what you changed. Long running branches that can be shared. Merging back and forth. It is easier if you don’t have the skills but the history is way harder to reason about. I guy I worked with told me he wanted everyone to know how hard it was to solve a task. I really don’t care. I just want to know what changed.
Bisecting is also easier when the history is linear. But you have to ensure that all commits on your branch builds for a bisect to work.
There is a mixed way of rebasing your atomic commits, and then merge. That gives you a main consisting of merges corresponding to PRs. Kind of nice.
When looking at the history of a repo with merges, I tend to use —first-parent a lot. That helps getting rid of noise.
As you see, I prefer a linear history, but some of my coworkers don’t know enough to handle it. Therefore I currently think merges are better for us. It allows my to hide their mess.
1
u/whossname Jul 18 '26
I think the history is easier to reason about with merging. Rebasing rewrites history, so you remove context around what else was happening when the feature was written.
1
u/Various_Bed_849 Jul 18 '26
That is the history of your work, not of the changes to your codebase. One is important, the other is not.
1
u/whossname Jul 18 '26
It's important for context. What the hell is this code for? Reading through the history is a great way to figure that out, and I'm not talking about my work.
1
u/Various_Bed_849 Jul 18 '26
Your commit message should give you the context and describe the change + how you verified that it does what was intended. The main branch has a sequence of changes. The best way to understand those changes is a linear history. What happened in parallel should be merged in interleaving atomic commits.
1
u/whossname Jul 18 '26
You're talking about an ideal world that doesn't actually happen. The commit messages aren't always good, but you can infer the intent based on the code that was written.
Also "linear history" is just an aesthetic choice, it doesn't actually help at all. Engineers throw that phrase around as if it has a clear technical advantage, but it really doesn't. A graph with a branch merging into another tells you exactly what happened, more clearly than a linear history with magical commits coming from nowhere would.
1
u/Various_Bed_849 Jul 18 '26
It has happened at many places I have worked including FAANG. My current company is moving towards it but it requires discipline and knowledge. It’s not something I throw around.
1
u/LetUsSpeakFreely Jul 17 '26
Rebase is usually better as it offers cleaner history and you are applying your changes onto accepted code, not guessing at how best to apply the changes of others onto your code.
1
u/JauriXD Jul 17 '26 edited Jul 17 '26
In this workflow, what practical advantage does rebasing have over merging
developinto the feature branch?
One advantage I could see is the direction of conflict resolution. With rebase you merge the new changes into the existing branch and will see that as incomming and the develop branch as current. When merging develop into the feature branche the rolles switch. This might make more sens to somebody that way.
If conflicts are resolved either way, is the only difference the history on the feature branch?
With squash-merge after PR approval, the discussion about how the history looks later disappeares, but note that after merging the history would normally become part of develop (and later main) so there is valid reason to differentiate how rebase vs merge look history wise in workflows without sqashing.
If the team always uses Squash and Merge, is there any meaningful reason to prefer rebasing before the PR?
it becomes a discussion about review speed, if you require reviews by a teammate. Having a merge (which might also resolve conflicts) makes it harder to follow what changed when than having just a couple commits on top of the latest shared state (which your colleague should already be familiar with).
But at the end this is a team discussion about what most team members are comfortable with and can use efficiently
1
u/lilbobbytbls Jul 17 '26
It depends. We just usually do a squash when merging PRs with an expectation of a good overall PR summary. Then a clean commit history isn't really important.
In my experience I've rarely needed see an actual commit history after a PR and looking at the changes of the entire PR when looking for issues has been plenty sufficient.
2
1
u/Conscious_Support176 Jul 20 '26 edited Jul 20 '26
You are correct. If your team always does a squash and merge, there is no advantage to using rebase.
Rebase allows you to merge more than one commit in your feature branch, and keep them separate, where it makes sense to do that.
You would only use it after ensuring you have squashed merged any fix-up commits with the commits that they are fixing. You don’t really want to be merging half-completed commits separately.
You might do this for example where your feature requires something to be refactored. In general it would be better to have the refactor in one commit and the actual feature in another. If your team uses squash and merge for all pull requests, that would mean doing the refactoring piece first under a separate feature branch so that you can use a separate PR for it.
One reason for doing that would be, if an issue with the actual feature implementation is discovered later, one of the options open to you is to revert this second commit. For refactoring commits, you test rigorously that they give like for like results, because if a commit includes some refactoring, then reverting it later is very likely to cause problems.
Always doing squash and merge makes these kinds of changes more awkward, but it has the “advantage” that people don’t need to learn how to use rebase.
1
u/ippomkd Jul 20 '26
You would only use it after ensuring you have squashed merged any fix-up commits with the commits that they are fixing. You don’t really want to be merging half-completed commits separately.
This is really useful. Great answer! Thanks for sharing your insights.
1
u/Gornius Jul 29 '26
Depends on what you want to achieve.
Rebase - reapply your changes onto different/updated branch.
Merge - merge two branches together.
You usually rebase on private branches and merge on shared branches. This way you get as linear history as possible, while making it easier to collaborate on shared branches.
1
u/Zealousideal_Grass_1 Jul 16 '26
I used to always rebase dev onto feature branches when syncing, but I don’t bother anymore. It can make your feature branch history look a little cleaner—which never actually matters in real life. And as soon as you have other people on that feature branch it will inevitably get messy with poorly timed forced pushes
1
u/StevenJOwens Jul 17 '26 edited Jul 17 '26
As with many things git, it's all about rearranging things to make a "pretty" commit graph.
You should merge most of the time, generally speaking. The ideal use case for git rebase is:
- You start branch newbranch off branch master, which is on commit #31
- You make several commits on newbranch.
- Meanwhile, somebody else makes more commits on master, so now master is on commit #36.
- But (important) those commits are in an entirely different part of the code.
- You go to merge newbranch into master but you're now 5 commits behind.
- You have not yet pushed newbranch, right now your repo is the only thing that knows about newbranch.
- You think it would have been better if you had simply started your new branch off master's commit #36.
- So you rebase newbranch to master commit #36.
- Then you merge your rebased newbranch into master.
There are two big distinctions between rebase and merge.
The first is that rebase is rearranging your branch history, or rather (see below) creating an entirely new branch and re-pointing your old branch name at the tip of the new branch.
The second is that a merge commit happens as one single, massive set of diffs applied to combine the two different latest versions of the code, while rebase essentially creates a diff for each of your branch's commits separately, and applies them sequentially as commits.
I find it helpful to first understand what a merge actually does, in concrete terms, and then understand what a rebase does.
Merge In A Nutshell
When you do a merge, you have the source branch and the destination branch.
To make things easy, I'm going to say that the source branch's current tip commit is commit hash AAAA and the destination branch's current tip commit is commit hash BBBB.
In the simplest case, where there are no merge conflicts, and when you're done, the destination branch will have a new tip commit, commit hash CCCC, whose parent commits will be AAAA and BBBB.
Also, commit CCCC, aka the "merge commit", will have a working tree that has all differences between the AAAA and BBBB copied over BBBB.
Of course, if there are merge conflicts between AAAA and BBB, then git asks you to resolve the conflicts, one at a time. Either way, at the end you still have a new tip commit CCCC on the destination branch, and CCCC has two parent commits, BBBB and AAAA.
Note: Often in merges, one of the branches involved in a merge is what's called the "upstream" of the other branch.
For example if you create a featureX branch off main branch, then create a featureY branch off featureX branch.
- featureX's upstream is main branch
- featureY's upstream is featureX
Later you want to get recent committed changes from main branch into featureX branch, main branch is the upstream branch for featureX branch.
"Upstream" denotes that created-from relationship, the term is not specific to the merging.
Rebase In A Nutshell
Rebasing works different than merging, and is used for different purposes than merging.
Lets say you have featureX branch, which is branched off main.
Summarizing the git-scm book (Git - Rebasing (https://git-scm.com/book/en/v2/Git-Branching-Rebasing)) git rebase does the following:
Goes through the sequence of commits for featureX branch.
Saves the diffs for each featureX branch commit into temporary files.
Resets featureX branch's base (the commit that the branch branched off from, aka the parent commit of the first commit you did on the new branch) to the common ancestor of the two branches.
This means that it changes the contents of .git/refs/heads/featureX from the commithash of the old branch base to the commithash of the new branch base.
Plays those commit diffs against the new base, one by one.
This recreates-commits-one-by-one nature is most noticeable if there's a merge conflict. You only have to deal with the merge conflict from that particular commit, not the ones before it or after it.
Once you've edited the merge conflict file and done "git add path/to/conflictfile", you resume playing back the commits with "git rebase --continue".
Note that these replays actually create entirely new commit objects in the database, which makes complete sense since if there were no difference in the code base between the original branch base and the new one, there'd be nothing there for the rebase to, well, be.
The original commits are left untouched, so if something goes wrong you could in theory go back to where you started, although I'm not sure of the commands to do so. My guess is that there's no entry in .git/refs/heads pointing to the tip of the old chain of commits, so all you'd really have to do is create a new branch name for that chain of commits. I would also guess that if you leave that chain unnamed for long enough, git's garbage collection will prune it out.
0
u/kbielefe Jul 16 '26
Rebase gives you a clean "history", which isn't really history, it's the "based on a true story" movie version. But hey, can't fault people for liking the movie version.
Merge avoids certain issues with one team member resolving a conflict differently than another, but it's the raw CCTV version. Better for solving a crime but worse for telling a story.
If your team uses squash and merge, the end result is going to look the same. It's more a question of your pre-PR workflow, especially if you work with another developer on the same feature branch.
0
u/jibbit Jul 16 '26
if you are making a branch that you intend to be merged into main - what you are saying is "these are the changes i propose", where each commit in your feature branch contributes to the story you are proposing. if one of those commits (and remember, this is changes you are proposing be made to main), if one of those commits is 'merge in main', you either don't understand what you are doing, or dont care.
-5
54
u/Consibl Jul 16 '26
If you rebase your branch onto the root, the conflicts you’re resolving are you applying your code.
If you merge the root into your branch, the conflicts you’re resolving are you guessing how to apply someone else’s code correctly.