diff --git a/resources/sql/patches/013.commitdetail.sql b/resources/sql/patches/013.commitdetail.sql
new file mode 100644
index 000000000..940a1717d
--- /dev/null
+++ b/resources/sql/patches/013.commitdetail.sql
@@ -0,0 +1,2 @@
+ALTER TABLE phabricator_repository.repository_commitdata
+  ADD commitDetails LONGBLOB NOT NULL;
diff --git a/src/applications/diffusion/controller/base/DiffusionController.php b/src/applications/diffusion/controller/base/DiffusionController.php
index 6736571ba..55ef58e9a 100644
--- a/src/applications/diffusion/controller/base/DiffusionController.php
+++ b/src/applications/diffusion/controller/base/DiffusionController.php
@@ -1,240 +1,238 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class DiffusionController extends PhabricatorController {
 
   protected $diffusionRequest;
 
   public function willProcessRequest(array $data) {
     $this->diffusionRequest = DiffusionRequest::newFromAphrontRequestDictionary(
       $data);
   }
 
   public function setDiffusionRequest(DiffusionRequest $request) {
     $this->diffusionRequest = $request;
     return $this;
   }
 
   protected function getDiffusionRequest() {
     return $this->diffusionRequest;
   }
 
   public function buildStandardPageResponse($view, array $data) {
 
     $page = $this->buildStandardPageView();
 
     $page->setApplicationName('Diffusion');
     $page->setBaseURI('/diffusion/');
     $page->setTitle(idx($data, 'title'));
     $page->setGlyph("\xE2\x89\x88");
     $page->appendChild($view);
 
     $response = new AphrontWebpageResponse();
     return $response->setContent($page->render());
   }
 
   final protected function buildSideNav($selected, $has_change_view) {
     $nav = new AphrontSideNavView();
 
     $navs = array(
       'history' => 'History View',
       'browse'  => 'Browse View',
       'change'  => 'Change View',
     );
 
     if (!$has_change_view) {
       unset($navs['change']);
     }
 
     $drequest = $this->getDiffusionRequest();
     $repository = $drequest->getRepository();
     $callsign = $repository->getCallsign();
 
     $branch_uri = $drequest->getBranchURIComponent($drequest->getBranch());
     $path_uri = $branch_uri.$drequest->getPath();
 
     $commit_uri = null;
     $raw_commit = $drequest->getRawCommit();
     if ($raw_commit) {
       $commit_uri = ';'.$drequest->getCommitURIComponent($raw_commit);
     }
 
     foreach ($navs as $uri => $name) {
       $nav->addNavItem(
         phutil_render_tag(
           'a',
           array(
             'href'  => "/diffusion/{$callsign}/{$uri}/{$path_uri}{$commit_uri}",
             'class' =>
               ($uri == $selected
                 ? 'aphront-side-nav-selected'
                 : null),
           ),
           $name));
     }
 
     return $nav;
   }
 
   public function buildCrumbs(array $spec = array()) {
-    $drequest = $this->diffusionRequest;
-
     $crumbs = new AphrontCrumbsView();
+    $crumb_list = $this->buildCrumbList($spec);
+    $crumbs->setCrumbs($crumb_list);
+    return $crumbs;
+  }
+
+  private function buildCrumbList(array $spec = array()) {
+    $drequest = $this->getDiffusionRequest();
 
     $crumb_list = array();
 
     $repository = $drequest->getRepository();
     if ($repository) {
       $crumb_list[] = phutil_render_tag(
         'a',
         array(
           'href' => '/diffusion/',
         ),
         'Diffusion');
     } else {
       $crumb_list[] = 'Diffusion';
-      $crumbs->setCrumbs($crumb_list);
-      return $crumbs;
+      return $crumb_list;
     }
 
     $callsign = $repository->getCallsign();
     $repository_name = phutil_escape_html($repository->getName()).' Repository';
 
     $branch_name = $drequest->getBranch();
     if ($branch_name) {
       $repository_name .= ' ('.phutil_escape_html($branch_name).')';
     }
 
     $branch_uri = $drequest->getBranchURIComponent($drequest->getBranch());
 
     if (empty($spec['view']) && empty($spec['commit'])) {
       $crumb_list[] = $repository_name;
-      $crumbs->setCrumbs($crumb_list);
-      return $crumbs;
+      return $crumb_list;
     }
 
     $crumb_list[] = phutil_render_tag(
       'a',
       array(
         'href' => "/diffusion/{$callsign}/",
       ),
       $repository_name);
 
     $raw_commit = $drequest->getRawCommit();
     if (isset($spec['commit'])) {
       $crumb_list[] = "r{$callsign}{$raw_commit}";
-      $crumbs->setCrumbs($crumb_list);
-      return $crumbs;
+      return $crumb_list;
     }
 
     $view = $spec['view'];
 
     $path = null;
     if (isset($spec['path'])) {
       $path = $drequest->getPath();
     }
 
     if ($raw_commit) {
       $commit_link = DiffusionView::linkCommit(
         $repository,
         $raw_commit);
     } else {
       $commit_link = '';
     }
 
     switch ($view) {
       case 'history':
         $view_name = 'History';
         break;
       case 'browse':
         $view_name = 'Browse';
         break;
       case 'change':
         $view_name = 'Change';
         $crumb_list[] = phutil_escape_html($path).' ('.$commit_link.')';
-        $crumbs->setCrumbs($crumb_list);
-        return $crumbs;
+        return $crumb_list;
     }
 
     $view_root_uri = "/diffusion/{$callsign}/{$view}/{$branch_uri}";
     $jump_href = $view_root_uri;
 
     $view_tail_uri = null;
     if ($raw_commit) {
       $view_tail_uri = ';'.$drequest->getCommitURIComponent($raw_commit);
     }
 
     if (!strlen($path)) {
       $crumb_list[] = $view_name;
     } else {
 
       $crumb_list[] = phutil_render_tag(
         'a',
         array(
           'href' => $view_root_uri.$view_tail_uri,
         ),
         $view_name);
 
       $path_parts = explode('/', $path);
       do {
         $last = array_pop($path_parts);
       } while ($last == '');
 
       $path_sections = array();
       $thus_far = '';
       foreach ($path_parts as $path_part) {
         $thus_far .= $path_part.'/';
         $path_sections[] = phutil_render_tag(
           'a',
           array(
             'href' => $view_root_uri.$thus_far.$view_tail_uri,
           ),
           phutil_escape_html($path_part));
       }
 
       $path_sections[] = phutil_escape_html($last);
       $path_sections = '/'.implode('/', $path_sections);
 
       $jump_href = $view_root_uri.$thus_far.$last;
 
       $crumb_list[] = $path_sections;
     }
 
     $last_crumb = array_pop($crumb_list);
 
     if ($raw_commit) {
       $jump_link = phutil_render_tag(
         'a',
         array(
           'href' => $jump_href,
         ),
         'Jump to HEAD');
       $last_crumb .= " @ {$commit_link} ({$jump_link})";
     } else {
       $last_crumb .= " @ HEAD";
     }
 
     $crumb_list[] = $last_crumb;
 
-
-    $crumbs->setCrumbs($crumb_list);
-
-    return $crumbs;
+    return $crumb_list;
   }
 
 }
