diff --git a/scripts/repository/test_connection.php b/scripts/repository/test_connection.php
new file mode 100755
index 0000000000..3f511d40c5
--- /dev/null
+++ b/scripts/repository/test_connection.php
@@ -0,0 +1,90 @@
+#!/usr/bin/env php
+<?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.
+ */
+
+$root = dirname(dirname(dirname(__FILE__)));
+require_once $root.'/scripts/__init_script__.php';
+require_once $root.'/scripts/__init_env__.php';
+
+phutil_require_module('phutil', 'console');
+phutil_require_module('phutil', 'future/exec');
+
+if (empty($argv[1])) {
+  echo "usage: test_connection.php <repository_callsign>\n";
+  exit(1);
+}
+
+echo phutil_console_wrap(
+  phutil_console_format(
+    'This script will test that you have configured valid credentials for '.
+    'access to a repository, so the Phabricator daemons can pull from it. '.
+    'You should run this as the **same user you will run the daemons as**, '.
+    'from the **same machine they will run from**. Doing this will help '.
+    'detect various problems with your configuration, such as SSH issues.'));
+
+list($whoami) = execx('whoami');
+$whoami = trim($whoami);
+
+$ok = phutil_console_confirm("Do you want to continue as '{$whoami}'?");
+if (!$ok) {
+  die(1);
+}
+
+$callsign = $argv[1];
+echo "Loading '{$callsign}' repository...\n";
+$repository = id(new PhabricatorRepository())->loadOneWhere(
+  'callsign = %s',
+  $argv[1]);
+if (!$repository) {
+  throw new Exception("No such repository exists!");
+}
+
+$vcs = $repository->getVersionControlSystem();
+
+PhutilServiceProfiler::installEchoListener();
+
+echo "Trying to connect to the remote...\n";
+switch ($vcs) {
+  case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+    $err = $repository->passthruRemoteCommand(
+      '--limit 1 log %s',
+      $repository->getRemoteURI());
+    break;
+  case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+    // Do an ls-remote on a nonexistent ref, which we expect to just return
+    // nothing.
+    $err = $repository->passthruRemoteCommand(
+      'ls-remote %s %s',
+      $repository->getRemoteURI(),
+      'just-testing');
+    break;
+  default:
+    throw new Exception("Unsupported repository type.");
+}
+
+if ($err) {
+  echo phutil_console_format(
+    "<bg:red>** FAIL **</bg> Connection failed. The credentials for this ".
+    "repository appear to be incorrectly configured.\n");
+  exit(1);
+} else {
+  echo phutil_console_format(
+    "<bg:green>** OKAY **</bg> Connection successful. The credentials for ".
+    "this repository appear to be correctly configured.\n");
+}
+
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
index 21038f4976..83d7e1f9cc 100644
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1,1564 +1,1566 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'celerity_mapper.php' to rebuild
  * it.
  * @generated
  */
 
 celerity_register_resource_map(array(
   'aphront-attached-file-view-css' =>
   array(
     'uri' => '/res/a6ca5487/rsrc/css/aphront/attached-file-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/attached-file-view.css',
   ),
   'aphront-calendar-view-css' =>
   array(
     'uri' => '/res/c86d9a4b/rsrc/css/aphront/calendar-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/calendar-view.css',
   ),
   'aphront-contextbar-view-css' =>
   array(
     'uri' => '/res/3e2f3045/rsrc/css/aphront/context-bar.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/context-bar.css',
   ),
   'aphront-crumbs-view-css' =>
   array(
     'uri' => '/res/9009e6bd/rsrc/css/aphront/crumbs-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/crumbs-view.css',
   ),
   'aphront-dark-console-css' =>
   array(
     'uri' => '/res/1a9f84bb/rsrc/css/aphront/dark-console.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/dark-console.css',
   ),
   'aphront-dialog-view-css' =>
   array(
     'uri' => '/res/1c0a5f75/rsrc/css/aphront/dialog-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/dialog-view.css',
   ),
   'aphront-error-view-css' =>
   array(
     'uri' => '/res/e4c5e4ed/rsrc/css/aphront/error-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/error-view.css',
   ),
   'aphront-form-view-css' =>
   array(
     'uri' => '/res/c79fd668/rsrc/css/aphront/form-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/form-view.css',
   ),
   'aphront-headsup-action-list-view-css' =>
   array(
     'uri' => '/res/84743e20/rsrc/css/aphront/headsup-action-list-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/headsup-action-list-view.css',
   ),
   'aphront-list-filter-view-css' =>
   array(
     'uri' => '/res/0f5ddaba/rsrc/css/aphront/list-filter-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/list-filter-view.css',
   ),
   'aphront-pager-view-css' =>
   array(
     'uri' => '/res/43fb79f0/rsrc/css/aphront/pager-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/pager-view.css',
   ),
   'aphront-panel-view-css' =>
   array(
     'uri' => '/res/58da9c70/rsrc/css/aphront/panel-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/panel-view.css',
   ),
   'aphront-request-failure-view-css' =>
   array(
     'uri' => '/res/c9a43002/rsrc/css/aphront/request-failure-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/request-failure-view.css',
   ),
   'aphront-side-nav-view-css' =>
   array(
     'uri' => '/res/f92966bd/rsrc/css/aphront/side-nav-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/side-nav-view.css',
   ),
   'aphront-table-view-css' =>
   array(
     'uri' => '/res/f4f39a2e/rsrc/css/aphront/table-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/table-view.css',
   ),
   'aphront-tokenizer-control-css' =>
   array(
     'uri' => '/res/f530af47/rsrc/css/aphront/tokenizer.css',
     'type' => 'css',
     'requires' =>
     array(
       0 => 'aphront-typeahead-control-css',
     ),
     'disk' => '/rsrc/css/aphront/tokenizer.css',
   ),
   'aphront-typeahead-control-css' =>
   array(
     'uri' => '/res/a05236a6/rsrc/css/aphront/typeahead.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/aphront/typeahead.css',
   ),
   'differential-changeset-view-css' =>
   array(
     'uri' => '/res/59279999/rsrc/css/application/differential/changeset-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/changeset-view.css',
   ),
   'differential-core-view-css' =>
   array(
     'uri' => '/res/584d40e8/rsrc/css/application/differential/core.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/core.css',
   ),
   'differential-inline-comment-editor' =>
   array(
     'uri' => '/res/5e4f0aa4/rsrc/js/application/differential/DifferentialInlineCommentEditor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-dom',
       1 => 'javelin-workflow',
       2 => 'javelin-util',
       3 => 'javelin-stratcom',
       4 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/application/differential/DifferentialInlineCommentEditor.js',
   ),
   'differential-local-commits-view-css' =>
   array(
     'uri' => '/res/8cdacd82/rsrc/css/application/differential/local-commits-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/local-commits-view.css',
   ),
   'differential-revision-add-comment-css' =>
   array(
     'uri' => '/res/849748d3/rsrc/css/application/differential/add-comment.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/add-comment.css',
   ),
   'differential-revision-comment-css' =>
   array(
     'uri' => '/res/9fb8013b/rsrc/css/application/differential/revision-comment.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-comment.css',
   ),
   'differential-revision-comment-list-css' =>
   array(
     'uri' => '/res/3b31faa3/rsrc/css/application/differential/revision-comment-list.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-comment-list.css',
   ),
   'differential-revision-detail-css' =>
   array(
     'uri' => '/res/015e5995/rsrc/css/application/differential/revision-detail.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-detail.css',
   ),
   'differential-revision-history-css' =>
   array(
     'uri' => '/res/0d7d515d/rsrc/css/application/differential/revision-history.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/revision-history.css',
   ),
   'differential-table-of-contents-css' =>
   array(
     'uri' => '/res/d173445b/rsrc/css/application/differential/table-of-contents.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/differential/table-of-contents.css',
   ),
   'diffusion-commit-view-css' =>
   array(
     'uri' => '/res/bc39d876/rsrc/css/application/diffusion/commit-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/diffusion/commit-view.css',
   ),
   'diffusion-source-css' =>
   array(
     'uri' => '/res/db4566b6/rsrc/css/application/diffusion/diffusion-source.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/diffusion/diffusion-source.css',
   ),
   'files-css' =>
   array(
     'uri' => '/res/a265a77d/rsrc/css/application/files/files.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/files/files.css',
   ),
   'herald-css' =>
   array(
     'uri' => '/res/5051f3ab/rsrc/css/application/herald/herald.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/herald/herald.css',
   ),
   'herald-rule-editor' =>
   array(
     'uri' => '/res/4d6dff2b/rsrc/js/application/herald/HeraldRuleEditor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'multirow-row-manager',
       1 => 'javelin-install',
       2 => 'javelin-typeahead',
       3 => 'javelin-util',
       4 => 'javelin-dom',
       5 => 'javelin-tokenizer',
       6 => 'javelin-typeahead-preloaded-source',
       7 => 'javelin-stratcom',
       8 => 'javelin-json',
       9 => 'phabricator-prefab',
     ),
     'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
   ),
   'herald-test-css' =>
   array(
     'uri' => '/res/c0cd6bdb/rsrc/css/application/herald/herald-test.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/herald/herald-test.css',
   ),
   'javelin-behavior' =>
   array(
     'uri' => '/res/24ce4647/rsrc/js/javelin/lib/behavior.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-magical-init',
     ),
     'disk' => '/rsrc/js/javelin/lib/behavior.js',
   ),
   'javelin-behavior-aphront-basic-tokenizer' =>
   array(
     'uri' => '/res/5e183bd5/rsrc/js/application/core/behavior-tokenizer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-typeahead',
       2 => 'javelin-tokenizer',
       3 => 'javelin-typeahead-preloaded-source',
       4 => 'javelin-dom',
       5 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
   ),
   'javelin-behavior-aphront-drag-and-drop' =>
   array(
     'uri' => '/res/ac21045a/rsrc/js/application/core/behavior-drag-and-drop.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-util',
       3 => 'phabricator-drag-and-drop-file-upload',
     ),
     'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
   ),
   'javelin-behavior-aphront-drag-and-drop-textarea' =>
   array(
     'uri' => '/res/fa7527f9/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'phabricator-drag-and-drop-file-upload',
     ),
     'disk' => '/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
   ),
   'javelin-behavior-aphront-form-disable-on-submit' =>
   array(
     'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-form.js',
   ),
   'javelin-behavior-countdown-timer' =>
   array(
     'uri' => '/res/5ee9cb13/rsrc/js/application/countdown/timer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/application/countdown/timer.js',
   ),
   'javelin-behavior-dark-console' =>
   array(
     'uri' => '/res/c80156c4/rsrc/js/application/core/behavior-dark-console.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-util',
       3 => 'javelin-dom',
       4 => 'javelin-request',
       5 => 'phabricator-keyboard-shortcut',
     ),
     'disk' => '/rsrc/js/application/core/behavior-dark-console.js',
   ),
   'javelin-behavior-differential-accept-with-errors' =>
   array(
     'uri' => '/res/41c4685b/rsrc/js/application/differential/behavior-accept-with-errors.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-accept-with-errors.js',
   ),
   'javelin-behavior-differential-add-reviewers-and-ccs' =>
   array(
     'uri' => '/res/fc352745/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-tokenizer',
       3 => 'javelin-typeahead',
       4 => 'javelin-typeahead-preloaded-source',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js',
   ),
   'javelin-behavior-differential-comment-jump' =>
   array(
     'uri' => '/res/be77fced/rsrc/js/application/differential/behavior-comment-jump.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-util',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-comment-jump.js',
   ),
   'javelin-behavior-differential-diff-radios' =>
   array(
     'uri' => '/res/004cb66f/rsrc/js/application/differential/behavior-diff-radios.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-diff-radios.js',
   ),
   'javelin-behavior-differential-edit-inline-comments' =>
   array(
     'uri' => '/res/af3bf064/rsrc/js/application/differential/behavior-edit-inline-comments.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
       3 => 'javelin-util',
       4 => 'javelin-vector',
       5 => 'differential-inline-comment-editor',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js',
   ),
   'javelin-behavior-differential-feedback-preview' =>
   array(
     'uri' => '/res/cb399257/rsrc/js/application/differential/behavior-comment-preview.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
       3 => 'javelin-request',
       4 => 'javelin-util',
       5 => 'phabricator-shaped-request',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js',
   ),
   'javelin-behavior-differential-keyboard-navigation' =>
   array(
     'uri' => '/res/e36415a2/rsrc/js/application/differential/behavior-keyboard-nav.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'phabricator-keyboard-shortcut',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-keyboard-nav.js',
   ),
   'javelin-behavior-differential-populate' =>
   array(
     'uri' => '/res/6efe5cd2/rsrc/js/application/differential/behavior-populate.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-workflow',
       2 => 'javelin-util',
       3 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-populate.js',
   ),
   'javelin-behavior-differential-show-all-comments' =>
   array(
     'uri' => '/res/bcc990f0/rsrc/js/application/differential/behavior-show-all-comments.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-show-all-comments.js',
   ),
   'javelin-behavior-differential-show-more' =>
   array(
     'uri' => '/res/eaaccd7a/rsrc/js/application/differential/behavior-show-more.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-workflow',
       3 => 'javelin-util',
       4 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/application/differential/behavior-show-more.js',
   ),
   'javelin-behavior-diffusion-jump-to' =>
   array(
     'uri' => '/res/7c42e1ba/rsrc/js/application/diffusion/behavior-jump-to.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-util',
       2 => 'javelin-vector',
       3 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/diffusion/behavior-jump-to.js',
   ),
   'javelin-behavior-diffusion-pull-lastmodified' =>
   array(
     'uri' => '/res/29fe2790/rsrc/js/application/diffusion/behavior-pull-lastmodified.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-util',
       3 => 'javelin-request',
     ),
     'disk' => '/rsrc/js/application/diffusion/behavior-pull-lastmodified.js',
   ),
   'javelin-behavior-error-log' =>
   array(
     'uri' => '/res/a5cb42a5/rsrc/js/application/core/behavior-error-log.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-error-log.js',
   ),
   'javelin-behavior-files-drag-and-drop' =>
   array(
     'uri' => '/res/0e84cc42/rsrc/js/application/core/behavior-files-drag-and-drop.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-uri',
       3 => 'phabricator-drag-and-drop-file-upload',
     ),
     'disk' => '/rsrc/js/application/core/behavior-files-drag-and-drop.js',
   ),
   'javelin-behavior-herald-rule-editor' =>
   array(
     'uri' => '/res/77a0c945/rsrc/js/application/herald/herald-rule-editor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'herald-rule-editor',
       1 => 'javelin-behavior',
     ),
     'disk' => '/rsrc/js/application/herald/herald-rule-editor.js',
   ),
   'javelin-behavior-maniphest-project-create' =>
   array(
     'uri' => '/res/85a0eaf9/rsrc/js/application/maniphest/behavior-project-create.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-stratcom',
       3 => 'javelin-workflow',
     ),
     'disk' => '/rsrc/js/application/maniphest/behavior-project-create.js',
   ),
   'javelin-behavior-maniphest-transaction-controls' =>
   array(
     'uri' => '/res/94a2a395/rsrc/js/application/maniphest/behavior-transaction-controls.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-tokenizer',
       3 => 'javelin-typeahead',
       4 => 'javelin-typeahead-preloaded-source',
     ),
     'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
   ),
   'javelin-behavior-maniphest-transaction-expand' =>
   array(
     'uri' => '/res/966410de/rsrc/js/application/maniphest/behavior-transaction-expand.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-workflow',
       3 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/application/maniphest/behavior-transaction-expand.js',
   ),
   'javelin-behavior-maniphest-transaction-preview' =>
   array(
     'uri' => '/res/34e22038/rsrc/js/application/maniphest/behavior-transaction-preview.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-util',
       3 => 'phabricator-shaped-request',
     ),
     'disk' => '/rsrc/js/application/maniphest/behavior-transaction-preview.js',
   ),
