Should I be doing something different? As I said, rebase in that situation is disastrous. Many folks recommend it vigorously but are not around when things inevitably go wrong.
Should I be doing something different? As I said, rebase in that situation is disastrous. Many folks recommend it vigorously but are not around when things inevitably go wrong.
I think you're referring to what I call "conflict cascades"? E.g., you're 4 commits ahead of "master", and want to rebase on top of it - but you have a conflict with your first commit, and after you resolve it, this results in conflicts in the second one, etc.?
It's a very interesting question because it's one of the main pain points of rebase that simply does not exist with merge.
What I've found for myself is that it's very important to create "atomic commits", which do not rely on other commits to make sense or "be complete". For example, a very common thing that I see is "fix formatting", "fix tests" commits, because some build was complaining about something being broken. And it's often these kind of commits that create weird conflicts (especially the formatting ones) because just a lot of code is moved/refactored/added/removed etc.
But do these commits make sense on their own? For example, if you have a history as follows:
<commit-1> Introduce new fancy feature
<commit-2> Fix formatting
<commit-3> Fix tests
Does it make sense to ever check out <commit-1>? No, right, because that commit is broken - wrong formatting, broken tests. What you want is always <commit-3>. So why not just adding those changes directly to commit-1 by using commit --amend when committing or by using fixup in an interactive rebase.This was of course just an example, but in my experience, if people follow these kinds of trains of thoughts - what can be in a separate commit, what should be together, etc., there is an inherently smaller conflict-cascade-potential, which pays off a lot when rebasing. On top of that, it also makes history generally more readable and individual commits more meaningful and easier to cherry-pick.
What is wrong with a master branch whose history reads "Merged feature foo" after "Merged feature bar"? If you need more detail then check out the feature branch and git bisect to your heart's content.
I do wish that we could "archive" branches from the output of `git branch -a` but really it's not a big deal when branch names are prefixed by Jira ticket identifiers.
I think it's also a bit a question of team size. If your project has just a handful of devs working on it, merge-commits really don't matter all that much. If there are a few dozen with a good amount of juniors on the repo (a bit over 40 devs in my case), the commit-history becomes an absolutely unreadable spiderweb, that's at least my experience.
At work, we want to move towards trunk-based development (merge to master goes straight to prod), but whenever master was broken, a look at the commit-history didn't really all tell you that much about the who, how and why without checking it out and digging into it. So we've recently started to enforce linear history, in which case it is immediately obvious without the shred of a doubt who's responsible. Analyzing the build became an absolute breeze, at the cost of having a bit of a harder time at "insertion"-point (which is perfectly fine, given the fact that after merge, it should eventually go straight to prod).
It took a while to get everyone on board, but it was really worth the efforts and can only recommend it (I most definitely will prefer a workplace that is open to such practices in the future). In general, most devs were also quite happy to be guided through the process, as it is clear that git is probably one of the longer-lasting constants in our industry.
Also, I can't repeat it enough; speaking from experience, most devs severely overestimate the complexity of cherry-pick/rebase/reset/reflog etc. Whenever I had sessions explaining it or helping someone out with a problem, they had their first "aha!"-moments after a few minutes, and after that with some do-it-yourself-experience most of them get there rather fast. Obviously, there are always a few outliers who need a little bit of extra-nudging and extra-help to stay in line, but those are usually the ones who often also need that sort of assistance in other areas so that's okay.
git log --first-parent gives you a nice linear history of (generally) your top level merge commits. If you follow a PR workflow, it's PR-by-PR breakdown of activity. The DAG gives you the power to "explore the spiderweb" if want/need to, but also pull back and say "give me the high level over" (--first-parent). I still think a lot of the emphasis on rebase-only workflows would disappear if more of the graphic UI tools (including and especially GitHub) had a better --first-parent experience by default. With the GitHub PR flow it has always surprised me that the main commit log isn't just a --first-parent view with an optional drilldown experience.
Maybe the advantage is for people who use graphical tools? I'm still in the stone age using Git in bash.
Thanks.
You never rebase something into something else, you rebase onto something. I suppose you meant you want to rebase feature branch onto master.
If you have conflicts, you'll have them anyway, regardless if you're merging feature branch into master, merging master into feature branch or rebasing feature branch onto master. Conflicts are not a consequence of rebasing.
When I pull from main (git pull origin develop) there are rarely conflicts. When there are they are easy to understand and fix. Clicking squash on my request hides all intermediate commits so history stays clear in any case.
When I try to rebase from main (git pull --rebase origin develop) often almost every file in the project is in conflict, and the conflicts themselves are incomprehensible. I then run rebase --abort and go back to the merge strategy.
I've tried this about four times in the last few years; no one is able to explain what went wrong. If I had to guess it may have something to do with me pushing the branch when creating it.
git checkout master
git pull => fetch and merge the new things on master
git checkout develop => go back to your work branch
git rebase master => simple rebase, no squash, no nothing. The conflicts are the same as you'd get with git merge
do not forget to force push the changes of your branch git push origin --force develop => to update your branch on the serverThere's a bunch of resources on that and once you wrap your head around what git is doing it shouldn't be too hard to figure out how to navigate conflicts.
In many cases also there are problems with git being line oriented rather than token oriented, IMHO I would have expected language aware diffs (for definition of "words" and maybe parenthesis) for common languaged to be common place by now (maybe even with a language server support)
In my team we are lucky as most conflict are a matter of simply choosing a side and overwriting the other.
Yes, this is true. But in a lot of cases this is a relatively trivial fix. That being said, I do think git could do better here. But I have not had it happen very often (and usually I know when it's about to happen so I know what to expect).
> In many cases also there are problems with git being line oriented rather than token oriented, IMHO I would have expected language aware diffs (for definition of "words" and maybe parenthesis) for common languages to be common place by now (maybe even with a language server support)
Language aware diffs would be a neat idea indeed, but I guess the issue may be that now git would need to have metadata to tell it which language server to use for which file.
I wonder if it would be possible to give git a plugin system for diffs, just have an option to offload to a diff program which could handle the metadata separately.
Often it is simple but it is tricky and hard to practice. Part of the problem is not git fault, it is just that thinking in diffs is not easy.
> metadata to tell it which language server to use for which file
My understanding is that git already does this (via filename or custom helpers) for binary/text files, I believe that it is used only for newline conversion, but for most usecases filename-based rules in a gitignore derived syntax would be enough.