How to add an experimental feature to your code, without having it in your main version control
I've been infatuated with loggers for development recently. The idea is, I can put log statements in various parts of my application, and it will log the relevant data to a file. This means no having to set breakpoints and enable/disable my debugger, no scrolling back up the developer terminal. I just leave it running and then when I need it I find the relevant log.
Useful as this is for me, it's not practical for me to convince the rest of the organisation to adopt this pattern formally, not in the pull request I'm working on at least. Besides, I'm still working out what the interfaces should be, and interfaces have this nasty habit of being difficult to change once they're being used.
But I still like the tool and I still want to keep using it.
Here's how I keep it around, without having to commit it to version control proper.
The strategy is that we create one shell alias to cherry-pick a commit in to add the feature, and then another alias to rebase that single commit out.
A---B (master)
\
C---D---E---F---G (my-branch)
^ ^
the experimental feature commits
Let's assume commits D and F are ones where I implement the feature.
1. Create a single commit that removes the feature
We'll call this commit Q.
A---B (master)
\
C---D---E---F---G---Q (my-branch)
^
removes the feature
2. Revert that commit
git revert Q
A---B (master)
\
C---D---E---F---G---Q---R (my-branch)
^ ^
removes the feature reverts the removal
We'll call this commit R.
Essentially this is a commit that will add the feature back in.
The idea is, when we later want to add the feature in, we'll cherry-pick a commit like this to add it in.
However, because we're likely squash merging to master, the commit Q that the revert commit R is reverting, will no longer exist.
ie. after we merge to master, the git tree looks like this:
A---B---X (master)
Where X is all
C---D---E---F---G---Q
squashed into one commit.
3. Transform commit R to a regular commit
git reset HEAD~1
git commit
A---B (master)
\
C---D---E---F---G---Q---S (my-branch)
^ ^
removes the feature Adds the feature back in
We'll call this commit S.
Now, we can selectively call
git cherry-pick S
to have our feature apply.
4. Create shell aliases to add and remove the commits
Add these to your .bashrc
or .zshrc
etc.
feature_commit=S
feature_commit_message=some-unique-commit-message-here
alias addfeature="git cherry-pick $feature_commit && git commit --amend -m $feature_commit_message"
alias removefeature='added_commit_sha=$(git log --grep="$feature_commit_message" --pretty=format:%h); git rebase --onto "$added_commit_sha"^ "$added_commit_sha"'
How this works:
Adding the feature is pretty straightfoward. The content of commit S exists in our local git object store, and git can retrieve it via the SHA S. When we apply the commit, git will actually give it a different SHA. That's why need to also rename the commit message, so we can later find this applied commit by commit message, not by SHA.
Removing the commit now involves searching the git log for our unique commit message, and then we do a rebase to remove the commit.
We don't want to do a revert, because we don't want to pollute our git history with a whole bunch of applying the feature and removing it. It'll be squashed away anyway, but still.
5. Tidy up
Manually remove commit S
git rebase -i HEAD~1
6. Create a remote branch containing commit S
The solution works by commit content existing in your local git object store.
If your laptop died and you hadn't pushed it remotely, you'll have lost it.
7. You're ready to go!
Apply the feature with
addfeature
Remove the feature with
removefeature
Note that you may need to deal with merge conflicts when you apply the feature, but that's pretty to be expected.
8. Updating the feature later
Apply the feature
addfeature
Make your changes.
Amend the commit to include the new changes.
git add -A
git commit --amend
Update your shell rc script to use this new commit SHA.
Let me know if you find this helpful!
Questions? Comments? Criticisms? Get in the comments! 👇
Spotted an error? Edit this page with Github