diff --git a/src/applications/diffusion/controller/change/DiffusionChangeController.php b/src/applications/diffusion/controller/change/DiffusionChangeController.php
index e6cb4318a..5d73afcab 100644
--- a/src/applications/diffusion/controller/change/DiffusionChangeController.php
+++ b/src/applications/diffusion/controller/change/DiffusionChangeController.php
@@ -1,61 +1,60 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class DiffusionChangeController extends DiffusionController {
 
   public function processRequest() {
     $drequest = $this->diffusionRequest;
 
     $content = array();
 
     $content[] = $this->buildCrumbs(
       array(
         'branch' => true,
         'path'   => true,
         'view'   => 'change',
       ));
 
 
     $diff_query = DiffusionDiffQuery::newFromDiffusionRequest($drequest);
     $changeset = $diff_query->loadChangeset();
 
     $changeset_view = new DifferentialChangesetListView();
     $changeset_view->setChangesets(array($changeset));
     $changeset_view->setRenderURI(
       '/diffusion/'.$drequest->getRepository()->getCallsign().'/diff/');
 
     // TODO: This is pretty awkward, unify the CSS between Diffusion and
     // Differential better.
     require_celerity_resource('differential-core-view-css');
     $content[] =
       '<div class="differential-primary-pane">'.
         $changeset_view->render().
       '</div>';
 
-
     $nav = $this->buildSideNav('change', true);
     $nav->appendChild($content);
 
     return $this->buildStandardPageResponse(
       $nav,
       array(
         'title' => 'Change',
       ));
   }
 
 }
