I have found Hexo a great tool for building a blog and apply many well-known software development principles. One of them is automation. This is why I have decided to integrate this blog with Travis CI to perform a deployment to GitHub pages. It was a great decision, however a few days later I have noticed one significant issue - deploying a new version of the blog from the CI server caused removing all commits from master branch and starting with initial commit over and over again. It took me a while to find working solution to this problem. This blog post explains a simple solution to this problem.

Why does hexo deploy removes the history in the first place?

Let’s start with understanding what actually happens. When you run hexo deploy [1] command for a git deployment option, Hexo creates a hidden folder called .deploy_git and it copies generated files from the public folder to it. Next, it initializes git repository (if it does not yet exist) that targets Hexo’s remote deploy branch and it executes git push --force from this folder to a repository and a branch you have defined in the _config.yml [2] file.

Listing 1. Deployment configuration of e.printstacktrace.blog
  type: git
  repo: [email protected]:wololock/wololock.github.io.git
  branch: master

If you build and deploy your blog from your local computer and you never delete (or accidentally lost) your blog source code, you may never face this issue. When you do it from the workspace that does not get wiped, then the folder .deploy_git with it’s full history exists and hexo deploy pushes only those files that were actually modified. When you move to CI server like Travis CI, this is not true anymore, because it executes build with the clean workspace and the fresh clone of the repository. In this case .deploy_git folder simply does not exist and gets recreated from scratch.

How to deploy and keep the history then?

The solution I have found working for me well is fairly simple. Previously my .travis.yml file part responsible for the deployment looked something like this:

Listing 2. Previous Travis CI deployment configuration
  skip_cleanup: true
  provider: script
  script: hexo deploy
    branch: develop

It simply triggered hexo deploy whenever I pushed changes to develop branch. In this case it ended up creating a new .deploy_git folder and force pushing an initial commit to the GitHub repository. Then, I have made a small improvement - I’ve created a short bash script instead.

Listing 3. Deploy script used by e.printstacktrace.blog

# Initialize target with currently deployed files
git clone --depth 1 --branch=master https://github.com/wololock/wololock.github.io.git .deploy_git

cd .deploy_git

# Remove all files before they get copied from ../public/
# so git can track files that were removed in the last commit
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null

cd ../

# Run deployment
hexo clean
hexo deploy

This script does exactly what it says in the comments:

  • It clones master branch from remote repository to .deploy_git to get existing commits history.

  • Then it removes all non-git objects repository files from .deploy_git so copying files back from public folder will track deleted files.

  • And finally - it executes hexo deploy command that does the regular deployment.

And the last but not least, here is what deployment configuration part looks like after introducing deployment bash script:

Listing 4. Current Travis CI deployment configuration
  skip_cleanup: true
  provider: script
  script: sh deploy.sh
    branch: develop

Thanks to this solution I was able to keep the history of site updates and to track changes of files that were actually modified with the given site update.

github hexo history

Final words

I hope you find this post useful. It describes solution for Hexo + Travis CI + GitHub use case, but it can solve problems that other similar static site generators may have when running from CI server environment.

Szymon Stepniak

Groovista, Upwork's Top Rated freelancer, Toruń Java User Group founder, open source contributor, Stack Overflow addict, bedroom guitar player. I walk through e.printStackTrace() so you don't have to.