diff --git a/README.txt b/README.txt
index 98d181e..a44a087 100644
--- a/README.txt
+++ b/README.txt
@@ -6,3 +6,12 @@ An alternative to using git_deploy is to install modules and themes using the
 --gitinfofile". This performs a git clone and checkout and then inserts the
 desired version information into the .info file. The "drush make" command will
 automatically write packaging information without additional options.
+
+If you use this module for several sites on the same machine, you can make use of the
+shared file cache by adding this or something like it to your settings file:
+
+$conf['git_deploy_file_cache_directory'] = '/tmp/git_deploy';
+
+The git status is also saved in a (site-specific) cache bin called
+cache_git_deploy, in case you want to use an alternative backend for your
+particular instance.
diff --git a/git_deploy.install b/git_deploy.install
index eb66c85..5123c71 100644
--- a/git_deploy.install
+++ b/git_deploy.install
@@ -1,13 +1,54 @@
 <?php
 
+/**
+ * Implements hook_requirements().
+ */
 function git_deploy_requirements($phase) {
   $requirements = array();
   if ($phase == 'runtime') {
-    $requirements['git'] = array(
-      'title' => t('git binary'),
-      'value' => t('The git binary is present and executable'),
+    $requirements['git_deploy_git_binary'] = array(
+      'title' => t('Git binary'),
+      'value' => t('The Git binary is present and executable'),
       'severity' => function_exists('exec') && exec('git') ? REQUIREMENT_OK : REQUIREMENT_ERROR,
     );
+
+    if ($directory = variable_get('git_deploy_file_cache_directory')) {
+      $requirements['git_deploy_file_cache_directory'] = array(
+        'title' => t('Git deploy - Shared cache directory'),
+        'value' => t('The shared Git deploy temporary directory is exists and writable.'),
+        'severity' => is_dir($directory) && is_writable($directory) ? REQUIREMENT_OK : REQUIREMENT_ERROR,
+      );
+    }
   }
+
   return $requirements;
 }
+
+/**
+ * Implements hook_uninstall().
+ */
+function git_deploy_uninstall() {
+  variable_del('git_deploy_file_cache_directory');
+}
+
+/**
+ * Implements hook_schema().
+ */
+function git_deploy_schema() {
+  $table = drupal_get_schema_unprocessed('system', 'cache');
+  $table['description'] = 'Cache table for git deploy to store exec command results.';
+  $schema = array('cache_git_deploy' => $table);
+
+  return $schema;
+}
+
+/**
+ * Add a separate cache table.
+ */
+function git_deploy_update_7001() {
+  if (!db_table_exists('cache_git_deploy')) {
+    $table = drupal_get_schema_unprocessed('system', 'cache');
+    $table['description'] = 'Cache table for git deploy to store exec command results.';
+    db_create_table('cache_git_deploy', $table);
+  }
+}
diff --git a/git_deploy.module b/git_deploy.module
index e35ff7c..3c7576d 100644
--- a/git_deploy.module
+++ b/git_deploy.module
@@ -7,19 +7,22 @@
  */
 
 /**
- * Implement hook_system_info_alter() to provide metadata to drupal from git.
+ * Implement hook_system_info_alter().
  *
- * We support populating $info['version'] and $info['project'].
+ * Provide metadata to drupal from git. We support populating $info['version'],
+ * $info['project'] and $info['datestamp'].
  *
- * @param $info
+ * @param array $info
  *   The module/theme info array we're altering.
- * @param $file
+ * @param object $file
  *   An object describing the filesystem location of the module/theme.
- * @param $type
+ * @param string $type
  *   Can be module or theme.
  */
 function git_deploy_system_info_alter(&$info, $file, $type = NULL) {
-  if (empty($info['version'])) {
+  static $additions = array();
+
+  if (!array_key_exists($file->uri, $additions) && empty($info['version'])) {
     $directory = dirname($file->uri);
     // Check whether this belongs to core. Speed optimization.
     if (substr($directory, 0, strlen($type)) != $type) {
@@ -29,59 +32,164 @@ function git_deploy_system_info_alter(&$info, $file, $type = NULL) {
       $git_dir = "$directory/.git";
       // Theoretically /.git could exist.
       if ($directory && file_exists($git_dir)) {
-        $git = "git --git-dir $git_dir";
-        // Find first the project name based on fetch URL.
-        // Eat error messages. >& is valid on Windows, too. Also, $output does
-        // not need initialization because it's taken by reference.
-        exec("$git remote show -n origin 2>&1", $output);
-        if ($fetch_url = preg_grep('/^\s*Fetch URL:/', $output)) {
-          $fetch_url = current($fetch_url);
-          $project_name = substr($fetch_url, strrpos($fetch_url, '/') + 1);
-          if (substr($project_name, -4) == '.git') {
-            $project_name = substr($project_name, 0, -4);
-          }
-          $info['project'] = $project_name;
+        // Git executable with basic parameters.
+        $git = 'git --git-dir ' . escapeshellarg($git_dir);
+
+        // Commit hash is required for the cache id.
+        exec("$git rev-parse --verify HEAD 2>&1", $head_hash);
+
+        // Cannot live more than one project (profile, module, theme,
+        // theme_engine) with same name in one Drupal instance, but this cache
+        // id is usable for more Drupal instance too.
+        $cache_id = "git_deploy:{$file->name}:{$head_hash[0]}";
+
+        if ($cache = _git_deploy_cache_get($cache_id)) {
+          $additions[$file->uri] = $cache->data;
         }
-        // Try to fill in branch and tag.
-        exec("$git rev-parse --abbrev-ref HEAD 2>&1", $branch);
-        $tag_found = FALSE;
-        if ($branch) {
-          $branch = $branch[0];
-          // Any Drupal-formatted branch.
-          $branch_preg =  '\d+\.x-\d+\.';
-          if (preg_match('/^' . $branch_preg . 'x$/', $branch)) {
-            $info['version'] = $branch . '-dev';
-            // Nail down the core and the major version now that we know
-            // what they are.
-            $branch_preg = preg_quote(substr($branch, 0, -1));
+        else {
+          // Find first the project name based on fetch URL.
+          // Eat error messages. >& is valid on Windows, too. Also, $output does
+          // not need initialization because it's taken by reference.
+          exec("$git remote show -n origin 2>&1", $output);
+          if ($fetch_url = preg_grep('/^\s*Fetch URL:/', $output)) {
+            $fetch_url = current($fetch_url);
+            $project_name = substr($fetch_url, strrpos($fetch_url, '/') + 1);
+            if (substr($project_name, -4) == '.git') {
+              $project_name = substr($project_name, 0, -4);
+            }
+            $additions[$file->uri]['project'] = $project_name;
           }
-          // Now try to find a tag.
-          exec("$git rev-list --topo-order --max-count=1 HEAD 2>&1", $last_tag_hash);
-          if ($last_tag_hash) {
-            exec("$git describe  --tags $last_tag_hash[0] 2>&1", $last_tag);
-            if ($last_tag) {
-              $last_tag = $last_tag[0];
-              // Make sure the tag starts as Drupal formatted (for eg.
-              // 7.x-1.0-alpha1) and if we are on a proper branch (ie. not
-              // master) then it's on that branch.
-              if (preg_match('/^(' . $branch_preg . '\d+(?:-[^-]+)?)(-(\d+-)g[0-9a-f]{7})?$/', $last_tag, $matches)) {
-                $tag_found = TRUE;
-                $info['version'] = isset($matches[2]) ? $matches[1] . '.' . $matches[3] . 'dev' : $last_tag;
+
+          // Try to fill in branch and tag.
+          exec("$git rev-parse --abbrev-ref HEAD 2>&1", $branch);
+          $tag_found = FALSE;
+          if ($branch) {
+            $branch = $branch[0];
+            // Any Drupal-formatted branch.
+            $branch_preg =  '\d+\.x-\d+\.';
+            if (preg_match('/^' . $branch_preg . 'x$/', $branch)) {
+              $additions[$file->uri]['version'] = $branch . '-dev';
+              // Nail down the core and the major version now that we know
+              // what they are.
+              $branch_preg = preg_quote(substr($branch, 0, -1));
+            }
+            // Now try to find a tag.
+            exec("$git rev-list --topo-order --max-count=1 HEAD 2>&1", $last_tag_hash);
+            if ($last_tag_hash) {
+              exec("$git describe  --tags $last_tag_hash[0] 2>&1", $last_tag);
+              if ($last_tag) {
+                $last_tag = $last_tag[0];
+                // Make sure the tag starts as Drupal formatted (for eg.
+                // 7.x-1.0-alpha1) and if we are on a proper branch (ie. not
+                // master) then it's on that branch.
+                if (preg_match('/^(' . $branch_preg . '\d+(?:-[^-]+)?)(-(\d+-)g[0-9a-f]{7})?$/', $last_tag, $matches)) {
+                  $tag_found = TRUE;
+                  $additions[$file->uri]['version'] = isset($matches[2]) ? $matches[1] . '.' . $matches[3] . 'dev' : $last_tag;
+                }
               }
             }
           }
-        }
-        if (!$tag_found) {
-          $last_tag = '';
-        }
-        // The git log -1 command always succeeds and if we are not on a
-        // tag this will happen to return the time of the last commit which
-        // is exactly what we wanted.
-        exec("$git log -1 --pretty=format:%at $last_tag 2>&1", $datestamp);
-        if ($datestamp && is_numeric($datestamp[0])) {
-          $info['datestamp'] = $datestamp[0];
+
+          if (!$tag_found) {
+            $last_tag = '';
+          }
+          // The git log -1 command always succeeds and if we are not on a
+          // tag this will happen to return the time of the last commit which
+          // is exactly what we wanted.
+          exec("$git log -1 --pretty=format:%at $last_tag 2>&1", $datestamp);
+          if ($datestamp && is_numeric($datestamp[0])) {
+            $additions[$file->uri]['datestamp'] = $datestamp[0];
+          }
+
+          // Save values into cache.
+          _git_deploy_cache_set($cache_id, $additions[$file->uri]);
         }
       }
     }
   }
+
+  if (array_key_exists($file->uri, $additions)) {
+    foreach ($additions[$file->uri] as $key => $value) {
+      $info[$key] = $value;
+    }
+  }
+}
+
+/**
+ * Returns cache object from the cache_git_deploy cache bin.
+ *
+ * @param string $cache_id
+ *   Cache identifier.
+ *
+ * @return object|bool
+ *   Cache object or FALSE.
+ */
+function _git_deploy_cache_get($cache_id) {
+  $cache_bin = 'cache_git_deploy';
+  $cache_object = cache_get($cache_id, $cache_bin);
+  if (empty($cache_object)) {
+    $cache_data = _git_deploy_file_cache_read($cache_id);
+    if ($cache_data) {
+      cache_set($cache_id, $cache_data, $cache_bin);
+      $cache_object = cache_get($cache_id, $cache_bin);
+    }
+  }
+
+  return $cache_object;
+}
+
+/**
+ * @param string $cache_id
+ *   Cache identifier.
+ * @param array $cache_data
+ */
+function _git_deploy_cache_set($cache_id, $cache_data) {
+  $cache_bin = 'cache_git_deploy';
+  cache_set($cache_id, $cache_data, $cache_bin);
+  _git_deploy_file_cache_write($cache_id, $cache_data);
+}
+
+/**
+ * Convert cache id to file name.
+ *
+ * @param string $cache_id
+ *   Cache identifier.
+ *
+ * @return string
+ *   File name.
+ */
+function _git_deploy_file_cache_name($cache_id) {
+  return str_replace(array(':', '/'), '-', $cache_id);
+}
+
+function _git_deploy_file_cache_read($cache_id) {
+  if ($directory = variable_get('git_deploy_file_cache_directory', '')) {
+    $file_name = str_replace(array(':', '/'), '-', $cache_id);
+    if (is_file("$directory/$file_name")
+      && is_readable("$directory/$file_name")
+      && ($cache_data = file_get_contents("$directory/$file_name"))
+    ) {
+      return unserialize($cache_data);
+    }
+  }
+
+  return NULL;
+}
+
+function _git_deploy_file_cache_write($cache_id, $cache_data) {
+  if ($directory = variable_get('git_deploy_file_cache_directory', '')) {
+    $file_name = _git_deploy_file_cache_name($cache_id);
+    if (is_dir($directory) || file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
+      return file_put_contents("$directory/$file_name", serialize($cache_data));
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function git_deploy_flush_caches() {
+  return array('cache_git_deploy');
 }