+  0 =>
+  array(
+    'uri' => '/res/1da00bfe/rsrc/js/javelin/lib/__tests__/URI.js',
+    'type' => 'js',
+    'requires' =>
+    array(
+      0 => 'javelin-uri',
+      1 => 'javelin-php-serializer',
+    ),
+    'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
+  ),
   'javelin-behavior-owners-path-editor' =>
   array(
     'uri' => '/res/9cf78ffc/rsrc/js/application/owners/owners-path-editor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'owners-path-editor',
       1 => 'javelin-behavior',
     ),
     'disk' => '/rsrc/js/application/owners/owners-path-editor.js',
   ),
   'javelin-behavior-phabricator-keyboard-shortcuts' =>
   array(
     'uri' => '/res/94b009e2/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-workflow',
       2 => 'javelin-json',
       3 => 'phabricator-keyboard-shortcut',
     ),
     'disk' => '/rsrc/js/application/core/behavior-keyboard-shortcuts.js',
   ),
   'javelin-behavior-phabricator-object-selector' =>
   array(
     'uri' => '/res/1f7867ca/rsrc/js/application/core/behavior-object-selector.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-request',
       3 => 'javelin-util',
       4 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
   ),
   'javelin-behavior-phabricator-watch-anchor' =>
   array(
     'uri' => '/res/46298448/rsrc/js/application/core/behavior-watch-anchor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-util',
       3 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-watch-anchor.js',
   ),
   'javelin-behavior-phriction-document-preview' =>
   array(
     'uri' => '/res/f1665ecd/rsrc/js/application/phriction/phriction-document-preview.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-dom',
       2 => 'javelin-util',
       3 => 'phabricator-shaped-request',
     ),
     'disk' => '/rsrc/js/application/phriction/phriction-document-preview.js',
   ),
   'javelin-behavior-projects-resource-editor' =>
   array(
     'uri' => '/res/a54d5616/rsrc/js/application/projects/projects-resource-editor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'phabricator-prefab',
       2 => 'multirow-row-manager',
       3 => 'javelin-tokenizer',
       4 => 'javelin-typeahead-preloaded-source',
       5 => 'javelin-typeahead',
       6 => 'javelin-dom',
       7 => 'javelin-json',
       8 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/application/projects/projects-resource-editor.js',
   ),
   'javelin-behavior-refresh-csrf' =>
   array(
     'uri' => '/res/88beba4c/rsrc/js/application/core/behavior-refresh-csrf.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-request',
       1 => 'javelin-behavior',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/core/behavior-refresh-csrf.js',
   ),
   'javelin-behavior-workflow' =>
   array(
     'uri' => '/res/079f49c3/rsrc/js/application/core/behavior-workflow.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-behavior',
       1 => 'javelin-stratcom',
       2 => 'javelin-workflow',
     ),
     'disk' => '/rsrc/js/application/core/behavior-workflow.js',
   ),
   'javelin-color' =>
   array(
     'uri' => '/res/b0439fc9/rsrc/js/javelin/ext/fx/Color.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/javelin/ext/fx/Color.js',
   ),
   'javelin-dom' =>
   array(
     'uri' => '/res/0fb82146/rsrc/js/javelin/lib/DOM.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-magical-init',
       1 => 'javelin-install',
       2 => 'javelin-util',
       3 => 'javelin-vector',
       4 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/javelin/lib/DOM.js',
   ),
   'javelin-dynval' =>
   array(
     'uri' => '/res/d89c6f88/rsrc/js/javelin/ext/reactor/core/DynVal.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-reactornode',
       2 => 'javelin-util',
       3 => 'javelin-reactor',
     ),
     'disk' => '/rsrc/js/javelin/ext/reactor/core/DynVal.js',
   ),
   'javelin-event' =>
   array(
     'uri' => '/res/f14f00d7/rsrc/js/javelin/core/Event.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/javelin/core/Event.js',
   ),
   'javelin-fx' =>
   array(
     'uri' => '/res/2b609814/rsrc/js/javelin/ext/fx/FX.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-color',
       1 => 'javelin-install',
       2 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/ext/fx/FX.js',
   ),
   'javelin-install' =>
   array(
     'uri' => '/res/a7a57bf8/rsrc/js/javelin/core/install.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-util',
       1 => 'javelin-magical-init',
     ),
     'disk' => '/rsrc/js/javelin/core/install.js',
   ),
   'javelin-json' =>
   array(
     'uri' => '/res/561b8056/rsrc/js/javelin/lib/JSON.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/javelin/lib/JSON.js',
   ),
   'javelin-magical-init' =>
   array(
     'uri' => '/res/6a069362/rsrc/js/javelin/core/init.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/core/init.js',
   ),
   'javelin-mask' =>
   array(
     'uri' => '/res/03ef78b8/rsrc/js/javelin/lib/Mask.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-vector',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/javelin/lib/Mask.js',
   ),
   'javelin-reactor' =>
   array(
     'uri' => '/res/64d41874/rsrc/js/javelin/ext/reactor/core/Reactor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-dynval',
       2 => 'javelin-reactornode',
       3 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/ext/reactor/core/Reactor.js',
   ),
   'javelin-reactor-dom' =>
   array(
     'uri' => '/res/d8ab6a5f/rsrc/js/javelin/ext/reactor/dom/RDOM.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-dom',
       1 => 'javelin-dynval',
       2 => 'javelin-reactornode',
       3 => 'javelin-install',
       4 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/ext/reactor/dom/RDOM.js',
   ),
   'javelin-reactor-node-calmer' =>
   array(
     'uri' => '/res/b8a3ae50/rsrc/js/javelin/ext/reactor/core/ReactorNodeCalmer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-reactor',
       2 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/ext/reactor/core/ReactorNodeCalmer.js',
   ),
   'javelin-reactornode' =>
   array(
     'uri' => '/res/f278cc27/rsrc/js/javelin/ext/reactor/core/ReactorNode.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-reactor',
       2 => 'javelin-util',
       3 => 'javelin-reactor-node-calmer',
     ),
     'disk' => '/rsrc/js/javelin/ext/reactor/core/ReactorNode.js',
   ),
   'javelin-request' =>
   array(
     'uri' => '/res/80744b69/rsrc/js/javelin/lib/Request.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-stratcom',
       2 => 'javelin-util',
       3 => 'javelin-behavior',
       4 => 'javelin-json',
     ),
     'disk' => '/rsrc/js/javelin/lib/Request.js',
   ),
   'javelin-resource' =>
   array(
     'uri' => '/res/1ebc5a0d/rsrc/js/javelin/lib/Resource.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-magical-init',
       1 => 'javelin-stratcom',
       2 => 'javelin-util',
       3 => 'javelin-uri',
     ),
     'disk' => '/rsrc/js/javelin/lib/Resource.js',
   ),
   'javelin-stratcom' =>
   array(
     'uri' => '/res/da8a2726/rsrc/js/javelin/core/Stratcom.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-event',
       2 => 'javelin-util',
       3 => 'javelin-magical-init',
     ),
     'disk' => '/rsrc/js/javelin/core/Stratcom.js',
   ),
   'javelin-tokenizer' =>
   array(
     'uri' => '/res/5b541fb2/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-dom',
       1 => 'javelin-util',
       2 => 'javelin-stratcom',
       3 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
   ),
   'javelin-typeahead' =>
   array(
     'uri' => '/res/9169c000/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-dom',
       2 => 'javelin-vector',
       3 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
   ),
   'javelin-typeahead-composite-source' =>
   array(
     'uri' => '/res/7c0d631f/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-typeahead-source',
       2 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js',
   ),
   'javelin-typeahead-normalizer' =>
   array(
     'uri' => '/res/8b6055d4/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
   ),
   'javelin-typeahead-ondemand-source' =>
   array(
     'uri' => '/res/d82d7faf/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-stratcom',
       3 => 'javelin-request',
       4 => 'javelin-typeahead-source',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js',
   ),
   'javelin-typeahead-preloaded-source' =>
   array(
     'uri' => '/res/d464efd2/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-stratcom',
       3 => 'javelin-request',
       4 => 'javelin-typeahead-source',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js',
   ),
   'javelin-typeahead-source' =>
   array(
     'uri' => '/res/57299d35/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-dom',
       3 => 'javelin-typeahead-normalizer',
     ),
     'disk' => '/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
   ),
   'javelin-uri' =>
   array(
     'uri' => '/res/a6d12b2c/rsrc/js/javelin/lib/URI.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-stratcom',
     ),
     'disk' => '/rsrc/js/javelin/lib/URI.js',
   ),
   'javelin-util' =>
   array(
     'uri' => '/res/21ca8b6c/rsrc/js/javelin/core/util.js',
     'type' => 'js',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/js/javelin/core/util.js',
   ),
   'javelin-vector' =>
   array(
     'uri' => '/res/50535cb8/rsrc/js/javelin/lib/Vector.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-event',
     ),
     'disk' => '/rsrc/js/javelin/lib/Vector.js',
   ),
   'javelin-workflow' =>
   array(
     'uri' => '/res/76fa44f3/rsrc/js/javelin/lib/Workflow.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-stratcom',
       1 => 'javelin-request',
       2 => 'javelin-dom',
       3 => 'javelin-vector',
       4 => 'javelin-install',
       5 => 'javelin-util',
       6 => 'javelin-mask',
       7 => 'javelin-uri',
     ),
     'disk' => '/rsrc/js/javelin/lib/Workflow.js',
   ),
   'mainphest-task-detail-css' =>
   array(
     'uri' => '/res/e2fbb468/rsrc/css/application/maniphest/task-detail.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/maniphest/task-detail.css',
   ),
   'maniphest-task-summary-css' =>
   array(
     'uri' => '/res/14cb4b5d/rsrc/css/application/maniphest/task-summary.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/maniphest/task-summary.css',
   ),
   'maniphest-transaction-detail-css' =>
   array(
     'uri' => '/res/149fccab/rsrc/css/application/maniphest/transaction-detail.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/maniphest/transaction-detail.css',
   ),
   'multirow-row-manager' =>
   array(
     'uri' => '/res/0a9b3dee/rsrc/js/application/core/MultirowRowManager.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-stratcom',
       2 => 'javelin-dom',
       3 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/application/core/MultirowRowManager.js',
   ),
   'owners-path-editor' =>
   array(
     'uri' => '/res/e6c51eb6/rsrc/js/application/owners/OwnersPathEditor.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'multirow-row-manager',
       1 => 'javelin-install',
       2 => 'path-typeahead',
       3 => 'javelin-dom',
       4 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/application/owners/OwnersPathEditor.js',
   ),
   'owners-path-editor-css' =>
   array(
     'uri' => '/res/9bc5332c/rsrc/css/application/owners/owners-path-editor.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/owners/owners-path-editor.css',
   ),
   'path-typeahead' =>
   array(
     'uri' => '/res/50246fb6/rsrc/js/application/herald/PathTypeahead.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-typeahead',
       2 => 'javelin-dom',
       3 => 'javelin-request',
       4 => 'javelin-typeahead-ondemand-source',
       5 => 'javelin-util',
     ),
     'disk' => '/rsrc/js/application/herald/PathTypeahead.js',
   ),
   'phabricator-content-source-view-css' =>
   array(
     'uri' => '/res/8c738a93/rsrc/css/application/contentsource/content-source-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/contentsource/content-source-view.css',
   ),
   'phabricator-core-buttons-css' =>
   array(
     'uri' => '/res/3059cf79/rsrc/css/core/buttons.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/buttons.css',
   ),
   'phabricator-core-css' =>
   array(
     'uri' => '/res/bd20d04b/rsrc/css/core/core.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/core.css',
   ),
   'phabricator-countdown-css' =>
   array(
     'uri' => '/res/0f646281/rsrc/css/application/countdown/timer.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/countdown/timer.css',
   ),
   'phabricator-directory-css' =>
   array(
     'uri' => '/res/a3d307c5/rsrc/css/application/directory/phabricator-directory.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/directory/phabricator-directory.css',
   ),
   'phabricator-drag-and-drop-file-upload' =>
   array(
     'uri' => '/res/63a06ad9/rsrc/js/application/core/DragAndDropFileUpload.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-request',
       3 => 'javelin-dom',
       4 => 'javelin-uri',
     ),
     'disk' => '/rsrc/js/application/core/DragAndDropFileUpload.js',
   ),
   'phabricator-feed-css' =>
   array(
     'uri' => '/res/32e5879b/rsrc/css/application/feed/feed.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/feed/feed.css',
   ),
   'phabricator-keyboard-shortcut' =>
   array(
     'uri' => '/res/beed38cd/rsrc/js/application/core/KeyboardShortcut.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'phabricator-keyboard-shortcut-manager',
     ),
     'disk' => '/rsrc/js/application/core/KeyboardShortcut.js',
   ),
   'phabricator-keyboard-shortcut-manager' =>
   array(
     'uri' => '/res/04767571/rsrc/js/application/core/KeyboardShortcutManager.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-stratcom',
       3 => 'javelin-dom',
       4 => 'javelin-vector',
     ),
     'disk' => '/rsrc/js/application/core/KeyboardShortcutManager.js',
   ),
   'phabricator-object-selector-css' =>
   array(
     'uri' => '/res/608461d2/rsrc/css/application/objectselector/object-selector.css',
     'type' => 'css',
     'requires' =>
     array(
       0 => 'aphront-dialog-view-css',
     ),
     'disk' => '/rsrc/css/application/objectselector/object-selector.css',
   ),
   'phabricator-prefab' =>
   array(
     'uri' => '/res/5784a112/rsrc/js/application/core/Prefab.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-dom',
     ),
     'disk' => '/rsrc/js/application/core/Prefab.js',
   ),
