How to neatly deploy with Git on OSX and Linux

First and foremost, let me apologize if you got here by a misleading title. I always strive to find a good title for my posts but this time I’m not satisfied. This post is about my specific needs in the described scenario, and I decided to publish it mainly for my own record. Unfortunately, a more precise title would have had an unfeasible length.

Lately I’ve been making changes to a web project I had downloaded to my Mac from a Linux server provided by 1and1. My first mistake was to use FTP on the root tree. Even if I have a 50 Mb connection at home, it was a never ending task. After hours waiting for the download to complete, I found out that both FileZilla and Cyberduck had problems with some filenames.

Then I discovered that 1and1 has a file manager in the admin interface that allows you to make a zip. My second mistake was to think that the feature would work as advertised. That was not the case. Apparently you can select many files and directories at once an compress them to a zip file by means of a button. The button pops up a second window from where you can choose the filename. When you accept, it starts working, and working, and working, up to when it exhausts the time allowed for running the task (15 minutes, I think).

I made two wrong assumptions here really: first that selecting multiple files had an effect on the subsequent operation and second that the button would do it. Luckily for me, I discovered that if I selected just one file or directory and used the compress option from its contextual menu, it worked fine. It took some time to compress about 500 MB but the resulting file took merely a bunch of seconds to download by FTP.

I started making my changes to the code and testing them on localhost. Meanwhile, also the remote web tree was changing because users were uploading to and deleting files from the web. (If I make a web, code and data are clearly set apart, such that a trunk cannot branch without control. But this is not a web I made, and the code is so weird and highly duplicated that I understand it with some difficulty.)

It was at that point in time when I discovered that 1and1 had Git installed and ssh was available. So I realized Git could help me deploy changes from my development environment back to the production environment with minimum downtime. I wanted to be able to move changes the other way around too, for example to get new data files. My third mistake was to think that I could do all of it with a production repo cloned to a development repo. Unfortunately, Git (by default) don’t allow to push to a standard (non-bare) repo. Neither could I easily pull from the development repo to the production repo, because of my dynamic IP at home.

I understood that I needed three repos: a development repo, a production repo, and a proxy, internet-reachable, bare repo. Not only I had to find a place for the proxy repo but it also needed to exist before the other two, so that I could clone them from it. The problem I faced was that the website was already fully deployed right into the htdocs directory and 1and1 didn’t allow me to write outside. Thank Git, a clone is equal to each other (mostly) and .gitignore is very effective, so I solved it like this:

  1. init a production repo into htdocs
  2. add /bare.git/ to htdocs/.gitignore
  3. commit anything (to the production repo)
  4. bare-clone the production repo (htdocs/.git) to the proxy repo (htdocs/bare.git)
  5. reverse the clone relationship between both repos

I know it’s weird but works like a charm.

$ cd /<path>/htdocs
$ git init
$ cat > .gitignore

/bare.git/<CTRL+D>

$ git commit -a -m "initial files"
$ git clone --bare -l ./ ./bare.git
$ cd bare.git
$ git remote rm origin
$ cd ..
$ git remote add origin /<path>/htdocs/bare.git
$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master

After cloning the proxy repo to the development repo I could push and pull without any issue, but my fourth mistake was to think that all was nearly done. Instead UTF-8 filenames were my next headache because git status detected all those files as untracked. I tried hard to solve that issue, but in the end I decided just to work around it by committing those untracked files again.

$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .gitignore
#   ".../1... Foz d'Iguac314247u 2010.doc"
$ git add ".../1... Foz d'Iguac314247u 2010.doc"
fatal: pathspec '.../1... Foz d'Iguac314247u 2010.doc' did not match any files
$ git add ".../1... Foz d'Iguaçu 2010.doc"
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   ".../1... Foz d'Iguac314247u 2010.doc"

One of the problems is that Git/OSX is showing escaped filenames in the output of git status, but it wants them added unescaped. After adding, committing and pushing those files I got this from Cyberduck (I only clipped the same confidential data from all the lines):

Mirrored copies of the same files, also with the same filenames! Oh, that’s weird! Of course they have different encodings.

Git/Linux and Git/OSX work perfectly well inside their respective operating systems with UTF-8 filenames but as soon as you cross the border of a system, issues arise. Linux and OSX support UTF-8 filenames differently. I think Git is to blame because it should offer a state of the art layer for supporting a distributed VCS. I read something about it being fixed in a new version, but I cannot install anything on the remote Linux machine to test it.

Even after all of the above, at some point (I do not remember how it happened) git status kept showing again the pathspec error. So I completely removed all the data files directories from Git and added them to .gitignore. That solved all the issues… hopefully.

Testing my WordPress for Android

I got this special build, just for me for now, with a fix that allows me to provide the endpoint for XML-RPC access. So the next version of my Login Dongle plugin is going to work for Android app too. I want to thank here the devs of this app that found a fix in no time, less than a day !!