diff --git a/src/applications/diffusion/controller/commit/DiffusionCommitController.php b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
index 575348a30..4f515f0e5 100644
--- a/src/applications/diffusion/controller/commit/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/commit/DiffusionCommitController.php
@@ -1,150 +1,171 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class DiffusionCommitController extends DiffusionController {
 
   public function processRequest() {
     $drequest = $this->getDiffusionRequest();
 
     $callsign = $drequest->getRepository()->getCallsign();
 
     $content = array();
     $content[] = $this->buildCrumbs(array(
       'commit' => true,
     ));
 
     $detail_panel = new AphrontPanelView();
 
     $repository = $drequest->getRepository();
     $commit = $drequest->loadCommit();
 
     if (!$commit) {
       // TODO: Make more user-friendly.
       throw new Exception('This commit has not parsed yet.');
     }
 
     $commit_data = $drequest->loadCommitData();
 
     $factory = new DifferentialMarkupEngineFactory();
     $engine = $factory->newDifferentialCommentMarkupEngine();
 
     require_celerity_resource('diffusion-commit-view-css');
     require_celerity_resource('phabricator-remarkup-css');
 
     $detail_panel->appendChild(
       '<div class="diffusion-commit-view">'.
         '<div class="diffusion-commit-dateline">'.
           'r'.$callsign.$commit->getCommitIdentifier().
           ' &middot; '.
           date('F jS, Y g:i A', $commit->getEpoch()).
         '</div>'.
         '<h1>Revision Detail</h1>'.
         '<div class="diffusion-commit-details">'.
           '<table class="diffusion-commit-properties">'.
             '<tr>'.
               '<th>Author:</th>'.
               '<td>'.phutil_escape_html($commit_data->getAuthorName()).'</td>'.
             '</tr>'.
           '</table>'.
           '<hr />'.
           '<div class="diffusion-commit-message phabricator-remarkup">'.
             $engine->markupText($commit_data->getCommitMessage()).
           '</div>'.
         '</div>'.
       '</div>');
 
     $content[] = $detail_panel;
 
     $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
       $drequest);
     $changes = $change_query->loadChanges();
 
     $change_table = new DiffusionCommitChangeTableView();
     $change_table->setDiffusionRequest($drequest);
     $change_table->setPathChanges($changes);
 
     // TODO: Large number of modified files check.
 
     $count = number_format(count($changes));
 
     $bad_commit = null;
     if ($count == 0) {
       $bad_commit = queryfx_one(
         id(new PhabricatorRepository())->establishConnection('r'),
         'SELECT * FROM %T WHERE fullCommitName = %s',
         PhabricatorRepository::TABLE_BADCOMMIT,
         'r'.$callsign.$commit->getCommitIdentifier());
     }
 
     if ($bad_commit) {
       $error_panel = new AphrontErrorView();
       $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE);
       $error_panel->setTitle('Bad Commit');
       $error_panel->appendChild(
         phutil_escape_html($bad_commit['description']));
 
       $content[] = $error_panel;
     } else {
       $change_panel = new AphrontPanelView();
       $change_panel->setHeader("Changes ({$count})");
       $change_panel->appendChild($change_table);
 
       $content[] = $change_panel;
 
       if ($changes) {
         $changesets = DiffusionPathChange::convertToDifferentialChangesets(
           $changes);
-        foreach ($changesets as $changeset) {
+
+        $vcs = $repository->getVersionControlSystem();
+        switch ($vcs) {
+          case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+            $vcs_supports_directory_changes = true;
+            break;
+          case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+            $vcs_supports_directory_changes = false;
+            break;
+          default:
+            throw new Exception("Unknown VCS.");
+        }
+
+        foreach ($changesets as $key => $changeset) {
+          $file_type = $changeset->getFileType();
+          if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
+            if (!$vcs_supports_directory_changes) {
+              unset($changesets[$key]);
+              continue;
+            }
+          }
+
           $branch = $drequest->getBranchURIComponent(
             $drequest->getBranch());
           $filename = $changeset->getFilename();
           $commit = $drequest->getCommit();
           $reference = "{$branch}{$filename};{$commit}";
           $changeset->setRenderingReference($reference);
         }
 
         $change_list = new DifferentialChangesetListView();
         $change_list->setChangesets($changesets);
         $change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
 
         // TODO: This is pretty awkward, unify the CSS between Diffusion and
         // Differential better.
         require_celerity_resource('differential-core-view-css');
         $change_list =
           '<div class="differential-primary-pane">'.
             $change_list->render().
           '</div>';
       } else {
         $change_list =
           '<div style="margin: 2em; color: #666; padding: 1em;
             background: #eee;">'.
             '(no changes blah blah)'.
           '</div>';
       }
 
       $content[] = $change_list;
     }
 
     return $this->buildStandardPageResponse(
       $content,
       array(
         'title' => 'Diffusion',
       ));
   }
 
 }
