diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -789,6 +789,7 @@
     'DiffusionRepositorySymbolsManagementPanel' => 'applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php',
     'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
     'DiffusionRepositoryTestAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryTestAutomationController.php',
+    'DiffusionRepositoryURICredentialController' => 'applications/diffusion/controller/DiffusionRepositoryURICredentialController.php',
     'DiffusionRepositoryURIDisableController' => 'applications/diffusion/controller/DiffusionRepositoryURIDisableController.php',
     'DiffusionRepositoryURIEditController' => 'applications/diffusion/controller/DiffusionRepositoryURIEditController.php',
     'DiffusionRepositoryURIViewController' => 'applications/diffusion/controller/DiffusionRepositoryURIViewController.php',
@@ -5020,6 +5021,7 @@
     'DiffusionRepositorySymbolsManagementPanel' => 'DiffusionRepositoryManagementPanel',
     'DiffusionRepositoryTag' => 'Phobject',
     'DiffusionRepositoryTestAutomationController' => 'DiffusionRepositoryEditController',
+    'DiffusionRepositoryURICredentialController' => 'DiffusionController',
     'DiffusionRepositoryURIDisableController' => 'DiffusionController',
     'DiffusionRepositoryURIEditController' => 'DiffusionController',
     'DiffusionRepositoryURIViewController' => 'DiffusionController',
diff --git a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
--- a/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
+++ b/src/applications/diffusion/application/PhabricatorDiffusionApplication.php
@@ -97,6 +97,8 @@
               => 'DiffusionRepositoryURIDisableController',
             $this->getEditRoutePattern('edit/')
               => 'DiffusionRepositoryURIEditController',
+            'credential/(?P<id>[0-9]\d*)/(?P<action>edit|remove)/'
+              => 'DiffusionRepositoryURICredentialController',
           ),
           'edit/' => array(
             '' => 'DiffusionRepositoryEditMainController',
diff --git a/src/applications/diffusion/controller/DiffusionMirrorEditController.php b/src/applications/diffusion/controller/DiffusionMirrorEditController.php
--- a/src/applications/diffusion/controller/DiffusionMirrorEditController.php
+++ b/src/applications/diffusion/controller/DiffusionMirrorEditController.php
@@ -108,7 +108,7 @@
           ->setName('remoteURI')
           ->setValue($v_remote)
           ->setError($e_remote))
