diff --git a/bin/spack b/bin/spack
index 454a9a5b2d55b70bfd56f38611f8b300633cb457..cc9450ade74d2aefc87936fae25ef33cd426f662 100755
--- a/bin/spack
+++ b/bin/spack
@@ -156,8 +156,8 @@ def main():
         import spack.util.debug as debug
         debug.register_interrupt_handler()
 
-    from spack.yaml_version_check import check_yaml_versions
-    check_yaml_versions()
+    # Run any available pre-run hooks
+    spack.hooks.pre_run()
 
     spack.spack_working_dir = working_dir
     if args.mock:
diff --git a/lib/spack/spack/hooks/__init__.py b/lib/spack/spack/hooks/__init__.py
index ff4ebc2e5798f2b73508e22463daea9df41fb700..6454a865b6f12e1fb2b666bdf196edb3c1ccd796 100644
--- a/lib/spack/spack/hooks/__init__.py
+++ b/lib/spack/spack/hooks/__init__.py
@@ -64,16 +64,19 @@ class HookRunner(object):
     def __init__(self, hook_name):
         self.hook_name = hook_name
 
-    def __call__(self, pkg):
+    def __call__(self, *args, **kwargs):
         for module in all_hook_modules():
             if hasattr(module, self.hook_name):
                 hook = getattr(module, self.hook_name)
                 if hasattr(hook, '__call__'):
-                    hook(pkg)
+                    hook(*args, **kwargs)
+
 
 #
 # Define some functions that can be called to fire off hooks.
 #
+pre_run = HookRunner('pre_run')
+
 pre_install = HookRunner('pre_install')
 post_install = HookRunner('post_install')
 
diff --git a/lib/spack/spack/hooks/case_consistency.py b/lib/spack/spack/hooks/case_consistency.py
new file mode 100644
index 0000000000000000000000000000000000000000..faf38f7ae3fe3885aace14708812e091da33fc9f
--- /dev/null
+++ b/lib/spack/spack/hooks/case_consistency.py
@@ -0,0 +1,101 @@
+##############################################################################
+# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
+# Produced at the Lawrence Livermore National Laboratory.
+#
+# This file is part of Spack.
+# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
+# LLNL-CODE-647188
+#
+# For details, see https://github.com/llnl/spack
+# Please also see the LICENSE file for our notice and the LGPL.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License (as
+# published by the Free Software Foundation) version 2.1, February 1999.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
+# conditions of the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##############################################################################
+from __future__ import absolute_import
+import os
+import re
+import platform
+
+from llnl.util.filesystem import *
+
+import spack
+from spack.util.executable import *
+
+
+def pre_run():
+    if platform.system() != "Darwin":
+        return
+
+    git_case_consistency_check(spack.repo.get_repo('builtin').packages_path)
+
+
+def git_case_consistency_check(path):
+    """Re-sync case of files in a directory with git.
+
+    On case-insensitive but case-preserving filesystems like Mac OS X,
+    Git doesn't properly rename files that only had their case changed.
+
+    This checks files in a directory against git and does a
+    case-restoring rename (actually two renames, e.g.::
+
+        name -> tmp -> NAME
+
+    We use this in Spack to ensure package directories are named
+    correctly.
+
+    TODO: this check can probably be removed once package names have been
+    TODO: lowercase for a long while.
+
+    """
+    with working_dir(path):
+        # Don't bother fixing case if Spack isn't in a git repository
+        git = which('git')
+        if not git:
+            return
+
+        try:
+            git_filenames = git('ls-tree', '--name-only', 'HEAD', output=str)
+            git_filenames = set(re.split(r'\s+', git_filenames.strip()))
+        except ProcessError:
+            return  # Ignore errors calling git
+
+        lower_to_mixed = {}
+        for fn in git_filenames:
+            lower = fn.lower()
+            mixed = lower_to_mixed.setdefault(lower, [])
+            mixed.append(fn)
+
+        # Iterate through all actual files and make sure their names are
+        # the same as corresponding names in git
+        actual_filenames = os.listdir('.')
+        for actual in actual_filenames:
+            lower = actual.lower()
+
+            # not tracked by git
+            if lower not in lower_to_mixed:
+                continue
+
+            # Don't know what to do with multiple matches
+            if len(lower_to_mixed[lower]) != 1:
+                continue
+
+            # Skip if case is already correct
+            git_name = lower_to_mixed[lower][0]
+            if git_name == actual:
+                continue
+
+            # restore case with two renames
+            tmp_name = actual + '.spack.tmp'
+            os.rename(actual, tmp_name)
+            os.rename(tmp_name, git_name)
diff --git a/lib/spack/spack/yaml_version_check.py b/lib/spack/spack/hooks/yaml_version_check.py
similarity index 98%
rename from lib/spack/spack/yaml_version_check.py
rename to lib/spack/spack/hooks/yaml_version_check.py
index 2c5b511d7f84ba08dc47d7b93e272eb42d272285..a4b38198bc9bb4c7b6eafeb3e25d38898296b48e 100644
--- a/lib/spack/spack/yaml_version_check.py
+++ b/lib/spack/spack/hooks/yaml_version_check.py
@@ -31,7 +31,7 @@
 import spack.config
 
 
-def check_yaml_versions():
+def pre_run():
     check_compiler_yaml_version()