diff --git a/src/applications/diffusion/controller/commit/__init__.php b/src/applications/diffusion/controller/commit/__init__.php
index ac585c939..578fd0f94 100644
--- a/src/applications/diffusion/controller/commit/__init__.php
+++ b/src/applications/diffusion/controller/commit/__init__.php
@@ -1,25 +1,27 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
+phutil_require_module('phabricator', 'applications/differential/constants/changetype');
 phutil_require_module('phabricator', 'applications/differential/parser/markup');
 phutil_require_module('phabricator', 'applications/differential/view/changesetlistview');
 phutil_require_module('phabricator', 'applications/diffusion/controller/base');
 phutil_require_module('phabricator', 'applications/diffusion/data/pathchange');
 phutil_require_module('phabricator', 'applications/diffusion/query/pathchange/base');
 phutil_require_module('phabricator', 'applications/diffusion/view/commitchangetable');
+phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
 phutil_require_module('phabricator', 'applications/repository/storage/repository');
 phutil_require_module('phabricator', 'infrastructure/celerity/api');
 phutil_require_module('phabricator', 'storage/queryfx');
 phutil_require_module('phabricator', 'view/form/error');
 phutil_require_module('phabricator', 'view/layout/panel');
 
 phutil_require_module('phutil', 'markup');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('DiffusionCommitController.php');
diff --git a/src/applications/diffusion/query/diff/git/DiffusionGitDiffQuery.php b/src/applications/diffusion/query/diff/git/DiffusionGitDiffQuery.php
index 38245cb4b..38099827b 100644
--- a/src/applications/diffusion/query/diff/git/DiffusionGitDiffQuery.php
+++ b/src/applications/diffusion/query/diff/git/DiffusionGitDiffQuery.php
@@ -1,61 +1,61 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DiffusionGitDiffQuery extends DiffusionDiffQuery {
 
   protected function executeQuery() {
     $drequest = $this->getRequest();
     $repository = $drequest->getRepository();
 
     $options = array(
       '-M',
       '-C',
       '--no-ext-diff',
       '--no-color',
       '--src-prefix=a/',
       '--dst-prefix=b/',
       '-U65535',
     );
     $options = implode(' ', $options);
 
     list($raw_diff) = execx(
       "(cd %s && git diff {$options} %s^ %s -- %s)",
       $repository->getDetail('local-path'),
       $drequest->getCommit(),
       $drequest->getCommit(),
       $drequest->getPath());
 
     $parser = new ArcanistDiffParser();
     $parser->setDetectBinaryFiles(true);
     $changes = $parser->parseDiff($raw_diff);
 
     $diff = DifferentialDiff::newFromRawChanges($changes);
     $changesets = $diff->getChangesets();
     $changeset = reset($changesets);
 
     $id =
-      $drequest->getBranch().'/'.
+      $drequest->getBranchURIComponent($drequest->getBranch()).
       $drequest->getPath().';'.
       $drequest->getCommit();
 
     $changeset->setID($id);
 
     return $changeset;
   }
 
 }
