You are here

Version Control

A sign in git tag's documentation

I was reading the git tag and found this bit:

But if you have pushed things out (or others could just read your repository directly), then others will have already seen the old tag. In that case you can do one of two things:

  1. The sane thing. Just admit you screwed up, and use a different name. Others have already seen one tag-name, and if you keep the same name, you may be in the situation that two people both have "version X", but they actually have different "X"'s. So just call it "X.1" and be done with it.
  2. The insane thing. You really want to call the new version "X" too, even though others have already seen the old one. So just use git-tag -f again, as if you hadn't already published the old one.

I find it surprising that this has to be said in the documentation. The "sane" method is, to me, clearly the most obvious one. However, as Joel Spolsky once wrote of a "No Dogs Allowed" sign at a restaurant: "The real reason that sign is there is historical: it is a historical marker that indicates that people used to try to bring their dogs into the restaurant." So I suppose enough people have used or wanted to use the "insane" method for it to be mentioned.

Using Git For Websites

I've written before about using subversion and Piston for hosting this particular site.

The problem with using subversion for websites is that it is not available when working offline. (Assuming that your repository does not reside on your laptop.) While this is not normally an issue for sites based around a CMS, e.g WordPress or Drupal, there are times that having access to version control would be useful. Examples include working on a new Drupal theme or doing old-fashioned HTML web development.

The solution to this problem is to use a distributed version control system. In a distributed control system, a local repository is maintained on the development machine which is then periodically synchronized with the master repository. One such system is git. Git is used by the Linux kernel developers and many other projects besides. It also has the feature of being able to interact with subversion which is useful for me since I'm not ready to phase out my subversion repository.

