9 ms·
Unfortunately, git rebase has a very very annoying limitation that git merge doesn't. If you have a branch with, say, masterX + 10 commits, and commit 1 from yo
by simiones 4y ago
Unfortunately, git rebase has a very very annoying limitation that git merge doesn't. If you have a branch with, say, masterX + 10 commits, and commit 1 from your branch is in conflict with masterX+1, then when you rebase your branch onto masterX+1, you will have to resolve the conflict 10 times (assuming all 10 commits happen in the same area that had the original conflict). If instead you merge masterX+1 onto your branch, you will only have to resolve the conflict once.
Even though I much prefer a linear history, losing 1h or more to the tedious work of re-resolving the same conflict over and over is not worth it, in my opinion.
- broeng 4y agoIn your example, you pretty much have to change the same line, or neighbouring line, those 10 times to end in that scenario. If it's just somewhere else in the file, git auto-merging will handle it just fine. It seems like a very contrived example to me. We have been running rebase/fast-forward only for close to 10 years now, and I have never experienced anything that unfortunate.
- ncann 4y agoYeah that scenario only ever happens if you have an extremely large branch that hasn't been merged into the target branch for a long time (like a feature branch that takes months to develop), which btw isn't really something that should be done anyway (always try for more frequent merge with small side branches).
- michaelt 4y agoSounds like you've never worked on a project with a file everyone wants to append to :) If every error in your system needs a separate entry in the error enum, or every change needs an entry in the changelog - loads of changes will try to modify the last line of the file.
- account42 4y agoEven multiple appends are not that bad for rebasing - if you put the remote changes before your own then after the first commit the context for your remaining commits will be the same. If order actually matters then yeah, git can't magically know where each new line should go.
- olddustytrail 4y agoDepending on the format of your files, entries like "changelog merge=union" in your .gitattributes file might work for you.
- broeng 4y agoOh, I have. :-) I'm not saying these situations are impossible. But you can work towards reducing when they arise. If everyone needs to change the same file, then it sounds like something should be refactored (it's probably a quite big file as well?). If every error needs to go to the same error enum, that sounds like an error enum that might benefit from being split up. And if every change needs to write to a common changelog file, I would personally find a new way to produce that changelog. If it's that big a painpoint, then I would look into different ways to get around it.
- simiones 4y agoIt happens pretty often when two different people are adding a new function in the same area of a file. It's likely that as you're working on that function, you'll be modifying the surrounding lines a few times (say, you have a first pass for the happy path, then start adding error handling in various passes; or, handling one case of an algorithm in each commit). Rebase is still by far the most common case in our repo, as yes, these cases appear very rarely. But when they do happen, it's often worth it to do a merge and mess up a history a little bit (or squash, which messes with history in another way) rather than resolving conflicts over and over. Someone else was also suggesting rerere for this use case, but I've never used it myself and I don't know how well it actually handles these types of use cases.
- broeng 4y agoIt definitely can, and it also sometimes happens to us. But we try to reduce the chance this happens quite a bit, by avoiding letting files grow too big, for example. Other things we do, is use codeformatting with rules that reduce the chance of merge conflicts. For instance, instead of having imports like: import SomePackage.{A, B, C} we format it to: import SomePackage.A import SomePackage.B import SomePackage.C That alone helps a lot. Other formatting rules that avoid dense lines, and instead splits over multiple lines also have a huge impact on merge-conflicts.
- collinvandyck76 4y agoIt's not as contrived as you may think. I, along with what I imagine are many others, do a lot of frequent micro-commits as time goes on and the feature becomes more complete, with a lot of commits in the same area of any given file. Rebasing a development branch in this state is pretty gnarly when a conflict arises. Sadly, my current approach is to just reset my development branch to the merge base and make one huge commit, and then rebase.
- broeng 4y agoI do a lot of micro-commits as well, though I rarely find that other members of my team are doing the same, to the same files, at the same time. When that happens, we look into if it's possible to do more frequent merges (fast-forward rebases through Gerrit, to be specific) of our smaller commits to master, so we don't accumulate too much in isolation. I find it helps reducing bugs as well, if two or more members are doing active work in the same area in that way, it's not good to be working in complete isolation as it just opens up for bugs because of incompatibility with the work going on in parallel.
- Beltalowda 4y ago> It seems like a very contrived example to me. I run in to this quite frequently, even on projects where I'm the only one working on it (I tend to have a lot of things going on in parallel). Once branches diverge and commits accumulate it can become a right pain. Usually my solution is to merge master into the branch just to keep up to date and then just undo everything, make one new commit in the master, and rebase that. But in some more difficult cases it was "just merge and fuck it because life's too short". I've also just manually "copy/paste merged" things to a new branch, because that seemed quicker than dealing with all the merges/conflicts. Maybe there are better ways of doing this, and arguably I shouldn't have all these long-lived branches in the first place (but it works well for me, so...), but it's not that much of a contrived edge case.
- couchand 4y ago> arguably I shouldn't have all these long-lived branches in the first place This is the problem here. If you have multiple long-lived branches, there's no technical solution to preventing rot -- you must actively keep them in sync. Regularly merging in main is the opposite of the proper solution. Constantly rebasing on top of main is the proper solution.
- adgjlsfhk1 4y agoyou can often solve this by squashing before rebasing.
- MaxBarraclough 4y agoThat has its own problems. Separating whitespace-only reformatting commits from substantive commits makes it much easier to inspect the real changes, for instance. Also, more fine-grain commits can help you trace down a bug, perhaps with the help of git bisect. Once you've tracked down the commit that introduced the bug, things will be easier if that commit is small. Fortunately you can just merge from master, bringing your code back in sync with master without touching master itself. I see Beltalowda has mentioned this.
- IshKebab 4y agoYou mean you can often give up and avoid solving the problem by squashing before rebasing?
- erik_seaberg 4y agoReviewing a squashed branch is much harder than reviewing one set of closely related deltas, and then reviewing a different set of closely related deltas that happen to overlap.
- quadhome 4y agoman git-rerere
- simiones 4y agoIt often amuses me that some people will say "git is actually easy, you just need to know git commit, git pull, git push, and git branch", but when you go into the details, you find out you have to learn a hundred other rarer tools to actually fix the 5% or 1% use cases that everyone eventually hits. For what it's worth, I had heard of git rerere before, and have looked at the man page, but haven't understood how it's supposed to work, and haven't had time to play with it to see how well it actually works in practice. `git merge` or `git squash` and accepting a little bit of a mess in history seems much easier than spending time to learn another git tool for some use case, but I fully admit I may be missing out.
- Izkata 4y agoWhen you hit a merge conflict, rerere (re)members how you (re)solved it and (re)applies the same fix when the same conflict happens again. But using it can create a new problem/annoyance: If you make a mistake with the initial resolution, and revert the merge/rebase to try again, it'll remember the wrong one next time. So you have find and tell it to forget that resolution.
- YetAnotherNick 4y agoYou should do reverse rebase(if it makes sense lol) for this. Instead of rebasing branch to master, rebase master to branch. The only downside is that it requires many force push in the branch.
- moonchrome 4y agoYeah force push on master is a huge no no - I can't even remember the number of times I've force pushed wrong shit in a hurry - I can't imagine entire team dealing with this.
- simiones 4y agoI would first say that I would sooner re-code the whole feature by hand from memory than ever rebasing master onto anything for any serious project. Even if we were to do that, rebasing master is likely to lead to the same issue. My preferred solution is rebase featureB onto master for the 99% or 99.9% of use cases where this is smooth, and in the rare case that you have too many conflicts to resolve, merge master into featureB (and/or squash featureB then rebase onto master, depending on use case).
- cerved 4y ago> it requires many force push in the branch Can't say I recommend this approach.
- semiquaver 4y agoAs sibling mentioned, this is totally solved by git-rerere.
- booleandilemma 4y agoWhen can we move to Sapling again?
- Shish2k 4y agoI already have - it’s pretty great :D
- IshKebab 4y agoHow would Sapling avoid this? As I understand it it uses the same data model as Mercurial which is really the same as Git's. I think you would need something like Pijul to solve it nicely. At least as far as I can tell. I might actually try this in Pijul because I too encounter this semi-regularly (it's not a freak occurrence at all) and my solution is basically to give up and squash my branch before rebasing.
- BiteCode_dev 4y agoPartially. Unfortunatly, rerere is not perfect, and will only solve 80% of the cases. For big rebase, this can add up to a lot, which I just paid the price last week.
- mamcx 4y agoI was converted to rebase by my current team, and this hit every time. I wish it works like merge, or exist a way to merge, resolve conflict, rebase?
- HWR_14 4y agoCan I asked how they converted you (or do you mean by dictate, as opposed to becoming convinced it was better)? I find myself loving merges and never using rebases. It's not that I cannot describe technically what's happening, but I just don't understand the love.
- xeyownt 4y agoIn my case, I switched rapidly to git-rebase because it produces history that is much cleaner and easier to understand. I only do merge if there is a good reason to preserve history (e.g. some other branches depend on it, or some test reports refer to a given commit).
- HWR_14 4y agoI guess I find it easier to parse with feature branches than all on main.
- mamcx 4y agoMostly is about the way they do things, and I always adopt the team ways (also: my initial PRs look weird to them!).
- scubbo 4y ago(Not the person you replied to, but a passionate rebase-preferred) For me there are two reasons - one aesthetic, one practical. The aesthetic reason is that it tells a more coherent story. The codebase is a single entity, with a linear history. If I asked you "how old were you last year", and you asked "which me are you asking about?", I'd be confused. Similarly, if I want the answer to the question "what was the codebase like at this point in time // immediately prior to some point?", you shouldn't need to ask clarifying questions. `HEAD^` should only ever point to a single commit. The practical reason is that it discourages a bad-practice - long-lived branches. The only vaguely compelling reason I have heard for merge commits is that they preserve the history of the change, so that when you look at a change you can see how it was developed. But that's only the case if you're developing it (in isolation) for a long-enough time that `main` will get ahead of you. You should be pushing every time you have a not-incorrect change that moves you closer towards the goal, not waiting until you have a complete feature! If you make it difficult to do the wrong thing _while also_ making it easy to do the right thing (too many zealots forget the second part!), you will incentivize better behaviour. (Disclaimer - I've been lucky enough to work in environments where feature flagging, CI/CD, etc. were robust enough that this was a practical approach. I recognize this might not be the case in other situations) And yeah, I'm kinda intentionally invoking Cunningham's Law here, hoping that Merge-aficionados can tell me what I'm missing!
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]
- acchow 4y agoHmmm I think in your scenario you could avoid resolving the conflict 10 times by using `git rebase --onto` Suppose "masterX+1" is called latest Suppose "masterX" is the SHA of your mergebase with master (on top of which you have 10 commits) `git rebase --onto latest masterX`
- jacobsenscott 4y agoYes. Usually I just squash merge to main and then `git checkout my-branch; git rebase --hard main`. Sure it squashes all the commits, but keeping them all is nearly never needed.
- TheRealPomax 4y agoTo be fair, if you have 10 commits that all change the same file: squash with respect to your first commit, _then_ rebase. If you have lots of commits, always first squash-rebase to your own first commit, and only rebase to current main once that's done. Rebase is being annoying here mostly because it's doing exactly what you want it to do: warn you about merge conflicts for every commit in the chain that might have any.
- jamespwilliams 4y agoIf you squash you decrease the granularity of your git history, though.
- TheRealPomax 4y agoIf you have ten different commits all touching the same part(s) of the same file(s), dial down your granularity a little: you've over-committed. Either that, or you lobbed 10 different issues into the same branch, which is a whole different barrel of "no one benefits from this, you're just making it harder to generate a changelog, can you please not" fish.
- desbo 4y agohttps://git-scm.com/docs/git-rerere https://git-scm.com/docs/git-rerere