Version control

Repo switcheroo

I am a little late to this, but my AutoPkg recipe repository is now available alongside many others. So, I made the switch:

$ autopkg repo-delete “$HOME/Library/AutoPkg/RecipeRepos/com.github.Jaharmi.autopkg_recipes"
$ autopkg repo-add 'https://github.com/autopkg/jaharmi-recipes.git'

So, if you’ve been following my whimsical AutoPkg repository (Acorn! Fantastical! LaunchBar! XRG!), I suggest you switch, too.

Source code update for Penn State MacAdmins Conference 2013 Luggage talk

In case you were still interested, I've updated the source code link for my Penn State MacAdmins Conference 2013 Luggage talk. My examples from the talk are finally on-line, so you can follow along in a little more detail if the information already presented in the slides wasn't enough.

The main repository for the talk contains links to several Mercurial subrepositories, since I tracked each example separately as its own project.

Reinstall Xcode if easy_install fails with missing files like python.h

I was trying to install Dulwich on a Mac OS X Lion system this week and ran into difficulty. I kept getting installation failures that included a missing “python.h” and, eventually, llvm-gcc-4.2 failed to compile the module.

I found the situation frustrating, partly because I pretty much own the top search hits about how to install Dulwich and Hg-Git on Mac OS X Lion, thanks to some earlier article.

It turns out that I had reinstalled Lion about two weeks ago, and had not reinstalled Xcode 4. So, I updated to Xcode 4.2 and this completely eliminated my problem. Presumably, it would also work for you — and for future me, since I’m likely to repeat this — even if Lion hadn’t been reinstalled in between.

Things that didn’t work included but were not limited to:

  • cursing under my breath
  • stomping
  • hand-waving
  • complaining on Twitter
  • the silent treatment
  • waiting for 4, 12, and then 24 hours to see if it would fix itself
  • installing the latest version of setuptools, v0.6c11, from an egg
  • installing the current version of Distribute, new hotness or not
  • any steps involving Linux distribution package managers like apt-get.

Install Hgsubversion with Mercurial 1.9 on Mac OS X

In previous articles, I described how to Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Snow Leopard and Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Lion. For either of those operating systems, we can further extend Mercurial with support for connecting to Subversion repositories. While we could enable the bundled Convert extension, the Hgsubversion extension adds live access to remote networked Subversion repos, so it sounds more interesting.

My personal goal in doing this is to be able to work with the InstaDMG repository. It is hosted on Google Code, uses a Subversion repo, and depends upon Subversion keyword substitution. It presents an interesting challenge.

So, let’s install Hgsubversion to add Subversion support to the Git support we’ve previously set up. We need to take the following additional steps, going beyond what is done in the previous articles. To continue, you must already have installed:

  1. the Xcode tools (including Subversion), as appropriate for your operating system (probably either Xcode 3.2.6 or 4.1 at this point)
  2. Mercurial 1.9.2 or later.

Hgsubversion also needs Subversion to be installed. The Mercurial extension then has two different ways of working with Subversion. The Hgsubversion docs I read indicated that Subvertpy is preferred over the other method — using Subversion’s SWIG bindings — so we’ll install this additional Python module first. The docs also recommend running tests on Hgsubversion before using it on projects, so we will try that, as well.

