DEV Community

Maria Campbell
Maria Campbell

Posted on • Updated on

Deploying to gh pages with git subtree

Git Version Control

So I just completed a todo app the other day using React. This was the second time I followed the course "Build Your First Production Quality React Application" on The first time I followed the course exactly, using "create-react-app". However, I kept on encountering errors in the console. Probably some of them were due to errors on my part, but some definitely related to create-react-app. And the thing is, I wasn't able to figure out what the issue was without running the eject command to see what was under the hood. And if I had done that, there was no going back! Well, that just didn't sit well with me. I decided to learn more about Webpack 2+ so that I could start using the new features it had to offer to create my own React workflow, thereby only adding features that I needed. I love creating my own automation processes for my workflows. I have worked hard at creating seamless processes for regular ES6+ JavaScript applications using Gulp, templating engines, and Webpack. Call me nerdy, but I find it a lot of fun and extremely useful! I am yet to take a deep dive into a more complex workflow for Hugo, but that will eventually happen as well.

While on this journey during the advent of Webpack 2+, I came across a great course called "Webpack 2: The Complete Developer's Guide" by Stephen Grider on Udemy. It didn't cover EVERYTHING, but it definitely provides a solid foundation for getting to know Webpack 2's new features. These changes were of course in response to the great overhaul that took place in JavaScript with ES6+. I was a bit skeptical at first, because I thought that nothing could replace the modularity of the Gulp workflow. But, after getting to know Webpack 2 better, and getting to know React a bit, I came to realize that a Webpack 2+ workflow was much better suited to React than a Gulp workflow. Developers such as Stephen Grider used to use Gulp with React, and switched over to exclusively using Webpack when version 2 was introduced. That says something!

So I as I got to know the new Webpack and React better, I was introduced to new Git commands as well. I was introduced to a different and more efficient way of deploying to Github's gh-pages, which was awesome!

When I had first started using gh-pages a few years ago, I would create a gh-pages branch, checkout into it, remove the files and folders I didn't need or that were preventing successful deployment of the project, and then push the project to the remote gh-pages branch. Each time I would make a change in my master branch, I would go into the gh-pages branch, do a git rebase master to incorporate changes made in master into gh-pages, and then push those changes to the remote gh-pages branch. Then when I started creating much more complex JavaScript applications, I found this approach to be cumbersome and a time waster. I looked for alternatives. That's how I got introduced to Gulp. When I mastered the Gulp workflow using Webpack was also when I decided to take on React. This was around the time when Webpack 2 was released. Using Webpack without Gulp meant tweaking my deployment to gh-pages. With Gulp, I used the npm package gulp-gh-pages, and created a Gulp deploy task with deploy.js. But when I started using Webpack without Gulp for my React projects, I had to revisit my approach.

After much research and learning, I came across "git subtree". I have to say, I am really loving it. And it's the fastest deployment process to gh-pages I have come across so far!

This is the way it works:

• First complete your project so that it's production ready for deployment to your gh-pages site.

• Next, run the command git checkout -b gh-pages. This will create a new branch gh-pages and check you out to the new gh-pages branch with a single command.

• You need to make sure that you push an empty branch to your remote gh-pages branch. To achieve that, run the git rm -rf . command. rm means remove, and r stands for recursive. f stands for force. And . means everything in root. In other words, all your folders in your project and all the files within those folders. Sometimes rm -r just doesn't cut it, so you have to run rm -rf. rm -rf . gets rid of everything in a single command.

• Next you have to stage and then commit those changes. You can stage and commit along with a commit message all in one command:

git commit -am "First commit to gh-pages branch"

The a in am is short for git add, which stages your changes, and the m is short for git commit -m. Also, make sure that you remember to have open and closing quotes for your commit message, otherwise you will be held hostage in the Terminal window. If by any chance that does happen, you can close your instance of the Terminal window with the command ctrl + c on your keyboard. It exits the prompt > that appears when you haven't added a closing quote. However, Bash/zsh does allow you to enter the closing quote after the > prompt. Then hit return. To learn more about exiting your git commit message, please visit this StackOverflow thread: How do I exit my git commit message?.

• Now you are ready to push these changes to your remote gh-pages branch. You can do so with git push origin gh-pages.

• Next we need to establish our git subtree in order for the process to work. We first have to go back into the master branch. We do that by running the command git checkout master. The great thing about git subtree as with gulp-gh-pages, is we don't have to be in the gh-pages branch in order to deploy to remote! Cool, right? And a big time saver. So our crucial git command which we run next is:

git push origin git subtree split --prefix dist gh-pages:gh-pages --force

(A backtick is needed before git subtree and after dist gh-pages. Due to markdown, it does not show up here. Please refer to the related articles below for further clarification if necessary.)