diff --git a/src/applications/diffusion/query/diff/svn/__init__.php b/src/applications/diffusion/query/diff/svn/__init__.php
index 17a8b4ac4..114f3781e 100644
--- a/src/applications/diffusion/query/diff/svn/__init__.php
+++ b/src/applications/diffusion/query/diff/svn/__init__.php
@@ -1,23 +1,24 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('arcanist', 'parser/diff');
 
 phutil_require_module('phabricator', 'applications/differential/constants/changetype');
 phutil_require_module('phabricator', 'applications/differential/storage/diff');
+phutil_require_module('phabricator', 'applications/diffusion/data/pathchange');
 phutil_require_module('phabricator', 'applications/diffusion/query/diff/base');
 phutil_require_module('phabricator', 'applications/diffusion/query/pathchange/base');
 
 phutil_require_module('phutil', 'filesystem');
 phutil_require_module('phutil', 'filesystem/tempfile');
 phutil_require_module('phutil', 'future');
 phutil_require_module('phutil', 'future/exec');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('DiffusionSvnDiffQuery.php');
diff --git a/src/applications/diffusion/request/git/DiffusionGitRequest.php b/src/applications/diffusion/request/git/DiffusionGitRequest.php
index 56351b65e..f215aabfe 100644
--- a/src/applications/diffusion/request/git/DiffusionGitRequest.php
+++ b/src/applications/diffusion/request/git/DiffusionGitRequest.php
@@ -1,121 +1,128 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class DiffusionGitRequest extends DiffusionRequest {
 
   protected function initializeFromAphrontRequestDictionary() {
     parent::initializeFromAphrontRequestDictionary();
 
     $path = $this->path;
     $parts = explode('/', $path);
 
     $branch = array_shift($parts);
     $this->branch = $this->decodeBranchName($branch);
 
     foreach ($parts as $key => $part) {
       // Prevent any hyjinx since we're ultimately shipping this to the
       // filesystem under a lot of git workflows.
       if ($part == '..') {
         unset($parts[$key]);
       }
     }
 
     $this->path = implode('/', $parts);
 
     if ($this->repository) {
       $local_path = $this->repository->getDetail('local-path');
 
       // TODO: This is not terribly efficient and does not produce terribly
       // good error messages, but it seems better to put error handling code
       // here than to try to do it in every query.
 
       $branch = $this->getBranch();
 
       execx(
         '(cd %s && git rev-parse --verify %s)',
         $local_path,
         $branch);
 
       if ($this->commit) {
         list($commit) = execx(
           '(cd %s && git rev-parse --verify %s)',
           $local_path,
           $this->commit);
 
         // Beyond verifying them, expand commit short forms to full 40-character
         // sha1s.
         $this->commit = trim($commit);
 
+/*
+
+  TODO: Unclear if this is actually a good idea or not; it breaks commit views
+  at the very least.
+
         list($contains) = execx(
           '(cd %s && git branch --contains %s)',
           $local_path,
           $this->commit);
         $contains = array_filter(explode("\n", $contains));
         $found = false;
         foreach ($contains as $containing_branch) {
           $containing_branch = trim($containing_branch, "* \n");
           if ($containing_branch == $branch) {
             $found = true;
             break;
           }
         }
         if (!$found) {
           throw new Exception(
             "Commit does not exist on this branch!");
         }
+*/
+
       }
     }
 
 
   }
 
   public function getBranch() {
     if ($this->branch) {
       return $this->branch;
     }
     if ($this->repository) {
-      return $this->repository->getDetail('default-branch', 'master');
+      return $this->repository->getDetail('default-branch', 'origin/master');
     }
     throw new Exception("Unable to determine branch!");
   }
 
   public function getUriPath() {
     return '/diffusion/'.$this->getCallsign().'/browse/'.
       $this->branch.'/'.$this->path;
   }
 
   public function getCommit() {
     if ($this->commit) {
       return $this->commit;
     }
     return $this->getBranch();
   }
 
   public function getBranchURIComponent($branch) {
     return $this->encodeBranchName($branch).'/';
   }
 
   private function decodeBranchName($branch) {
     return str_replace(':', '/', $branch);
   }
 
   private function encodeBranchName($branch) {
     return str_replace('/', ':', $branch);
   }
 
 }
