Happy 28th, Macintosh

macosx-workstation-snowleopard-10.6.8-10K549-i386-gbUf5C-20120124-063208.png

The Mac turns 28 today. It has come a long way, and it’s amazing to think about how the original Macintosh compares to, say, a 27-inch LED-backlit aluminum-and-glass-clad LCD iMac of today. I think one could imagine the iMac being a successor to the original iMac, from its exterior design, through the software it runs, to the input devices used to interface with it.

But, will the Mac make it to thirty — and still be recognizable as a Macintosh? A lot can change in the next two years.

Change the activity name in Harmony Remote Software

I’m updating the settings for my Harmony 520 and discovered that I couldn’t change the names of activities in Harmony Remote Software v7.7.0. I am not alone, but I couldn’t find a solution. While I had the problem on Mac OS X 10.6.8, others reported problems on various other operating systems, including Microsoft Windows.

The software wouldn’t let me to change the name of more than one activity. Instead, it would let me select and highlight the text, but not edit it. It was maddening, especially when it would beep instead of accepting characters I typed.

Then, by happenstance, I clicked over to Safari to do something while I was frustrated. When I flipped back to the Harmony Remote Software, it finally accepted my typed changes.

Therefore, if you have come across this problem, try switching to another application and back to the Harmony software again.

I’m continually astounded at how aggravating the Harmony remote can be, after all the hype. I suppose it is still better than many, if not most, of the alternatives, but that isn’t saying much.

One year with a Sony Alpha SLT-A55

I’ve now spent just over one year with my EVIL (electronic viewfinder, interchangeable lens) DSLR-like camera, the Sony Alpha SLT-A55V. It has been a rewarding year for me photographically.

I can’t express how much more fun it is to use this camera compared to anything I’ve had earlier. I was in the land of point-and-shoots (including a “super zoom” of its time, the Olympus C-750UZ) before the SLT-A55.

There isn’t much I don’t like, and most of that is simply comes with the territory of a DSLR-like camera. The extra expense and bulk and even uncertainty (which lens should I take?) of having interchangeable lenses could be a drawback. All things considered, I am a happy and satisfied SLT-A55 owner, nothing more.

But beyond that, I have found this camera very freeing. I worry less and less about the pictures I take. I love the camera’s:

  • fast startup time
  • focus and depth-of-field
  • fast shot-to-shot time and rapid-fire shooting
  • automatic GPS tagging (although for best performance, you should keep the GPS Assist data up-to-date; I Download Sony GPS Assist data automatically when a memory card mounts with a LaunchAgent)
  • 1080i movies that I can play on my flat panel TV
  • availability of Minolta lenses on eBay (I wish I’d known more about this up-front), as well as new Sony lenses (online and in a more limited fashion at retail)

There’s certainly more, but this camera has removed so many barriers for me. I’ve taken at least 17,375 photos with it in a year, and I haven’t counted how many video clips. The best part is that I think a higher percentage of my images have been good than ever before.

The camera has gotten some press but I’m continually surprised that I don’t see it advertised more or presented in the weekly sales circulars (where it’s all Canon and Nikon, predictably).

Mystery of the failed SSH logins solved by discovery of empty files

I had a repeat of a problem that I’ve probably had many times over the years. I wanted to write down the solution before I forgot it, because it had been long enough since the last time that I’d forgotten the fix.

Let’s set the stage: I wanted to SSH into a remote system. In my case, that remote system was a Mac OS Snow Leopard system. The client was also Mac OS X.

On that remote system, I had enabled sshd. This is done on Snow Leopard with System Preferences > Sharing > Remote Login. (In the Sharing System Preferences, you set up the users that are allowed to remote log in, either allowing all users or setting up an ACL that only allows specific users. If you set up the ACL, it should supercede whatever additional user/group login restrictions you’ve configured in /private/etc/sshd_config.)

Then, I tried to SSH from my client system. Instead of a successful login, I was told the following — before I was prompted for a password.

