1. 7
  1. 23

    If I had to give two real high-dollar items for making git branching less painful:

    • Rebase, not merge, your feature branch. Do not squash.
    • Rebase your feature branch against master/main/whatever at least every day .

    I’d also suggest knowing when to cut losses–if you have a zombie branch a few months old, take the parts that are super neat and throw away the rest.

    1. 7

      This is pretty much it. GitHub exacerbates this problem because its pull request workflow based on branches is broken. What you really want is a pull request on a cherry picked range of commits onto another branch. That way you can have commits from your branch flowing into main while you continue to develop along your branch.

      1. 2

        Indeed, any of these advices would have solved the problem. Instead, I ended up spending almost a month just trying to solve the mess.

        1. 3

          My condolences, friend. Happens to all of us eventually.

        2. 2

          Some time ago I setup an alias for git cherry-pull <mainline>, which (rebase style) lets you assign each commit to a new branch, pushes them, then opens the ‘new pull request’ page for each branch.

          I should dust off the code and publish it.

          [edit] A colleague pointed out that a script needs to be ~perfect because it obscures the details of the git operations, while doing it longhand keeps them front of mind. Might explain why I rarely use it anymore.

          1. 1

            Sounds nifty! Is the interface like rebase interactive? And I’d recommend a new name, because google autocorrupts it to cherry-pick.

            1. 1

              I’ve written similar scripts. The two things that I kept hitting are:

              1. Getting this to play nicely with rebase -i and merging/splitting commits is…not trivial.
              2. Automation in PRs is often triggered by the main/master branch, so all but your bottom PR doesn’t get checked.
          2. 3

            It seems to be fairly unmaintained, but I really like git-imerge for long-lived branches. It does pairwise merges of each of your commits against each of the upstream ones giving you an NxM matrix of all possible merges. It builds this as a frontier from the top-left (shared parent) branch to the bottom-right (final merge). You get to resolve conflicts exactly on the pair of your commit vs theirs that introduced it. You then have the full history for bisecting if the end result doesn’t work. You can then choose one of three ways for it to clean up the resulting history:

            • The equivalent of a git merge.
            • The equivalent of a git rebase.
            • A rebase-with-history, where it gives you a rebase but also sets the parent of each of your commits such that downstream users can still merge from your branch.
            1. 1

              I’ve tried git-imerge recently, in a situation where 2 branches had diverged from hundreds of commits, but many commits were common and cherry-picked from one side to the other.

              The performance was catastrophic. After half an hour eating my cpu with absolutely no progress information, I investigated externally and saw imerge was painstakingly creating 1 tag per commit, so had created hundreds of tags and was still not finished yet. Not knowing how long it would take after or what would be its next step and how long it would take, I understood it was simply not designed for my case, so I stopped it, cleaned the mess it created and uninstalled it.

              1. 2

                The worst case for me was merging 8 months of LLVM changes into a tree that had had active development over that time. Several thousand upstream comments, over a hundred local ones. It took about two weeks of CPU time but, critically, only about half an hour of my time. Fixing the conflicts was incredibly easy because it showed me the precise pair of commits where I and upstream had modified the same things. I’d done a similar merge previously without the aid of git-imerge and it took well over a week of my time.

                In general, if I can trade my time for CPU time, I’m happy. I can trivially buy more CPU time, I can’t buy more of my time.

            2. 2

              I’d recommend squashing before the rebase simply so there’s only one commit you have to resolve conflicts on.

              But yes, rebase often. This is the way.

            3. 6

              Beware when you have conflicts in the dependency files (package.json, go.mod, requirements.txt…). Do not remove dependencies until you are sure that they are not needed at the very end of the merge. Add any dependency suggested during the conflict resolution and decide at the end if it is necessary.

              Or just create file .gitattributes with:

              your.lockfile merge=binary

              And then it will only mark a file as conflicted without putting conflict markers. It will allow you to simply your-dep-tool update to update the lock file to newest configuration. This makes working with lock files much neater and cleaner.

              1. 1

                Wow, I’ve never heard of that. That’s a great tip!

                1. 1

                  This one is a great tip! This aligns with the recommendation of regenerating files.

                2. 2

                  Let me introduce you to incremental merge for git. It helps a lot resolving merge conflicts bit by bit. And despite the name it can help with rebases, too.

                  1. 1

                    Git has exhausted its potential in my eyes. God be merciful, I’ll write my own vcs. For myself. Maybe not even going to make it publicly available, but only to those who actually get it.

                    1. 2

                      Good luck! You’ll be following in the footsteps of many dead or antiquated VCS systems, like (off the top of my head…) Fossil, BitKeeper, Mercurial, Bazaar, and Darcs.

                      1. 1

                        I sort of want https://pijul.org/ to break out and become the next big thing, but I worry it’s not better enough to overthrow the consensus choice.

                      2. 2

                        You could also write a new porcelain on top of git. git’s internals are a pretty straightforward set of primitives about a content addressable file system and commits.