-      ->appendChild(
+      ->appendControl(
         id(new PassphraseCredentialControl())
           ->setLabel(pht('Credentials'))
           ->setName('credential')
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
--- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
@@ -510,6 +510,7 @@
       ->setAdjustFormPageCallback(array($this, 'adjustAuthPage'))
       ->addControl(
         id(new PassphraseCredentialControl())
+          ->setViewer($this->getViewer())
           ->setName('credential'));
   }
 
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/diffusion/controller/DiffusionRepositoryURICredentialController.php
@@ -0,0 +1,159 @@
+<?php
+
+final class DiffusionRepositoryURICredentialController
+  extends DiffusionController {
+
+  public function handleRequest(AphrontRequest $request) {
+    $response = $this->loadDiffusionContextForEdit();
+    if ($response) {
+      return $response;
+    }
+
+    $viewer = $this->getViewer();
+    $drequest = $this->getDiffusionRequest();
+    $repository = $drequest->getRepository();
+
+    $id = $request->getURIData('id');
+    $uri = id(new PhabricatorRepositoryURIQuery())
+      ->setViewer($viewer)
+      ->withIDs(array($id))
+      ->withRepositories(array($repository))
+      ->requireCapabilities(
+        array(
+          PhabricatorPolicyCapability::CAN_VIEW,
+          PhabricatorPolicyCapability::CAN_EDIT,
+        ))
+      ->executeOne();
+    if (!$uri) {
+      return new Aphront404Response();
+    }
+
+    $is_builtin = $uri->isBuiltin();
+    $has_credential = (bool)$uri->getCredentialPHID();
+    $view_uri = $uri->getViewURI();
+    $is_remove = ($request->getURIData('action') == 'remove');
+
+    if ($is_builtin) {
+      return $this->newDialog()
+        ->setTitle(pht('Builtin URIs Do Not Use Credentials'))
+        ->appendParagraph(
+          pht(
+            'You can not set a credential for builtin URIs which Phabricator '.
+            'hosts and serves. Phabricator does not fetch from these URIs or '.
+            'push to these URIs, and does not need credentials to '.
+            'authenticate any activity against them.'))
+        ->addCancelButton($view_uri);
+    }
+
+    if ($request->isFormPost()) {
+      $xactions = array();
+
+      if ($is_remove) {
+        $new_phid = null;
+      } else {
+        $new_phid = $request->getStr('credentialPHID');
+      }
+
+      $type_credential = PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL;
+
+      $xactions[] = id(new PhabricatorRepositoryURITransaction())
+        ->setTransactionType($type_credential)
+        ->setNewValue($new_phid);
+
+      $editor = id(new DiffusionURIEditor())
+        ->setActor($viewer)
+        ->setContinueOnNoEffect(true)
+        ->setContinueOnMissingFields(true)
+        ->setContentSourceFromRequest($request)
+        ->applyTransactions($uri, $xactions);
+
+      return id(new AphrontRedirectResponse())->setURI($view_uri);
+    }
+
+    $command_engine = $uri->newCommandEngine();
+    $is_supported = $command_engine->isCredentialSupported();
+
+    $body = null;
+    $form = null;
+    $width = AphrontDialogView::WIDTH_DEFAULT;
+    if ($is_remove) {
+      if ($has_credential) {
+        $title = pht('Remove Credential');
+        $body = pht(
+          'This credential will no longer be used to authenticate activity '.
+          'against this URI.');
+        $button = pht('Remove Credential');
+      } else {
+        $title = pht('No Credential');
+        $body = pht(
+          'This URI does not have an associated credential.');
+        $button = null;
+      }
+    } else if (!$is_supported) {
+      $title = pht('Unauthenticated Protocol');
+      $body = pht(
+        'The protocol for this URI ("%s") does not use authentication, so '.
+        'you can not provide a credential.',
+        $command_engine->getDisplayProtocol());
+      $button = null;
+    } else {
+      $effective_uri = $uri->getEffectiveURI();
+
+      $label = $command_engine->getPassphraseCredentialLabel();
+      $credential_type = $command_engine->getPassphraseDefaultCredentialType();
+
+      $provides_type = $command_engine->getPassphraseProvidesCredentialType();
+      $options = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withIsDestroyed(false)
+        ->withProvidesTypes(array($provides_type))
+        ->execute();
+
+      $control = id(new PassphraseCredentialControl())
+        ->setName('credentialPHID')
+        ->setLabel($label)
+        ->setValue($uri->getCredentialPHID())
+        ->setCredentialType($credential_type)
+        ->setOptions($options);
+
+      $default_user = $effective_uri->getUser();
+      if (strlen($default_user)) {
+        $control->setDefaultUsername($default_user);
+      }
+
+      $form = id(new AphrontFormView())
+        ->setViewer($viewer)
+        ->appendControl($control);
+
+      if ($has_credential) {
+        $title = pht('Update Credential');
+        $button = pht('Update Credential');
+      } else {
+        $title = pht('Set Credential');
+        $button = pht('Set Credential');
+      }
+
+      $width = AphrontDialogView::WIDTH_FORM;
+    }
+
+    $dialog = $this->newDialog()
+      ->setWidth($width)
+      ->setTitle($title)
+      ->addCancelButton($view_uri);
+
+    if ($body) {
+      $dialog->appendParagraph($body);
+    }
+
+    if ($form) {
+      $dialog->appendForm($form);
+    }
+
+    if ($button) {
+      $dialog->addSubmitButton($button);
+    }
+
+    return $dialog;
+  }
+
+}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
--- a/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryURIViewController.php
@@ -82,6 +82,7 @@
 
   private function buildCurtain(PhabricatorRepositoryURI $uri) {
     $viewer = $this->getViewer();
+    $repository = $uri->getRepository();
     $id = $uri->getID();
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
@@ -89,7 +90,6 @@
       $uri,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-
     $curtain = $this->newCurtainView($uri);
 
     $edit_uri = $uri->getEditURI();
@@ -102,6 +102,43 @@
         ->setWorkflow(!$can_edit)
         ->setDisabled(!$can_edit));
 
+    $credential_uri = $repository->getPathURI("uri/credential/{$id}/edit/");
+    $remove_uri = $repository->getPathURI("uri/credential/{$id}/remove/");
+    $has_credential = (bool)$uri->getCredentialPHID();
+
+    if ($uri->isBuiltin()) {
+      $can_credential = false;
+    } else if (!$uri->newCommandEngine()->isCredentialSupported()) {
+      $can_credential = false;
+    } else {
+      $can_credential = true;
+    }
+
+    $can_update = ($can_edit && $can_credential);
+    $can_remove = ($can_edit && $has_credential);
+
+    if ($has_credential) {
+      $credential_name = pht('Update Credential');
+    } else {
+      $credential_name = pht('Set Credential');
+    }
+
+    $curtain->addAction(
+      id(new PhabricatorActionView())
+        ->setIcon('fa-key')
+        ->setName($credential_name)
+        ->setHref($credential_uri)
+        ->setWorkflow(true)
+        ->setDisabled(!$can_edit));
+
+    $curtain->addAction(
+      id(new PhabricatorActionView())
+        ->setIcon('fa-times')
+        ->setName(pht('Remove Credential'))
+        ->setHref($remove_uri)
+        ->setWorkflow(true)
+        ->setDisabled(!$can_remove));
+
     if ($uri->getIsDisabled()) {
       $disable_name = pht('Enable URI');
       $disable_icon = 'fa-check';
@@ -110,7 +147,7 @@
       $disable_icon = 'fa-ban';
     }
 
-    $disable_uri = $uri->getRepository()->getPathURI("uri/disable/{$id}/");
+    $disable_uri = $repository->getPathURI("uri/disable/{$id}/");
 
     $curtain->addAction(
       id(new PhabricatorActionView())
@@ -130,7 +167,84 @@
       ->setUser($viewer);
 
     $properties->addProperty(pht('URI'), $uri->getDisplayURI());
-    $properties->addProperty(pht('Credential'), 'TODO');
+
+    $credential_phid = $uri->getCredentialPHID();
+    $command_engine = $uri->newCommandEngine();
+    $is_optional = $command_engine->isCredentialOptional();
+    $is_supported = $command_engine->isCredentialSupported();
+    $is_builtin = $uri->isBuiltin();
+
+    if ($is_builtin) {
+      $credential_icon = 'fa-circle-o';
+      $credential_color = 'grey';
+      $credential_label = pht('Builtin');
+      $credential_note = pht('Builtin URIs do not use credentials.');
+    } else if (!$is_supported) {
+      $credential_icon = 'fa-circle-o';
+      $credential_color = 'grey';
+      $credential_label = pht('Not Supported');
+      $credential_note = pht('This protocol does not support authentication.');
+    } else if (!$credential_phid) {
+      if ($is_optional) {
+        $credential_icon = 'fa-circle-o';
+        $credential_color = 'green';
+        $credential_label = pht('No Credential');
+        $credential_note = pht('Configured for anonymous access.');
+      } else {
+        $credential_icon = 'fa-times';
+        $credential_color = 'red';
+        $credential_label = pht('Required');
+        $credential_note = pht('Credential required but not configured.');
+      }
+    } else {
+      // Don't raise a policy exception if we can't see the credential.
+      $credentials = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withPHIDs(array($credential_phid))
+        ->execute();
+      $credential = head($credentials);
+
+      if (!$credential) {
+        $handles = $viewer->loadHandles(array($credential_phid));
+        $handle = $handles[$credential_phid];
+        if ($handle->getPolicyFiltered()) {
+          $credential_icon = 'fa-lock';
+          $credential_color = 'grey';
+          $credential_label = pht('Restricted');
+          $credential_note = pht(
+            'You do not have permission to view the configured '.
+            'credential.');
+        } else {
+          $credential_icon = 'fa-times';
+          $credential_color = 'red';
+          $credential_label = pht('Invalid');
+          $credential_note = pht('Configured credential is invalid.');
+        }
+      } else {
+        $provides = $credential->getProvidesType();
+        $needs = $command_engine->getPassphraseProvidesCredentialType();
+        if ($provides != $needs) {
+          $credential_icon = 'fa-times';
+          $credential_color = 'red';
+          $credential_label = pht('Wrong Type');
+        } else {
+          $credential_icon = 'fa-check';
+          $credential_color = 'green';
+          $credential_label = $command_engine->getPassphraseCredentialLabel();
+        }
+        $credential_note = $viewer->renderHandle($credential_phid);
+      }
+    }
+
+    $credential_item = id(new PHUIStatusItemView())
+      ->setIcon($credential_icon, $credential_color)
+      ->setTarget(phutil_tag('strong', array(), $credential_label))
+      ->setNote($credential_note);
+
+    $credential_view = id(new PHUIStatusListView())
+      ->addItem($credential_item);
+
+    $properties->addProperty(pht('Credential'), $credential_view);
 
 
     $io_type = $uri->getEffectiveIOType();
diff --git a/src/applications/diffusion/editor/DiffusionURIEditEngine.php b/src/applications/diffusion/editor/DiffusionURIEditEngine.php
--- a/src/applications/diffusion/editor/DiffusionURIEditEngine.php
+++ b/src/applications/diffusion/editor/DiffusionURIEditEngine.php
@@ -83,6 +83,14 @@
   protected function buildCustomEditFields($object) {
     $viewer = $this->getViewer();
 
+    if ($object->isBuiltin()) {
+      $is_builtin = true;
+      $uri_value = (string)$object->getDisplayURI();
+    } else {
+      $is_builtin = false;
+      $uri_value = $object->getURI();
+    }
+
     return array(
       id(new PhabricatorHandlesEditField())
         ->setKey('repository')
@@ -104,12 +112,13 @@
       id(new PhabricatorTextEditField())
         ->setKey('uri')
         ->setLabel(pht('URI'))
-        ->setIsRequired(true)
         ->setTransactionType(PhabricatorRepositoryURITransaction::TYPE_URI)
         ->setDescription(pht('The repository URI.'))
         ->setConduitDescription(pht('Change the repository URI.'))
         ->setConduitTypeDescription(pht('New repository URI.'))
-        ->setValue($object->getURI()),
+        ->setIsRequired(!$is_builtin)
+        ->setIsLocked($is_builtin)
+        ->setValue($uri_value),
       id(new PhabricatorSelectEditField())
         ->setKey('io')
         ->setLabel(pht('I/O Type'))
diff --git a/src/applications/diffusion/editor/DiffusionURIEditor.php b/src/applications/diffusion/editor/DiffusionURIEditor.php
--- a/src/applications/diffusion/editor/DiffusionURIEditor.php
+++ b/src/applications/diffusion/editor/DiffusionURIEditor.php
@@ -70,7 +70,42 @@
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorRepositoryURITransaction::TYPE_URI:
+        if (!$this->getIsNewObject()) {
+          $old_uri = $object->getEffectiveURI();
+        } else {
+          $old_uri = null;
+        }
+
         $object->setURI($xaction->getNewValue());
+
+        // If we've changed the domain or protocol of the URI, remove the
+        // current credential. This improves behavior in several cases:
+
+        // If a user switches between protocols with different credential
+        // types, like HTTP and SSH, the old credential won't be valid anyway.
+        // It's cleaner to remove it than leave a bad credential in place.
+
+        // If a user switches hosts, the old credential is probably not
+        // correct (and potentially confusing/misleading). Removing it forces
+        // users to double check that they have the correct credentials.
+
+        // If an attacker can't see a symmetric credential like a username and
+        // password, they could still potentially capture it by changing the
+        // host for a URI that uses it to `evil.com`, a server they control,
+        // then observing the requests. Removing the credential prevents this
+        // kind of escalation.
+
+        // Since port and path changes are less likely to fall among these
+        // cases, they don't trigger a credential wipe.
+
+        $new_uri = $object->getEffectiveURI();
+        if ($old_uri) {
+          $new_proto = ($old_uri->getProtocol() != $new_uri->getProtocol());
+          $new_domain = ($old_uri->getDomain() != $new_uri->getDomain());
+          if ($new_proto || $new_domain) {
+            $object->setCredentialPHID(null);
+          }
+        }
         break;
       case PhabricatorRepositoryURITransaction::TYPE_IO:
         $object->setIOType($xaction->getNewValue());
@@ -184,6 +219,11 @@
             continue;
           }
 
+          // Anyone who can edit a URI can remove the credential.
+          if ($credential_phid === null) {
+            continue;
+          }
+
           $credential = id(new PassphraseCredentialQuery())
             ->setViewer($viewer)
             ->withPHIDs(array($credential_phid))
diff --git a/src/applications/diffusion/protocol/DiffusionCommandEngine.php b/src/applications/diffusion/protocol/DiffusionCommandEngine.php
--- a/src/applications/diffusion/protocol/DiffusionCommandEngine.php
+++ b/src/applications/diffusion/protocol/DiffusionCommandEngine.php
@@ -57,6 +57,10 @@
     return $this->protocol;
   }
 
+  public function getDisplayProtocol() {
+    return $this->getProtocol().'://';
+  }
+
   public function setCredentialPHID($credential_phid) {
     $this->credentialPHID = $credential_phid;
     return $this;
@@ -197,34 +201,82 @@
     return $env;
   }
 
-  protected function isSSHProtocol() {
+  public function isSSHProtocol() {
     return ($this->getProtocol() == 'ssh');
   }
 
-  protected function isSVNProtocol() {
+  public function isSVNProtocol() {
     return ($this->getProtocol() == 'svn');
   }
 
-  protected function isSVNSSHProtocol() {
+  public function isSVNSSHProtocol() {
     return ($this->getProtocol() == 'svn+ssh');
   }
 
-  protected function isHTTPProtocol() {
+  public function isHTTPProtocol() {
     return ($this->getProtocol() == 'http');
   }
 
-  protected function isHTTPSProtocol() {
+  public function isHTTPSProtocol() {
     return ($this->getProtocol() == 'https');
   }
 
-  protected function isAnyHTTPProtocol() {
+  public function isAnyHTTPProtocol() {
     return ($this->isHTTPProtocol() || $this->isHTTPSProtocol());
   }
 
-  protected function isAnySSHProtocol() {
+  public function isAnySSHProtocol() {
     return ($this->isSSHProtocol() || $this->isSVNSSHProtocol());
   }
 
+  public function isCredentialSupported() {
+    return ($this->getPassphraseProvidesCredentialType() !== null);
+  }
+
+  public function isCredentialOptional() {
+    if ($this->isAnySSHProtocol()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public function getPassphraseCredentialLabel() {
+    if ($this->isAnySSHProtocol()) {
+      return pht('SSH Key');
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return pht('Password');
+    }
+
+    return null;
+  }
+
+  public function getPassphraseDefaultCredentialType() {
+    if ($this->isAnySSHProtocol()) {
+      return PassphraseSSHPrivateKeyTextCredentialType::CREDENTIAL_TYPE;
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return PassphrasePasswordCredentialType::CREDENTIAL_TYPE;
+    }
+
+    return null;
+  }
+
+  public function getPassphraseProvidesCredentialType() {
+    if ($this->isAnySSHProtocol()) {
+      return PassphraseSSHPrivateKeyCredentialType::PROVIDES_TYPE;
+    }
+
+    if ($this->isAnyHTTPProtocol() || $this->isSVNProtocol()) {
+      return PassphrasePasswordCredentialType::PROVIDES_TYPE;
+    }
+
+    return null;
+  }
+
   protected function getSSHWrapper() {
     $root = dirname(phutil_get_library_root('phabricator'));
     return $root.'/bin/ssh-connect';
diff --git a/src/applications/passphrase/view/PassphraseCredentialControl.php b/src/applications/passphrase/view/PassphraseCredentialControl.php
--- a/src/applications/passphrase/view/PassphraseCredentialControl.php
+++ b/src/applications/passphrase/view/PassphraseCredentialControl.php
@@ -42,10 +42,50 @@
     foreach ($this->options as $option) {
       $options_map[$option->getPHID()] = pht(
         '%s %s',
-        'K'.$option->getID(),
+        $option->getMonogram(),
         $option->getName());
     }
 
+    // The user editing the form may not have permission to see the current
+    // credential. Populate it into the menu to allow them to save the form
+    // without making any changes.
+    $current_phid = $this->getValue();
+    if (strlen($current_phid) && empty($options_map[$current_phid])) {
+      $viewer = $this->getViewer();
+
+      $user_credential = id(new PassphraseCredentialQuery())
+        ->setViewer($viewer)
+        ->withPHIDs(array($current_phid))
+        ->executeOne();
+      if (!$user_credential) {
+        // Pull the credential with the ominipotent viewer so we can look up
+        // the ID and tell if it's restricted or invalid.
+        $omnipotent_credential = id(new PassphraseCredentialQuery())
+          ->setViewer(PhabricatorUser::getOmnipotentUser())
+          ->withPHIDs(array($current_phid))
+          ->executeOne();
+        if ($omnipotent_credential) {
+          $current_name = pht(
+            '%s (Restricted Credential)',
+            $omnipotent_credential->getMonogram());
+        } else {
+          $current_name = pht(
+            'Invalid Credential ("%s")',
+            $current_phid);
+        }
+      } else {
+        $current_name = pht(
+          '%s %s',
+          $user_credential->getMonogram(),
+          $user_credential->getName());
+      }
+
+      $options_map = array(
+        $current_phid => $current_name,
+      ) + $options_map;
+    }
+
+
     $disabled = $this->getDisabled();
     if ($this->allowNull) {
       $options_map = array('' => pht('(No Credentials)')) + $options_map;
diff --git a/src/applications/repository/storage/PhabricatorRepositoryURI.php b/src/applications/repository/storage/PhabricatorRepositoryURI.php
--- a/src/applications/repository/storage/PhabricatorRepositoryURI.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryURI.php
@@ -193,11 +193,73 @@
 
 
   public function getDisplayURI() {
-    $uri = new PhutilURI($this->getURI());
+    return $this->getURIObject(false);
+  }
+
+  public function getEffectiveURI() {
+    return $this->getURIObject(true);
+  }
+
+  private function getURIObject($normalize) {
+    // Users can provide Git/SCP-style URIs in the form "user@host:path".
+    // These are equivalent to "ssh://user@host/path". We use the more standard
+    // form internally, and convert to it if we need to specify a port number,
+    // but try to preserve what the user typed when displaying the URI.
+
+    if ($this->isBuiltin()) {
+      $builtin_protocol = $this->getForcedProtocol();
+      $builtin_domain = $this->getForcedHost();
+      $raw_uri = "{$builtin_protocol}://{$builtin_domain}";
+    } else {
+      $raw_uri = $this->getURI();
+    }
+
+    $port = $this->getForcedPort();
+
+    $default_ports = array(
+      'ssh' => 22,
+      'http' => 80,
+      'https' => 443,
+    );
+
+    $uri = new PhutilURI($raw_uri);
+    if (!$uri->getProtocol()) {
+      $git_uri = new PhutilGitURI($raw_uri);
+
+      // We must normalize this Git-style URI into a normal URI
+      $must_normalize = ($port && ($port != $default_ports['ssh']));
+      if ($must_normalize || $normalize) {
+        $domain = $git_uri->getDomain();
 
-    $protocol = $this->getForcedProtocol();
-    if ($protocol) {
-      $uri->setProtocol($protocol);
+
+        $uri = id(new PhutilURI("ssh://{$domain}"))
+          ->setUser($git_uri->getUser())
+          ->setPath($git_uri->getPath());
+      } else {
+        $uri = $git_uri;
+      }
+    }
+
+    $is_normal = ($uri instanceof PhutilURI);
+
+    if ($is_normal) {
+      $protocol = $this->getForcedProtocol();
+      if ($protocol) {
+        $uri->setProtocol($protocol);
+      }
+
+      if ($port) {
+        $uri->setPort($port);
+      }
+
+      // Remove any explicitly set default ports.
+      $uri_port = $uri->getPort();
+      $uri_protocol = $uri->getProtocol();
+
+      $uri_default = idx($default_ports, $uri_protocol);
+      if ($uri_default && ($uri_default == $uri_port)) {
+        $uri->setPort(null);
+      }
     }
 
     $user = $this->getForcedUser();
@@ -210,11 +272,6 @@
       $uri->setDomain($host);
     }
 
-    $port = $this->getForcedPort();
-    if ($port) {
-      $uri->setPort($port);
-    }
-
     $path = $this->getForcedPath();
     if ($path) {
       $uri->setPath($path);
@@ -223,6 +280,7 @@
     return $uri;
   }
 
+
   private function getForcedProtocol() {
     switch ($this->getBuiltinProtocol()) {
       case self::BUILTIN_PROTOCOL_SSH:
@@ -446,6 +504,16 @@
     );
   }
 
+  public function newCommandEngine() {
+    $repository = $this->getRepository();
+    $protocol = $this->getEffectiveURI()->getProtocol();
+
+    return DiffusionCommandEngine::newCommandEngine($repository)
+      ->setCredentialPHID($this->getCredentialPHID())
+      ->setProtocol($protocol);
+  }
+
+
 
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
@@ -569,7 +637,8 @@
       'repositoryPHID' => $this->getRepositoryPHID(),
       'uri' => array(
         'raw' => $this->getURI(),
-        'effective' => (string)$this->getDisplayURI(),
+        'display' => (string)$this->getDisplayURI(),
+        'effective' => (string)$this->getEffectiveURI(),
       ),
       'io' => array(
         'raw' => $this->getIOType(),
diff --git a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
--- a/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryURITransaction.php
@@ -18,6 +18,26 @@
     return PhabricatorRepositoryURIPHIDType::TYPECONST;
   }
 
+  public function getRequiredHandlePHIDs() {
+    $phids = parent::getRequiredHandlePHIDs();
+
+    $old = $this->getOldValue();
+    $new = $this->getNewValue();
+
+    switch ($this->getTransactionType()) {
+      case self::TYPE_CREDENTIAL:
+        if ($old) {
+          $phids[] = $old;
+        }
+        if ($new) {
+          $phids[] = $new;
+        }
+        break;
+    }
+
+    return $phids;
+  }
+
   public function getTitle() {
     $author_phid = $this->getAuthorPHID();
 
@@ -61,6 +81,24 @@
             '%s enabled this URI.',
             $this->renderHandleLink($author_phid));
         }
+      case self::TYPE_CREDENTIAL:
+        if ($old && $new) {
+          return pht(
+            '%s changed the credential for this URI from %s to %s.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($old),
+            $this->renderHandleLink($new));
+        } else if ($old) {
+          return pht(
+            '%s removed %s as the credential for this URI.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($old));
+        } else if ($new) {
+          return pht(
+            '%s set the credential for this URI to %s.',
+            $this->renderHandleLink($author_phid),
+            $this->renderHandleLink($new));
+        }
 
     }
 
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php
@@ -34,6 +34,7 @@
       ->execute();
 
     return id(new PassphraseCredentialControl())
+      ->setViewer($this->getViewer())
       ->setLabel($this->getFieldName())
       ->setName($this->getFieldKey())
       ->setCaption($this->getCaption())