-  0 =>
-  array(
-    'uri' => '/res/936e8e81/rsrc/js/javelin/docs/onload.js',
-    'type' => 'js',
-    'requires' =>
-    array(
-    ),
-    'disk' => '/rsrc/js/javelin/docs/onload.js',
-  ),
   'phabricator-profile-css' =>
   array(
     'uri' => '/res/ebe1ac2f/rsrc/css/application/profile/profile-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/profile/profile-view.css',
   ),
   'phabricator-remarkup-css' =>
   array(
     'uri' => '/res/7ca89111/rsrc/css/core/remarkup.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/remarkup.css',
   ),
   'phabricator-search-results-css' =>
   array(
     'uri' => '/res/f8a86e27/rsrc/css/application/search/search-results.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/search/search-results.css',
   ),
   'phabricator-shaped-request' =>
   array(
     'uri' => '/res/ee0667c4/rsrc/js/application/core/ShapedRequest.js',
     'type' => 'js',
     'requires' =>
     array(
       0 => 'javelin-install',
       1 => 'javelin-util',
       2 => 'javelin-request',
     ),
     'disk' => '/rsrc/js/application/core/ShapedRequest.js',
   ),
   'phabricator-slowvote-css' =>
   array(
     'uri' => '/res/94d20443/rsrc/css/application/slowvote/slowvote.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/slowvote/slowvote.css',
   ),
   'phabricator-standard-page-view' =>
   array(
     'uri' => '/res/325b5b0f/rsrc/css/application/base/standard-page-view.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/base/standard-page-view.css',
   ),
   'phabricator-ui-example-css' =>
   array(
     'uri' => '/res/0cef078b/rsrc/css/application/uiexample/example.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/uiexample/example.css',
   ),
   'phriction-document-css' =>
   array(
     'uri' => '/res/a0eb9ea1/rsrc/css/application/phriction/phriction-document-css.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/phriction/phriction-document-css.css',
   ),
   'project-edit-css' =>
   array(
     'uri' => '/res/c192b5f9/rsrc/css/application/projects/project-edit.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/application/projects/project-edit.css',
   ),
   'syntax-highlighting-css' =>
   array(
     'uri' => '/res/14bc2ee1/rsrc/css/core/syntax.css',
     'type' => 'css',
     'requires' =>
     array(
     ),
     'disk' => '/rsrc/css/core/syntax.css',
   ),
 ), array(
   'packages' =>
   array(
     '03ef179e' =>
     array(
       'name' => 'diffusion.pkg.css',
       'symbols' =>
       array(
         0 => 'diffusion-commit-view-css',
       ),
       'uri' => '/res/pkg/03ef179e/diffusion.pkg.css',
       'type' => 'css',
     ),
     '3dbf4083' =>
     array(
       'name' => 'javelin.pkg.js',
       'symbols' =>
       array(
         0 => 'javelin-util',
         1 => 'javelin-install',
         2 => 'javelin-event',
         3 => 'javelin-stratcom',
         4 => 'javelin-behavior',
         5 => 'javelin-request',
         6 => 'javelin-vector',
         7 => 'javelin-dom',
         8 => 'javelin-json',
         9 => 'javelin-uri',
       ),
       'uri' => '/res/pkg/3dbf4083/javelin.pkg.js',
       'type' => 'js',
     ),
     '7bf96a66' =>
     array(
       'name' => 'differential.pkg.css',
       'symbols' =>
       array(
         0 => 'differential-core-view-css',
         1 => 'differential-changeset-view-css',
         2 => 'differential-revision-detail-css',
         3 => 'differential-revision-history-css',
         4 => 'differential-table-of-contents-css',
         5 => 'differential-revision-comment-css',
         6 => 'differential-revision-add-comment-css',
         7 => 'differential-revision-comment-list-css',
       ),
       'uri' => '/res/pkg/7bf96a66/differential.pkg.css',
       'type' => 'css',
     ),
     '95c67dcd' =>
     array(
       'name' => 'workflow.pkg.js',
       'symbols' =>
       array(
         0 => 'javelin-mask',
         1 => 'javelin-workflow',
         2 => 'javelin-behavior-workflow',
         3 => 'javelin-behavior-aphront-form-disable-on-submit',
         4 => 'phabricator-keyboard-shortcut-manager',
         5 => 'phabricator-keyboard-shortcut',
         6 => 'javelin-behavior-phabricator-keyboard-shortcuts',
       ),
       'uri' => '/res/pkg/95c67dcd/workflow.pkg.js',
       'type' => 'js',
     ),
     '982ad44b' =>
     array(
       'name' => 'differential.pkg.js',
       'symbols' =>
       array(
         0 => 'javelin-behavior-differential-feedback-preview',
         1 => 'javelin-behavior-differential-edit-inline-comments',
         2 => 'javelin-behavior-differential-populate',
         3 => 'javelin-behavior-differential-show-more',
         4 => 'javelin-behavior-differential-diff-radios',
       ),
       'uri' => '/res/pkg/982ad44b/differential.pkg.js',
       'type' => 'js',
     ),
     'ac869011' =>
     array(
       'name' => 'typeahead.pkg.js',
       'symbols' =>
       array(
         0 => 'javelin-typeahead',
         1 => 'javelin-typeahead-normalizer',
         2 => 'javelin-typeahead-source',
         3 => 'javelin-typeahead-preloaded-source',
         4 => 'javelin-typeahead-ondemand-source',
         5 => 'javelin-tokenizer',
         6 => 'javelin-behavior-aphront-basic-tokenizer',
       ),
       'uri' => '/res/pkg/ac869011/typeahead.pkg.js',
       'type' => 'js',
     ),
     70966590 =>
     array(
       'name' => 'core.pkg.css',
       'symbols' =>
       array(
         0 => 'phabricator-core-css',
         1 => 'phabricator-core-buttons-css',
         2 => 'phabricator-standard-page-view',
         3 => 'aphront-dialog-view-css',
         4 => 'aphront-form-view-css',
         5 => 'aphront-panel-view-css',
         6 => 'aphront-side-nav-view-css',
         7 => 'aphront-table-view-css',
         8 => 'aphront-crumbs-view-css',
         9 => 'aphront-tokenizer-control-css',
         10 => 'aphront-typeahead-control-css',
         11 => 'aphront-list-filter-view-css',
         12 => 'phabricator-directory-css',
         13 => 'phabricator-remarkup-css',
         14 => 'syntax-highlighting-css',
       ),
       'uri' => '/res/pkg/70966590/core.pkg.css',
       'type' => 'css',
     ),
   ),
   'reverse' =>
   array(
     'aphront-crumbs-view-css' => '70966590',
     'aphront-dialog-view-css' => '70966590',
     'aphront-form-view-css' => '70966590',
     'aphront-list-filter-view-css' => '70966590',
     'aphront-panel-view-css' => '70966590',
     'aphront-side-nav-view-css' => '70966590',
     'aphront-table-view-css' => '70966590',
     'aphront-tokenizer-control-css' => '70966590',
     'aphront-typeahead-control-css' => '70966590',
     'differential-changeset-view-css' => '7bf96a66',
     'differential-core-view-css' => '7bf96a66',
     'differential-revision-add-comment-css' => '7bf96a66',
     'differential-revision-comment-css' => '7bf96a66',
     'differential-revision-comment-list-css' => '7bf96a66',
     'differential-revision-detail-css' => '7bf96a66',
     'differential-revision-history-css' => '7bf96a66',
     'differential-table-of-contents-css' => '7bf96a66',
     'diffusion-commit-view-css' => '03ef179e',
     'javelin-behavior' => '3dbf4083',
     'javelin-behavior-aphront-basic-tokenizer' => 'ac869011',
     'javelin-behavior-aphront-form-disable-on-submit' => '95c67dcd',
     'javelin-behavior-differential-diff-radios' => '982ad44b',
     'javelin-behavior-differential-edit-inline-comments' => '982ad44b',
     'javelin-behavior-differential-feedback-preview' => '982ad44b',
     'javelin-behavior-differential-populate' => '982ad44b',
     'javelin-behavior-differential-show-more' => '982ad44b',
     'javelin-behavior-phabricator-keyboard-shortcuts' => '95c67dcd',
     'javelin-behavior-workflow' => '95c67dcd',
     'javelin-dom' => '3dbf4083',
     'javelin-event' => '3dbf4083',
     'javelin-install' => '3dbf4083',
     'javelin-json' => '3dbf4083',
     'javelin-mask' => '95c67dcd',
     'javelin-request' => '3dbf4083',
     'javelin-stratcom' => '3dbf4083',
     'javelin-tokenizer' => 'ac869011',
     'javelin-typeahead' => 'ac869011',
     'javelin-typeahead-normalizer' => 'ac869011',
     'javelin-typeahead-ondemand-source' => 'ac869011',
     'javelin-typeahead-preloaded-source' => 'ac869011',
     'javelin-typeahead-source' => 'ac869011',
     'javelin-uri' => '3dbf4083',
     'javelin-util' => '3dbf4083',
     'javelin-vector' => '3dbf4083',
     'javelin-workflow' => '95c67dcd',
     'phabricator-core-buttons-css' => '70966590',
     'phabricator-core-css' => '70966590',
     'phabricator-directory-css' => '70966590',
     'phabricator-keyboard-shortcut' => '95c67dcd',
     'phabricator-keyboard-shortcut-manager' => '95c67dcd',
     'phabricator-remarkup-css' => '70966590',
     'phabricator-standard-page-view' => '70966590',
     'syntax-highlighting-css' => '70966590',
   ),
 ));