$ ssh user@server.example.com
Connection closed by 192.168.1.10.

Scratching your head, you add verbosity to the SSH connection attempt.

$ ssh userid@server.example.com
[snip]
debug1: SSH2_MSG_KEXINIT sent
Connection closed by 192.168.1.10.

I searched for other people having the same problem with “debug1: SSH2_MSG_KEXINIT sent,” because other people always have the same problem and some of them wrote about it and some of those solved it. Right?

Well, in this case, the other solutions I found were not helpful to my specific situation. But many people responding to pleas for help did mention the always-good advice to “check the server logs.” Which I could do, so I did.

In the secure.log, I found several groups of lines like this corresponding to the times I had tried to log in:

Nov 5 18:47:20 server sshd[1239]: error: Could not load host key: /etc/ssh_host_key
Nov 5 18:47:20 server sshd[1239]: error: Could not load host key: /etc/ssh_host_rsa_key
Nov 5 18:47:20 server sshd[1239]: error: Could not load host key: /etc/ssh_host_dsa_key
Nov 5 18:47:20 server sshd[1239]: Disabling protocol version 1. Could not load host key

That reminded me of the common problem with SSH host keys on Radmind-managed computers. See, Mac OS X will try to create the host keys if they are missing, but not if they are zero-length. On Radmind-managed computers, it was trivially easy to get zero-length SSH host key files in /private/etc because the tendency was to manage them with negative transcripts. Files listed in negative transcripts would be created if they were missing, but they would be created as empty files (by design).

Empty SSH host key files will prevent you from logging into that system with SSH.

I checked the server and — sure enough — the host key files were zero length. I deleted them, then stopped and restarted Remote Login for good measure. This solved the problem, and I could log in from the client.

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.

Read preferences from a property list within a Mac OS X Python script

I had a need to read some settings from a Python script on Mac OS X recently. I wanted to be able to change selected parameters for the script — some of which could be site or implementation specific — without embedding them directly in the code. Since the script was Mac OS X-only, using a property list seemed like a good idea.

With customizable settings from a property list, a script could become useful and more customizable for a wider community — or even different internal audiences.

I thought reading preferences was going to take a lot of effort. I was, however, pleasantly surprised at how easily I was able to accomplish it.

Asking others who had been down this route before resulted in some links to Apple docs. I wanted a more concrete example of how it was done in Python, and I got a great one.

That example came from reading the source to munkilib from the open source project, Munki. Since Munki had its own preference file, it needed to read from it, and its example was very enlightening.

Frogor directed me to a specific spot in the Munki code. That spot demonstrated how to read a preferences file with CoreFoundation.

Munki also has its own internal defaults for preferences. The munkilib/munkicommon.py example showed how to implement default settings in their absence in a property list. That provides a fallback position so that you always have some value available. In Munki, setting the defaults was done within a function. It seemed that it would be more generic if those defaults were separated out of the preference-reading function.

