Alien-Factory has a number of projects held on BitBucket and uses Mercurial (Hg) for source control.

Alien-Factory also deploys its web applications to Heroku, which requires the project to be stored in a Git repository.

This article explains how to use Mercurial and Git together for the purpose of deploying projects to Heroku...

Mercurial + Git = Heroku

Why Not Just Use Git?

If Heroku requires the web application to be stored in Git then you may ask, why not just use a Git repository on BitBucket?

Well, Git is great for distributed file systems, and Mercurial is great for project repositories. And Mercurial, as they proclaim on their website, just works!

Why Not Use Hg-Git?

There is the Hg-Git project, which lets Mercurial push and pull to / from a Git server... But it does not play nicely with Heroku.

So How Then?

Git keeps its files in a .git directory and Mercurial keeps its files in a .hg directory. Because of this, you can store your project in both systems at the same time. The general approach is:

  • Use any Mercurial repository for source control
  • Use Heroku's default Git repository (because you have to)
  • Check out both repositories into the same directory
  • Ensure both systems ignore each others files
  • Commit, push and pull to Mercurial as required
  • Only commit and push to Git to deploy the application.

In essence, Git is just used as a deployment mechanism. So when we push to Git, we forcefully push everything and never look back. We use the same Git commands, in a script, that commitd and pushes the entire contents of the current directory to Heroku.

Job done!

Lets look at those steps in detail...

Check out Mercurial and Git

Assuming your project is already in BitBucket's Mercurial and you've created a new Heroku application, you want to check out both projects into the same directory.

As both the Mercurial and Git clone command will only clone into an empty directory, we check them out into separate directories and manually merge them. Our goal is end up with one directory that contains our project, Mercurial's .hg directory, and Git's hidden .git directory.

select all
C:\> hg clone <hg-repo-url> hg-dir
     ...

C:\> heroku git:clone --app <heroku-app-name> git-dir
     ...

C:\> mv git-dir\.git hg-dir\.git
     1 dir(s) moved.

C:\> rmdir /S git-dir

As you can see, the project was checked out into hg-dir, and Heroku into git-dir. The all important .git directory was then copied into hg-dir and git-dir was deleted.

By using the heroku clone cmd, and not straight Git, we let Heroku do some magic. Otherwise you end up with all sorts of Git problems and have to associate remotes, add keys, etc...

(Note that the Powershell mv command and not move because move cannot move hidden files or folders)

Make them ignore each other

They say ignorance is bliss, and it is no different for these two mortal enemies!

You need to make sure the .hg and .git directories are ignored in the relevant systems - otherwise you'll have Mercurial files checked into your Git repo and Git files checked into your Mercurial repo! D'Oh!

Create a .hgignore file in the root of your project and add the following lines:

syntax: glob
.git

Create a .gitignore file in the root of your project and add the following line:

.hg/

Use Mercurial

Use the Mercurial repository as per normal; using commits to track your changes. Push and pull when you feel like it, and generally ignore the fact that the Git repository exists.

Deploy to Heroku

The only time you should use Git is when you want to deploy (a new version of) your application to Heroku.

Be heavy handed with Git, commit everything at once! Don't even bother with commit messages, I mean, who cares!? We're not using it for source control, we're only using it as a deploy hook for Heroku.

In fact, at Alien-Factory, our only interaction with Git is through this simple deploy-to-heroku.cmd script:

git add -A
git commit -m "Git is!"
git push heroku master

Epilogue

To use Mercurial as your source control manager while, at the same time, deploying to Git on Heroku you need to:

  • Check out Mercurial and Git into the same directory
  • Make them ignore each other
  • Use Mercurial as normal
  • Push everything to Git to deploy

Have fun!

:)


Discuss