diff --git a/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php b/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php
index 72c11b159..8d3623355 100644
--- a/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php
+++ b/src/applications/diffusion/view/commitchangetable/DiffusionCommitChangeTableView.php
@@ -1,83 +1,84 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DiffusionCommitChangeTableView extends DiffusionView {
 
   private $pathChanges;
 
   public function setPathChanges(array $path_changes) {
     $this->pathChanges = $path_changes;
     return $this;
   }
 
   public function render() {
     $rows = array();
 
     // TODO: Experiment with path stack rendering.
 
     // TODO: Copy Away and Move Away are rendered junkily still.
 
     foreach ($this->pathChanges as $change) {
       $change_verb = DifferentialChangeType::getFullNameForChangeType(
         $change->getChangeType());
 
-      $suffix = null;
+      $path = $change->getPath();
       if ($change->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
-        $suffix = '/';
-      }
+        $path_column = phutil_escape_html($path).'/';
+      } else {
+        $hash = substr(md5($path), 0, 8);
 
-      $path = $change->getPath();
-      $hash = substr(sha1($path), 0, 7);
+        $path_column = phutil_render_tag(
+          'a',
+          array(
+            'href' => '#'.$hash,
+          ),
+          phutil_escape_html($path));
+      }
 
       $rows[] = array(
         $this->linkHistory($change->getPath()),
         $this->linkBrowse($change->getPath()),
         $this->linkChange(
           $change->getChangeType(),
           $change->getFileType(),
           $change->getPath()),
-        phutil_render_tag(
-          'a',
-          array(
-            'href' => '#'.$hash,
-          ),
-          phutil_escape_html($path).$suffix),
+        $path_column,
       );
     }
 
     $view = new AphrontTableView($rows);
     $view->setHeaders(
       array(
         'History',
         'Browse',
         'Change',
         'Path',
       ));
     $view->setColumnClasses(
       array(
         '',
         '',
         '',
         'wide',
       ));
     $view->setNoDataString('This change has not been fully parsed yet.');
 
     return $view->render();
   }
 
 }
diff --git a/src/applications/repository/storage/commitdata/PhabricatorRepositoryCommitData.php b/src/applications/repository/storage/commitdata/PhabricatorRepositoryCommitData.php
index 1e4cf5e92..119393a13 100644
--- a/src/applications/repository/storage/commitdata/PhabricatorRepositoryCommitData.php
+++ b/src/applications/repository/storage/commitdata/PhabricatorRepositoryCommitData.php
@@ -1,31 +1,35 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 class PhabricatorRepositoryCommitData extends PhabricatorRepositoryDAO {
 
   protected $commitID;
   protected $authorName;
   protected $commitMessage;
+  protected $commitDetails = array();
 
   public function getConfiguration() {
     return array(
       self::CONFIG_TIMESTAMPS => false,
+      self::CONFIG_SERIALIZATION => array(
+        'commitDetails' => self::SERIALIZATION_JSON,
+      ),
     ) + parent::getConfiguration();
   }
 
 }