diff --git a/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php b/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
index 159cc45c0c..91d3715d2d 100644
--- a/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
+++ b/src/applications/repository/controller/edit/PhabricatorRepositoryEditController.php
@@ -1,592 +1,645 @@
 <?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 PhabricatorRepositoryEditController
   extends PhabricatorRepositoryController {
 
   private $id;
   private $view;
   private $repository;
   private $sideNav;
 
   public function willProcessRequest(array $data) {
     $this->id = $data['id'];
     $this->view = idx($data, 'view');
   }
 
   public function processRequest() {
 
     $request = $this->getRequest();
 
     $repository = id(new PhabricatorRepository())->load($this->id);
     if (!$repository) {
       return new Aphront404Response();
     }
 
     $views = array(
       'basic'     => 'Basics',
       'tracking'  => 'Tracking',
     );
 
     $vcs = $repository->getVersionControlSystem();
     if ($vcs == DifferentialRevisionControlSystem::GIT) {
       if (!$repository->getDetail('github-token')) {
         $token = substr(base64_encode(Filesystem::readRandomBytes(8)), 0, 8);
         $repository->setDetail('github-token', $token);
 
         $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
         $repository->save();
         unset($unguarded);
       }
 
       $views['github'] = 'GitHub';
     }
 
     $this->repository = $repository;
 
     if (!isset($views[$this->view])) {
       reset($views);
       $this->view = key($views);
     }
 
     $nav = new AphrontSideNavView();
     foreach ($views as $view => $name) {
       $nav->addNavItem(
         phutil_render_tag(
           'a',
           array(
             'class' => ($view == $this->view
               ? 'aphront-side-nav-selected'
               : null),
             'href'  => '/repository/edit/'.$repository->getID().'/'.$view.'/',
           ),
           phutil_escape_html($name)));
     }
 
     $this->sideNav = $nav;
 
     switch ($this->view) {
       case 'basic':
         return $this->processBasicRequest();
       case 'tracking':
         return $this->processTrackingRequest();
       case 'github':
         return $this->processGithubRequest();
       default:
         throw new Exception("Unknown view.");
     }
   }
 
   protected function processBasicRequest() {
     $request = $this->getRequest();
     $user = $request->getUser();
     $repository = $this->repository;
     $repository_id = $repository->getID();
 
     $errors = array();
 
     $e_name = true;
 
     if ($request->isFormPost()) {
       $repository->setName($request->getStr('name'));
 
       if (!strlen($repository->getName())) {
         $e_name = 'Required';
         $errors[] = 'Repository name is required.';
       } else {
         $e_name = null;
       }
 
       $repository->setDetail('description', $request->getStr('description'));
 
       if (!$errors) {
         $repository->save();
         return id(new AphrontRedirectResponse())
           ->setURI('/repository/edit/'.$repository_id.'/basic/?saved=true');
       }
     }
 
     $error_view = null;
     if ($errors) {
       $error_view = new AphrontErrorView();
       $error_view->setErrors($errors);
       $error_view->setTitle('Form Errors');
     } else if ($request->getStr('saved')) {
       $error_view = new AphrontErrorView();
       $error_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
       $error_view->setTitle('Changes Saved');
       $error_view->appendChild(
         'Repository changes were saved.');
     }
 
     $form = new AphrontFormView();
     $form
       ->setUser($user)
       ->setAction('/repository/edit/'.$repository->getID().'/')
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel('Name')
           ->setName('name')
           ->setValue($repository->getName())
           ->setError($e_name)
           ->setCaption('Human-readable repository name.'))
       ->appendChild(
         id(new AphrontFormTextAreaControl())
           ->setLabel('Description')
           ->setName('description')
           ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
           ->setValue($repository->getDetail('description')))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel('Callsign')
           ->setName('callsign')
           ->setValue($repository->getCallsign()))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel('Type')
           ->setName('type')
           ->setValue($repository->getVersionControlSystem()))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel('ID')
           ->setValue($repository->getID()))
       ->appendChild(
         id(new AphrontFormStaticControl())
           ->setLabel('PHID')
           ->setValue($repository->getPHID()))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue('Save'));
 
     $panel = new AphrontPanelView();
     $panel->setHeader('Edit Repository');
     $panel->appendChild($form);
     $panel->setWidth(AphrontPanelView::WIDTH_FORM);
 
 
     $nav = $this->sideNav;
 
     $nav->appendChild($error_view);
     $nav->appendChild($panel);
 
     return $this->buildStandardPageResponse(
       $nav,
       array(
         'title' => 'Edit Repository',
       ));
   }
 
   private function processTrackingRequest() {
     $request = $this->getRequest();
     $user = $request->getUser();
     $repository = $this->repository;
     $repository_id = $repository->getID();
 
     $errors = array();
 
     $e_uri = null;
     $e_path = null;
 
     $is_git = false;
     $is_svn = false;
 
     switch ($repository->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $is_git = true;
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $is_svn = true;
         break;
       default:
         throw new Exception("Unsupported VCS!");
     }
 
     if ($request->isFormPost()) {
       $tracking = ($request->getStr('tracking') == 'enabled' ? true : false);
       $repository->setDetail('tracking-enabled', $tracking);
       $repository->setDetail('remote-uri', $request->getStr('uri'));
       if ($is_git) {
         $repository->setDetail('local-path', $request->getStr('path'));
       }
       $repository->setDetail(
         'pull-frequency',
         max(1, $request->getInt('frequency')));
 
       if ($is_git) {
         $repository->setDetail(
           'default-branch',
           $request->getStr('default-branch'));
       }
 
       $repository->setDetail(
         'default-owners-path',
         $request->getStr(
           'default-owners-path',
           '/'));
 
+      $repository->setDetail('ssh-login', $request->getStr('ssh-login'));
+      $repository->setDetail('ssh-key', $request->getStr('ssh-key'));
+
       $repository->setDetail(
         'herald-disabled',
         $request->getInt('herald-disabled', 0));
 
       if ($is_svn) {
         $repository->setUUID($request->getStr('uuid'));
         $subpath = ltrim($request->getStr('svn-subpath'), '/');
         if ($subpath) {
           $subpath = rtrim($subpath, '/').'/';
         }
         $repository->setDetail('svn-subpath', $subpath);
       }
 
       $repository->setDetail(
         'detail-parser',
         $request->getStr(
           'detail-parser',
           'PhabricatorRepositoryDefaultCommitMessageDetailParser'));
 
       if ($tracking) {
         if (!$repository->getDetail('remote-uri')) {
           $e_uri = 'Required';
           $errors[] = "Repository URI is required.";
         } else if ($is_svn &&
           !preg_match('@/$@', $repository->getDetail('remote-uri'))) {
 
           $e_uri = 'Invalid';
           $errors[] = 'Subversion Repository Root must end in a slash ("/").';
         } else {
           $e_uri = null;
         }
 
         if ($is_git) {
           if (!$repository->getDetail('local-path')) {
             $e_path = 'Required';
             $errors[] = "Local path is required.";
           } else {
             $e_path = null;
           }
         }
       }
 
       if (!$errors) {
         $repository->save();
         return id(new AphrontRedirectResponse())
           ->setURI('/repository/edit/'.$repository_id.'/tracking/?saved=true');
       }
     }
 
     $error_view = null;
     if ($errors) {
       $error_view = new AphrontErrorView();
       $error_view->setErrors($errors);
       $error_view->setTitle('Form Errors');
     } else if ($request->getStr('saved')) {
       $error_view = new AphrontErrorView();
       $error_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
       $error_view->setTitle('Changes Saved');
       $error_view->appendChild(
         'Tracking changes were saved. You may need to restart the daemon '.
         'before changes will take effect.');
     }
 
     switch ($repository->getVersionControlSystem()) {
       case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
         $is_git = true;
         break;
       case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
         $is_svn = true;
         break;
     }
 
     $doc_href = PhabricatorEnv::getDoclink('article/Diffusion_User_Guide.html');
     $user_guide_link = phutil_render_tag(
       'a',
       array(
         'href' => $doc_href,
       ),
       'Diffusion User Guide');
 
     $form = new AphrontFormView();
     $form
       ->setUser($user)
       ->setAction('/repository/edit/'.$repository->getID().'/tracking/')
       ->appendChild(
         '<p class="aphront-form-instructions">Phabricator can track '.
         'repositories, importing commits as they happen and notifying '.
         'Differential, Diffusion, Herald, and other services. To enable '.
         'tracking for a repository, configure it here and then start (or '.
         'restart) the daemons. More information is available in the '.
-        '<strong>'.$user_guide_link.'</strong>.</p>')
+        '<strong>'.$user_guide_link.'</strong>.</p>');
+
+    $form
+      ->appendChild(
+        '<h1>Basics</h1><div class="aphront-form-inset">')
       ->appendChild(
         id(new AphrontFormStaticControl())
-          ->setLabel('Repository')
+          ->setLabel('Repository Name')
           ->setValue($repository->getName()))
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setName('tracking')
           ->setLabel('Tracking')
           ->setOptions(array(
             'disabled'  => 'Disabled',
             'enabled'   => 'Enabled',
           ))
           ->setValue(
             $repository->getDetail('tracking-enabled')
               ? 'enabled'
-              : 'disabled'));
+              : 'disabled'))
+      ->appendChild('</div>');
+
+    $form->appendChild(
+      '<h1>Remote URI</h1>'.
+      '<div class="aphront-form-inset">');
 
     $uri_label = 'Repository URI';
     if ($is_git) {
       $instructions =
-        'NOTE: The user the tracking daemon runs as must have permission to '.
-        '<tt>git clone</tt> from this URI.';
+        'Enter the URI to clone this repository from. It should look like '.
+        '<tt>git@github.com:example/example.git</tt> or '.
+        '<tt>ssh://user@host.com/git/example.git</tt>';
       $form->appendChild(
         '<p class="aphront-form-instructions">'.$instructions.'</p>');
     } else if ($is_svn) {
       $instructions =
         'Enter the <strong>Repository Root</strong> for this SVN repository. '.
         'You can figure this out by running <tt>svn info</tt> and looking at '.
         'the value in the <tt>Repository Root</tt> field. It should be a URI '.
         'and look like <tt>http://svn.example.org/svn/</tt> or '.
-        '<tt>svn+ssh://svn.example.com/svnroot/</tt>.'.
-        '<br /><br />'.
-        'NOTE: The user the daemons run as must be able to execute '.
-        '<tt>svn log</tt> against this URI.';
+        '<tt>svn+ssh://svn.example.com/svnroot/</tt>';
       $form->appendChild(
         '<p class="aphront-form-instructions">'.$instructions.'</p>');
       $uri_label = 'Repository Root';
     }
 
     $form
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setName('uri')
           ->setLabel($uri_label)
