Changeset View
Changeset View
Standalone View
Standalone View
src/repository/remote/ArcanistRepositoryURINormalizer.php
- This file was added.
| <?php | |||||
| /** | |||||
| * Normalize repository URIs. For example, these URIs are generally equivalent | |||||
| * and all point at the same repository: | |||||
| * | |||||
| * ssh://user@host/repo | |||||
| * ssh://user@host/repo/ | |||||
| * ssh://user@host:22/repo | |||||
| * user@host:/repo | |||||
| * ssh://user@host/repo.git | |||||
| * | |||||
| * This class can be used to normalize URIs like this, in order to detect | |||||
| * alternate spellings of the same repository URI. In particular, the | |||||
| * @{method:getNormalizedPath} method will return: | |||||
| * | |||||
| * repo | |||||
| * | |||||
| * ...for all of these URIs. Generally, usage looks like this: | |||||
| * | |||||
| * $norm_a = new ArcanistRepositoryURINormalizer($type, $uri_a); | |||||
| * $norm_b = new ArcanistRepositoryURINormalizer($type, $uri_b); | |||||
| * | |||||
| * if ($norm_a->getNormalizedPath() === $norm_b->getNormalizedPath()) { | |||||
| * // URIs appear to point at the same repository. | |||||
| * } else { | |||||
| * // URIs are very unlikely to be the same repository. | |||||
| * } | |||||
| * | |||||
| * Because a repository can be hosted at arbitrarily many arbitrary URIs, there | |||||
| * is no way to completely prevent false negatives by only examining URIs | |||||
| * (that is, repositories with totally different URIs could really be the same). | |||||
| * However, normalization is relatively aggressive and false negatives should | |||||
| * be rare: if normalization says two URIs are different repositories, they | |||||
| * probably are. | |||||
| * | |||||
| * @task normal Normalizing URIs | |||||
| */ | |||||
| final class ArcanistRepositoryURINormalizer | |||||
| extends Phobject { | |||||
| const TYPE_GIT = 'git'; | |||||
| const TYPE_SVN = 'svn'; | |||||
| const TYPE_MERCURIAL = 'hg'; | |||||
| private $type; | |||||
| private $uri; | |||||
| private $domainMap = array(); | |||||
| public function __construct($type, $uri) { | |||||
| switch ($type) { | |||||
| case self::TYPE_GIT: | |||||
| case self::TYPE_SVN: | |||||
| case self::TYPE_MERCURIAL: | |||||
| break; | |||||
| default: | |||||
| throw new Exception(pht('Unknown URI type "%s"!', $type)); | |||||
| } | |||||
| $this->type = $type; | |||||
| $this->uri = $uri; | |||||
| } | |||||
| public static function getAllURITypes() { | |||||
| return array( | |||||
| self::TYPE_GIT, | |||||
| self::TYPE_SVN, | |||||
| self::TYPE_MERCURIAL, | |||||
| ); | |||||
| } | |||||
| public function setDomainMap(array $domain_map) { | |||||
| foreach ($domain_map as $key => $domain) { | |||||
| $domain_map[$key] = phutil_utf8_strtolower($domain); | |||||
| } | |||||
| $this->domainMap = $domain_map; | |||||
| return $this; | |||||
| } | |||||
| /* -( Normalizing URIs )--------------------------------------------------- */ | |||||
| /** | |||||
| * @task normal | |||||
| */ | |||||
| public function getPath() { | |||||
| switch ($this->type) { | |||||
| case self::TYPE_GIT: | |||||
| $uri = new PhutilURI($this->uri); | |||||
| return $uri->getPath(); | |||||
| case self::TYPE_SVN: | |||||
| case self::TYPE_MERCURIAL: | |||||
| $uri = new PhutilURI($this->uri); | |||||
| if ($uri->getProtocol()) { | |||||
| return $uri->getPath(); | |||||
| } | |||||
| return $this->uri; | |||||
| } | |||||
| } | |||||
| public function getNormalizedURI() { | |||||
| return $this->getNormalizedDomain().'/'.$this->getNormalizedPath(); | |||||
| } | |||||
| /** | |||||
| * @task normal | |||||
| */ | |||||
| public function getNormalizedPath() { | |||||
| $path = $this->getPath(); | |||||
| $path = trim($path, '/'); | |||||
| switch ($this->type) { | |||||
| case self::TYPE_GIT: | |||||
| $path = preg_replace('/\.git$/', '', $path); | |||||
| break; | |||||
| case self::TYPE_SVN: | |||||
| case self::TYPE_MERCURIAL: | |||||
| break; | |||||
| } | |||||
| // If this is a Phabricator URI, strip it down to the callsign. We mutably | |||||
| // allow you to clone repositories as "/diffusion/X/anything.git", for | |||||
| // example. | |||||
| $matches = null; | |||||
| if (preg_match('@^(diffusion/(?:[A-Z]+|\d+))@', $path, $matches)) { | |||||
| $path = $matches[1]; | |||||
| } | |||||
| return $path; | |||||
| } | |||||
| public function getNormalizedDomain() { | |||||
| $domain = null; | |||||
| $uri = new PhutilURI($this->uri); | |||||
| $domain = $uri->getDomain(); | |||||
| if (!strlen($domain)) { | |||||
| return '<void>'; | |||||
| } | |||||
| $domain = phutil_utf8_strtolower($domain); | |||||
| foreach ($this->domainMap as $domain_key => $domain_value) { | |||||
| if ($domain === $domain_value) { | |||||
| $domain = $domain_key; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return $domain; | |||||
| } | |||||
| } | |||||