Sitting on #drupal-support on IRC, you see people drop by with update problems from time to time. With Drupal 6.21, 6.22, 7.1 and 7.2 released earlier in the week, today was such a day.
The person in question had attempted a Drupal core update via drush, but ran it in the wrong directory. Drupal had picked up this incorrect location for its core modules, so when the drupal-6.22 directory got deleted, it was unable to load any of the core modules. Oops.
This led me to wondering whether the way I manage my Drupal updates is odd and whether sharing it would be useful. Being an open source person, I am of the opinion that sharing is virtual always useful (except when it comes to cheese) so I'll document the way I manage updates here.
There's a few ways to update Drupal core to a new minor version. I'll not discuss major version upgrades, for which it's best to follow the procedure set out in the UPGRADE.txt file that comes with Drupal anyway.
Unpack tarball or zip
I imagine a lot of people follow that UPGRADE.txt file regardless, so an update means down-time for them whilst they move directories out of the way, unpack the tarball or zip and then copy the files directory back. It's also tedious. if you have a large files directory, you'll spend ages waiting for it to copy. On the plus side, you know you have a backup.
The even more tedious version of this involves a server where all you have is FTP access, so you need to unpack the archive locally and then upload it file by file.
Update via git fetch/rebase1
If you've installed Drupal from git, you can simply fetch the new tag from the Drupal git repository and then rebase your local Drupal version. Certainly anyone who hates kittens is already doing this, as it allows you to hack core and keep your changes separate and version controlled.
It's also nice and efficient, as git fetch would download only the changes to the code, not the full Drupal source. Admittedly, that's only 1MB (or 2MB for Drupal 7) which isn't that much of a deal these days.
1 ... because we don't git merge, do we? :-)
Make a patch and apply it
The way I handle Drupal updates is by making a patch file that contains the difference between the versions. It's effectively a combination of the tarball and git ways. I like it because by applying a patch I can see if there are any conflicting changes (changing .htaccess is hacking core too!) and manage them sensibly. In addition, I can apply such a patch to any staging sites I manage via git as well as stand-alone production sites.
Make a patch
Making a patch is trivially easy. First I download a copy of the original and new Drupal versions via drush:
drush dl drupal-6.20 drush dl drupal-6.22
Then I create a patch using the diff utility, where the urN options control the patch file format. By default diff outputs to the screen, so I redirect this output to my patch file instead:
diff -urN drupal-6.20 drupal-6.22 > drupal-620-to-622.patch
If you have a look at the patch file, you'll see that the contents list what gets removed (-) and what gets added (+) at which line numbers. Pretty straighforward.
diff -urN drupal-6.20/CHANGELOG.txt drupal-6.22/CHANGELOG.txt --- drupal-6.20/CHANGELOG.txt 2010-12-16 08:11:22.000000000 +1100 +++ drupal-6.22/CHANGELOG.txt 2011-05-26 06:43:55.000000000 +1000 @@ -1,4 +1,14 @@ -// $Id: CHANGELOG.txt,v 126.96.36.199 2010/12/15 21:11:22 goba Exp $ + +Drupal 6.22, 2011-05-25 +---------------------- +- Made Drupal 6 work better with IIS and Internet Explorer. +- Fixed .po file imports to work better with custom textgroups.
Apply the patch
Now all that remains is to apply the patch to any Drupal instances that need updating, using the patch utility. It reads data from standard input, so I use a redirect2 again:
cd /path/to/drupal/root patch -p1 --dry-run < /path/to/drupal-620-to-622.patch
When you run that, patch will tell you which files it's patching and whether any errors have occurred. The -p1 option tells patch to remove the top level part of the file path from each file that's listed in the patch. In this example, it would strip off "drupal-6.20/" and "drupal-6.22", leaving it to patch the CHANGELOG.txt file in the current directory, which is what we want.
The --dry-run part means patch is not actually modifying the Drupal files yes, but only telling you what it would do. If there are any errors, you can find out what they are and then decide whether or not to apply the patch. To apply the patch for real, use:
patch -p1 < /path/to/drupal-6.20-to-6.22.patch
If there were errors that you decided to ignore during the dry-run, you'll find that patch has created created two copies of all the files it failed to patch successfully. One with the suffix .orig, which is the original unpatched copy of the file and one with the suffix .rej, which contains a listing of the parts of the patch that failed to apply to the file.
Fixing those up is effectively identical to resolving a failed git rebase or merge.
And there you have it, my reasonably fast, reasonably fool-proof and - above all - reasonably lazy way of updating a Drupal installation.
2Because I'm an opponent of the needless use of cat, as in cat drupal-6.20-to-6.22.patch | patch -p1