diff --git a/includes/VersioncontrolGitRepository.php b/includes/VersioncontrolGitRepository.php
index 27f0260..78d4a23 100644
--- a/includes/VersioncontrolGitRepository.php
+++ b/includes/VersioncontrolGitRepository.php
@@ -36,7 +36,7 @@ class VersioncontrolGitRepository extends VersioncontrolRepository {
    *
    * @return VersioncontrolGitEvent
    *
-   * @see VersioncontrolBackend::generateCodeArrivalEvent()
+   * @see VersioncontrolRepository::generateCodeArrivalEvent()
    */
   public function generateCodeArrivalEvent($data) {
     // Unpack all the post-receive data.
@@ -47,12 +47,24 @@ class VersioncontrolGitRepository extends VersioncontrolRepository {
         continue; // last element is often empty, skip it
       }
       list($start, $end, $refpath) = explode(' ', $refdata);
-      $merge_base_logs = $this->exec("merge-base $start $end");
-      if (count($merge_base_logs) == 1) {
-        $is_ff = (int)($merge_base_logs[0] == $start);
+      $commit_hashes = array();
+      if ($start == GIT_NULL_REV || $end == GIT_NULL_REV) {
+        // Adding or removing refs is considered a ff.
+        $is_ff = 1;
       }
       else {
-        $is_ff = 0;
+        $merge_base_logs = $this->exec("merge-base $start $end");
+        if (count($merge_base_logs) == 2) {
+          $is_ff = (int)(next($merge_base_logs) == $start);
+        }
+        else {
+          $is_ff = 0;
+        }
+        // Get the list of commits in the ref change.
+        $logs = $this->exec(sprintf('rev-list --reverse %s..%s', $start, $end));
+        while (($line = next($logs)) !== FALSE) {
+          $commit_hashes[] = trim($line);
+        }
       }
       // TODO need to accommodate other ref namespaces, such as notes
       list(, $type, $ref) = explode('/', $refpath);
@@ -62,6 +74,7 @@ class VersioncontrolGitRepository extends VersioncontrolRepository {
         'label_id' => 0, // init label_id to 0; it'll be updated later.
         'old_sha1' => $start,
         'new_sha1' => $end,
+        'commits' => $commit_hashes,
         'ff' => $is_ff,
       );
     }
@@ -285,10 +298,13 @@ class VersioncontrolGitRepository extends VersioncontrolRepository {
       $this->setEnv();
     }
     $logs = array();
-    $git_bin = _versioncontrol_git_get_binary_path();
-    exec(escapeshellcmd("$git_bin $command"), $logs);
-    array_unshift($logs, '');
-    reset($logs); // Reset the array pointer, so that we can use next().
+    if ($git_bin = _versioncontrol_git_get_binary_path()) {
+      exec(escapeshellcmd("$git_bin $command"), $logs);
+      // FIXME doing it this way is just wrong.
+      array_unshift($logs, '');
+      // Reset the array pointer, so that we can use next().
+      reset($logs);
+    }
     return $logs;
   }
 
diff --git a/includes/plugins/reposync/VersioncontrolGitRepositoryHistorySynchronizerDefault.class.php b/includes/plugins/reposync/VersioncontrolGitRepositoryHistorySynchronizerDefault.class.php
index 36c2ce0..12922a3 100644
--- a/includes/plugins/reposync/VersioncontrolGitRepositoryHistorySynchronizerDefault.class.php
+++ b/includes/plugins/reposync/VersioncontrolGitRepositoryHistorySynchronizerDefault.class.php
@@ -34,14 +34,8 @@ class VersioncontrolGitRepositoryHistorySynchronizerDefault implements Versionco
     return $branches;
   }
 