diff --git a/src/applications/repository/worker/base/PhabricatorRepositoryCommitParserWorker.php b/src/applications/repository/worker/base/PhabricatorRepositoryCommitParserWorker.php
index 87ce4a69d..fade2cda7 100644
--- a/src/applications/repository/worker/base/PhabricatorRepositoryCommitParserWorker.php
+++ b/src/applications/repository/worker/base/PhabricatorRepositoryCommitParserWorker.php
@@ -1,103 +1,106 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class PhabricatorRepositoryCommitParserWorker
   extends PhabricatorWorker {
 
   protected $commit;
+  protected $repository;
 
   final public function doWork() {
     $commit_id = $this->getTaskData();
     if (!$commit_id) {
       return;
     }
 
     $commit = id(new PhabricatorRepositoryCommit())->load($commit_id);
 
     if (!$commit) {
       // TODO: Communicate permanent failure?
       return;
     }
 
     $this->commit = $commit;
 
     $repository = id(new PhabricatorRepository())->load(
       $commit->getRepositoryID());
 
     if (!$repository) {
       return;
     }
 
+    $this->repository = $repository;
+
     return $this->parseCommit($repository, $commit);
   }
 
   abstract protected function parseCommit(
     PhabricatorRepository $repository,
     PhabricatorRepositoryCommit $commit);
 
   /**
    * This method is kind of awkward here but both the SVN message and
    * change parsers use it.
    */
   protected function getSVNLogXMLObject($uri, $revision, $verbose = false) {
 
     if ($verbose) {
       $verbose = '--verbose';
     }
 
     try {
       list($xml) = execx(
         "svn log --xml {$verbose} --limit 1 --non-interactive %s@%d",
         $uri,
         $revision);
     } catch (CommandException $ex) {
       // HTTPS is generally faster and more reliable than svn+ssh, but some
       // commit messages with non-UTF8 text can't be retrieved over HTTPS, see
       // Facebook rE197184 for one example. Make an attempt to fall back to
       // svn+ssh if we've failed outright to retrieve the message.
       $fallback_uri = new PhutilURI($uri);
       if ($fallback_uri->getProtocol() != 'https') {
         throw $ex;
       }
       $fallback_uri->setProtocol('svn+ssh');
       list($xml) = execx(
         "svn log --xml {$verbose} --limit 1 --non-interactive %s@%d",
         $fallback_uri,
         $revision);
     }
 
     // Subversion may send us back commit messages which won't parse because
     // they have non UTF-8 garbage in them. Slam them into valid UTF-8.
     $xml = phutil_utf8ize($xml);
 
     return new SimpleXMLElement($xml);
   }
 
   protected function isBadCommit($full_commit_name) {
     $repository = new PhabricatorRepository();
 
     $bad_commit = queryfx_one(
       $repository->establishConnection('w'),
       'SELECT * FROM %T WHERE fullCommitName = %s',
       PhabricatorRepository::TABLE_BADCOMMIT,
       $full_commit_name);
 
     return (bool)$bad_commit;
   }
 
 }
