git rebase (not) --interactive
tl;dr: How to build a Node.JS script to re-write history.
pre-requisites: Familiarity with git rebase --interacitve.
Once upon a time, there was a Whatsapp clone tutorial was born. Since then it has been through different incantations, but they all shared a common principle — the tutorial used git as a version control.
What’s special about this tutorial is its commits history — every commit represents a step in the tutorial; this way it’s very easy to navigate through it and reference a specific part of it.
For the end user it was all cakes and ale, but maintenance was hell. To put things simple, imagine you had the following git-log:
Step 100: description of step 100th
Step 3: description of third step
Step 2: description of second step
Step 1: description of first step
Now let’s say that you would like to remove step 2. The only solution for that would be using a git-rebase --interactive starting step 2 and save the following file:
reword xxxxxxx Step 100: description of step 100th
reword xxxxxxx Step 3: description of third step
This means that the editor process would have to be opened and closed 98 times (100 - 3 included), each time it does so we would manually have to change step n to step (n + 1). Do you understand now why it was a maintenance hell? I’ll save the explanation for myself.
The obvious question is — what if a script could do that for me? Followed by — how can I implement such a script?
Following that, I have wandered across git’s documentation and Stack Overflow and have found an answer. Here’s the method which starts the editing process written in git-rebase--interactive.sh, a file in git’s implementation:
As you can see (or not), git looks for the editor’s file path in a global var named
GIT_SEQUENCE_EDITOR and executes it with all the given arguments. Without getting into more of the implementation, knowing
vim which are the most commonly used git-editors, the first argument that their process accepts the edited file’s path, which makes total sense.
BUT! Why does the
GIT_SEQUENCE_EDITOR environment variable has to reference an actual text editor? What if we set that to reference Node.JS’s executable? Aha! JACKPOT!
Now, hypothetically instead of opening
vim and editing the file manually we can run whatever manipulation we want on the file using a script and then once the process exists with no errors (code 0) git will just proceed with the rebase as usual.
Using this principle, here’s a cool script that will remove a range of commits from the middle of the commits-stack:
Using the code snippet above we can take an initial step towards solving the problem presented at the beginning of this article by simply running
$ git-remove.js <anchor> <amount> [where
anchor represents a git object and
amount represents the amount of commits that we would like to remove].
Sure, we still need to figure out which step we would like to remove by its index, and we need to take care of automatic rewording, but at least now you have the idea behind such method where you can solve problems like these as well as far more complex ones, with a little bit of creativity.