+          ->setID('remote-uri')
           ->setValue($repository->getDetail('remote-uri'))
           ->setError($e_uri));
 
+    $form->appendChild(
+      '<div class="aphront-form-instructions">'.
+        'If you want to connect to this repository over SSH, enter the '.
+        'username and private key to use. You can leave these fields blank if '.
+        'the repository does not use SSH. <strong>NOTE: This feature is not '.
+        'yet fully supported.</strong>'.
+      '</div>');
+
+    $form
+      ->appendChild(
+        id(new AphrontFormTextControl())
+          ->setName('ssh-login')
+          ->setLabel('SSH User')
+          ->setValue($repository->getDetail('ssh-login')))
+      ->appendChild(
+        id(new AphrontFormTextAreaControl())
+          ->setName('ssh-key')
+          ->setLabel('SSH Private Key')
+          ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
+          ->setValue($repository->getDetail('ssh-key')))
+      ->appendChild(
+        id(new AphrontFormMarkupControl())
+          ->setLabel('Test Connection')
+          ->setValue(
+            'To test these credentials, <strong>save this form</strong> and '.
+            'then run: <code>phabricator/scripts/repository/test_connection'.
+            '.php '.phutil_escape_html($repository->getCallsign()).'</code>'));
+
+    $form->appendChild('</div>');
+
+    $form->appendChild(
+      '<h1>Importing Repository Information</h1>'.
+      '<div class="aphront-form-inset">');
+
     if ($is_git) {
       $form->appendChild(
         '<p class="aphront-form-instructions">Select a path on local disk '.
         'which the daemons should <tt>git clone</tt> the repository into. '.
         'This must be readable and writable by the daemons, and readable by '.
         'the webserver. The daemons will <tt>git fetch</tt> and keep this '.
         'repository up to date.</p>');
       $form->appendChild(
         id(new AphrontFormTextControl())
           ->setName('path')
           ->setLabel('Local Path')
           ->setValue($repository->getDetail('local-path'))
           ->setError($e_path));
     } else if ($is_svn) {
       $form->appendChild(
         '<p class="aphront-form-instructions">If you only want to parse one '.
         'subpath of the repository, specify it here, relative to the '.
         'repository root (e.g., <tt>trunk/</tt> or <tt>projects/wheel/</tt>). '.
         'If you want to parse multiple subdirectories, create a separate '.
         'Phabricator repository for each one.</p>');
       $form->appendChild(
         id(new AphrontFormTextControl())
           ->setName('svn-subpath')
           ->setLabel('Subpath')
           ->setValue($repository->getDetail('svn-subpath'))
           ->setError($e_path));
     }
 
     $form
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setName('frequency')
           ->setLabel('Pull Frequency')
           ->setValue($repository->getDetail('pull-frequency', 15))
           ->setCaption(
             'Number of seconds daemon should sleep between requests. Larger '.
             'numbers reduce load but also decrease responsiveness.'));
 
