kuniga.me > NP-Incompleteness > 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.
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.
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
.
Github supports creating private repositories for free. Let’s call it draft-blog
.
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
$ git clone git@github.com:exampleuser/draft-blog.git
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)
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
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: $(git rev-parse --abbrev-ref HEAD)$"
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
, resetting 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
Finally we can make the post public by pushing it to the public remote:
git push public
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
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.