Start with installing Subvertpy:

  1. Run the easy_install command in Terminal.
    $ sudo easy_install 'subvertpy>=0.8.7'
    Searching for subvertpy>=0.8.7
    Reading <a href="http://pypi.python.org/simple/subvertpy/
    Reading"
    title="http://pypi.python.org/simple/subvertpy/
    Reading"
    >http://pypi.python.org/simple/subvertpy/
    Reading</a> <a href="http://samba.org/~jelmer/subvertpy
    Reading"
    title="http://samba.org/~jelmer/subvertpy
    Reading"
    >http://samba.org/~jelmer/subvertpy
    Reading</a> <a href="http://launchpad.net/subvertpy
    Best"
    title="http://launchpad.net/subvertpy
    Best"
    >http://launchpad.net/subvertpy
    Best</a> match: subvertpy 0.8.7
    Downloading <a href="http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing"
    title="http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing"
    >http://samba.org/~jelmer/subvertpy/subvertpy-0.8.7.tar.gz
    Processing</a> subvertpy-0.8.7.tar.gz
    Running subvertpy-0.8.7/setup.py -q bdist_egg --dist-dir /tmp/easy_install-hU1kY_/subvertpy-0.8.7/egg-dist-tmp-Qgbloj
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_init’:
    subvertpy/_ra.c:2180: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_set_parameter’:
    subvertpy/_ra.c:2215: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘py_ssl_server_trust_prompt’:
    subvertpy/_ra.c:2681: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/wc.c: In function ‘py_dict_to_wcprop_changes’:
    subvertpy/wc.c:1127: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/_ra.c: In function ‘auth_init’:
    subvertpy/_ra.c:2180: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘auth_set_parameter’:
    subvertpy/_ra.c:2215: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/_ra.c: In function ‘py_ssl_server_trust_prompt’:
    subvertpy/_ra.c:2681: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/repos.c: In function ‘fs_root_file_length’:
    subvertpy/repos.c:747: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/wc.c: In function ‘py_dict_to_wcprop_changes’:
    subvertpy/wc.c:1127: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/wc.c: In function ‘adm_process_committed’:
    subvertpy/wc.c:1205: warning: ‘svn_wc_process_committed4’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:3765)
    subvertpy/wc.c: In function ‘get_pristine_copy_path’:
    subvertpy/wc.c:2451: warning: ‘svn_wc_get_pristine_copy_path’ is deprecated (declared at /usr/include/subversion-1/svn_wc.h:4994)
    subvertpy/util.c: In function ‘string_list_to_apr_array’:
    subvertpy/util.c:235: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘path_list_to_apr_array’:
    subvertpy/util.c:258: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/util.c: In function ‘revnum_list_to_apr_array’:
    subvertpy/util.c:497: warning: implicit conversion shortens 64-bit value into a 32-bit value
    subvertpy/editor.c: In function ‘txdelta_call’:
    subvertpy/editor.c:133: warning: implicit conversion shortens 64-bit value into a 32-bit value
    zip_safe flag not set; analyzing archive contents...
    subvertpy.__init__: module references __file__
    subvertpy.ra_svn: module references __file__
    Adding subvertpy 0.8.7 to easy-install.pth file
    Installing subvertpy-fast-export script to /usr/local/bin

    Installed /Library/Python/2.7/site-packages/subvertpy-0.8.7-py2.7-macosx-10.7-intel.egg
    Processing dependencies for subvertpy>=0.8.7
    Finished processing dependencies for subvertpy>=0.8.7
  2. Clone the Hgsubversion repository from Bitbucket to a temporary location on your system. Under normal circumstances, I would use easy_install again to get hgsubversion. However, version 1.2.1 in PyPi didn’t work for me. So, to get a more current working version, I went to the source repository to get the latest software.
    $ cd /tmp
    $ hg clone <a href="https://bitbucket.org/durin42/hgsubversion" title="https://bitbucket.org/durin42/hgsubversion">https://bitbucket.org/durin42/hgsubversion</a> hgsubversion-work
  3. Run the setup tool from the repository.
    $ sudo python setup.py install
    running install
    running bdist_egg
    running egg_info
    creating hgsubversion.egg-info
    writing hgsubversion.egg-info/PKG-INFO
    writing top-level names to hgsubversion.egg-info/top_level.txt
    writing dependency_links to hgsubversion.egg-info/dependency_links.txt
    writing manifest file 'hgsubversion.egg-info/SOURCES.txt'
    reading manifest file 'hgsubversion.egg-info/SOURCES.txt'
    reading manifest template 'MANIFEST.in'
    warning: no files found matching '*.rst'
    writing manifest file 'hgsubversion.egg-info/SOURCES.txt'
    installing library code to build/bdist.macosx-10.7-intel/egg
    running install_lib
    running build_py
    creating build
    creating build/lib
    creating build/lib/hgsubversion
    copying hgsubversion/__init__.py -> build/lib/hgsubversion
    copying hgsubversion/__version__.py -> build/lib/hgsubversion
    copying hgsubversion/editor.py -> build/lib/hgsubversion
    copying hgsubversion/maps.py -> build/lib/hgsubversion
    copying hgsubversion/pushmod.py -> build/lib/hgsubversion
    copying hgsubversion/replay.py -> build/lib/hgsubversion
    copying hgsubversion/stupid.py -> build/lib/hgsubversion
    copying hgsubversion/svncommands.py -> build/lib/hgsubversion
    copying hgsubversion/svnexternals.py -> build/lib/hgsubversion
    copying hgsubversion/svnmeta.py -> build/lib/hgsubversion
    copying hgsubversion/svnrepo.py -> build/lib/hgsubversion
    copying hgsubversion/util.py -> build/lib/hgsubversion
    copying hgsubversion/wrappers.py -> build/lib/hgsubversion
    creating build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/__init__.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/common.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/subvertpy_wrapper.py -> build/lib/hgsubversion/svnwrap
    copying hgsubversion/svnwrap/svn_swig_wrapper.py -> build/lib/hgsubversion/svnwrap
    creating build/lib/hgsubversion/help
    copying hgsubversion/help/subversion.rst -> build/lib/hgsubversion/help
    creating build/bdist.macosx-10.7-intel
    creating build/bdist.macosx-10.7-intel/egg
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/__init__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/__version__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/editor.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion/help
    copying build/lib/hgsubversion/help/subversion.rst -> build/bdist.macosx-10.7-intel/egg/hgsubversion/help
    copying build/lib/hgsubversion/maps.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/pushmod.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/replay.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/stupid.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svncommands.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnexternals.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnmeta.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/svnrepo.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    creating build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/__init__.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/common.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/subvertpy_wrapper.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/svnwrap/svn_swig_wrapper.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap
    copying build/lib/hgsubversion/util.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    copying build/lib/hgsubversion/wrappers.py -> build/bdist.macosx-10.7-intel/egg/hgsubversion
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/__init__.py to __init__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/__version__.py to __version__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/editor.py to editor.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/maps.py to maps.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/pushmod.py to pushmod.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/replay.py to replay.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/stupid.py to stupid.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svncommands.py to svncommands.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnexternals.py to svnexternals.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnmeta.py to svnmeta.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnrepo.py to svnrepo.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/__init__.py to __init__.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/common.py to common.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/subvertpy_wrapper.py to subvertpy_wrapper.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/svnwrap/svn_swig_wrapper.py to svn_swig_wrapper.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/util.py to util.pyc
    byte-compiling build/bdist.macosx-10.7-intel/egg/hgsubversion/wrappers.py to wrappers.pyc
    creating build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/PKG-INFO -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/SOURCES.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/dependency_links.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    copying hgsubversion.egg-info/top_level.txt -> build/bdist.macosx-10.7-intel/egg/EGG-INFO
    zip_safe flag not set; analyzing archive contents...
    hgsubversion.__init__: module references __file__
    hgsubversion.util: module references __file__
    creating dist
    creating 'dist/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg' and adding 'build/bdist.macosx-10.7-intel/egg' to it
    removing 'build/bdist.macosx-10.7-intel/egg' (and everything under it)
    Processing hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    creating /Library/Python/2.7/site-packages/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    Extracting hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg to /Library/Python/2.7/site-packages
    Adding hgsubversion 1.2.1-34-f28e0f54a6ef to easy-install.pth file

    Installed /Library/Python/2.7/site-packages/hgsubversion-1.2.1_34_f28e0f54a6ef-py2.7.egg
    Processing dependencies for hgsubversion==1.2.1-34-f28e0f54a6ef
    Finished processing dependencies for hgsubversion==1.2.1-34-f28e0f54a6ef
  4. Check the version of Hgsubversion. Since we haven’t enabled it yet, it won’t report anything terribly useful.
    $ hg version --svn
    hg version: option --svn not recognized
    hg version

    output version and copyright information

    use "hg help version" to show the full help text
  5. Add Hgsubversion to your ~/.hgrc to enable the extension. We previously enabled the “bookmarks” and “hggit” extensions.
    [extensions]
    hgext.bookmarks =
    hggit =
    hgsubversion =
  6. Check Hgsubversion’s version again. Now that it is enabled, it should report information similar to what you see below, which is very different than what we saw above.
    $ hg version --svn
    Mercurial Distributed SCM (version 1.9.2+20110831)
    (see <a href="http://mercurial.selenic.com" title="http://mercurial.selenic.com">http://mercurial.selenic.com</a> for more information)

    Copyright (C) 2005-2011 Matt Mackall and others
    This is free software; see the source for copying conditions. There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    hgsubversion: 1.2.1+34-f28e0f54a6ef
    Subversion: 1.6.16
    bindings: Subvertpy 0.8.7
  7. Run the Hgsubversion test suite. In my case, I had failures, a situation which appears to already have been reported in the Hgsubversions issue tracker.
    $ python tests/run.py
    .................................................F.........FF................................FEF............................................................................................................................................................................................................................................................................................................................................................................................................
    ======================================================================
    ERROR: test_file_map_exclude_stupid (test_fetch_mappings.MapTests)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 130, in test_file_map_exclude_stupid
        self.test_file_map_exclude(True)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 125, in test_file_map_exclude
        self.wc_path, filemap=self.filemap)
      File "/Library/Python/2.7/site-packages/mercurial/commands.py", line 1041, in clone
        branch=opts.get('branch'))
      File "/Library/Python/2.7/site-packages/mercurial/hg.py", line 334, in clone
        destrepo.clone(srcrepo, heads=revs, stream=stream)
      File "/Library/Python/2.7/site-packages/mercurial/localrepo.py", line 1946, in clone
        return self.pull(remote, heads)
      File "/private/tmp/hgsubversion_work/hgsubversion/svnrepo.py", line 48, in wrapper
        return fn(self, *args, **opts)
      File "/private/tmp/hgsubversion_work/hgsubversion/svnrepo.py", line 63, in pull
        return wrappers.pull(self, remote, heads, force)
      File "/private/tmp/hgsubversion_work/hgsubversion/wrappers.py", line 346, in pull
        firstrun)
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 642, in convert_rev
        ui, svn, meta, b, branches[b], r, parentctx)
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 228, in diff_branchrev
        files_data = patchrepo(ui, meta, parentctx, cStringIO.StringIO(d2))
      File "/private/tmp/hgsubversion_work/hgsubversion/stupid.py", line 168, in patchrepo
        ret = patch.patchbackend(ui, backend, patchfp, 0, touched)
      File "/Library/Python/2.7/site-packages/mercurial/patch.py", line 1412, in patchbackend
        raise PatchError(_('patch failed to apply'))
    PatchError: patch failed to apply

    ======================================================================
    FAIL: test_many_special_cases_diff (test_fetch_command.TestBasicRepoLayout)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 72, in test_many_special_cases_diff
        self._many_special_cases_checks(repo)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 83, in _many_special_cases_checks
        '4e256962fc5df545e2e0a51d0d1dc61c469127e6')
    AssertionError: '13c5dc1514ad8619c589a8929bfe0ece5c00f18e' != '4e256962fc5df545e2e0a51d0d1dc61c469127e6'

    ======================================================================
    FAIL: test_oldest_not_trunk_and_tag_vendor_branch (test_fetch_command.TestStupidPull)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 224, in test_oldest_not_trunk_and_tag_vendor_branch
        '1a6c3f30911d57abb67c257ec0df3e7bc44786f7')
    AssertionError: 'fa799f2781255dba874645e849d75af837472518' != '1a6c3f30911d57abb67c257ec0df3e7bc44786f7'

    ======================================================================
    FAIL: test_stupid (test_fetch_command.TestStupidPull)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_command.py", line 201, in test_stupid
        '4e256962fc5df545e2e0a51d0d1dc61c469127e6')
    AssertionError: '13c5dc1514ad8619c589a8929bfe0ece5c00f18e' != '4e256962fc5df545e2e0a51d0d1dc61c469127e6'

    ======================================================================
    FAIL: test_file_map_exclude (test_fetch_mappings.MapTests)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 127, in test_file_map_exclude
        self.assertEqual(node.hex(self.repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1')
    AssertionError: '81ae7af456c0e414ddc380ff641b37da84a9df8f' != 'b37a3c0297b71f989064d9b545b5a478bbed7cc1'

    ======================================================================
    FAIL: test_file_map_stupid (test_fetch_mappings.MapTests)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 115, in test_file_map_stupid
        self.test_file_map(True)
      File "/private/tmp/hgsubversion_work/tests/test_fetch_mappings.py", line 112, in test_file_map
        self.assertEqual(node.hex(self.repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155')
    AssertionError: 'ecf9b521a1799ebb0e01c1d1e86305ea8b542d2e' != 'e524296152246b3837fe9503c83b727075835155'

    ----------------------------------------------------------------------
    Ran 492 tests in 1212.923s

    FAILED (failures=5, errors=1)

That’s it! Hgsubversion has been installed and enabled. Even though we got errors from the tests, we can proceed.

  1. Clone a Subversion repository, like the InstaDMG one.
    • This gets you just the trunk and current state:
      $ hg clone <a href="http://instadmg.googlecode.com/svn/trunk" title="http://instadmg.googlecode.com/svn/trunk">http://instadmg.googlecode.com/svn/trunk</a> /tmp/instadmg_trunk_work
    • This gets you the full history.
      $ hg clone <a href="http://instadmg.googlecode.com/svn/" title="http://instadmg.googlecode.com/svn/">http://instadmg.googlecode.com/svn/</a> /tmp/instadmg_full_work
  2. Enable the Mercurial Keywords extension in the cloned repo’s .hg/hgrc. (According to the documentation, you should not enable the Keywords extension on a global basis in your ~/.hgrc, but instead do so on a repo-by-repo basis in .hg/hgrc.) We’ll also set it so it only affects Bash scripts with the right filename extension, because we want keyword expansion on just the “instadmg.bash” file.
    [paths]
    default = http://instadmg.googlecode.com/svn/trunk/

    [extensions]
    keyword =

    [keyword]
    # expand keywords in all bash and python files in working dir
    **.bash =
  3. Set your HGUSER environment variable. The next step will need this. The following steps will do it temporarily for your current terminal session, so when you get a chance, you’ll want to read about how to do this more permanently.
    $ HGUSER="anonymous"
    $ export HGUSER
  4. Try kwdemo. You must be in the directory for the cloned repository and have enabled the Keyword extension.
    $ hg kwdemo --default

     configuration using default svn keywordset
    [extensions]
    keyword =
    [keyword]
    **.bash =
    demo.txt =
    [keywordset]
    svn = True
    [keywordmaps]
    Author = {author|user}
    Date = {date|svnisodate}
    Id = {file|basename},v {node|short} {date|svnutcdate} {author|user}
    LastChangedBy = {author|user}
    LastChangedDate = {date|svnisodate}
    LastChangedRevision = {node|short}
    Revision = {node|short}

     keywords expanded
    $Author: anonymous $
    $Date: 2011-09-26 14:32:06 -0400 (Mon, 26 Sep 2011) $
    $Id: demo.txt,v 5e86fb5c9fae 2011-09-26 18:32:06Z anonymous $
    $LastChangedBy: anonymous $
    $LastChangedDate: 2011-09-26 14:32:06 -0400 (Mon, 26 Sep 2011) $
    $LastChangedRevision: 5e86fb5c9fae $
    $Revision: 5e86fb5c9fae $
  5. Edit the .hg/hgrc file for the cloned repository so that you can set up keyword substitution for the “Revision” keyword. In my experience, you’ll need to set up a map for this, unless you want to get the Hg revision number that you see in the default output of kwdemo above.
    [paths]
    default = http://instadmg.googlecode.com/svn/

    [extensions]
    keyword =

    [keyword]
    # expand keywords in all bash and python files in working dir
    **.bash =

    [keywordmaps]
    Revision = {svnrev}
  6. Try the demo again. I never get this part to work, but you may have more luck. Actual shrinking/expansion, which we’ll see next, does work for me.
    $ hg kwdemo --rcfile .hg/hgrc

        configuration using custom keyword template maps
       extending current template maps
    [extensions]
    keyword =
    [keyword]
    **.bash =
    demo.txt =
    [keywordset]
    svn = False
    [keywordmaps]
    Revision = {svnrev}

        keywords expanded
    $Revision:  $
  7. Expand the keywords in your local InstaDMG repository.
    $ hg kwshrink # Run after each keyword configuration change
    $ grep Revision instadmg.bash
    SVN_REVISION=`/bin/echo '$Revision$' | /usr/bin/awk '{ print $2 }'`
    $ hg kwexpand # Run to expand the keywords in the repository. Be sure to kwshrink before making and committing changes.
    $ grep Revision instadmg.bash
    SVN_REVISION=`/bin/echo '$Revision: 425 $' | /usr/bin/awk '{ print $2 }'`
    $ hg kwshrink

Notice that “Revision: 425” is returned when keywords are expanded, replacing the “$Revision” keyword. This is precisely what we need with InstaDMG, where the revision information is reported when the instadmg.bash script is run.

Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Lion

Hg-Git is the Mercurial extension to use if you want to connect to local or remote Git repositories. I exclusively use Mercurial and Hg-Git for all of my Github transactions, so I can personally vouch that it works.

Now that Hg-Git has been updated to better support Mercurial 1.9, let’s see if we can get an Hg toolchain working on Lion. Since I did that on Snow Leopard a few days ago, Hg-Git has made it into PyPI. The installation instructions this time are a bit more streamlined, because we can now use easy_install to get Hg-Git and its dependencies.

To get the toolchain set up, we’ll need Xcode. The Xcode suite includes tools we’ll need to make Python easy_install work, along with Subversion (a prerequisite for Hgsubversion, which I’ll talk about in a later article) and other useful tools.

The Xcode installation is a multi-step install process. Both current download methods — the developer download through connect.apple.com (if you have a paid Mac Developer Account) and the Mac App Store — give you an “Install Xcode” application. That application runs a second, real installer that you have to finish before you actually have the Xcode tools available in a ready-to-use state. This is very similar to the situation for Mac OS X Lion, so you may be developing a sense of familiarity with the situation.

To install Mercurial:

  1. Download Mercurial 1.9.2 or later. The binary packages are standard Mac OS X packages; get the one for Lion.
  2. Install Mercurial.

To add Hg-Git to Mercurial on Lion:

  1. Download Xcode 4.1 or later if you don’t already have it. You can do this through connect.apple.com or via the Mac App Store.
  2. Install Xcode if it is not already installed.
    • Open the developer disk image, run the installer inside it, and then run the “Install Xcode” application that was placed in /Applications.
    • Run the “Install Xcode” application that was placed in /Applications by the Mac App Store.
  3. Open Terminal. Run the following command, which will install hg-git and its dependencies (including dulwich, of which you’ll want version 0.8.0 or later):
    $ sudo easy_install 'hg-git>=0.3.1'
    Password:
    Searching for hg-git>=0.3.1
    Reading <a href="http://pypi.python.org/simple/hg-git/
    Reading"
    title="http://pypi.python.org/simple/hg-git/
    Reading"
    >http://pypi.python.org/simple/hg-git/
    Reading</a> <a href="http://hg-git.github.com/
    Best"
    title="http://hg-git.github.com/
    Best"
    >http://hg-git.github.com/
    Best</a> match: hg-git 0.3.1
    Downloading <a href="http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=4b15867a07abb0be985177581ce64cee
    Processing"
    title="http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=4b15867a07abb0be985177581ce64cee
    Processing"
    >http://pypi.python.org/packages/source/h/hg-git/hg-git-0.3.1.tar.gz#md5=...</a> hg-git-0.3.1.tar.gz
    Running hg-git-0.3.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-_Uauza/hg-git-0.3.1/egg-dist-tmp-rERQMH
    zip_safe flag not set; analyzing archive contents...
    Adding hg-git 0.3.1 to easy-install.pth file

    Installed /Library/Python/2.7/site-packages/hg_git-0.3.1-py2.7.egg
    Processing dependencies for hg-git>=0.3.1
    Searching for dulwich>=0.8.0
    Reading <a href="http://pypi.python.org/simple/dulwich/
    Reading"
    title="http://pypi.python.org/simple/dulwich/
    Reading"
    >http://pypi.python.org/simple/dulwich/
    Reading</a> <a href="http://samba.org/~jelmer/dulwich
    Reading"
    title="http://samba.org/~jelmer/dulwich
    Reading"
    >http://samba.org/~jelmer/dulwich
    Reading</a> <a href="http://launchpad.net/dulwich
    Best"
    title="http://launchpad.net/dulwich
    Best"
    >http://launchpad.net/dulwich
    Best</a> match: dulwich 0.8.0
    Downloading <a href="http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing"
    title="http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing"
    >http://samba.org/~jelmer/dulwich/dulwich-0.8.0.tar.gz
    Processing</a> dulwich-0.8.0.tar.gz
    Running dulwich-0.8.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-bHRaTM/dulwich-0.8.0/egg-dist-tmp-MNy6RK
    dulwich/_objects.c: In function ‘py_parse_tree’:
    dulwich/_objects.c:101: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c: In function ‘cmp_tree_item’:
    dulwich/_objects.c:148: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c:152: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c: In function ‘py_sorted_tree_items’:
    dulwich/_objects.c:192: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_objects.c:224: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_pack.c: In function ‘py_apply_delta’:
    dulwich/_pack.c:98: warning: implicit conversion shortens 64-bit value into a 32-bit value
    dulwich/_pack.c:101: warning: implicit conversion shortens 64-bit value into a 32-bit value
    zip_safe flag not set; analyzing archive contents...
    dulwich.tests.__init__: module references __file__
    dulwich.tests.test_index: module references __file__
    dulwich.tests.test_objects: module references __file__
    dulwich.tests.test_pack: module references __file__
    dulwich.tests.utils: module references __file__
    Adding dulwich 0.8.0 to easy-install.pth file
    Installing dul-daemon script to /usr/local/bin
    Installing dul-web script to /usr/local/bin
    Installing dulwich script to /usr/local/bin

    Installed /Library/Python/2.7/site-packages/dulwich-0.8.0-py2.7-macosx-10.7-intel.egg
    Finished processing dependencies for hg-git>=0.3.1
  4. Edit your ~/.hgrc to enable the Hg-Git Mercurial extension, as noted in the Hg-Git documentation.
    [extensions]
    hgext.bookmarks =
    hggit =

That’s it! Once Mercurial 1.9 plus Hg-Git 0.3.1 or later are installed and you’ve enabled Hg-Git in your ~/.hgrc, you are ready to use Mercurial with local and remote Git repositories.

Install Mercurial 1.9, Dulwich, and Hg-Git on Mac OS X Snow Leopard

Here is my current installation method for Mercurial 1.9 and Hg-Git on Mac OS X Snow Leopard. I use Hg-Git on a sporadic basis to work with projects on Github. That activity is frequent enough that it’s really helpful to be able to get Hg-Git set up and working quickly.

I had a rough time with Hg-Git after I upgraded to Mercurial 1.9 a few weeks ago. It was notable because that was the first time I can remember having incompatibilities between this stack of software. Since then, Hg-Git has changes available that allow it to install and work with newer versions of Mercurial, including version 1.9, and the most recent Dulwich release. These changes weren’t as readily available at the time I upgraded, so I thought it was worth writing this to remind me how to put the right pieces together.

  1. Install Mercurial 1.9.2. The installers for Leopard, Snow Leopard, and Lion are available from the main Mercurial site.
  2. Install Dulwich 0.8.0 using easy_install. Here is where I learned you could specify a minimum version or a specific version with easy_install. While doing so is not strictly necessary in this case, I am writing it down to remember the next time I need it.
    sudo easy_install 'dulwich>=0.8'
  3. Obtain hg-git 0.3.1 using Augie Fackler’s repository on Bitbucket.org. We will clone it into /tmp, so that it will exist there only until reboot. It’ll get cleaned up and removed, which is fine, because I typically don’t need this repository stored on my drive.
    cd /tmp
    hg clone ssh://hg@bitbucket.org/durin42/hg-git hg-git-work
  4. Run the setup process for hg-git from your local clone.
    cd /tmp/hg-git-work
    sudo python setup.py install
  5. Add hg-git to the "[extensions]" section of your ~/.hgrc file, creating the ~/.hgrc file if needed, as documented for Hg-Git.

I don't yet have a working installation method for all of this software on Mac OS X Lion. I ran into a stumbling block getting Dulwich installed there, and Dulwich is a necessity for Hg-Git. Update: The situation changed a few days later, and I have instructions for Mac OS X Lion available now.

This may have been a problem isolated to the particular Lion system I was using. I haven't had the chance to duplicate it elsewhere or troubleshoot the problem on the system where I encountered it.

Update: Since I wrote this, Hg-Git 0.3.1 became available through PyPI, which means you can install it via easy_install. You no longer need to clone the Hg-Git repository and run the setup process manually. Instead, you can run the following single command to get Hg-Git 0.3.1 and its dependencies (Dulwich 0.8.0 or later):

$ sudo easy_install 'hg-git>=0.3.1'

This simplifies the instructions quite a bit, but I leave the original steps above for reference.

Updating Acquia Drupal versions in a Mercurial repository

After using Acquia Drupal for a while, I took advantage of a trial subscription to the Acquia Network. The network’s services showed me that I had files present in my install that the agent could not account for.

I suspected this was happening because of the way I manage my Acquia Drupal installation with Mercurial. So, I’ve modified my previous process (and updated my instructions) to extract the downloaded tar archive with the --recursive-unlink option. This option appears to successfully remove the contents of every directory before putting new files back into them.

$ tar --strip-components=1 --directory=acquia_drupal --recursive-unlink -zxvf acquia-drupal-1.2.12.5047.tar.gz
acquia-drupal-1.2.8/
acquia-drupal-1.2.8/robots.txt
...
acquia-drupal-1.2.8/INSTALL.txt

When the archive is extracted in this way, my repository’s working directory shows modified, unknown, and deleted files. This allows me to treat each category of files individually before I commit the changes for a Drupal update as a revision.

$ hg status

The modified files will be tracked normally because they’ve already been added to the Mercurial repository, so I don’t need to do anything special for them.

The unknown files are ones that are completely new, and have not appeared in the same position in a previous revision. They have yet to be tracked by Mercurial, so I have to add them to the repository. To add just those unknown files, then, I have to pick them out from the status listing:

$ hg status --unknown

In order to operate just on those files to add them to the repository, I run a for loop:

$ for FILEPATH in <code>hg status --unknown --no-status</code>
for> do
for>    hg add "$FILEPATH"
for> done

This changes the “?” status to “A,” because the files were successfully being tracked by Mercurial.
I use the “--no-status” flag on the “status” command so that just the file paths are printed; the actual status code is not, which is appropriate for the target of the “add” command in the loop.

I do the same basic steps with deleted files. These are files that were in the previous revisions but have been deleted by the --recursive-unlink option from the tar extraction and not replaced with the extraction of the new Acquia Drupal tar archive. If the deleted files had been replaced by the tar extraction, they would either be unchanged (which would not show up in the “status” output) or marked as modified.

To remove the files that are marked as deleted from the repository’s working directory:

$ for FILEPATH in <code>hg status --deleted --no-status</code>
for> do
for>    hg remove "$FILEPATH"
for> done

However, that may be the same as simply using the following, which I have to explore further:

$ hg remove --after

So, to follow all of these changes in the repository, I run the loop for the uknown files and the loop for the deleted files. The modified files are already tracked, so I don’t need to do anything additional for them. After that, a “commit” will record all of the changes — modifications, additions, and deletions — in the repo.

These commands are based on my current understanding of Mercurial, and they do work for me right now. There could certainly be another better way to do this in one fell swoop — or at least fewer steps. I would welcome that, so if you’re aware of a way, feel free to comment or contact me.

Update: I found that the “hg addremove” command cleanly replaces all of the shell loops I mentioned above. Therefore, I recommend using it instead of the “for” loops I described.

Check a new version of Acquia Drupal into a Mercurial repository

Here is a sequence of commands and output that show how I keep the Acquia Drupal open source content management system up to date with Mercurial, the open source distributed version control system.

In the example below, my Mercurial repositories for Drupal are located in the “drupal” subdirectory of my “repo” folder. Once I’ve moved into that directory, I download the Acquia Drupal distribution with curl and then extract it into my previously-created Mercurial working directory, “acquia_drupal,” using tar.

$ cd repo/drupal
$ curl -O http://acquia.com/files/downloads/acquia-drupal-1.2.0.3780.tar.gz
$ tar --strip-path=1 --directory=acquia_drupal --recursive-unlink -zxvf acquia-drupal-1.2.0.3780.tar.gz

(Update: I added the --recursive-unlink option after I noticed that the Acquia Network control panel keeps track of extra — possibly unneeded — files and folders you have in your install. The recursive unlink option seems to avoid having stray files from old versions of modules hanging around in your repository after you install updates.)

After extracting Acquia Drupal my Mercurial working directory, I get the status of the repository. It shows there are changes from the last version I checked in — and this includes new files, denoted by a “?” at the beginning of their line.

$ cd acquia_drupal
$ hg status
M profiles/acquia/acquia.profile
? modules/acquia/acquia_connector/README.txt
? modules/acquia/acquia_connector/acquia_agent/acquia.ico
? modules/acquia/acquia_connector/acquia_agent/acquia_agent.info
? modules/acquia/acquia_connector/acquia_agent/acquia_agent.install
? modules/acquia/acquia_connector/acquia_agent/acquia_agent.module
? modules/acquia/acquia_connector/acquia_agent/acquia_agent.pages.inc
? modules/acquia/acquia_connector/acquia_agent/acquia_agent_drupal_version.inc
? modules/acquia/acquia_connector/acquia_agent/acquia_agent_streams.inc
? modules/acquia/acquia_connector/acquia_spi/acquia_spi.info
? modules/acquia/acquia_connector/acquia_spi/acquia_spi.install
? modules/acquia/acquia_connector/acquia_spi/acquia_spi.module

Since there are new files, I have to add them so they’ll be tracked by the repository. I only need to add in the parent directory for any changed files, and any new files within it will also be added for tracking.

$ hg add modules/acquia/acquia_connector
adding modules/acquia/acquia_connector/README.txt
adding modules/acquia/acquia_connector/acquia_agent/acquia.ico
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent.info
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent.install
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent.module
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent.pages.inc
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent_drupal_version.inc
adding modules/acquia/acquia_connector/acquia_agent/acquia_agent_streams.inc
adding modules/acquia/acquia_connector/acquia_spi/acquia_spi.info
adding modules/acquia/acquia_connector/acquia_spi/acquia_spi.install
adding modules/acquia/acquia_connector/acquia_spi/acquia_spi.module
$ hg status
M profiles/acquia/acquia.profile
A modules/acquia/acquia_connector/README.txt
A modules/acquia/acquia_connector/acquia_agent/acquia.ico
A modules/acquia/acquia_connector/acquia_agent/acquia_agent.info
A modules/acquia/acquia_connector/acquia_agent/acquia_agent.install
A modules/acquia/acquia_connector/acquia_agent/acquia_agent.module
A modules/acquia/acquia_connector/acquia_agent/acquia_agent.pages.inc
A modules/acquia/acquia_connector/acquia_agent/acquia_agent_drupal_version.inc
A modules/acquia/acquia_connector/acquia_agent/acquia_agent_streams.inc
A modules/acquia/acquia_connector/acquia_spi/acquia_spi.info
A modules/acquia/acquia_connector/acquia_spi/acquia_spi.install
A modules/acquia/acquia_connector/acquia_spi/acquia_spi.module

Excellent; the new files have been added. After this, I just need to accommodate the deleted files that no longer need to be tracked (created when using the “--recursive-unlink” option on tar). For that, see my newer instructions.

Now that the right files are being tracked, I need to commit the changes — modified, added, and deleted files — to the repository. This will create a new revision in the repository’s history, which I’ll tag with the text “Acquia Drupal 1.2.0.”

$ hg commit -m "Acquia Drupal 1.2.0 imported."
$ hg tag "Acquia Drupal 1.2.0"
$ hg tip
changeset:   10:423c84439928
tag:         tip
user:        Jeremy Reichman <jaharmi@jaharmi.com>
date:        Wed Jan 14 21:07:23 2009 -0600
summary:     Added tag Acquia Drupal 1.2.0 for changeset 0df92d3d243d

Once this revision is checked in, I can use it to propagate changes to other repositories. I keep the main Acquia Drupal distribution in its own repository, and then use the “hg fetch” command to pull its changes into one where I track contributed modules. That second repository is then pulled into a third repository which stores just the changes for my production Web site. The use of three repositories in this way modularizes and isolates the updates.

List changed files in a Mercurial repository with a custom output style

While trying to troubleshoot what I’d done to mess up the Mercurial repositories managing my Drupal installations last weekend, I really would have liked a way to see what files had changes in specific revisions. Each revision to a Mercurial repository affects some files, of course, but it seems awfully hard to figure what files changed in that check-in.

I have since found a way to do that by customizing the output of Mercurial. To customize output, you can create templates on the command line (with --template) or for more powerful reformatting, create an output style file.

I struggled for a while to figure out how to use style files, and eventually came up with something that works for me so far.

Since I’ve installed Mercurial from Lee Cantey’s standard binary package for Mac OS X Leopard, I created the file “map-cmdline.changedfiles” at the “/Library/Python/2.5/site-packages/mercurial/templates” path. (Where you put the file may vary depending on where Mercurial is installed, and I’m sorry but I don’t know where it gets installed on other systems.) The contents of “map-cmdline.changedfiles” are below, along with my possibly inept description of what each line is doing:

# Get all of the files in the selected revision

and stringify them, whatever that means

but do not 'tabindent' or wrap them to 68/76 columns

Without first setting changeset to the list of files

you won't get output from subsequent lines

changeset = '{files|stringify}'

List modified files, one per line

preceded by M to mimic hg status

file = 'M {file}\n'
last_file = 'M {file}\n'

List added files, one per line

preceded by A to mimic hg status

file_add = 'A {file_add}\n'
last_file_add = 'A {file_add}\n'

List deleted files, one per line

preceded by ! to mimic hg status

file_del = '! {file_del}\n'
last_file_del = '! {file_del}\n'

I don’t know why the “map-cmdline.” portion of the filename is there, but as long as I have it, I can call the style file from the command line with what follows the period. So, I can call the style with “--style changedfiles” — and that tiny bit of voodoo seems reasonable enough to me. (The other styles in the directory above, many of which end in “.tmpl” extensions, seem related to the Mercurial Web server, hgweb. I tried, but I couldn’t use their names at the command line, with or without their extensions. Plus, their contents looked HTML-ish.)

With the “map-cmdline.changedfiles” style file saved in that location, I can call Mercurial’s “log” command:

$ hg log --style changedfiles -r tip

… which gives me a list of the files changed in the “tip” (or latest revision) of the repository. I could substitute in any revision identifier for “tip.”

I haven’t actually seen the “file_add” and “file_del” keywords in action; every time I’ve used this style file in the manner described, I’ve only seen files marked as “M” — even if I’m looking at a revision where new files were first checked into the repo. I’m confused by that, but I’m not going to let it sour my day at this point.

There might have been an easier way to do this but I didn’t find one last weekend. It took me some time to figure even this bit out, and I hope writing this post saves someone new to Mercurial from future frustration.

Untar archive contents directly into a target folder

In my Mercurial-based workflow for updating Drupal sites, there is a sequence of commands I need whenever a new version of Drupal comes out. I have a hard time remembering the options for “tar” in this sequence — and my original source for the instructions differs from what I need to do on my Web host — so I need to help my memory. The tar command, as constructed below, places its output into the specified destination directory.

Here it is, with tar’s “--strip-path=1” and “-C” options:

$ cd path/to/repository/parent/directory
$ curl -O http://ftp.drupal.org/files/projects/drupal-5.12.tar.gz
$ tar --strip-path=1 -C drupal_source -zxv -f drupal-5.12.tar.gz

Syndicate content