Let me start this thread by confessing that I am not a git guru or anything. Since 2015, I knew that there is this thing called 'git' but all I thought was that git is nothing but a sophisticated dropbox for saving your code to the cloud (boii, was I wrong?)
I actively started using git since 2018 and I honestly say that it changed my life. Before getting myself familiarized with git bash, I used to manage my repositories using github desktop and then I switched to GitKraken. Later when I became comfortable with handling git operations from the command line, I kinda ditched both the UI clients.
In my workplace, my day to day job involves writing a bunch of bash scripts to automate certain aspects of our application hosted in a linux server and Node JS scripts also play an important role in these automation solutions. Our team uses git from the command line, but things started to get a bit tedious when we wanted to visualize the changes we made to the code base on a regular basis.
We make a bunch of changes to the scripts on Friday and when we retouch the code on Monday, it was not that easy to check line by line code differences to find out what has changed. There are git beginners in our team who were in dire need of a GUI client to manage the git repositories residing in the linux servers. Now, these are linux servers with no luxury of accessing the GUI directly and all we have is SSH access. So there goes our chance for installing a full fledged tool like GitKraken and github desktop is not a thing for linux.
This pushed me to work on a UI client which is compatible with linux servers. When we think about an unified platform which can be accessed no matter what platform you are on, the first thing that pops atop your head is the web!
Yeah, I planned to build a web based UI client which should be sufficient for managing the local git repos and initially I planned to build it only for linux. This decision will take a sharp turn soon.
For a git UI client which needs to get changes from the target repo in real time, the frontend needs to be super dynamic and the only frontend web framework (or library, some say?) I am comfortable with is react. So I chose react as the view layer.
What better option is there to be a suitable couple for a react application, other than the one and only Node JS?
Of course, I went with Node JS for the backend. This way, I can have a code base for both the UI and backend which can be tinkered by fellow JS developers.
The UI choice was simple and straight forward. All it has to do is ,fetch some data from the server, manipulate it slightly and render some colorful UI components on the screen but the server on the other hand needs to do things like executing git commands, keeping track of the repos, listening to repo changes and a bunch of other things. Both the front end and back end uses some notable dependencies which are laid out below.
I initially looked for some suitable node libraries to work with git, but after some internal conflicts I planned to just execute git commands from node by tapping into the target system's command line. This way, I can make the server run only controlled git commands and the users can also open the scripts to make sure that the server is not running any spooky stuffs behind their backs. For this option, I went with
child_process is a simple to use option for executing system commands on the go in a single line. As my initial plan was to make a linux only platform, I even piped (|) some linux specific commands to the vanilla git commands to format the data returned by the commands.
After the initial beta of the platform, I made a decision to remove the 'linux only' agenda, as the backend does nothing other than running a bunch of git commands which can be executed on any platform provided 'git' can be tapped in from the command line. So those "custom linux commands piped to the git commands" I mentioned above, I removed those and refactored the code base to make it compatible with all platforms.
If it is node based application, then can any one imagine it without
express (or hapi or your favorite ones)?
From the beginning, I wanted to use
graphql instead of REST. This is because of its powerful querying capabilities and type safety. In some modules, the server will run a bunch of git commands, but the front end will need the results from only a couple of them. In such scenarios, graphql came to the rescue resolving the usual "under fetching" and "over fetching" problems.
Graphql cannot be used as such with node, so it is tied to
express to utilize the handy routing features provided out of the box.
You may ask, "why do you need a file based Database for an application which is just going to run some git commands within a repo?"
Well, let me answer this question. The platform includes an option to list out all the commits in the current branch and along with this, it also lets the users search for required commits based on the "hash" or "commit message". During the initial testing stages, I used some of my private repos with around 100+ commits to test the search feature (crafted using normal JS regex validations). It worked just as fine with no hiccups.
But I knew that 100+ commits is not a count at all for a git repo. So I cloned the well known 'flutter' repo from github and ran a search on it. Guess what? this repo has not 100, not 200, but a whooping 20,000+ commits. Imagine running a loop assisted regex validation on 20k entries. It will not be a walk in the park.
So I did some due diligence and planned to use a file based Database system which can provide efficient querying mechanisms to fulfill the needs. This made me choose
sqlite instead of the normal regex match which was a nightmare for huge repos
While adding a repo to the platform, a dedicated module collects all the commit logs from the repo and pushed it into the sqlite table. Later, this table will be queried to fetch the required results.
Now the commit log search problem is solved, but while reading the above use case, did you wonder how this will include new commits to the search DB?
During the initialization, if the repo had 100 commits, then all those will be inserted into the DB. After a day, if you add an additional 15 commits, how will the search DB be updated ?
This is where
chokidar hops out of the hole to help us out. If you are not familiar with this library, then it is a file system watcher which looks for file system changes and reports the same.
Now, this option is not particularly my favorite, as listening file system listeners are known to cause spikes in resource utilization. But in this case, it is a necessary evil.
Once the server is started, a listener module is spun up which will listen to the repositories which are added to the platform and if the listener notices any changes to the repos (adding a new commit will trigger a change), then immediately the new commits logs will be inserted into the search Database. This is not a super effective solution, but it solved the problem in hand.
In the starting stages of this long blog, I would have mentioned that we wanted to visualize the changes made to the repo. This includes internal source code changes as well. This means, the platform should be able to lay out line by line code difference to the user.
If it is code, then will it be interesting without the colorful syntax highlighting? The packages mentioned above are all specific to the backed but this is used by the react application to provide smooth syntax highlighting to the files stored in the repo.
The git difference results is formatted in a specific way and supplied to prismjs which will inject its custom styles to enable language specific syntax highlighting to the code view. This is not only used in the git difference view, but also in a file explorer view which lets you view your code within the platform.
- I had to improvise!
In addition to all the well known node packages mentioned above, I had to make something of my own to give something more out of the platform.
For syntax highlighting, prismjs is good but it cannot be used just like that with react applications. If you supply the programming language used in a file to prism, then it will inject the required styling based on the keywords.
I googled for a long time to look for a suitable npm package which could return the programming language used in a file by supplying the actual file itself, but all search results came back empty with no suitable solutions. So I had to improvise and cook my own package.
I created a new library which internally uses a flattened and refined version of the github linguist data set which enables github to display the languages used in a repository.
The programming language used in a file can be identified by this custom library and a custom field returned by this library will be supplied to prismjs to load the required language style set. Using this, the platform was able to display a slick file difference view with appropriate syntax highlighting
Who would have thought that the following
<span class="bg-green-400 p-3 rounded shadow curser-pointer hover:bg-green-500">Click</span> will spit out a cool rounded corner button with a hover effect without writing even a single line of CSS?
Tailwind made styling the UI components a piece of cake. If you are not a fan of polluting your
div's with multiple classed, then this is not the one for you, but for me it served the purpose.
The above are some of the notable dependency modules used in the platform
I maintain the react frontend repo and node backend repo as two separate public repos in github. The code base is not a simple one, so I decided to have separate repos for ease of maintenance.
We as young padawan JS devs and wise Jedi JS devs, will be capable of building a react + node application from the source but the same will not be the case for beginners. So I crafted a custom
github actions pipeline which runs a bunch of unit tests and bundles the react application. This is then combined with the backend modules and pushed into a separate repo. This repo contains the project releases and the repo can be cloned to kick start the application with a simple
npm start command.
The bundle is also published to npm as a complete package once the release is finalized. Github actions has helped a lot with all these integration stuffs making it look like an easy task.
Well, this is a long post... I answered "How", now let me answer Why I made a web based GUI client for git?
I have been a web enthusiast since I started to code and its been nearly 5 years. If an application is a web application, then it means that there will be no tightly coupled platform dependencies (provided it does not require any native platform support). The git UI clients I used and I am familiar with are platform specific and are install-to-use software applications. Git can be accessed easily from the command line and all I thought was, why do you need an installable software to do that? The primary driving force was to provide a UI client for linux server users which later morphed into a platform independent web application.
The whole platform is open source and any enthusiast who wants to take a look at the code or want to contribute to the project are more than welcome.
This project will stay open source with no restrictions, so