I'm using Git all the time and I love it. There was time when I was using SVN and I could definitely say that I wasn't so happy and excited. Git is a wonderful piece of software that makes writing code easier. I think most people take it for granted and don't realize how easy our life is because of this tool.
I spent couple of months building Poet and one of the problems that I tackle was the data management and transfer with the back-end. Poet allows us to have that interactive experience where we write code in the browser and we see it working instantly. This is all fine and could be seen on many places. My problem with them though is the lack of history of my changes. Especially when we are talking about technical writing. I want to develop an example and show to the reader how I progress through it. And Git perfectly fits into this idea. Imagine how I generate commits while writing the code and then those commits become part of the whole Story. This is what Poet is all about. Mocking quickly an example, explain it and then share it with others.
That is all cool but to make it persistent we have to save every single change to a database. This means constant amount of requests to an API. Such type of apps may become really expensive in terms of data transfer and storage.
So, I decided to solve the problem in a similar fashion and designed Poet with a Git like experience.
The raw interface
Let's start with the basics. Git has three states and we need to represent them in our implementation.
Commited - it means that our changes are stored to the local database.
Modified - it means that we did some changes but there are still not in the database.
Staged - it means that our changes are marked to go into the next commit.
working will represent the modified state, staging the staged state and commits will play the role of our local database.
In Git we have the concept of HEAD. That is simply said a pointer to the tip of our current branch. In our case the HEAD will point to a specific commit in the commits field. Every commit also must have an unique identifier which in Git we define as a hash. We will simplify that and will use a counter variable i. And with those two things we end up with the following data object:
save will add something to our working directory. get will return the content of the same field. add will stage our changes. In Git is possible to stage only some of the changes but here we will assume that the developer wants to stage everything. commit will get whatever is in the staging field and will form a commit which will be stored the commits map. Finally checkout will allow us to jump to specific record by getting the content of the commit and setting it back to the _working_ field so we can use get and read it.
Saving and retrieving files from the working directory
Because we took the decision to use an object as a working directory field we will use the filepath as a key and the content as a value.
Same file with the same content now exists on both places.
Commiting to our local database
There are couple of things that should happen here. The first one is to generate an unique hash for our commit. Second we should get the content of the staging area and together with the commit message store it into the commits field. We should also empty the staging area so we are in a good position for the further changes. Also to stick to what Git is doing. At the end the head should point to that new commit.
Notice how our counter i is now increased to 1 which means that the second commit will have a hash of _2. The staging is again empty and there is one commit registered. The head points to the right place as well. Let's move on with the wonderful checkout method.
To illustrate what the checkout method does we need to have at least two commits. So, let's add another file foo.js to the database and see what is the final state of the data object.
We have to again clone here because otherwise every saving to the working directory will amend the commit in commits field. With that done we are ready with our implementation. Now we are able to store information, retrieve it, create a history of the changes and travel through them. If we call git.checkout('_1') the export method shows the following:
Here is a simple example of two strings compared by diff-match-patch and how the diff looks like:
const str1 = 'Hello world';
const str2 = 'Goodbye world';
var dmp = new diff_match_patch();
var diff = dmp.diff_main(str1, str2);
// outputs: -1,Hello,1,Goodbye,0, world
I had lots of fun creating Gitfred. I would love seeing it battle tested which you can help with by doing two things - (a) use the library in some of your projects or (b) start using Poet. Feel free to post your feedback here.