I recently found myself in a situation where I wanted to keep a file in a Git branch I was merging into unchanged, i.e. ignoring the file from the branch I was merging and instead keep the version of the file in the target branch. This proved to be a lot less straightforward than I would have thought.
I have to mention that I'm still more or less a Git noob. So the way I'm doing this might be totally wrong. Instead of the setup I describe below, it might make more sense to rethink my development workflow and how I manage my projects. But judge for yourself and if you have a better idea how to handle this, feel free to leave a comment.
To explain why this particular problem even needed to be solved, I'll start out by giving you an overview of the project I'm working on and how it's organized.
A partner and I are developing a theme for Shopify. From what I've learned so far, it appears to be good practice to do all your development in Git branches and continuously merge them into a master branch when they're "done". So each of us is working on a specific feature in a dedicated branch and then merging into the master branch. We've also set up a policy to the effect that the master branch is "merge-only", which means we never make changes to files in the master branch other than by merging another branch into it.
The requirement to keep files in master untouched by merges comes from the fact that, to develop Shopify themes, you need to continuously upload changed files to a development store to test your changes. You can't develop (i.e. test) a Shopify theme locally, as there's no way to parse Shopify's Liquid theme templating language. So where you could just fire up a web server on your local development machine when you're developing HTML, PHP, etc., with Shopify you need to upload changes to Shopify's servers before you can check the results in your browser.
The way we've set up our development workflow is that for every Git branch we create, a corresponding theme is set up in our development store. So while I'm developing some feature or whatever in a particular Git branch, file changes get uploaded to a corresponding theme in the development store. Themes have an ID, and that ID is used by the upload tool we use to identify to which theme it should upload the changes.
The theme ID is kept alongside other settings in a file called config.yml in the root of the theme folder. Since the theme ID corresponds directly to a Git branch, the config.yml is version-controlled with Git, just like the theme files themselves. So every Git branch has a complete set of theme files alongside a config.yml file with the theme ID that corresponds to the appropriate theme in our development store.
The problem we ran into was that when we merged a branch into master, the config.yml file would also get merged. So after merging, say, some-branch into master, the config.yml in master would now have the theme ID set to the theme of some-branch. But since the master branch should correspond to the master theme in the development store, if we uploaded the newly merged master to Shopify's servers, we would be uploading to the wrong theme.
So we needed some way of keeping the config.yml in the master branch untouched when we merged development branches into the master branch.
Git hooks and merge options to the rescue
After doing some googling, my initial plan was to use a merge driver. I don't fully understand this mechanism yet, but what I have learned is that it doesn't help in our particular case.
In short, and as far as I understand it, a merge driver lets you configure how you want merge conflicts to be handled by default. In our case, I set up merges of config.yml to always favor the "local" or target branch, i.e. master. In general, this approach would work, but only if there was an actual merge conflict. If merging the config.yml from a branch into the master branch could be achieved by a simple fast-forward merge, the merge driver didn't trigger. So I had to look for a different solution.
The solution I came up with isn't exactly elegant. But it's the best (read: only) way I could find and despite not being elegant, it works.
The general approach is to configure Git to not auto-commit merges and then, after the merge but before the commit, restore the config.yml from the master branch and then commit the merge. What this effectively achieves is that all files except the config.yml file are merged normally, but config.yml always remains the unchanged "original" file from the master branch.
Here's how to set this up.
First, I had to set up Git so it doesn't auto-commit changes after a merge. I set this up only for the Git project in question, not globally. I work on non-Shopify projects too, and for those I want Git to work as usual. The command to prevent Git from doing an automatic commit for a project is this (run inside the project folder):
git config branch.master.mergeoptions "--no-ff --no-commit"
What this does is add the following settings to the project's .git/config file for the master branch:
--no-commit means exactly what you'd expect: It prevents Git from automatically committing changes after a merge. When the merge completes, the changes to the files have already been made, but you can still roll them back - which is exactly what we're going to do in the next step, detailed below.
no-ff in there stands for "no fast-forward". From what (little) I understand, this forces Git to always do a merge, even if it could do a fast-forward commit. To be honest, I don't fully understand if this parameter is required in this case. But it doesn't seem to hurt, so I left it in there (it's probably a good thing I don't program rockets or surgery robots).
Now that we've prevented Git from automatically committing the changes the merge already applied to our files, we need to use this "pause button" in the default merge process to insert a little magic that will restore the config.yml from the master branch.
The config.yml in the working tree has already been changed by the merge, but the changes have not been committed yet. So what we need to do now is restore the config.yml to the state we want and only then do we commit.
The config.yml we want is in the master branch. So all we need to do is "restore" the it in our working tree to that state. This is achieved with a
git reset HEAD config.yml, which unstages the changes made by the merge, and then a
git checkout config.yml, which actually restores the config.yml in the working tree to its master branch state.
Of course you could do all this manually. But that would be nuts. Fortunately, Git has a thing called hooks.
Hooks allow you to run shell scripts at pre-defined points in time in the Git process. In our case the hook we needed is the pre-commit hook. As the name suggests, this one runs before every commit.
To set this up, I created a file called pre-commit in the project folder under .git/hooks and gave it an executable flag (
chmod +x pre-commit). Here's the contents of this file:
git reset HEAD config.yml git checkout config.yml
Now, when I do a merge into master, the merge stops just before the commit. Then I run a manual commit, this triggers the pre-commit hook that resets the config.yml file to the version in the master branch and then commits everything else as usual.
You might be asking yourself why the whole
--no-commit thing is even necessary. This is because a merge normally does a commit, so you'd think the pre-commit hook would run then too, right? Unfortunately, wrong. At least in my tests the pre-commit hook did not run on a regular auto-commit within a merge. It appears that it only runs on an explicit commit.
I've been using MailMate for years now and I'm a supporter of the ongoing development of MailMate 2.0 through the 2013 crowdfunding campaign. Today MailMate got a pretty big beta update that, among other, more important things, also includes a fresh new app icon and toolbar icons. Yes, we didn't have those before, at least not for all the toolbar buttons. It never really bothered me, but it's nice to see some attention paid to the GUI.
With this update MailMate's developer also introduced an interesting model to continue to support the ongoing development of MailMate. The original crowdfunding campaign for MailMate 2.0 raised roughly $42k, and that was all the way back in 2013. I think I paid $50 and have been using MailMate daily since then.
The new support model is called MailMate Patron. You pay at least $10 every 3 months or a multiple thereof. This doesn't get you a license, you still have to buy that as a one-off purchase (which I already did via the crowdfunding campaign). But if you want to help ensure MailMate will still be around for years to come, you can choose to pay this recurring patronage for however long you like. If you stop paying, you still have your license.
Next to a web browser, my email client is probably the second most used app on my Mac. Unfortunately, the state of email clients on the Mac is generally a pretty sad one. Apple Mail is all fine and dandy, but it lacks many power features and is a bit too sluggish and GUI-heavy for my taste. All the other options available (AirMail, Thunderbird, Outlook and others) never really held up under closer inspection. I found all of them either to be too flimsy, bloated, unstable or awkward.
Given that an app I use hundreds of times a day is a major part of my daily business, supporting the future development of MailMate is a no-brainer for me. So I signed up for $10 every 3 months. So that's $40 a year for an app that I'd really, really miss if it were to disappear. I think it's a bargain.
The other day I came across a blog post somewhere that reported that the Freewrite, formerly called Hemingwrite, is now shipping. I had taken note of the Kickstarter back in 2014/early 2015, but had dismissed it as totally ridiculous, albeit quite nerdy in a quirky sort of way.
Now that the Freewrite has started to ship, I took another look. It's still the same basic concept:
You write on a Cherry MX mechanical keyboard and your words appear on a 6-inch e-ink screen. Files are saved to the internal flash storage and, if Wifi is turned on, synced every couple of seconds to Freewrite's cloud, Postbox, from where it can then sync to Google Drive, Evernote and Dropbox.
Files can be organized into three folders which you select with a wonderfully retro, if a bit silly, hardware switch. You use the same kind of switch to turn Wifi on and off. Below the main screen there's a smaller, bar-shaped status screen that lets you cycle through various information, such as the current folder, time, word count, etc.
The main idea behind the Freewrite is to let you focus on your writing. Writing tools like Scrivener or even Ulysses come with lots of potential distractions and the only way to turn them off – besides discipline - is to use the apps' full-screen modes.
But modern operating systems like Windows, OS X and even iOS are full of potential distractions themselves. So even if you writing app of choice is 100% distraction-free, the environment it runs in may not be quite as clean.
Age of Distraction
Now, one could certainly argue that all the fuss about distraction-free writing environments is a bit silly. You could be led to think that the only way to get any sort of writing done is by using a typewriter, sitting in a cave. I never use any kind of specifically distraction-free tools or modes for writing, save for maybe putting Ulysses into full-screen on my 13-inch MacBook Pro's internal screen.
But I mostly write non-fiction, like this very blog post, and that tends to involve some level of "research", like googling facts, referring to images, etc. These "distractions" are an integral part of my writing process.
I do realize, though, that writing fiction is a different ball game altogether. When you're telling a story, you want to really get into the scene you're currently writing, experience your characters through their eyes, and just really immerse yourself in the world you've invented. To achieve that, minimizing any kind of outside interference can indeed be very helpful.
That's what the Freewrite attempts to do. It puts you in a writing environment that is so minimalistic that you automatically focus on getting words out. The Freewrite offers no way to edit the words you write other than by using the backspace key. No arrow keys, no touch screen, no mouse pointer, nothing. Your best bet is to just keep on writing, moving that insertion point to the right and down.
It's safe to say that the Freewrite is more of a drafting environment than one for writing. Due to the device's limitations, you won't be doing any kind of editing on the Freewrite, or even try to organize your writing in any way. The Freewrite wants to help you get your initial draft out of your head and onto (electronic) paper. All the editing, organizing and other futzing around is delegated to more capable, general-purpose solutions like Scrivener or Ulysses.
At $499 (soon to become $549), the Freewrite is not cheap. Many people will argue that you can get an iPad for that kind of money, which obviously can do a lot more than the Freewrite. While that's factually correct, they're missing the whole point of the Freewrite: it's explicitly not about features, it's about what it can't do, or, more to the point, won't let you do.
And while you're paying the price tag in exchange for a piece of (apparently very well made) hardware, much like you pay 7,99€ for a paperback book, what you're really buying are the benefits this device offers (or knowledge or entertainment in case of the book). To some, $499 for enabling them to get their first drafts completed is going to be a bargain.
Despite the benefits the Freewrite offers (and the sheer nerdiness of the thing), I'm not really willing to put down almost $600 (including import duties and tax). But the appeal of the Freewrite's concept got me wondering if you couldn't maybe replicate the experience using an iPad, at least to some extent.
The Freewrite clearly has the look and feel of a typewriter, albeit one that was invented over 100 years later than its mechanical counterpart, and then sent back in time. In my opinion, your best bet in replicating the Freewrite experience on an iPad is not through one of the seemingly dozens of distraction-free writing apps, but rather an app that emulates a typewriter.
I found two apps that are worth mentioning. The first one is Hanx Writer, devised by actor and typewriter connoisseur Tom Hanks. The app replicates the look, feel and sound of a real typewriter to a fault. Paired with an external keyboard you can really fool yourself into thinking you're writing on an actual typewriter. You can even set the app to x-out letters instead of deleting them when you backspace.
The second app, Typing Writer, is not quite as realistic, but lets you use white-out to make corrections, which is kind of cute.
Overall I would strongly recommend Hanx Writer. Sitting at a desk with the iPad in a stand and paired with a Bluetooth keyboard, this is as close to a real typewriter as you'll ever get using an iPad. And it makes for a great focused writing environment.
My workflow for posting to this site for the last year or two has been pretty straightforward:
- Write a post in a Markdown (.md) file on any device I have at hand
- Wait a couple of seconds for the file to sync through Dropbox
- Log on to my server and run the site generation script
Making small tweaks to the site's design worked in exactly the same way, as I had the theme files stored in the same folder that contained the Markdown files, all of which got synced to the server via Dropbox.
Dropbox for content, BitBucket for the theme
So I moved the theme folder out of the folder it shared with the content folder into a separate location outside of my Dropbox. That way the source files for the content (posts, images, etc.) would still be synced to my server via Dropbox, but I could control which theme files got synced to the server and when.
I created a Git repository to which I added the theme folder in its current state (what you see on the live site at the time of this writing). I also created a remote repository on BitBucket., to which I pushed the initial state of the theme.
Now for the part that lets me develop my site design "in private" while not affecting the current state of the theme that's live on thomasborowski.de.
Separating production from development
The version of the theme that corresponds to the live site lives in the master branch of the Git repository, while any changes I'm working on live in one or more branches of their own. I modified to Pelican site generation script to do a
git pull origin master on the theme folder before generating the site so it applies any changes in the master branch. So if I merge any changes from a development branch into the master branch, those changes will go live the next time I generate the site. Any other (unfinished) changes I might be working on in separate branches will remain private until I merge those branches into the master branch.
I should have probably made this change a long time ago. But changes to the site design have always been so minor, that I was able to make them in-between posts (and it's not like my average post frequency here wouldn't allow enough room for that). But for more involved changes that I'll want to test thoroughly before applying them to the live site, Git and Bitbucket are very useful additions to my workflow.
This has to be the most persistent bug in any software I've ever come across. I switched from Chrome to Firefox the other day and started noticing that the blinking caret sometimes does not show when editing Trello cards. According to this bugzilla entry report, this has been around since Firefox 1.0 on Windows XP in 2003.