Writing Posts

kuniga.me > NP-Incompleteness > Writing Posts

Writing Posts

01 Sep 2021

This is a meta post to describe my flow for writing posts. When I moved to static Github pages over a year ago, I didn’t have a flow that I was satisfied with but I’ve recently settled in one which I think is worth documenting.

It’s worth recalling that in static Github pages I write in markdown and manage them using git (and Github).

The overall idea is to be able to write a post in draft mode, which is hidden from view and only when it’s ready I merge it on master, which is automatically picked up by Github pages and made public.

Early drafts

I usually have a bunch of topics I’d like to write about at one time, so I keep these early stage drafts as Google docs, which are mostly a collection of links and some comments.

Once I have enough confidence on it becoming a future post, I move to markdown.

Private Branch

In the past I’d keep an un-committed markdown files plus images for the posts, occasionally backing them up by copying them to Dropbox.

Needless to say it’s a subpar flow. I was really looking for a way to use git to backup my changes but didn’t want them to show up in my repository while in development.

I started looking for private branches but they don’t exist. An alternative was proposed in this Stack Overflow answer [1]. The idea is to keep a private mirror of the main repo and commit only to the private one, occasionally syncing with the public.

Suppose the name of our main blog repository is blog.

Create a new private repo

Github supports creating private repositories for free. Let’s call it draft-blog.

Create a mirror repo

Here we copy the steps from the Github docs [3].

Create a bare clone of the repository:

$ git clone --bare https://github.com/exampleuser/blog.git

Mirror-push to the new repository:

cd blog
$ git push --mirror https://github.com/exampleuser/draft-blog.git

Remove the temporary local repository you created earlier.

$ cd ..
$ rm -rf blog

Clone the new repo locally

$ git clone git@github.com:exampleuser/draft-blog.git

Add remotes

A git remote is basically an alias for the URL of the remote repo. When we use git clone, it adds a default alias called origin, which points to the original repo. We can inspect via git remote -v:

origin  git@github.com:exampleuser/draft-blog.git (fetch)
origin  git@github.com:exampleuser/draft-blog.git (push)

Note we have one entry for read (fetch) and one for write (push).

We want to add another remote that points to the public blog, that is git@github.com:exampleuser/blog.git, and name it public:

$ git remote add public git@github.com:exampleuser/blog.git

If we type git remote -v we should see 4 entries now:

origin  git@github.com:exampleuser/draft-blog.git (fetch)
origin  git@github.com:exampleuser/draft-blog.git (push)
public  git@github.com:exampleuser/blog.git (fetch)
public  git@github.com:exampleuser/blog.git (push)

Writing

Before we start writing, we create a branch. For this very post I created one called post-writing:

git branch post-writing
git checkout post-writing

NOTE: It’s important we create branches off the master branch (we’ll see why later), so always do git checkout master before creating a new branch.

I usually write a bit every other way. When I’m done, I simply create a dummy commit and sync to a similarly named branch in the private repo for backup:

git commit -am "backup"
git push origin post-writing

Since I always push to a branch to the same name on remote, I set the push behavior to current, which pushes the current branch to a branch of the same name in the remote [4]:

git config push.default current

So we can simply do:

git commit -am "backup"
git push origin

Merging

Once the post is ready for publishing, we want to merge into the master, but we don’t want all those dummy backup commits polluting the logs.

This Stack Overflow answer [2] provides a way to squash all the commits from a branch into a single one:

git checkout post-writing
git reset $(git merge-base master $(git branch --show-current))
git add -A
git commit -m "new post: writing posts"

Let’s analyze the second command:

git branch --show-current

Simply returns the current branch name post-writing. Wrapped in $() means it’s treated as a variable, thus

git merge-base master $(git branch --show-current)

is really

git merge-base master post-writing

The command above returns the <hash> of the commit that is the lowest common ancestor to both master and post-writing. Finally we do

git reset <hash>

This will set the current index back to that ancestor commit and the changes from post-writing relative to that ancestor will show up as un-committed changes.

This command assumes post-writing was created off the master. It it was created off some other branch foo, reseting the index would include changes from foo as well. Hence the note in the Writing section.

To simplify things, we can alias the second command as compress:

git config alias.compress "! git reset $(git merge-base master $(git branch --show-current))"

Now we can merge it into master:

git checkout master
git merge post-writing

Syncing

Finally we can make the post public by pushing it to the public remote:

git push public

Post-editing

I usually want to fix typos or reword phrases after the post has been published. For this flow I don’t bother creating branches nor squash commits and do all from the master branch in draft-blog.

git checkout master
# fix / reword ...
git commit -am 'fix typos'
git push public

Conclusion

I only use Git for basic stuff (at work we use some flavor of Mercurial) and whenever I have to do some operation I’m not used to, Git gives me impostor syndrome.

I think I got a good handle of working with remotes through this process of trying to document my workflow. Having to setup another remote made and looking into different push behaviors [4] made things a lot clearer.

Though it’s worth noting my flow is super simple because I mostly write in one computer (occasionally I also write in a Linux machine) and each post is its own file, so conflicts are almost non-existent.

References