Massive Git Rebase how to avoid and what to do
OK, I made a big error the other day, I’ve been using Wowchemy Academic and working with the sample site. The problem is that I wanted the latest fixes to the sample site but I had made lots of edits. Here are the big lessons on to avoid a 350 commit rebase!
- To keep something up with an upstream that is updating a lot and when you are updating a lot, you need to rebase frequently this keeps the two branches from being wildly out of date, the number of commits that need to be fixed is the sum of all fixes you’ve made on your branch and the fixes made on the master. In my case, I had 150 and the upstream had 150, so don’t be that guy!
- Here is what you need to do to get this right, you clone the repo and then. make sure to set the upstream with
git remote add upstream firstname.lastname@example.org:wowchemy/starter-academicas an example of keeping the two in sync. Then the key command is to do a
git fetch -p --alwhich fetchs and prunes the upstream and your origin of the your fork
- Finally, you can do a
git rebase upstream/main -iwhich will interactively compare all your changes to what is in upstream, hopefully there are no conflicts and then you can
git push -fto get the upstream changes first and then your changes, so it is nice and orderly.
Oh no, I have lots of rebase conflicts
Here is what can happen if you wait too long. For example, if you are 300 commits behind and there is a config files that adds a parameter for say Hugo version that goes from 0.64 to 0.73 to 0.83, then you will have a merge conflict each and every time over hundreds of merges. Argh, this s really frustrating as you have to edit each one. So if this happens to you here are some tricks:
- You have to do a git status when you get a merge conflict during a rebase, theses can number in the hundreds, if you want to edit them all, then you are manually typing a lot, the solution is a one liner that tells you the name of all the files in conflict which will have duplicates, you make them unique and feed them as parameters to vim with xarg like this,
git diff --name-only | uniq | xargs viso you don’t have to type all the names.
- Now for each file, you will get a file which has
<<<<<<for the current branch and then below the
======you will have the content that is being merged in. This will be the stuff that is coming from upstream. Most of the time you will be taking what is coming upstream, but in the case noted above where you have let lots of commits go, you actually want the content that is.
- There is also something call vimdiff where you can go through files and put and get what is changed but I couldn’t quite get it to work. The idea is that
- Finally, it is definitely tempting to just do a massive
git rebase --skipto just get to the end of it all, but I don’t really understand the semantics of this, so in a rebase, what you are doing is pushing all the commits of the remote branch into the current branch (the mergee), so skipping doesn’t seem quite right. I need to think about it.
Rebase conflicts and Git LFS
One super annoying thing is that if you are trying to rebase from a repo that doesn’t have Git LFS turned on into one that does, you are going to get these problems, where the branch you are putting into expects Git LFS pointers, but instead gets real files and you get the dreaded “Git LFS expected a pointer message”. The fix seems to be to do a git lfs uninstall, then reinstall and a pull
Case conflicts and git mv
The final problem doing this rebase is that MacOS by default is case insensitive but Linux is case sensitive, so you can have a name like
IMG1234.jpg but it really
img1234.jpg and this is lower case. When you try to change the case and push, git will say there are no changes. The fix is that
git mv understands this, so you can delete the wrong case and stick the right one in its place with
git mv IMG1234.jpg img1234.jpg and it works