diff --git a/lib/Drupal/monitoring/Sensor/Sensors/SensorGitDirtyTree.php b/lib/Drupal/monitoring/Sensor/Sensors/SensorGitDirtyTree.php
index fac127a..eddd4fc 100644
--- a/lib/Drupal/monitoring/Sensor/Sensors/SensorGitDirtyTree.php
+++ b/lib/Drupal/monitoring/Sensor/Sensors/SensorGitDirtyTree.php
@@ -27,59 +27,94 @@ class SensorGitDirtyTree extends SensorConfigurable implements SensorExtendedInf
    *
    * @var array
    */
-  protected $cmdOutput;
+  protected $status;
+  protected $distance;
+  protected $branches;
+  protected $actualBranch;
+  protected $submodules;
 
   /**
    * {@inheritdoc}
    */
   public function runSensor(SensorResultInterface $result) {
-    $this->cmdOutput = trim(shell_exec($this->buildCMD()));
-    $result->setExpectedValue(0);
-
-    if (!empty($this->cmdOutput)) {
-      $result->setValue(count(explode("\n", $this->cmdOutput)));
-      $result->addStatusMessage('Files in unexpected state: ' . $this->getShortFileList($this->cmdOutput, 2));
+    // For security reasons, some hostings disable exec() related functions.
+    // Since they fail silently, we challenge exec() and check if the result
+    // matches the expectations.
+    if (exec('echo "enabled"') != 'enabled') {
+      $result->addStatusMessage('The function exec() is disabled. You need to enable it.');
       $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
+      return;
+    }
+
+    // Run commands.
+    $branch_control = $this->info->getSetting('check_branch');
+    if ($branch_control) {
+      $branch = $this->runSensorCommand($result, 'actual_branch_cmd');
+      $this->actualBranch = $branch[0];
+    }
+    $this->status = $this->runSensorCommand($result, 'status_cmd');
+    $this->distance = $this->runSensorCommand($result, 'ahead_cmd');
+    $this->submodules = $this->runSensorCommand($result, 'submodules_cmd');
+
+    $wrong_submodules = array();
+    foreach ($this->submodules as $submodule) {
+      $prefix = substr($submodule, 0, 1);
+      if ($prefix == '-' || $prefix == '+') {
+        $wrong_submodules[] = $submodule;
+      }
+    }
+
+    $is_expected_branch = $this->actualBranch === $this->info->getSetting('expected_branch');
+    if ($this->status || $this->distance || (!$is_expected_branch && $branch_control) || $wrong_submodules) {
+
+      //Critical situations.
+      if ($this->status) {
+        $result->addStatusMessage('@num_files files in unexpected state: @files', array(
+          '@num_files' => count($this->status),
+          '@files' => $this->getShortFileList($this->status, 2),
+        ));
+        $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
+      }
+      if ($wrong_submodules) {
+        $result->addStatusMessage('@num_submodules submodules in unexpected state: @submodules', array(
+          '@num_submodules' => count($wrong_submodules),
+          '@submodules' => $this->getShortFileList($wrong_submodules),
+        ));
+        $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
+      }
+
+      // Warnings.
+      if ($this->distance) {
+        $result->addStatusMessage('Branch is @distance ahead of origin', array('@distance' => count($this->distance)));
+        if ($result->getStatus() != SensorResultInterface::STATUS_CRITICAL) {
+          $result->setStatus(SensorResultInterface::STATUS_WARNING);
+        }
+      }
+      if (!$is_expected_branch && $branch_control) {
+        $result->addStatusMessage('Active branch @actual_branch, expected @expected_branch', array(
+          '@actual_branch' => $this->actualBranch,
+          '@expected_branch' => $this->info->getSetting('expected_branch'),
+        ));
+        if ($result->getStatus() != SensorResultInterface::STATUS_CRITICAL) {
+          $result->setStatus(SensorResultInterface::STATUS_WARNING);
+        }
+      }
     }
     else {
-      $result->setValue(0);
       $result->addStatusMessage('Git repository clean');
       $result->setStatus(SensorResultInterface::STATUS_OK);
     }
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function resultVerbose(SensorResultInterface $result) {
-    $output = 'CMD: ' . $this->buildCMD();;
-    $output .= "\n\n" . $this->cmdOutput;
-    return $output;
-  }
-
-  /**
-   * Build the command to be passed into shell_exec().
-   *
-   * @return string
-   *   Shell command.
-   */
-  protected function buildCMD() {
-    $repo_path = DRUPAL_ROOT . '/' . $this->info->getSetting('repo_path');
-    $cmd = $this->info->getSetting('cmd');
-    return 'cd ' . escapeshellarg($repo_path) . "\n$cmd  2>&1";
-  }
-
-  /**
    * Returns a shortened file list for the status message.
    *
    * @param string $input
    *   Result from running the git command.
-   *
    * @param int $max_files
    *   Limit the number of files returned.
-   *
    * @param int $max_length
-   *   Limit the length of the path to the file
+   *   Limit the length of the path to the file.
    *
    * @return string
    *   File names from $output.
@@ -87,9 +122,7 @@ class SensorGitDirtyTree extends SensorConfigurable implements SensorExtendedInf
   protected function getShortFileList($input, $max_files = 2, $max_length = 50) {
     $output = array();
     // Remove unnecessary whitespace.
-    $input = preg_replace('/\s\s+/', ' ', trim($input));
-    $lines = explode(PHP_EOL, $input);
-    foreach (array_slice($lines, 0, $max_files) as $line) {
+    foreach (array_slice($input, 0, $max_files) as $line) {
       // Separate type of modification and path to file.
       $parts = explode(' ', $line, 2);
       if (strlen($parts[1]) > $max_length) {
@@ -104,4 +137,196 @@ class SensorGitDirtyTree extends SensorConfigurable implements SensorExtendedInf
     }
     return implode(', ', $output);
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resultVerbose(SensorResultInterface $result) {
+    $output = array();
+
+    $branch_control = $this->info->getSetting('check_branch');
+    if ($branch_control) {
+      $output['check_branch'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Check branch'),
+        '#attributes' => array(),
+      );
+      $output['check_branch']['cmd'] = array(
+        '#type' => 'item',
+        '#title' => t('Command'),
+        '#markup' => $this->buildCommand('actual_branch_cmd'),
+      );
+      $output['check_branch']['output'] = array(
+        '#type' => 'item',
+        '#title' => t('Output'),
+        '#markup' => $this->actualBranch,
+        '#description' => t('Shows the current branch.'),
+        '#description_display' => 'after',
+      );
+    }
+    $output['ahead'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Ahead'),
+      '#attributes' => array(),
+    );
+    $output['ahead']['cmd'] = array(
+      '#type' => 'item',
+      '#title' => t('Command'),
+      '#markup' => $this->buildCommand('ahead_cmd'),
+    );
+    $output['ahead']['output'] = array(
+      '#type' => 'item',
+      '#title' => t('Output'),
+      '#markup' => '<pre>' . implode("\n", $this->distance) . '</pre>',
+      '#description' => t('Shows local commits that have not been pushed.'),
+      '#description_display' => 'after',
+    );
+    $output['status'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Status'),
+      '#attributes' => array(),
+    );
+    $output['status']['cmd'] = array(
+      '#type' => 'item',
+      '#title' => t('Command'),
+      '#markup' => $this->buildCommand('status_cmd'),
+    );
+    $output['status']['output'] = array(
+      '#type' => 'item',
+      '#title' => t('Output'),
+      '#markup' => '<pre>' . implode("\n", $this->status) . '</pre>',
+      '#description' => t('Shows uncommitted, changed and deleted files.'),
+      '#description_display' => 'after',
+    );
+    $output['submodules'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Submodules'),
+      '#attributes' => array(),
+    );
+    $output['submodules']['cmd'] = array(
+      '#type' => 'item',
+      '#title' => t('Command'),
+      '#markup' => $this->buildCommand('submodules_cmd'),
+    );
+    $output['submodules']['output'] = array(
+      '#type' => 'item',
+      '#title' => t('Output'),
+      '#markup' => '<pre>' . implode("\n", $this->submodules) . '</pre>',
+      '#description' => t('Run "git submodule init" to initialize missing submodules ("-" prefix) or "git submodule update" to update submodules to the correct commit ("+" prefix).'),
+      '#description_display' => 'after',
+    );
+
+    return $output;
+  }
+
+  /**
+   * Build the command to be passed into shell_exec().
+   *
+   * @param string $cmd
+   *   Command we want to run.
+   *
+   * @return string
+   *   Shell command.
+   */
+  protected function buildCommand($cmd) {
+    $repo_path = DRUPAL_ROOT . '/' . $this->info->getSetting('repo_path');
+    $cmd = $this->info->getSetting($cmd);
+    return 'cd ' . escapeshellarg($repo_path) . "\n$cmd  2>&1";
+  }
+
+  /**
+   * Run the command and set the status message and the status to the result.
+   *
+   * @param \Drupal\monitoring\Result\SensorResultInterface $result
+   *   Sensor result object.
+   * @param string $cmd
+   *   Command we want to run.
+   *
+   * @return array
+   *   Output of executing the Shell command.
+   */
+  protected function runSensorCommand(SensorResultInterface &$result, $cmd) {
+    $exit_code = 0;
+    $command = $this->buildCommand($cmd);
+    exec($command, $output, $exit_code);
+    if ($exit_code > 0) {
+      $result->addStatusMessage('Non-zero exit code @exit_code for command @command', array(
+        '@exit_code' => $exit_code,
+        '@command' => $command,
+      ));
+      $result->setStatus(SensorResultInterface::STATUS_CRITICAL);
+    }
+    return $output;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm($form, &$form_state) {
+    $form = parent::settingsForm($form, $form_state);
+
+    $form['repo_path'] = array(
+      '#type' => 'textfield',
+      '#default_value' => $this->info->getSetting('repo_path'),
+      '#title' => t('Repository path'),
+      '#description' => t('Path to the Git repository relative to the Drupal root directory.'),
+    );
+
+    $this->branches = $this->runCommand('branches_cmd', t('Failing to get Git branches, Git might not be available.'));
+    $expected_branch = $this->info->getSetting('expected_branch');
+    if (empty($expected_branch)) {
+      $expected_branch = $this->runCommand('actual_branch_cmd', t('Failing to get the actual branch, Git might not be available.'));
+    }
+    $options = array();
+    foreach ($this->branches as $branch) {
+      $options[$branch] = $branch;
+    }
+
+    $form['check_branch'] = array(
+      '#type' => 'checkbox',
+      '#default_value' => $this->info->getSetting('check_branch'),
+      '#title' => t('Branch control'),
+      '#description' => t('Check if the current branch is different from the selected.'),
+      '#disabled' => !$options,
+    );
+
+    $form['expected_branch'] = array(
+      '#type' => 'select',
+      '#default_value' => $expected_branch,
+      '#maxlength' => 255,
+      '#empty_option' => t('- Select -'),
+      '#options' => $options,
+      '#title' => t('Expected active branch'),
+      '#description' => t('The branch that is going to be checked out.'),
+      '#states' => array(
+        // Hide the branch selector when the check_branch checkbox is disabled.
+        'invisible' => array(
+          ':input[name="settings[check_branch]"]' => array('checked' => FALSE),
+        ),
+      ),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Run a command providing an error message.
+   *
+   * @param string $cmd
+   *   Command we want to run.
+   * @param string $error
+   *   Error message to show when failing.
+   *
+   * @return array
+   *   Output of executing the Shell command.
+   */
+  private function runCommand($cmd, $error) {
+    $exit_code = 0;
+    exec($this->buildCommand($cmd), $output, $exit_code);
+    if ($exit_code > 0) {
+      drupal_set_message($error, 'error');
+    }
+    return $output;
+  }
+
 }
diff --git a/monitoring.admin.inc b/monitoring.admin.inc
index 1e7abb7..4eac4ad 100644
--- a/monitoring.admin.inc
+++ b/monitoring.admin.inc
@@ -403,10 +403,15 @@ function monitoring_sensor_details($form, &$form_state, SensorInfo $sensor_info)
       );
     }
     elseif ($verbose_output = $result->getVerboseOutput()) {
-      $form['sensor_result']['verbose']['output'] = array(
-        '#type' => 'markup',
-        '#markup' => '<pre>' . $verbose_output . '</pre>',
-      );
+      if (is_array($verbose_output)) {
+        $form['sensor_result']['verbose']['output'] = $verbose_output;
+      }
+      else {
+        $form['sensor_result']['verbose']['output'] = array(
+          '#type' => 'markup',
+          '#markup' => '<pre>' . $verbose_output . '</pre>',
+        );
+      }
     }
     else {
       $form['sensor_result']['verbose']['output'] = array(
diff --git a/monitoring.monitoring_sensors.inc b/monitoring.monitoring_sensors.inc
index 3527af6..f4859ec 100644
--- a/monitoring.monitoring_sensors.inc
+++ b/monitoring.monitoring_sensors.inc
@@ -32,7 +32,13 @@ function monitoring_monitoring_sensor_info() {
       // Path of the repository relative to the Drupal root. Set to NULL if
       // the repository root is the same as Drupal root.
       'repo_path' => NULL,
-      'cmd' => 'git status --porcelain',
+      'status_cmd' => 'git status --porcelain',
+      'check_branch' => FALSE,
+      'ahead_cmd' => "git log --graph --pretty=format:'%h -%d %s (%ci)' @{u}..",
+      'branches_cmd' => 'git rev-parse --symbolic --branches',
+      'actual_branch_cmd' => 'git symbolic-ref HEAD --short',
+      'expected_branch' => NULL,
+      'submodules_cmd' => 'git submodule status --recursive',
     ),
   );
   $info['monitoring_disappeared_sensors'] = array(
diff --git a/test/tests/monitoring.core.test b/test/tests/monitoring.core.test
index d03571a..176c437 100644
--- a/test/tests/monitoring.core.test
+++ b/test/tests/monitoring.core.test
@@ -206,25 +206,103 @@ class MonitoringCoreTest extends MonitoringTestBase {
     // Enable the sensor and set cmd to output something
     monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
       'enabled' => TRUE,
-      'cmd' => 'printf "A addedfile.txt\nM sites/all/modules/monitoring/test/tests/monitoring.core.test\nD deleted file.txt"',
+      'status_cmd' => 'printf "A addedfile.txt\nM sites/all/modules/monitoring/test/tests/monitoring.core.test\nD deleted file.txt"',
+      'ahead_cmd' => 'true',
+      'submodules_cmd' => 'true',
     ));
     $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
     $this->assertTrue($result->isCritical());
     // The verbose output should contain the cmd output.
-    $this->assertTrue(strpos($result->getVerboseOutput(), "A addedfile.txt\nM sites/all/modules/monitoring/test/tests/monitoring.core.test\nD deleted file.txt") !== FALSE);
-    // Three lines of cmd output.
-    $this->assertEqual($result->getValue(), 3);
+    $verboseOutput = $result->getVerboseOutput();
+    $this->assertTrue(strpos($verboseOutput['status']['output']['#markup'], "<pre>A addedfile.txt\nM sites/all/modules/monitoring/test/tests/monitoring.core.test\nD deleted file.txt</pre>") !== FALSE);
     // Check if the sensor message has the expected value.
-    $this->assertEqual($result->getMessage(), 'Value 3, expected 0, Files in unexpected state: A addedfile.txt, M …modules/monitoring/test/tests/monitoring.core.test');
+    $this->assertEqual($result->getMessage(), "3 files in unexpected state: A addedfile.txt, M …modules/monitoring/test/tests/monitoring.core.test");
     // Now echo empty string
     monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
       'enabled' => TRUE,
-      'cmd' => 'echo ""',
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'submodules_cmd' => 'true',
     ));
-    $result = $this->runSensor('monitoring_git_dirty_tree');
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
     $this->assertTrue($result->isOk());
     // The message should say that it is ok.
     $this->assertTrue(strpos($result->getMessage(), 'Git repository clean') !== FALSE);
+    // Test 2 commits ahead of origin.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'printf "a4ea5e3ac5b7cca0c96aee4d00447c36121bd128\n299d85344fab9befbf6a275a4b64bda7b464b493"',
+      'submodules_cmd' => 'true',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isWarning());
+    $verboseOutput = $result->getVerboseOutput();
+    $this->assertTrue(strpos($verboseOutput['ahead']['output']['#markup'], "<pre>a4ea5e3ac5b7cca0c96aee4d00447c36121bd128\n299d85344fab9befbf6a275a4b64bda7b464b493</pre>") !== FALSE);
+    // Test not in main branch.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'check_branch' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'actual_branch_cmd' => 'printf "7.0.x"',
+      'expected_branch' => '8.0.x',
+      'submodules_cmd' => 'true',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isWarning());
+    $verboseOutput = $result->getVerboseOutput();
+    $this->assertTrue(strpos($verboseOutput['check_branch']['output']['#markup'], '7.0.x') !== FALSE);
+    $this->assertEqual($result->getMessage(), 'Active branch 7.0.x, expected 8.0.x');
+    // Test same as main branch.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'check_branch' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'actual_branch_cmd' => 'printf "8.0.x"',
+      'expected_branch' => '8.0.x',
+      'submodules_cmd' => 'true',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isOk());
+    $this->assertEqual($result->getMessage(), 'Git repository clean');
+    // Test submodule not initialized.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'submodules_cmd' => 'printf -- "-a5066d1778b9ec7c86631234ff2795e777bdff12 test"',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isCritical());
+    debug($result->getVerboseOutput());
+    $verboseOutput = $result->getVerboseOutput();
+    $this->assertTrue(strpos($verboseOutput['submodules']['output']['#markup'], "<pre>-a5066d1778b9ec7c86631234ff2795e777bdff12 test</pre>") !== FALSE);
+    $this->assertEqual($result->getMessage(), '1 submodules in unexpected state: -a5066d1778b9ec7c86631234ff2795e777bdff12 test');
+    // Test submodule with uncommitted changes.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'submodules_cmd' => 'printf "+156fff6ee58497fa22b57c32e22e3f13377b4120 test (8.x-1.0-240-g156fff6)"',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isCritical());
+    debug($result->getVerboseOutput());
+    $verboseOutput = $result->getVerboseOutput();
+    $this->assertTrue(strpos($verboseOutput['submodules']['output']['#markup'], "<pre>+156fff6ee58497fa22b57c32e22e3f13377b4120 test (8.x-1.0-240-g156fff6)</pre>") !== FALSE);
+    $this->assertEqual($result->getMessage(), '1 submodules in unexpected state: +156fff6ee58497fa22b57c32e22e3f13377b4120 test (8.x-1.0-240-g156fff6)');
+    // Test submodules in expected state.
+    monitoring_sensor_settings_save('monitoring_git_dirty_tree', array(
+      'enabled' => TRUE,
+      'status_cmd' => 'true',
+      'ahead_cmd' => 'true',
+      'submodules_cmd' => 'printf "a5066d1778b9ec7c86631234ff2795e777bdff12 test\n156fff6ee58497fa22b57c32e22e3f13377b4120 test (8.x-1.0-240-g156fff6)"',
+    ));
+    $result = monitoring_sensor_run('monitoring_git_dirty_tree', TRUE, TRUE);
+    $this->assertTrue($result->isOk());
+    $this->assertEqual($result->getMessage(), 'Git repository clean');
 
     // ======= Active sessions count tests ======= //
     // Create and login a user to have data in the sessions table.