diff --git a/webroot/index.php b/webroot/index.php
index 2c893c206..42f60905c 100644
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -1,179 +1,180 @@
 <?php
 
 /*
  * Copyright 2011 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 error_reporting(E_ALL | E_STRICT);
+ini_set('memory_limit', -1);
 
 $env = getenv('PHABRICATOR_ENV'); // Apache
 if (!$env) {
   if (isset($_ENV['PHABRICATOR_ENV'])) {
     $env = $_ENV['PHABRICATOR_ENV'];
   }
 }
 
 if (!$env) {
   phabricator_fatal_config_error(
     "The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ".
     "your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ".
     "is one of 'development', 'production', or a custom environment.");
 }
 
 if (!function_exists('mysql_connect')) {
   phabricator_fatal_config_error(
     "The PHP MySQL extension is not installed. This extension is required.");
 }
 
 if (!isset($_REQUEST['__path__'])) {
   phabricator_fatal_config_error(
     "__path__ is not set. Your rewrite rules are not configured correctly.");
 }
 
 require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php';
 
 $conf = phabricator_read_config_file($env);
 $conf['phabricator.env'] = $env;
 
 setup_aphront_basics();
 
 phutil_require_module('phabricator', 'infrastructure/env');
 PhabricatorEnv::setEnvConfig($conf);
 
 phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
 DarkConsoleXHProfPluginAPI::hookProfiler();
 
 phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api');
 set_error_handler(array('DarkConsoleErrorLogPluginAPI', 'handleError'));
 set_exception_handler(array('DarkConsoleErrorLogPluginAPI', 'handleException'));
 
 foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) {
   phutil_load_library($library);
 }
 
 
 $host = $_SERVER['HTTP_HOST'];
 $path = $_REQUEST['__path__'];
 
 switch ($host) {
   default:
     $config_key = 'aphront.default-application-configuration-class';
     $config_class = PhabricatorEnv::getEnvConfig($config_key);
     PhutilSymbolLoader::loadClass($config_class);
     $application = newv($config_class, array());
     break;
 }
 
 $application->setHost($host);
 $application->setPath($path);
 $application->willBuildRequest();
 $request = $application->buildRequest();
 $application->setRequest($request);
 list($controller, $uri_data) = $application->buildController();
 try {
   $response = $controller->willBeginExecution();
   if (!$response) {
     $controller->willProcessRequest($uri_data);
     $response = $controller->processRequest();
   }
 } catch (AphrontRedirectException $ex) {
   $response = id(new AphrontRedirectResponse())
     ->setURI($ex->getURI());
 } catch (Exception $ex) {
   $response = $application->handleException($ex);
 }
 
 $response = $application->willSendResponse($response);
 
 $response->setRequest($request);
 
 $response_string = $response->buildResponseString();
 
 $code = $response->getHTTPResponseCode();
 if ($code != 200) {
   header("HTTP/1.0 {$code}");
 }
 
 $headers = $response->getCacheHeaders();
 $headers = array_merge($headers, $response->getHeaders());
 foreach ($headers as $header) {
   list($header, $value) = $header;
   header("{$header}: {$value}");
 }
 
 // TODO: This shouldn't be possible in a production-configured environment.
 if (isset($_REQUEST['__profile__']) &&
     ($_REQUEST['__profile__'] == 'all')) {
   $profile = DarkConsoleXHProfPluginAPI::stopProfiler();
   $profile =
     '<div style="text-align: center; background: #ff00ff; padding: 1em;
                  font-size: 24px; font-weight: bold;">'.
       '<a href="/xhprof/profile/'.$profile.'/">'.
         '&gt;&gt;&gt; View Profile &lt;&lt;&lt;'.
       '</a>'.
     '</div>';
   if (strpos($response_string, '<body>') !== false) {
     $response_string = str_replace(
       '<body>',
       '<body>'.$profile,
       $response_string);
   } else {
     echo $profile;
   }
 }
 
 echo $response_string;
 
 
 /**
  * @group aphront
  */
 function setup_aphront_basics() {
   $aphront_root   = dirname(dirname(__FILE__));
   $libraries_root = dirname($aphront_root);
 
   $root = null;
   if (!empty($_SERVER['PHUTIL_LIBRARY_ROOT'])) {
     $root = $_SERVER['PHUTIL_LIBRARY_ROOT'];
   }
 
   ini_set('include_path', $libraries_root.':'.ini_get('include_path'));
   @include_once $root.'libphutil/src/__phutil_library_init__.php';
   if (!@constant('__LIBPHUTIL__')) {
     echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ".
          "include the parent directory of libphutil/.\n";
     exit(1);
   }
 
   // Load Phabricator itself using the absolute path, so we never end up doing
   // anything surprising (loading index.php and libraries from different
   // directories).
   phutil_load_library($aphront_root.'/src');
   phutil_load_library('arcanist/src');
 }
 
 function __autoload($class_name) {
   PhutilSymbolLoader::loadClass($class_name);
 }
 
 function phabricator_fatal_config_error($msg) {
   header('Content-Type: text/plain', $replace = true, $http_error = 500);
   $error = "CONFIG ERROR: ".$msg."\n";
 
   error_log($error);
   echo $error;
 
   die();
 }