+    $form->appendChild('</div>');
+
+    $form->appendChild(
+      '<h1>Application Configuration</h1>'.
+      '<div class="aphront-form-inset">');
+
     if ($is_git) {
       $form
         ->appendChild(
           id(new AphrontFormTextControl())
             ->setName('default-branch')
             ->setLabel('Default Branch')
             ->setValue(
               $repository->getDetail(
                 'default-branch',
                 'origin/master'))
             ->setCaption(
               'Default <strong>remote</strong> branch to show in Diffusion.'));
     }
 
     $form
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setName('default-owners-path')
           ->setLabel('Default Owners Path')
           ->setValue(
             $repository->getDetail(
               'default-owners-path',
               '/'))
           ->setCaption('Default path in Owners tool.'));
 
     $form
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setName('herald-disabled')
           ->setLabel('Herald Enabled')
           ->setValue($repository->getDetail('herald-disabled', 0))
           ->setOptions(
             array(
               0 => 'Enabled - Send Email',
               1 => 'Disabled - Do Not Send Email',
             ))
           ->setCaption(
-            'You can temporarily disable Herald notifications when reparsing '.
-            'a repository or importing a new repository.'));
+            'You can temporarily disable Herald commit notifications when '.
+            'reparsing a repository or importing a new repository.'));
 
     $parsers = id(new PhutilSymbolLoader())
       ->setAncestorClass('PhabricatorRepositoryCommitMessageDetailParser')
       ->selectSymbolsWithoutLoading();
     $parsers = ipull($parsers, 'name', 'name');
 
     $form
       ->appendChild(
         '<p class="aphront-form-instructions">If you extend the commit '.
         'message format, you can provide a new parser which will extract '.
         'extra information from it when commits are imported. This is an '.
         'advanced feature, and using the default parser will be suitable '.
         'in most cases.</p>')
       ->appendChild(
         id(new AphrontFormSelectControl())
           ->setName('detail-parser')
           ->setLabel('Detail Parser')
           ->setOptions($parsers)
           ->setValue(
             $repository->getDetail(
               'detail-parser',
               'PhabricatorRepositoryDefaultCommitMessageDetailParser')));
 
     if ($is_svn) {
       $form
         ->appendChild(
           id(new AphrontFormTextControl())
             ->setName('uuid')
             ->setLabel('UUID')
             ->setValue($repository->getUUID())
             ->setCaption('Repository UUID from <tt>svn info</tt>.'));
     }
 