-  protected function fetchCommitHashesInDatabase($label_id = 0) {
-    $conditions = array();
-
-    if (!empty($label_id)) {
-      $conditions['branches'] = $label_id;
-    }
-
-    $commits = $this->repository->loadCommits(array(), $conditions, array('may cache' => FALSE));
+  protected function fetchCommitHashesInDatabase($ids = array(), $conditions = array()) {
+    $commits = $this->repository->loadCommits($ids, $conditions, array('may cache' => FALSE));
 
     $revisions = array();
     foreach ($commits as $commit) {
@@ -477,18 +471,11 @@ class VersioncontrolGitRepositoryHistorySynchronizerDefault implements Versionco
     return $tag_string;
   }
 
+  /**
+   * Runs a command through the repository object.
+   */
   protected function execute($command) {
-    $output = array();
-    $git = _versioncontrol_git_get_binary_path();
-
-    if ($git) {
-      exec(escapeshellcmd("$git $command"), $output);
-
-      array_unshift($output, ''); // FIXME doing it this way is just wrong.
-      reset($output); // Reset the array pointer, so that we can use next().
-    }
-
-    return $output;
+    return $this->repository->exec($command);
   }
 
   public function syncInitial() {
@@ -508,16 +495,16 @@ class VersioncontrolGitRepositoryHistorySynchronizerDefault implements Versionco
       $range = "$start..$end";
     }
 
-    $command = "log --format=format:%H $range";
+    $command = "rev-list --reverse $range";
     $logs = $this->execute($command);
 
-    $commits = array();
+    $commit_hashes = array();
     while (($line = next($logs)) !== FALSE) {
       if (($line = trim($line)) && (strlen($line) == 40)) {
-        $commits[] = $line;
+        $commit_hashes[] = $line;
       }
     }
-    return $commits;
+    return $commit_hashes;
   }
 
   public function syncEvent(VersioncontrolEvent $event) {
@@ -539,24 +526,23 @@ class VersioncontrolGitRepositoryHistorySynchronizerDefault implements Versionco
     $tags = $this->repository->fetchTags();
 
     foreach ($event as $ref) {
-      // 1. Process labels
+      // 1. Process this label.
 
       $ref->syncLabel();
       $label_db = $ref->getLabel();
 
       switch ($ref->reftype) {
-        case VERSIONCONTROL_OPERATION_BRANCH:
+        case VERSIONCONTROL_LABEL_BRANCH:
           $label_repo = $branches[$ref->refname];
-
           break;
-        case VERSIONCONTROL_OPERATION_TAG:
+        case VERSIONCONTROL_LABEL_TAG:
           $label_repo = $tags[$ref->refname];
-
           break;
       }
 
       if ($ref->eventCreatedMe() && empty($label_db) && ($label = $label_repo)) {
         $label->insert();
+        $ref->label_id = $label->label_id;
       }
       elseif ($ref->eventDeletedMe() && empty($label_repo) && ($label = $label_db)) {
         $label->delete();
@@ -572,67 +558,52 @@ class VersioncontrolGitRepositoryHistorySynchronizerDefault implements Versionco
       unset($label_db);
       unset($label_repo);
 
-      // 2. Process commits for branches
-
-      if ($label->type == VERSIONCONTROL_OPERATION_BRANCH) {
+      // 2. Process commits.
 
-        // 2.1. Add all commits that aren't currently in the branch
-        $commits = $this->getCommitInterval($ref->old_sha1, $ref->new_sha1);
-        $commits_db = $this->fetchCommitHashesInDatabase($label->label_id);
-
-        // Don't even think about parsing when there are more than five commits.
-        if (count($commits) > 5) {
-          continue;
-        }
-
-        // Get a list of all branches.
+      if ($label->type == VERSIONCONTROL_LABEL_BRANCH) {
         $branches_db = $this->fetchBranchesInDatabase();
-
-        foreach(array_diff($commits, $commits_db) as $revision) {
-          $commits = $this->repository->loadCommits(array(), array('revision' => $revision));
-          $commit = reset($commits);
-
-          if (empty($commit)) {
-            // Insert completely new commit object into database.
-            $this->parseAndInsertCommit($revision, $branches_db);
-          }
-          else {
-            // Link existing commit object to branch.
-            foreach ($commit->labels as $key => $commit_label) {
-              if ($commit_label->label_id == $label->label_id) {
-                unset($commit->labels[$key]);
-              }
-            }
-
-            $commit->labels[] = $label;
-            $commit->update();
-          }
+        if (empty($ref->commits)) {
+          $ref->commits = $this->getCommitInterval($ref->old_sha1, $ref->new_sha1);
+        }
+        $interval_operations_in_db = $this->repository->loadCommits(array(), array('revision' => $ref->commits), array('may cache' => FALSE));
+
+        $interval_revisions_in_db = array();
+        foreach ($interval_operations_in_db as $interval_operation_in_db) {
+          $interval_revisions_in_db[] = $interval_operation_in_db->revision;
+          // Update labels for known operations.
+          $interval_operation_in_db->labels[] = $label;
+          $interval_operation_in_db->updateLabels();
+        }
+        // Add commits not in DB yet.
+        foreach (array_diff($ref->commits, $interval_revisions_in_db) as $hash) {
+          $this->parseAndInsertCommit($hash, $branches_db);
         }
 
-        // 2.2. Remove all connections with commits that aren't in the branch
-        $commits_branch = $this->repository->fetchCommits($label->name);
-
-        foreach(array_diff($commits_db, $commits_branch) as $revision) {
-          $commit = reset($this->repository->loadCommits(array(), array('revision' => $revision)));
-
-          if (count($commit->labels) > 1) {
-            // There are other labels that contain this commit, just only remove the connection to the current label.
-            foreach ($commit->labels as $key => $commit_label) {
-              if ($commit_label->label_id == $label->label_id) {
-                unset($commit->labels[$key]);
+        // Only on non-fast-forward, we look for commits not anymore related
+        // with the label and remove them.
+        if ($ref->ff == 0) {
+          $label_commit_hashes = $this->repository->fetchCommits($label->name);
+
+          $label_commit_hashes_removed = array_diff($interval_revisions_in_db, $label_commit_hashes);
+          $label_operations_removed = $this->repository->loadCommits(array(), array('revision' => $label_commit_hashes));
+          foreach ($label_operations_removed as $label_operation_removed) {
+            if (count($label_operation_removed->labels) > 1) {
+              // There are other labels that contain this commit, just only
+              // remove the connection to the current label.
+              foreach ($label_operation_removed->labels as $key => $commit_label) {
+                if ($commit_label->label_id == $label->label_id) {
+                  unset($label_operation_removed->labels[$key]);
+                  break;
+                }
               }
+              $label_operation_removed->updateLabels();
+            }
+            else {
+              // It's save to completly delete the commit from the database.
+              $label_operation_removed->delete();
             }
-
-            $commit->update();
-          }
-          else {
-            // It's save to completly delete the commit from the database.
-            $commit->delete();
           }
         }
-
-        $ref->commits = $commits;
-
       }
 
     }