My own example of how to read a plist is outlined below. This focuses on just what you need in order to read the preferences and provide default settings for a larger script. A single set of preferences can be shared between scripts, and each script can encode its set own defaults. While you can create your own keys and values, for the example below, I will use the keys “StringPreference,” “BooleanPreference,” “ArrayPreference,” and “DictionaryPreference.”

  1. Create a new property list. There are several ways to do this, including the property list editor that has been rolled into Xcode (and is no longer a standalone application) in v4.
  2. Save the file. The name will be used later.
  3. Set up the basic shell of the script that will read the plist and import CoreFoundation.
    #!/usr/bin/env python

    from Foundation import CFPreferencesCopyAppValue
  4. Add in a variable for the script’s bundle ID. The bundle ID uniquely identifies your script’s preferences. It is written in reverse DNS notation, which should be familiar to almost anyone who has dealt with property lists before. It’s handy to have this defined globally for your script so that you can refer back to it as needed.
    #!/usr/bin/env python

    from Foundation import CFPreferencesCopyAppValue

    this_bundle_id = ‘com.example.your-script-here’
  5. Create a function to read a preference by its bundle ID. The business end of the function is the use of CFPreferencesCopyAppValue get a value for a key.
    #!/usr/bin/env python

    from Foundation import CFPreferencesCopyAppValue

    this_bundle_id = ‘com.example.your-script-here’

    def get_preference(preference_key, bundle_id=this_bundle_id):
        """
        Get the preference value for a given combination of preference
        key and bundle ID. Returns the requested preference value.
        """

        # Get the specified preference key from the specified preference
        # bundle ID using CoreFoundation
        preference_value = CFPreferencesCopyAppValue(preference_key, bundle_id)
        return preference_value
  6. Tie the get_preferences function together with a default_preferences object. This way, any missing preferences will fall back to the defaults encoded in your script. Any preferences that are set in a property list will override the defaults.
    #!/usr/bin/env python

    from Foundation import CFPreferencesCopyAppValue

    this_bundle_id = ‘com.example.your-script-here’
    default_preferences = {
        ‘StringPreference’: False,
        ‘BooleanPreference’: False,
        ‘ArrayPreference’: list(),
        ‘DictionaryPreference’: dict(),
    }

    def get_preference(preference_key, bundle_id=this_bundle_id):
        """
        Get the preference value for a given combination of preference
        key and bundle ID. Returns the preference value.
        """

        # Get the specified preference key from the specified preference
        # bundle ID using CoreFoundation
        preference_value = CFPreferencesCopyAppValue(preference_key, bundle_id)
        # If the value is not set in the property list, get a default value
        # from the default_preferences objects
        if preference_value == None:
            preference_value = default_preferences.get(preference_key)
        return preference_value

The script won’t do anything yet, until we create a main() or other functions to call the get_preferences function. Since you’d be reading preferences as part of a larger script, this is fine for now. However, with what’s written already, you can test out reading preferences interactively with the shell and Python interpreter.

  1. Start by checking the property list in the shell. In my case, I created an example property list that sets two of the four preferences.
    $ defaults read com.example.your-script-here
    {
        BooleanPreference = True;
        StringPreference = abc;
    }
  2. Open Terminal, run “python” at the command prompt, and paste the script code above at the interactive Python “>>>” prompt.
  3. Run the get_preferences function and print its output.
    >>> print(get_preference(‘StringPreference’))
    abc
    >>> print(get_preference(‘BooleanPreference’))
    True
    >>> print(get_preference(‘ArrayPreference’))
    []
    >>> print(get_preference(‘DictionaryPreference’))
    {}

That’s it, the expected results were returned. Notice that the output for StringPreference and BooleanPreference are taken from the property list, while the empty array and dictionary come from the default_preferences in the script.

Update: Greg pointed out that there is a certain degree of danger using #!/usr/bin/env python as the shebang line in the script. Should a system have a non-Apple installation of Python, then /usr/bin/env python might return that. Another Python is probably less likely to be able to bridge CoreFoundation, and thus wouldn’t be able to use the CFPreferencesCopyAppValue call to read preferences.

I think the overall shebang danger is small because few Mac OS X systems will have an alternative Python installed, but clearly your chances of that increase in certain situations. To eliminate this risk, do what Munki does and insert the #!/usr/bin/python shebang instead.

That close

The news broke tonight that Steve Jobs died. That’s still sinking in — but as I paused for reflection, I was immediately taken back to an exciting moment when I was much younger and merely a few feet away from him.

t_hero.png

That was at Macworld Expo 1998 in New York, after his keynote address. I didn’t talk to him, but it certainly made my mental highlight reel.

Searching for that photo, it struck me that I’d been about the same distance away eight years later. The pictures weren’t as good and there was far more of a crowd, but you can see Steve in them.

t_hero2.png

Goodbye and Godspeed, Steve.

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.

Mac developers

Syndicate content