Jan 24, 2021 4 min read

How I keep my custom Ghost Casper theme in sync with the original code

How I keep two versions of my blog's theme up to date when the Office source theme Casper gets an update from its maintainers, by using git rebase.

How I keep my custom Ghost Casper theme in sync with the original code

My blog runs on the Ghost blogging software, and Ghost comes with a standard theme called Casper. I provide an alternate version of Casper with tweaks so it takes more screen width on larger screens, and it's appropriately called CasperWide. The theme I run on this blog is in it's turn also a modified version of CasperWide, which I call internally CasperForYr.
Casper is open source and gets regular updates, and I'd like those updates to trickle down to CasperWide and CasperForYr as well. I didn't want to spend too much time doing file diffs and figuring out the changes, so I learned some Git magic with forking and rebasing. With this process theme updates take mere minutes!

The idea

First of all, there is the main Casper GitHub repository where all development of Casper is happening. I forked Casper, and created a new repository for CasperWide. In there I made some changes, and then I forked this one again into it's own repository for CasperForYr, and in there I made some additional changes.
In a visual representation, it looks something like this:

From time to time, Casper gets updates and the visualization will start to look a bit like this:

New updates are made to Casper, which aren't in CasperWide or CasperForYr anymore. I'd like to apply the updates I made in CasperWide on top of the new updates in Casper, or you could say that I want to rebase my changes of CasperWide on the latest repository of Casper. The end result should look something like this:

And rebasing is exactly what I am going to do, with the Git Rebase command. It's a bit scary at first. and the docs make it sound even more scary. I hope that with my explanation above it "clicked" what rebasing actually is: applying your changes on a different point in time of the source repository (or branch)!

The preparation

Now I have my three repositories on GitHub:

  • original Casper
  • forked CasperWide (from Casper)
  • forked CasperForYr (from CasperWide)

I clone CasperWide and CasperForYr locally on my computer, so I have all their files on my hard drive. They are connected to their respective repositories on my GitHub account, which are called remotes. A repository can have multiple remotes, and the default remote is most often called origin. I use the multiple remotes feature to add another remote, called upstream. Upstream is generally used as the name of the repository of which you forked your own repository.

After adding the upstream remotes, this is the setup:

  • CasperWide
    • origin: my GitHub repository for CasperWide
    • upstream: the GitHub repository for Casper
  • CasperForYr
    • origin: my GitHub repository for CasperForYr
    • upstream: the GitHub repository for CasperWide

Now I am ready to get the changes of the upstream remote into my own repository, let the rebasing begin!

The execution

First step is to get CasperWide updated. Let's open a command prompt window with Git command line tools installed, and navigate to the directory containing CasperWide repository.

The first command will make Git aware of everything that has happened in the upstream repository since the last time that I executed the command:

git fetch upstream

Let's make sure I am working in the master branch of the local repository:

git checkout master

Now I am taking the changes I made in our repository and base them on top of the latest changes in the master branch of the upstream repository:

git rebase upstream/master

Next commands are very specific for the Casper theme. I will use the Yarn package manager to install new or updated packages, and then build and zip the new version of the theme:

yarn install
yarn zip

Lastly, I push the updated theme to the master branch of the origin remote (== my GitHub repository). It has to be with the --force modifier: due to rebasing the local and origin repository have a different configuration which can not easily get joined. With --force I override the origin with the local version of the repository.

git push origin master --force

In a visual way, it now looks something like this:

It isn't quite right yet. You can see that CasperWide is now forked after the new changes in Casper, but CasperForYr hasn't been updated yet. Due to the force push, the Git history point where CasperForYr was forked from CasperWide doesn't even exist anymore. This of course has no impact on CasperForYr because of how Git works.
Time to do the same thing for CasperForYr and get it rebased on top of the latest CasperWide repository. In the command prompt, navigate to the folder with the repository of CasperForYr and the rest of the steps are similar as before:

git fetch upstream
git checkout master
git rebase upstream/master
yarn install
yarn zip
git push origin master --force

And done! The end result looks like this and all the changes from Casper are now also included in CasperWide and CasperForYr:

Conclusion

Rebasing freaked me out in the beginning, but now I regularly use it to keep my forks updated with changes that are happening in the source repository. I explained it for my blog theme, but the same principle applies when I

  • fork PnP Framework to do a Pull Request
  • fork CLI for Microsoft 365 to do a Pull Request
  • fork other open source repositories to contribute

while I am working on that new Pull Request, the original repository might get many changes which I want to incorporate in my ongoing work. Git (rebase) makes it easy!

Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Yannick Reekmans.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.