At a glance, git appears to be superior for websites. Aside from the advantage of being distributed, it also stores its metadata in a subdirectory of the top-level directory called .git. Compare this against subversion which stores its metadata in a subdirectory called .svn within each directory and subdirectory of the repository. This means that if you have directories that are owned by the webserver (this happens on websites sometimes), you have to get them chown'd before you can add them to a subversion repository. With git, this isn't an issue. (Git will not preserve the file ownership but this isn't usually a significant issue.)

There are a few differences between subversion and git. First, git does not use a central repository like subversion. A common structure in subversion would be to have a central repository and then each project would be a subdirectory. In git, each project has its own repository. While you can check out specific directories from subversion, you can only check out the entire repository for git.

While subversion allows checking in empty directories, git does not. This can be an issue for some applications. The Git FAQ suggests, as a workaround, adding a fiele called .gitignore to each otherwise empty directory. Git will then check in the .gitignore file.

Git does not appear to support anything like svn:externals. However, this does not appear to be a significant issue. Version 2 of Piston supports git (as well as subversion) so it can be used to fulfill the same purpose. (Correction: As Jakub Narębski points out in the comments, git's submodule mechanism is a lot like svn:externals.)

Using git is mostly the same as with subversion. If you are familiar with subversion, you should pick up on the commands relatively quickly. (And if you're completely new to version control, I suggest getting Pragmatic Version Control Usng Git.) The one major catch is: Always remember to push commits to the central repository with svn push origin master. If you do not do this, the local commits will never reach the repository. This is an issue if you expect to redeploy the site from that repository in the future.

Finally, when using Git and Piston, you will need to add these directives to block access to the metadata:

RedirectMatch 404 /\\.git(/|$)
RedirectMatch 404 /\\.piston.yml

If still checking out from a CVS repository, e.g. from Drupal, you will still need to include this as well:

RedirectMatch 404 /CVS(/|$)

Changes Because of Piston 2.0

As mentioned elsewhere, I maintain the local copy of this site through a combination of subversion and Piston. Sometime in the past couple months, version 2.0.1 (and now version 2.0.2) of Piston was released and, when I updated my local Ruby gems, ended up upgrading.

Piston 2 allows for the ability to use Piston with Git as well as subversion. This requires significant changes to the backend. So in order to use Piston 2 with existing Piston-managed copies, you must first upgrade those copies to use Piston 2's new format. This can be done with piston upgrade.

Piston 1 stores all of its backend information as properties within subversion. Piston 2 instead saves it to a special file, called .piston.yml, since properties are not available with Git. To use this with a public site, access to .piston.yml should be blocked. This can be done with this directive:

RedirectMatch 404 /\\.piston.yml$

After doing all of this, I encountered a bug with Piston. Every time I tried to update a Piston-managed directory, I got an error like:

/usr/lib/ruby/gems/1.8/gems/piston-2.0.2/lib/piston/svn/revision.rb:30:in `check
out_to': Did not get the revision I wanted to checkout.  Subversion checked out
280, I wanted 280 (Piston::Svn::Revision::InvalidRevision)

280 is equal to 280, isn't it?

I'm not sure why this behavior would manifest. I did not see anything on it. I suspect it may be an issue with the upgrade process. A simple, hacky solution was to change line 29 from:

          elsif revision != $1.to_i then

to:

          elsif revision.to_i != $1.to_i then

(I haven't filed a bug report on this but I will shortly.)

After doing this, Piston ran normally as expected and I could install the pending updates for the Drupal modules.

Drupal, Subversion, CVS, and Piston

When I decided to start this blog, I decided to maintain the site almost exclusively in subversion. Having worked with this for a few weeks, I've found it to be much easier than the "traditional" method of setting up Drupal.

I'm not the only one to do this. Agaric Design's approach was helpful but left me scratching my head.

To do this, first, I created an area for Drupal in the vendor directory of my repository: svn mkdir drupal/5

I use a vendor directory because I have some projects that, while unrelated, rely on the same software. I could use a vendor branch in each project but I decided that more overhead than I wanted. (If I had a single repository per project, vendor branches would be fine. Instead, since I have multiple projects in a repository, a separate vendor directory makes more sense.)

(For those wondering, the 5 denotes the version of Drupal. Versions 5 and 6 are incompatible so it's necessary to separate the Drural code and modules by major version.)

Before I can import Drupal core into my repository, I need to check it out from Drupal's CVS. So in the directory I created and, following the directions, I run:

cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal co -r DRUPAL-5--14 drupal

Now, to add it to the repository:

svn add drupal
svn commit

To import a module, the steps are mostly the same but using the different CVS parameters: (the globalredirect module used as an example)

cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib checkout -r DRUPAL-5--1-4 -d globalredirect contributions/modules/globalredirect
svn add globalredirect
svn commit

Now, to add this into the site, create a repository for the site. I use 'web' as the main directory of the site and I want to install Drupal in that directory. So in the repository, I run:

svn update
piston import http://svn.ithiriel.com/svn/vendor/drupal/5/drupal ./web

Piston is a tool that synchronizes including code from other repositories or locations. It works like svn:externals but allows for local modifications and tracking those. This is useful since each Drupal site has different configuration files.

Piston requires the local directory to be at the latest revision. In almost all cases, this requires running svn update before running Piston.

To install a module, say the globalredirect module from before, I go to web/sites/all/modules and check it out in the same fashion:

svn update
piston import http://svn.ithiriel.com/svn/vendor/drupal/5/globalredirect ./globalredirect

This may seem a lot of work to do at first but I'm pretty sure it pays off the next time you need to update the site.

Since I first started work on the site, Drupal 5.15 was released and a new version of the globalredirect module was released. Both of these needed to be updated.

To update the local copy of the original Drupal source, I go back to the directories created before. In /vendor/drupal/5/drupal, I run:

cvs update -r DRUPAL-5-15 -dP

It's necessary to see if any files have been added or deleted. svn status will tell you if there are any. I add or delete as necessary. When the status checks out, I commit it with svn commit.

Back in the repository for the website, I go to the directory above the web directory and run:

svn update
piston update web
svn commit

Now the upgrade for Drupal core is done. All that's left to do is run the update script. All of that done with very little fuss.

To update the globalredirect module, I go to /vendor/drupal/5/globalredirect and run:

cvs update -r DRUPAL-5--1-5 -dP

As for the core, I see if any files have been added or deleted. When everything is ready, I commit the revision.

To update the module on the site, I go to web/sites/all/modules in the site's repository and run:

svn update
piston update globalredirect
svn commit

As before, I run the update script and it's done.

Upgrading modules isn't such a big deal but upgrading the core tends to feel like a pain. Being able to do it with a set of easy commands makes the process much more tolerable.

There are two issues with this method though.

The first is that when checking directly out of the Drupal CVS, the version numbers will not be set correctly. If using Drupal 6 or the update status module for Drupal 5, the CVS deploy module is required so the version numbers can be determined correctly.

The second is that this process exposes the CVS and .svn directories to the public. However, I can deny access to these with Apache with these directives:

RedirectMatch 404 /\\.svn(/|$)
RedirectMatch 404 /CVS(/|$)

(For those following along, if you do not have access to the Apache configuration file, the documentation for RedirectMatch indicates that it can be placed in .htaccess files. I haven't tried it so I make no guarantees.)

Edit: Made some grammatical and wording changes because I wasn't happy with the originals.

Subscribe to RSS - Version Control