diff --git a/.gitignore b/.gitignore
index 5c9e73c7b6ffc90a15059b96c59c461ca0157baf..e4a64f2142a8315460f6220762f69543f607aee3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,6 @@ htmlcov
 lextab.py
 yacctab.py
 
+loopy/_git_rev.py
+
 .cache
diff --git a/loopy/version.py b/loopy/version.py
index b3033c3a90735f09260947c4d88632ce5e9aded8..e2bdddf3639f4cbd1620ce5a8b86861324282201 100644
--- a/loopy/version.py
+++ b/loopy/version.py
@@ -21,6 +21,27 @@ THE SOFTWARE.
 """
 
 
+# {{{ find install- or run-time git revision
+
+import os
+if os.environ.get("AKPYTHON_EXEC_FROM_WITHIN_WITHIN_SETUP_PY") is not None:
+    # We're just being exec'd by setup.py. We can't import anything.
+    _git_rev = None
+
+else:
+    import loopy._git_rev as _git_rev_mod
+    _git_rev = _git_rev_mod.GIT_REVISION
+
+    # If we're running from a dev tree, the last install (and hence the most
+    # recent update of the above git rev could have taken place very long ago.
+    from pytools import find_module_git_revision
+    _runtime_git_rev = find_module_git_revision(__file__, n_levels_up=1)
+    if _runtime_git_rev is not None:
+        _git_rev = _runtime_git_rev
+
+# }}}
+
+
 VERSION = (2017, 2, 1)
 VERSION_STATUS = ""
 VERSION_TEXT = ".".join(str(x) for x in VERSION) + VERSION_STATUS
@@ -31,8 +52,7 @@ except ImportError:
     _islpy_version = "_UNKNOWN_"
 else:
     _islpy_version = islpy.version.VERSION_TEXT
-
-DATA_MODEL_VERSION = "v78-islpy%s" % _islpy_version
+DATA_MODEL_VERSION = "%s-islpy%s-%s-v0" % (VERSION_TEXT, _islpy_version, _git_rev)
 
 
 FALLBACK_LANGUAGE_VERSION = (2017, 2, 1)
diff --git a/setup.py b/setup.py
index ffa5e2fea79cbe82fe1d0c1c17137833620fa7d5..e69fea128a82542442cde64d4860bb3f66a098b5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+import os
 from setuptools import setup, find_packages
 
 ver_dic = {}
@@ -10,8 +11,59 @@ try:
 finally:
     version_file.close()
 
+os.environ["AKPYTHON_EXEC_FROM_WITHIN_WITHIN_SETUP_PY"] = "1"
 exec(compile(version_file_contents, "loopy/version.py", 'exec'), ver_dic)
 
+
+# {{{ capture git revision at install time
+
+# authoritative version in pytools/__init__.py
+def find_git_revision(tree_root):
+    # Keep this routine self-contained so that it can be copy-pasted into
+    # setup.py.
+
+    from os.path import join, exists, abspath
+    tree_root = abspath(tree_root)
+
+    if not exists(join(tree_root, ".git")):
+        return None
+
+    from subprocess import Popen, PIPE, STDOUT
+    p = Popen(["git", "rev-parse", "HEAD"], shell=False,
+              stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True,
+              cwd=tree_root)
+    (git_rev, _) = p.communicate()
+
+    import sys
+    if sys.version_info >= (3,):
+        git_rev = git_rev.decode()
+
+    git_rev = git_rev.rstrip()
+
+    retcode = p.returncode
+    assert retcode is not None
+    if retcode != 0:
+        from warnings import warn
+        warn("unable to find git revision")
+        return None
+
+    return git_rev
+
+
+def write_git_revision(package_name):
+    from os.path import dirname, join
+    dn = dirname(__file__)
+    git_rev = find_git_revision(dn)
+
+    with open(join(dn, package_name, "_git_rev.py"), "w") as outf:
+        outf.write("GIT_REVISION = %s\n" % repr(git_rev))
+
+
+write_git_revision("loopy")
+
+# }}}
+
+
 setup(name="loo.py",
       version=ver_dic["VERSION_TEXT"],
       description="A code generator for array-based code on CPUs and GPUs",
@@ -37,7 +89,7 @@ setup(name="loo.py",
           ],
 
       install_requires=[
-          "pytools>=2018.1",
+          "pytools>=2018.2",
           "pymbolic>=2016.2",
           "genpy>=2016.1.2",
           "cgen>=2016.1",