Now what does this all mean? First of all, a git subtree allows you to insert any repository as a subdirectory of another one. It allows sub-projects to be included within a subdirectory of the main project, optionally including the subproject's entire history. In our case here, the subdirectory is the dist folder being pushed from the master branch to the remote gh-pages branch. The subprojects are the files within the dist folder. A subtree is simply a subdirectory that can be committed to, branched, and merged along with your project any way you want. That being said, let's look at the rest of the command. We are creating a git subtree out of our dist folder located in the root of our project, and --split does exactly what it sounds like. It is splitting away dist from the rest of the project transforming it into a subdirectory. --prefix dist means that you are signaling that dist is the directory in your project that has been selected as the folder to be made into the subdirectory that is being pushed to the remote gh-pages branch. And only what is contained in that subdirectory will be pushed to gh-pages. :gh-pages --force means that you are forcing the push of the gh-pages branch to the remote gh-pages branch at origin.

Since you are likely to make changes to your project in the future and don't want to continuously write a long command like git subtree push --prefix dist origin gh-pages, you can add a local script in your package.json. I created the following:

"deploy": "npm run build && git subtree push --prefix dist origin gh-pages"

I took it up a notch more. I combined my build script with my deploy script. That way, whenever I make changes to my project, I first run the build command which entails deleting the previous dist build, replacing it with the current build, and then pushing it to the remote gh-pages branch. This ensures that your build is up to date with your latest changes in your project.

So not only has my coding evolved over time, but my devops skills have evolved as well. It reflects the need for greater workflow efficiency with more complex applications.

However, I cannot stress enough the need to understand every single aspect of commands that you implement. You need to know what you are doing, and not blindly execute commands focusing on the end goal and ignoring the process. Otherwise, you will not grow as a developer! I also can't emphasize enough how important it is to master Git AND to master the Command Line in Terminal. Git for the distributed version control, and Command Line so that you never have to leave the Terminal window. A great time saver. Lastly, practice makes perfect! Or at least ... nearly so!

Happy coding!

Related Links:

One line deployment of your site to gh-pages

The power of Git subtree

Webpack 2: The Complete Developer's Guide

Top comments (7)

maxwell_dev profile image
Max Antonucci

I've recently found out about this trick and it's helped me a lot as well! For one repo it makes it easy to save all our source files into a separate branch as a ruby gem, separate from everything else.

One obstacle I've encountered is that using subtree this way only works for files not being ignored by .gitignore. So using it with dist/ files I usually ignore (since they're just generated all the time) has made it hard to push it to gh-pages for that same repo. Have you found a way around this or just removed these areas from .gitignore?

letsbsocial1 profile image
Maria Campbell • Edited

Hi Max, I don't ignore my dist. In fact, you are right. It can't be in the .gitignore, otherwise the command won't work. Can I ask what kind of project are you trying to use this for? Is it a project using a static website generator like jekyll, or is it a rails project? I use a commands that are similar in concept for my hugo generated site that might help you determine which way to go. There, I push the contents of my public directory to gh-pages, but public is in .gitignore. Here is the link to the repo of this website: Feel free to take a look and see what is going on. I also wrote a post about how to deploy your Hugo generated website to gh-pages based on my workflow: Hope it helps!

maxwell_dev profile image
Max Antonucci

Sorry for the late response, but yes it is a Jekyll site. I've also hit a similar issue while using Pattern Lab, which is an open-source tool generating the public directory using Gulp. I tried to check the github link you shared, but it lead to a 404 page. But it is good to know how you've worked around this particular issue, I'll definitely give that post a closer look!

Thread Thread
letsbsocial1 profile image
Maria Campbell

Sorry for the bad link and for not getting back to you sooner. Was not bad, just did not have a space after it! Should work now. Hugo in terms of workflows is similar to Jekyll, especially since both use the public folder. Perhaps you can give the scripts I use for my Hugo site with Jekyll? And now you should be able to access the repo for inter-global media network blog. Let me know if this helps!

letsbsocial1 profile image
Maria Campbell

And since you would have to repeat the script referred to in the Hugo article, you can create a script to basically automate the process like I did in the case of my React project in this article. I just haven't gotten around to finessing that process yet, but will eventually and then will write about it and share it here.

nimit2801 profile image
Nimit Savant

It worked for me when I used
git subtree push --prefix dist origin gh-pages
instead of
git subtree push --prefix dist gh-pages

Works like a charm.
Some suggestion, please make the article more readable, it would be really helpful for beginners who want to try this to read it easily.

hardkoded profile image
Darío Kondratiuk

This guy here git subtree --split --prefix dist gh-pages
Shouldn't it be git subtree split --prefix dist gh-pages?

letsbsocial1 profile image
Maria Campbell • Edited

You are absolutely right. I will edit here. Thanks for catching the error! Done!