Rebasing tips when collaborating on feature branches
Disclosure: I began writing this nearly 4 years ago, and since then have fallen off the best practices of Git and what not. Take with a grain of salt. This might not be so relevant anymore.
If you’re a fan of rebasing your Git commits—interactive rebases to be specific— then you know it can get a bit tricky to keep everything lined up when working on a branch with other team members. If rebasing is your jam, keep reading!
Why rebase (in brief)?
My biggest argument for rebasing is this: real life is messy. And work, being part of real life, can get messy too. Committing that mess may mean that your team, clients, or even just your future self will have a hard time following the history of project. The Git log can provide clarity, but only if it the log itself is clear.
But really… let’s avoid a flame war…
This really isn’t an article about convincing you to rebase. This is geared towards those who already do this, and want to know how to make the most of it when working in a team. And if you’re on the fence, maybe this article will tip the scales in rebasing’s favour.
Managing rebases across even just two developers working on the same branch, though, can result in some unnecessary headaches. So here are some simple techniques I’ve used in the past.
Temp commits
A temp commit is usually just a stop gap to get someone else some code, or push up your work before you head home for the day. I admit it, temp commits are ugly .But that’s real life, and sometimes it has to happen.
Flagging temp commits allows you to identify them easily for future cleanup. Otherwise what might be a messy commit can easily make through when your feature branch is merged.
So what to do? Easy! It’s not revolutionary, but I like to mark these commits with a [t] to give me and others a heads up that this commit will likely change.
[t] We needed a nasty prototype of the new user endpoint (use real logging instead of console.log + remove lint).
Give a description of the commit as normal, and even why it’s a temp commit so you can figure what you need to address later.
Squashing
Picture this. You’re working on the kenny-logins branch for your fancy login screen and you’re at the stage of styling the form (groan). Its not a complex form, so a few CSS selectors will do. You commit your work and continue…
The login form should be styled for maximum logging-in-ness.
And then you move on to adding a legal disclaimer to the form because it seemed like a good idea at the time. You commit that…
We should pretend we know what we're doing by having a legal disclaimer on our login form.
That’s when you notice you used the wrong border colour on those damn form fields. Ugh. Should I go and edit that previous commit? Yah… I suppose I could. Or better yet, I know I can squash a commit into that first one with an interactive rebase. So you make the small change to your border-color and commit that. Just like the temp commits, mark these as a heads up to yourself and others that this commit is bound to get squashed.
[s] Login form style tweak.
Later, when you want to squash the commits, you know to squash that into your initial form styling. Huzzah!
But oh the conflicts!
The messy thing about rebasing when you’re collaborating with others is the fallout from force-pushing to a branch after performing an interactive rebase and then the conflicts that arise when other collaborator’s local branches no longer match the newly rebased remote branch. (egads — say that 5 times fast)
Step 1: Communicate. Give people a heads up about incoming rebases. Let them commit code they might be working on before you move forward. We call this being a decent human being. With everyone’s go-ahead, force push your rebased branch.
Step 2: Have others use pull with rebase OR reset your branch to the remote branch. For example:
git pull --rebase origin kenny-logins
The rebase flag basically prevents ugly merge commits, which would ruin your nice commit log.
OR … assuming you have stashed any in-progress work, try:
git reset --hard origin/kenny-logins
This makes your local branch match the newly pushed remote branch. Then unstash your work, and go from there.
Step 3: Use --force-with-lease
rather than just --force
for a new lease on life… er… rebasing. --force-with-lease
makes a decent attempt at checking if someone may have made changes to the remote branch before you blow it all away with a force push. It’s a bunch more to type, but it can save you from headaches.
Step 4: Despite your best efforts, still acknowledge the fact you might be creating conflicts, so work with your team to resolve them. Together.
Dev pairing is not just for writing code. Pair on big rebases, merges and conflict handling so that you don’t inadvertently lose someone’s work.
Knowing when to rebase
Make sure your commit log is cleaned up frequently enough so you don’t have stale temp and squash commits that have lost any relavant context, making it harder to rebase. And remember if you are only rebasing your own commits that you haven’t even pushed up to the remote branch yet, you can do so without making it a problem for your fellow team members. So work locally, do your commit cleanup and then push
normally.
If you are working with other team members, be sure to communicate to keep them informed of pending force pushes.
Take a sane approach to rebasing. Take a balanced approach so you aren’t creating unnecessary work. And don’t let those branches get out of control, keeping their scope in check means less chance of creating a mess.
So there you have it. Some tips on using rebase in a collaborative setting without everyone wanting to revert back to the days of FTP.