News for hackers. Stuff that matters.
Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @02:50PM (#38223631)
> The number of times I got stuck fixing (mine and other people's) git issues is way more than seems necessary.

I almost never have any issues with git, I wonder if you're working differently than I do?

> I have not used a merge at a workplace for so long that I forget how it works, because rebases make so much more sense.

Oh, that's the reason.

I've been using git almost as long as github.com has been a thing and I'm convinced that if you find yourself rebasing, _especially_ if rebasing is a normal part of your workflow, you're using git wrong.

Here's how I work:

    1. All development is on a features branch
    2. Feature branches are tight and focused
    3. Frequent merges from `main` to stay up-to-date
    4. Squash your branch into a single commit before merging
This gets you everything you want from source control (linear history of well-structured atomic changes) without any of the gymnastic skills necessary to be a competent rebaser.

Oh, and I never get stuck fixing weird issues, because it's impossible for me to generate weird issues. The worst thing I encounter is a merge conflict, and merge conflicts are the easiest possible conflicts to resolve.

Re:Git cherry-pick and revert use 3-way merge by dasil003 on Friday November 10, 2023 @03:21PM (#38224000)
Disagree pretty strongly about rebasing. If you are an experienced and full-time programmer then you should know how to rebase and merge, and understand when the time and place is for each one, and I personally always rebase my local topic branches. I understand this is a bit subjective and may depend on local context, but essentially my view is this:

When you merge, conflicts are resolved for a large number of independent commits all at once and crammed into a potentially large blackbox commit that is not directly related to either the work on main or your topic branch. When a developer makes a mistake in that merge (which happens often), it's hard to reason about their mental state and whether they fully grokked the state both both branches. Even worse is when there is a logical conflict, but the lines aren't near each other, so git just happily merges.

By contrast, when you rebase, you are essentially going through the exercise of updating your commits for the current state of the world, and each conflict is resolved locally to where it was authored, including completely removing things that no longer make sense. The result is fully bisectable, and this property can be preserved along with the ability to see original author and merge times with existing timestamps and merging to main with the -no-ff flag. Obviously this requires some expertise with git, but it's not very difficult once you get the hang of it, and in fact leads to more disciplined thinking about how your changes will roll out.

WRT to squashing, I think the value depends on the scale of the team and codebase you're working for. The larger it is, the more squashed you want things for posterity. However for small teams, as long as you keep CI building, finer grained history can be useful. This also depends on the size of your feature branches and deploy cadence, so YMMV.

Re:Git cherry-pick and revert use 3-way merge by aidos on Friday November 10, 2023 @04:03PM (#38224505)
It’s irrational, but I always stand on the sidelines of these conversations being a little bit cross that people slag off rebasing.

At some point someone pointed out that rebasing changed the history and if you change the history of a public branch everyone was going to have a bad time. That got turned into “rebasing is bad” and now people keep rolling it out the same old trope. It’s a tool, and a good one for managing your work. If you don’t know how to use it then you’re doing yourself a disservice.

In terms of squashing, my rule is that I squint at the PR and if the commits are going to have value in a years time then I’ll merge commit otherwise I’ll squash. In 10 years of running my codebase I’ve never blamed and thought to myself “I wish there were more fine grained commits here” so it’s obviously working fine for me.

As you say, do what works for your team but make sure you understand the tools and what they provide for you.

Re:Git cherry-pick and revert use 3-way merge by heavenlyblue on Saturday November 11, 2023 @12:35PM (#38232410)
You can rebase while keeping all of the commits in your feature branch, you will just be asked to resolve conflicts for each one of them (if any).

I don't understand what the fuss is all about.

Re:Git cherry-pick and revert use 3-way merge by IlliOnato on Saturday November 11, 2023 @03:09PM (#38234015)
I think squashing or not squashing really depends on the quality of individual commits in the topic branch.

Myself, I almost always "edit the history" of my topic branch before merging. Often enough some interactive rebasing and occasional commit-splitting is employed. I do it to turn my topic branch into a series of clean logical commits, so that each one can easily be reviewed on its own. I would not want to loose such history, so I avoid squashing unless it's mandated.

When people don't have time\energy\skills\inclination for this kind of clean-up and their topic branch commits are a mess of random things reflecting their actual development process -- including errors, fixing these errors, random experimentation, etc., there is not much value in keeping such history, and squashing often produces and cleaner and easier to review commit.

However, for me the end result of a development cycle is not just a good state of the code (functional, tested, documented), but a good history of that code too: reviewable and bisect-able. Git provides tools to maintain such manageable history, but does not do it for you.

Re:Git cherry-pick and revert use 3-way merge by happytoexplain on Friday November 10, 2023 @02:57PM (#38223712)
I have to disagree with #4. If your branch's commit history is unnecessarily fragmented because it evolved as you worked, then yes, squash those commits - but "one commit per feature" as a rule doesn't work for many kinds of features which by their nature can be made only so "tight and focused". Sometimes it's an undue burden to have to view a feature's patch as a single diff, even if the feature is functionally as atomic as it can get.
Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @03:09PM (#38223864)
First, my feature branch commit history is always fragmented and unusable. I commit extremely liberally, every few minutes if I'm actively making changes. Why not? If I always squash before I merge, nobody seems my junk, and being able to back up a few steps after coding down the wrong path is super useful.

Second, if I do create a feature that should be multiple commits for clarity, I find it a lot easier to do all my work in a scratch feature branch, get everything working correctly, tests passing, etc., then do a `git merge --no-commit` onto my final feature branch for the PR. At that point I can break my work up into a few different commits, as necessary.

Re:Git cherry-pick and revert use 3-way merge by yencabulator on Friday November 10, 2023 @03:39PM (#38224220)
Maybe consider you're just a messy person, and don't extrapolate from that to "if rebasing is a normal part of your workflow, you're using git wrong".

For me, rebasing is the means by which I keep my development branch neat and tidy. Each commit changes one thing. It's easy to see what's "prep work" and what's the ultimate change of functionality. Git bisect can find the commit that caused a bug. And so on.

I view squash merges largely used as a crutch to hide bad habits. I find the resulting history from lots of squash merges is not useful in figuring out why a change was made; by that time the specific change is part of a big ball of mud with a very vague high-level goal.

Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @04:01PM (#38224469)
> Maybe consider you're just a messy person, and don't extrapolate from that to "if rebasing is a normal part of your workflow, you're using git wrong".

Possibly? But I also find it hard to believe that there's this many developers out there typing out perfect code on the first try with no mistakes or false starts.

> I find the resulting history from lots of squash merges is not useful in figuring out why a change was made; by that time the specific change is part of a big ball of mud with a very vague high-level goal.

That's the developer's fault though. If someone is lazy enough to squash-merge a big ball of mud onto the main branch, they would also be lazy enough to generate a commit history filled with barely working and unclear commits, or incompetent enough to completely hose themselves with a rebase.

At the end of the day, I'm generating the same set of small, atomic commits as you. The difference is that I don't spend any time worrying about what the commit history looks like while I'm coding. It's only at the last second before sharing a PR that I consider it. 95% of the time I've got a tight one-commit PR containing a little code, a few docs, a few tests. Sometimes I break my changes into separate PRs, very rarely I'll add multiple commits to a single PR, but those aren't my default workflows.

Re:Git cherry-pick and revert use 3-way merge by jimbobimbo on Friday November 10, 2023 @04:09PM (#38224568)
"Messy" and "tidy" is not something I'd expect to see in the conversation about private branches.

Version control in the private branch is a tool which helps me maintain state and go back and forward in time. It looks exactly like I thought about the code or work I needed to do at some point in time. It is not messy or tidy, it's just what it is. I don't have to please anyone's sensibilities when I'm in the private branch.

The merge into main is squashed and atomic, of course.

Re:Git cherry-pick and revert use 3-way merge by yencabulator on Friday November 10, 2023 @05:38PM (#38225521)
> But I also find it hard to believe that there's this many developers out there typing out perfect code on the first try with no mistakes or false starts.

Cleaning up one's own mess is what git rebase -i is for.

Re:Git cherry-pick and revert use 3-way merge by mbork_pl on Friday November 10, 2023 @03:01PM (#38223765)
I have never understood the "squash a branch to one commit before merging" attitude. I prefer small commits, both during code review and later, during looking into repo history (which is needed once in a while) or bisecting.

Also, technically, "squashing to one commit before merging" is rebasing, so apparently rebasing is a normal part of your workflow, too.

That said, our discussion only confirms that Git is pretty versatile and flexible. (Of course, the UI is terrible, but Magit helps a lot in that department.)

Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @03:17PM (#38223966)
> I have never understood the "squash a branch to one commit before merging" attitude. I prefer small commits, both during code review and later, during looking into repo history (which is needed once in a while) or bisecting.

I 100% agree. You don't have to make massive feature branches for your changes. If your feature branch is huge, it's a sign that you probably should be breaking your work up into smaller chunks.

> Also, technically, "squashing to one commit before merging" is rebasing, so apparently rebasing is a normal part of your workflow, too.

Yeah, I know ¯\_(ツ)_/¯

This gets brought up every time I have this discussion, and it's correct, but also a little misleading. Squash-merges are never going to be any more challenging to make than regular old merges. Rebase conflicts are where people run into real trouble.

Re:Git cherry-pick and revert use 3-way merge by yencabulator on Friday November 10, 2023 @03:41PM (#38224240)
[delayed]
Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @03:47PM (#38224302)
That's true, but merge conflicts are the easiest possible conflicts to resolve. Main branch and my branch both changed this line, no matter what, you must resolve that conflict.

Rebase conflicts might be just as easy or they might be an order of magnitude more complex and confusing. They'll never be easier than a merge conflict.

Re:Git cherry-pick and revert use 3-way merge by ChrisSD on Friday November 10, 2023 @04:25PM (#38224752)
I'm not sure I understand. Why would rebasing ever be more complicated? They're the same thing at the end of the day, no? Main branch and my branch both changed this line no matter what, you must resolve that conflict.
Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @04:58PM (#38225084)
Image you have a feature branch where you changed a block of code two times and are rebasing that branch onto main, where that block of code was also changed. During the rebase, you'll need to resolve that conflict twice. If you had merged main into your branch you would only have to resolve it once.

No matter how complex your branch gets or how much it diverges from main, you'll only ever need to resolve a conflict once. For rebasing you may need to resolve conflicts multiple times for changes you may have made hours/days/weeks ago.

If you're strict about rebasing and keeping your working branch clean and remembering what you were doing in that commit from three weeks ago the rebase process might not be that bad, but it can never be easier than a merge.

Rebase conflicts are like short-selling stocks; there's no upper limit on how bad it can get.

Re:Git cherry-pick and revert use 3-way merge by IlliOnato on Saturday November 11, 2023 @03:22PM (#38234140)
It seems there are 2 types of rebases involved here.

I never rebase a feature branch on top of the changes in the main branch. The branching point of a branch always stays the same.

But I use rebase for editing commits in my feature branch all the time, i.e. I rebase interactively one feature-branch commit on top of another feature-branch commit.

Re:Git cherry-pick and revert use 3-way merge by seba_dos1 on Saturday November 11, 2023 @01:50AM (#38228125)
Merge conflicts are often much harder to resolve than rebase conflicts. I often find myself going through individual commits in order to understand how to resolve such conflict anyway, which rebase does for me automatically (especially with diff3), providing enough context right away. In many cases, it's much simpler to follow the changes step-by-step.

Sometimes I even ended up resolving merge conflicts by rebasing and then rewinding back and committing the resulting state as a merge. It was easier that way.

Sure, there are cases where rebasing naively gets super annoying as each commit conflicts on cherry picking, but you can end up with pretty nightmarish merges just as well if you're not careful.

Re:Git cherry-pick and revert use 3-way merge by samus on Friday November 10, 2023 @03:55PM (#38224393)
Rebase conflicts are just the dual to merge conflicts. Instead of applying the end result of my work, I apply my work step by step. I don't see the final big picture, but move towards it.

This is of course a nightmare if the branch is long-running, because then it becomes easy to forget what I was thinking and doing when making the first few commits. But this is where organizing and crafting good commits (good commit messages are just the beginning), and frequent rebases on top of the source branch help.

Frequent rebasing also makes it possible to start working on a feature that depends on somebody else's in-progress work. It works best if the other person also frequently commits, publishes and rebases their work to keep the trouble with rebasing to a minimum.

Git serves many kinds of dealing with concurrent activities in other branches well. Maybe too well :-)

Re:Git cherry-pick and revert use 3-way merge by jrockway on Friday November 10, 2023 @03:19PM (#38223988)
Because the evolution of the topic branch is largely irrelevant to future analysis. If you write tests firsts, then you have a commit in the repo where tests can never pass; fun for bisecting. If you write the code first, then you have a commit with a bunch of dead code in it because the tests appear in the next commit.

I would say that my average topic branch looks like: "edit the protos and rebuild them", "add feature", "add tests", "add CLI", "fix lint". There is no value in checking that into the main history in that order; the first commit doesn't compile because the API servers don't implement the API server interface anymore. The second commit is a bunch of dead code. The third commit is somewhat useful except the code doesn't follow the style guide and static checks fail. The fourth commit at least un-deadifies the code; something calls it now. The last commit is something that can be checked in. I really see no value in preserving that history; the set of commits makes more sense as one commit.

You could redo this to "edit the protos, and rebuild them, and add stubs that return Unimplemented", "add feature and tests to replace the stubs that return Unimplemented", "add CLI binding", which could stand alone if you felt like it. It is more work for the author, though, because you had to write code that you are literally going to delete in the next commit. Why bother? Personally, when I'm reviewing code, the more context I have, the better. If there is a design doc for the feature and someone sends me just the protos that they describe in the design doc and the design doc is actually good, fine. I got the context and this just speeds things along. If there isn't, how do I know this API is even right? If I see how it's implemented across the client and the server, then it's easy to make suggestions for improvement. If it's just the API, it sounds right, but I don't know if you can actually implement that feature until I see it done, right? More importantly, how do I know this set of 3 patches isn't going to be followed by "oops, forgot this in the protos", "oops, forgot this in the server", "oops, forgot this in the tests", and "oops, forgot this in the CLI"? Now we've turned one commit into 8. Why? Because "small PRs are good"? Prove it. I had to do 8 code reviews instead of 1. You had to write code that you're literally deleting the commit after you added it. Why bother? I'm unconvinced that the history of "do it", "oops fix it" is more useful when doing archaeology than "do it right the first time".

Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @03:44PM (#38224262)
> You had to write code that you're literally deleting the commit after you added it. Why bother? I'm unconvinced that the history of "do it", "oops fix it" is more useful when doing archaeology than "do it right the first time".

My suspicion is that people are using rebasing to clean up their prior commits as they go instead of just getting everything working and then breaking the work up into commits, if needed. And this is also how they work themselves into horrible knots trying to figure out what they did to hose their branch.

Re:Git cherry-pick and revert use 3-way merge by yencabulator on Friday November 10, 2023 @03:44PM (#38224263)
This is a problem with how you work. You're not making atomic commits, you're spreading a logical change over multiple commits. Non-atomic commits are just bad.

Here's an example of a correct way to use small commits: add a new function+unit tests into a library in one commit, then switch existing code to use it in one or more follow-up commits.

Re:Git cherry-pick and revert use 3-way merge by jkubicek on Friday November 10, 2023 @04:10PM (#38224590)
How do you correct the following commit history?

    1. New function & unit tests
    2. Switch existing code to use new function
    3. Whoops, new function broke some existing code
    4. I guess I need to fix tests too
    5. Spelin mistakes
There's at least three ways to clean this up:

First, you could maybe do an interactive rebase, move commits 3-5 up and squash into commit 1. Maybe easy, or maybe you hose yourself and break everything.

Second, just squash it into a single commit. No possible way to screw this up, works every time.

Third, do a `merge --no-commit`, commit your changes and your tests, commit the changes to the existing library to use your new method. Easy and straightforward and keeps the two change separate if that's something you're into.

Of the three choices, rebasing is the worst!

Re:Git cherry-pick and revert use 3-way merge by yencabulator on Friday November 10, 2023 @05:36PM (#38225506)

    1. git commit lib/bar.x
    2. git commit foo.x
    3. git commit --fixup sha_of_1
    4. git commit --fixup sha_of_1
    5. git commit --fixup whichever_is_appropriate
    n. git rebase -i where_ever_you_branched_off
You call git rebase "worst" purely because you don't currently see the value in granular commit history. If and when you do, git rebase is just the tool for the job.

`git merge --no-commit` only works for trivial things, real world is a lot messier than that. But perhaps you haven't needed to deal with the complex changes. For an idea of what complex changes look like, skim some linux kernel VFS or MM refactoring patch sets.

Re:Git cherry-pick and revert use 3-way merge by vvpan on Friday November 10, 2023 @03:35PM (#38224150)
I pretty much only work in the context of teams. My own git skills matter quiet little - there are many other people of various skill levels who are working on the same codebase. Junior developers get very easily confused by git.