+    $form->appendChild('</div>');
+
     $form
       ->appendChild(
         id(new AphrontFormSubmitControl())
-          ->setValue('Save'));
+          ->setValue('Save Configuration'));
 
     $panel = new AphrontPanelView();
     $panel->setHeader('Repository Tracking');
     $panel->appendChild($form);
     $panel->setWidth(AphrontPanelView::WIDTH_WIDE);
 
     $nav = $this->sideNav;
     $nav->appendChild($error_view);
     $nav->appendChild($panel);
 
     return $this->buildStandardPageResponse(
       $nav,
       array(
         'title' => 'Edit Repository Tracking',
       ));
   }
 
 
   private function processGithubRequest() {
     $request = $this->getRequest();
     $repository = $this->repository;
     $repository_id = $repository->getID();
 
     $token = $repository->getDetail('github-token');
     $path = '/github-post-receive/'.$repository_id.'/'.$token.'/';
     $post_uri = PhabricatorEnv::getURI($path);
 
     $gitform = new AphrontFormLayoutView();
     $gitform
       ->setBackgroundShading(true)
       ->setPadded(true)
       ->appendChild(
         '<p class="aphront-form-instructions">You can configure GitHub to '.
         'notify Phabricator after changes are pushed. Log into GitHub, go '.
         'to "Admin" &rarr; "Service Hooks" &rarr; "Post-Receive URLs", and '.
         'add this URL to the list. Obviously, this will only work if your '.
         'Phabricator installation is accessible from the internet.</p>')
       ->appendChild(
         '<p class="aphront-form-instructions"> If things are working '.
         'properly, push notifications should appear below once you make some '.
         'commits.</p>')
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel('URL')
           ->setCaption('Set this as a GitHub "Post-Receive URL".')
           ->setValue($post_uri))
       ->appendChild('<br /><br />')
       ->appendChild('<h1>Recent Commit Notifications</h1>');
 
     $notifications = id(new PhabricatorRepositoryGitHubNotification())
       ->loadAllWhere(
         'repositoryPHID = %s ORDER BY id DESC limit 10',
         $repository->getPHID());
 
     $rows = array();
     foreach ($notifications as $notification) {
       $rows[] = array(
         phutil_escape_html($notification->getRemoteAddress()),
         phabricator_format_timestamp($notification->getDateCreated()),
         $notification->getPayload()
           ? phutil_escape_html(substr($notification->getPayload(), 0, 32).'...')
           : 'Empty',
       );
     }
 
     $notification_table = new AphrontTableView($rows);
     $notification_table->setHeaders(
       array(
         'Remote Address',
         'Received',
         'Payload',
       ));
     $notification_table->setColumnClasses(
       array(
         null,
         null,
         'wide',
       ));
     $notification_table->setNoDataString(
       'Phabricator has not yet received any commit notifications for this '.
       'repository from GitHub.');
 
     $gitform->appendChild($notification_table);
 
     $github = new AphrontPanelView();
     $github->setHeader('GitHub Integration');
     $github->appendChild($gitform);
     $github->setWidth(AphrontPanelView::WIDTH_FORM);
 
     $nav = $this->sideNav;
     $nav->appendChild($github);
 
     return $this->buildStandardPageResponse(
       $nav,
       array(
         'title' => 'Repository Github Integration',
       ));
   }
 }
diff --git a/src/applications/repository/controller/edit/__init__.php b/src/applications/repository/controller/edit/__init__.php
index 7da55db338..4f61cde518 100644
--- a/src/applications/repository/controller/edit/__init__.php
+++ b/src/applications/repository/controller/edit/__init__.php
@@ -1,37 +1,38 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'aphront/response/404');
 phutil_require_module('phabricator', 'aphront/response/redirect');
 phutil_require_module('phabricator', 'aphront/writeguard');
 phutil_require_module('phabricator', 'applications/differential/constants/revisioncontrolsystem');
 phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
 phutil_require_module('phabricator', 'applications/repository/controller/base');
 phutil_require_module('phabricator', 'applications/repository/storage/githubnotification');
 phutil_require_module('phabricator', 'applications/repository/storage/repository');
 phutil_require_module('phabricator', 'infrastructure/env');
 phutil_require_module('phabricator', 'view/control/table');
 phutil_require_module('phabricator', 'view/form/base');
+phutil_require_module('phabricator', 'view/form/control/markup');
 phutil_require_module('phabricator', 'view/form/control/select');
 phutil_require_module('phabricator', 'view/form/control/static');
 phutil_require_module('phabricator', 'view/form/control/submit');
 phutil_require_module('phabricator', 'view/form/control/text');
 phutil_require_module('phabricator', 'view/form/control/textarea');
 phutil_require_module('phabricator', 'view/form/error');
 phutil_require_module('phabricator', 'view/form/layout');
 phutil_require_module('phabricator', 'view/layout/panel');
 phutil_require_module('phabricator', 'view/layout/sidenav');
 phutil_require_module('phabricator', 'view/utils');
 
 phutil_require_module('phutil', 'filesystem');
 phutil_require_module('phutil', 'markup');
 phutil_require_module('phutil', 'symbols');
 phutil_require_module('phutil', 'utils');
 
 
 phutil_require_source('PhabricatorRepositoryEditController.php');
diff --git a/src/applications/repository/storage/repository/PhabricatorRepository.php b/src/applications/repository/storage/repository/PhabricatorRepository.php
index 0235e1a435..bb1181ceea 100644
--- a/src/applications/repository/storage/repository/PhabricatorRepository.php
+++ b/src/applications/repository/storage/repository/PhabricatorRepository.php
@@ -1,58 +1,239 @@
 <?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 PhabricatorRepository extends PhabricatorRepositoryDAO {
 
   const TABLE_PATH = 'repository_path';
   const TABLE_PATHCHANGE = 'repository_pathchange';
   const TABLE_FILESYSTEM = 'repository_filesystem';
   const TABLE_SUMMARY = 'repository_summary';
   const TABLE_BADCOMMIT = 'repository_badcommit';
 
   protected $phid;
   protected $name;
   protected $callsign;
   protected $uuid;
 
   protected $versionControlSystem;
   protected $details = array();
 
+  private $sshKeyfile;
+
   public function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_SERIALIZATION => array(
         'details' => self::SERIALIZATION_JSON,
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorPHIDConstants::PHID_TYPE_REPO);
   }
 
   public function getDetail($key, $default = null) {
     return idx($this->details, $key, $default);
   }
 
   public function setDetail($key, $value) {
     $this->details[$key] = $value;
     return $this;
   }
 
