My Jekyll and Git Workflow

A few weeks ago, I decided that I wanted to start blogging again. The reasons for why belong to another post but this is a purely technical explanation of how I setup git and jekyll to do things like drafts, site design tweaks and a write-anywhere environment.

git: savepoints for poorly formed thoughts, and a little distribution

From painful past experiences in code, I knew that version control was a must and I knew git, so git it was. Also, I could fork Tom Preston-Werner’s original jekyll blog, which I liked because it was simple and was clean enough that I could edit it without too much effort.

Another reason I’m using git is so that I can write from different physical locations. Ground zero for clumsy sentences is my laptop. My anywhere-I-am writing location is sitting on the same server that I run the site from. Publishing from my laptop is just an rsync, and from the server is just a cp away.

I’ve got one central repository on the server that is just a bare checkout of the blog. This is my push and pull point for both my server and laptop writing repositories. I probably could set both writing environments to talk to each other directly, but I like the simplicity of having a third repository that just gets pushed to, especially if I do some writing in one and then switch to the other.

My github repository should more or less mirror what you see on the site. The only reason it won’t is if I’m holding back a few minor commits so I don’t spam people that follow me on and I’ll batch those up. It’s only my master branch though, which doesn’t include in-process items, and I’ll explain that in a bit

When I’m at home, or have my laptop with me, I can just write locally. If I don’t have Internet access for some reason, I can keep going. This is a great improvement over my last WordPress blog. Having to write in a form field in a web browser offers too much distraction since all those interesting things are just a tab away.

ssh -L 4000:localhost:4000

If I don’t have my laptop, I can just ssh into my box, locally forwarding port 4000, so I can use the webbrick server and see things in my browser by hitting “localhost:4000” as if I were working locally.

git: also, great for drafts

Now that I’ve got distribution and publishing down pat, I’ll explain how I do drafts. This is actually really simple: I just use a new branch coming off the master branch for every post I’m writing. When I’m ready to publish a post, I just checkout master and do a git merge –squash branchname and, bam, I have one commit on my master branch that introduces a new post, including any images that I’ve added.

I do a squash since I don’t really care to publish my fumblings with the English language. It’s a bit of a shame that I lose the historical record of how the post was formed since I have the data, but I realized that I would never look at it again and I don’t want the overhead of having to deal with a long standing branch for every draft.

Since I’m basically trying to keep two writing environments in sync, I found it’s crucial to not do any destructive branch operations like rebase. It’s tempting since I might make a change to my master branch, like a CSS change, and want to just move my draft to the end of the branch. It’s better to just do a merge and while that’s messy if it happens a lot, as soon as that branch is published, the branch goes away.

Whenever I stop writing for the session, I commit all changes to my current draft branch and do a git push. My origin is set up as that central repository on my server as I push and pull a lot more frequently to that and it really is central to my flow. When I pick up at another time or place, I just do a pull, getting in changes from the other environment, and get going.

Another change I made to my flow is adding the following to my bashrc:

alias jekylldraft='vi $(git diff --name-only master -- _posts/)'

With this, on one of my draft branches, I can just run jekylldraft and it figures out which file in the posts directory is not on master and loads it up.

design tweaks

If the design change I’m making is small, I’ll just add a new commit on my master branch. If something is more involved, I’ll create a new redesign branch and just merge that in when I’m done. Simple.

scripts make my life easier, and so can you!

I’ve got a little directory that has three scripts in it: one to publish locally, one to publish from laptop to server, and one to do my two git master pushes. The first two could probably be combined but I’m a simple guy.

The first thing either of my publish scripts does is checkout the master branch. This stops me from doing something dumb (again) like publishing one of my draft branches. I forget things but my script never does. And I use the && bash idiom so that if the checkout from master fails, the whole script stops.


I use tarsnap to do a nightly backup of my writing directories (here‘s my crappy perl script that does this). Git’s not a backup, especially the way I use it by only publishing the master branch to I have backups running on my Linode server, backups on tarsnap, and then the code physically stored in three spots (ie, laptop, server, and master branch on github). I’m not that paranoid about losing anything and requiring redundant backup methods, this was just more a snowball of backup systems that I didn’t fully think through.

other niceties

Since I’m writing on both a Linux laptop and a Linux server, I use vim as my common editor. Here’s the settings I use in my vimrc that seem to work for me. I’ve pieced these together using the vim docs and from other various helpful blogs that I’m a bad person for not writing down and remembering to thank them.

autocmd BufRead,BufNewFile *.markdown setlocal lbr 
autocmd BufRead,BufNewFile *.markdown map j gj
autocmd BufRead,BufNewFile *.markdown map k gk
autocmd BufRead,BufNewFile *.markdown setlocal smartindent
autocmd BufRead,BufNewFile *.markdown setlocal spell spelllang=en_us

The first three settings are the most helpful in writing prose instead of code. The lbr tells vim to wrap on word boundaries and don’t insert real linebreaks in the text. The j and k commands are mapped to to the helpful commands that goes down to the next row instead of the next line.

fork me

If you liked to see more about how everything is laid out, and get a head start on starting your own jekyll and git enviroment, check out my stuff and feel free to fork.

Update: I added the bashrc snippet for jekylldraft. A second update was made later that stopped the bash subsitution (the backticks) from running everytime bashrc was loaded.