+  public function getRemoteURI() {
+    $raw_uri = $this->getDetail('remote-uri');
+
+    $vcs = $this->getVersionControlSystem();
+    $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
+
+    // If there's no protocol (git implicit SSH) reformat the URI to be a
+    // normal URI. These git URIs look like "user@domain.com:path" instead of
+    // "ssh://user@domain/path".
+    $uri = new PhutilURI($raw_uri);
+    if ($is_git && !$uri->getProtocol()) {
+      list($domain, $path) = explode(':', $raw_uri, 2);
+      $uri = new PhutilURI('ssh://'.$domain.'/'.$path);
+    }
+
+    if ($this->isSSHProtocol($uri->getProtocol())) {
+      if ($this->getSSHLogin()) {
+        $uri->setUser($this->getSSHLogin());
+      }
+    }
+
+    return (string)$uri;
+  }
+
+  public function getLocalPath() {
+    return $this->getDetail('local-path');
+  }
+
+  public function execRemoteCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatRemoteCommand($args);
+    return call_user_func_array('exec_manual', $args);
+  }
+
+  public function execxRemoteCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatRemoteCommand($args);
+    return call_user_func_array('execx', $args);
+  }
+
+  public function passthruRemoteCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatRemoteCommand($args);
+    return call_user_func_array('phutil_passthru', $args);
+  }
+
+  public function execLocalCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatLocalCommand($args);
+    return call_user_func_array('exec_manual', $args);
+  }
+
+  public function execxLocalCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatLocalCommand($args);
+    return call_user_func_array('execx', $args);
+  }
+
+  public function passthruLocalCommand($pattern /*, $arg, ... */) {
+    $args = func_get_args();
+    $args = $this->formatLocalCommand($args);
+    return call_user_func_array('phutil_passthru', $args);
+  }
+
+  private function formatRemoteCommand(array $args) {
+    $pattern = $args[0];
+    $args = array_slice($args, 1);
+
+    if ($this->shouldUseSSH()) {
+      switch ($this->getVersionControlSystem()) {
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+          $pattern = "SVN_SSH=%s svn {$pattern}";
+          array_unshift(
+            $args,
+            csprintf(
+              'ssh -l %s -i %s',
+              $this->getSSHLogin(),
+              $this->getSSHKeyfile()));
+          break;
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+          $command = call_user_func_array(
+            'csprintf',
+            array_merge(
+              array(
+                "(ssh-add %s && git {$pattern})",
+                $this->getSSHKeyfile(),
+              ),
+              $args));
+          $pattern = "ssh-agent sh -c %s";
+          $args = array($command);
+          break;
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+          $pattern = "hg --config ui.ssh=%s {$pattern}";
+          array_unshift(
+            $args,
+            csprintf(
+              'ssh -l %s -i %s',
+              $this->getSSHLogin(),
+              $this->getSSHKeyfile()));
+          break;
+        default:
+          throw new Exception("Unrecognized version control system.");
+      }
+    } else {
+      switch ($this->getVersionControlSystem()) {
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+          $pattern = "svn {$pattern}";
+          break;
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+          $pattern = "git {$pattern}";
+          break;
+        case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+          $pattern = "hg {$pattern}";
+          break;
+        default:
+          throw new Exception("Unrecognized version control system.");
+      }
+    }
+
+    array_unshift($args, $pattern);
+
+    return $args;
+  }
+
+  private function formatLocalCommand(array $args) {
+    $pattern = $args[0];
+    $args = array_slice($args, 1);
+
+    switch ($this->getVersionControlSystem()) {
+      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
+        $pattern = "(cd %s && svn {$pattern})";
+        array_unshift($args, $this->getLocalPath());
+        break;
+      case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+        $pattern = "(cd %s && git {$pattern})";
+        array_unshift($args, $this->getLocalPath());
+        break;
+      case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
+        $pattern = "(cd %s && hg {$pattern})";
+        array_unshift($args, $this->getLocalPath());
+        break;
+      default:
+        throw new Exception("Unrecognized version control system.");
+    }
+
+    array_unshift($args, $pattern);
+
+    return $args;
+  }
+
+  private function getSSHLogin() {
+    return $this->getDetail('ssh-login');
+  }
+
+  private function getSSHKeyfile() {
+    if (!$this->sshKeyfile) {
+      $keyfile = new TempFile('phabricator-repository-ssh-key');
+      chmod($keyfile, 0600);
+      Filesystem::writeFile($keyfile, $this->getDetail('ssh-key'));
+      $this->sshKeyfile = $keyfile;
+    }
+
+    return (string)$this->sshKeyfile;
+  }
+
+  public function shouldUseSSH() {
+    $uri = new PhutilURI($this->getRemoteURI());
+    $protocol = $uri->getProtocol();
+    if ($this->isSSHProtocol($protocol)) {
+      return (bool)$this->getDetail('ssh-key');
+    } else {
+      return false;
+    }
+  }
+
+  private function isSSHProtocol($protocol) {
+    return ($protocol == 'ssh' || $protocol == 'svn+ssh');
+  }
+
 }
diff --git a/src/applications/repository/storage/repository/__init__.php b/src/applications/repository/storage/repository/__init__.php
index 6480f38dac..e08a467030 100644
--- a/src/applications/repository/storage/repository/__init__.php
+++ b/src/applications/repository/storage/repository/__init__.php
@@ -1,16 +1,22 @@
 <?php
 /**
  * This file is automatically generated. Lint this module to rebuild it.
  * @generated
  */
 
 
 
 phutil_require_module('phabricator', 'applications/phid/constants');
 phutil_require_module('phabricator', 'applications/phid/storage/phid');
+phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
 phutil_require_module('phabricator', 'applications/repository/storage/base');
 
+phutil_require_module('phutil', 'filesystem');
+phutil_require_module('phutil', 'filesystem/tempfile');
+phutil_require_module('phutil', 'future/exec');
+phutil_require_module('phutil', 'parser/uri');
 phutil_require_module('phutil', 'utils');
+phutil_require_module('phutil', 'xsprintf/csprintf');
 
 
 phutil_require_source('PhabricatorRepository.php');
diff --git a/webroot/index.php b/webroot/index.php
index 44159da28b..2c2154fadb 100644
--- a/webroot/index.php
+++ b/webroot/index.php
@@ -1,285 +1,285 @@
 <?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.
  */
 
 $__start__ = microtime(true);
 
 error_reporting(E_ALL | E_STRICT);
 
 phabricator_detect_insane_memory_limit();
 
 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.");
 }
 
 if (get_magic_quotes_gpc()) {
   phabricator_fatal_config_error(
     "Your server is configured with PHP 'magic_quotes_gpc' enabled. This ".
     "feature is 'highly discouraged' by PHP's developers and you must ".
     "disable it to run Phabricator. Consult the PHP manual for instructions.");
 }
 
 register_shutdown_function('phabricator_shutdown');
 
 require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php';
 
 try {
   setup_aphront_basics();
 
   $conf = phabricator_read_config_file($env);
   $conf['phabricator.env'] = $env;
 
   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');
 
   PhutilErrorHandler::initialize();
 
 } catch (Exception $ex) {
   phabricator_fatal("[Initialization Exception] ".$ex->getMessage());
 }
 
 $tz = PhabricatorEnv::getEnvConfig('phabricator.timezone');
 if ($tz) {
   date_default_timezone_set($tz);
 }
 phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api');
 phutil_require_module('phutil', 'error');
 
 PhutilErrorHandler::setErrorListener(
   array('DarkConsoleErrorLogPluginAPI', 'handleErrors'));
 
 foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) {
   phutil_load_library($library);
 }
 
 if (PhabricatorEnv::getEnvConfig('phabricator.setup')) {
   PhabricatorSetup::runSetup();
   return;
 }
 
 
 $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();
 
 $write_guard = new AphrontWriteGuard($request);
 
 $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);
 }
 
 try {
   $response = $application->willSendResponse($response);
   $response->setRequest($request);
   $response_string = $response->buildResponseString();
 } catch (Exception $ex) {
   $write_guard->dispose();
   phabricator_fatal('[Rendering Exception] '.$ex->getMessage());
 }
 
 $write_guard->dispose();
 
 
 $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 phabricator_fatal_config_error($msg) {
   phabricator_fatal("CONFIG ERROR: ".$msg."\n");
   die();
 }
 
 function phabricator_detect_insane_memory_limit() {
   $memory_limit = ini_get('memory_limit');
   $char_limit   = 12;
   if (strlen($memory_limit) <= $char_limit) {
     return;
   }
 
   // colmdoyle ran into an issue on an Ubuntu box with Suhosin where his
   // 'memory_limit' was set to:
   //
   //   3232323232323232323232323232323232323232323232323232323232323232M
   //
   // Not a typo. A wizard did it.
   //
   // Anyway, with this 'memory_limit', the machine would immediately fatal
   // when executing the ini_set() later. I wasn't able to reproduce this on my
   // EC2 Ubuntu + Suhosin box, but verified that it caused the problem on his
   // machine and that setting it to a more sensible value fixed it. Since I
   // have no idea how to actually trigger the issue, we look for a coarse
   // approximation of it (a memory_limit setting more than 12 characters in
   // length).
 
   phabricator_fatal_config_error(
     "Your PHP 'memory_limit' is set to something ridiculous ".
     "(\"{$memory_limit}\"). Set it to a more reasonable value (it must be no ".
     "more than {$char_limit} characters long).");
 }
 
 function phabricator_shutdown() {
   $event = error_get_last();
 
   if (!$event) {
     return;
   }
 
-  if ($event['type'] != E_ERROR) {
+  if ($event['type'] != E_ERROR && $event['type'] != E_PARSE) {
     return;
   }
 
   $msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n";
   if ($event) {
     // Even though we should be emitting this as text-plain, escape things just
     // to be sure since we can't really be sure what the program state is when
     // we get here.
     $msg .= phutil_escape_html($event['message'])."\n\n";
     $msg .= phutil_escape_html($event['file'].':'.$event['line']);
   }
 
   // flip dem tables
   $msg .= "\n\n\n";
   $msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf".
           "\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20".
           "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb";
 
   phabricator_fatal($msg);
 }
 
 function phabricator_fatal($msg) {
   header(
     'Content-Type: text/plain; charset=utf-8',
     $replace = true,
     $http_error = 500);
 
   error_log($msg);
   echo $msg;
 
   exit(1);
 }