Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diffusion/conduit/ConduitAPI_diffusion_historyquery_Method.php b/src/applications/diffusion/conduit/ConduitAPI_diffusion_historyquery_Method.php
index d6c6aab39e..3d552d0725 100644
--- a/src/applications/diffusion/conduit/ConduitAPI_diffusion_historyquery_Method.php
+++ b/src/applications/diffusion/conduit/ConduitAPI_diffusion_historyquery_Method.php
@@ -1,266 +1,267 @@
<?php
/**
* @group conduit
*/
final class ConduitAPI_diffusion_historyquery_Method
extends ConduitAPI_diffusion_abstractquery_Method {
private $parents = array();
public function getMethodDescription() {
return 'Returns history information for a repository at a specific '.
'commit and path.';
}
public function defineReturnType() {
return 'array';
}
protected function defineCustomParamTypes() {
return array(
'commit' => 'required string',
'path' => 'required string',
'offset' => 'required int',
'limit' => 'required int',
'needDirectChanges' => 'optional bool',
'needChildChanges' => 'optional bool',
);
}
protected function getResult(ConduitAPIRequest $request) {
$path_changes = parent::getResult($request);
return array(
'pathChanges' => mpull($path_changes, 'toDictionary'),
'parents' => $this->parents);
}
protected function getGitResult(ConduitAPIRequest $request) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commit_hash = $request->getValue('commit');
$path = $request->getValue('path');
$offset = $request->getValue('offset');
$limit = $request->getValue('limit');
list($stdout) = $repository->execxLocalCommand(
'log '.
'--skip=%d '.
'-n %d '.
'--pretty=format:%s '.
'%s -- %C',
$offset,
$limit,
'%H:%P',
$commit_hash,
// Git omits merge commits if the path is provided, even if it is empty.
(strlen($path) ? csprintf('%s', $path) : ''));
$lines = explode("\n", trim($stdout));
$lines = array_filter($lines);
if (!$lines) {
return array();
}
$hash_list = array();
$parent_map = array();
foreach ($lines as $line) {
list($hash, $parents) = explode(":", $line);
$hash_list[] = $hash;
$parent_map[$hash] = preg_split('/\s+/', $parents);
}
$this->parents = $parent_map;
return DiffusionQuery::loadHistoryForCommitIdentifiers(
$hash_list,
$drequest);
}
protected function getMercurialResult(ConduitAPIRequest $request) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commit_hash = $request->getValue('commit');
$path = $request->getValue('path');
$offset = $request->getValue('offset');
$limit = $request->getValue('limit');
$path = DiffusionPathIDQuery::normalizePath($path);
$path = ltrim($path, '/');
// NOTE: Older versions of Mercurial give different results for these
// commands (see T1268):
//
// $ hg log -- ''
// $ hg log
//
// All versions of Mercurial give different results for these commands
// (merge commits are excluded with the "." version):
//
// $ hg log -- .
// $ hg log
//
// If we don't have a path component in the query, omit it from the command
// entirely to avoid these inconsistencies.
// NOTE: When viewing the history of a file, we don't use "-b", because
// Mercurial stops history at the branchpoint but we're interested in all
// ancestors. When viewing history of a branch, we do use "-b", and thus
// stop history (this is more consistent with the Mercurial worldview of
// branches).
if (strlen($path)) {
$path_arg = csprintf('-- %s', $path);
$branch_arg = '';
} else {
$path_arg = '';
// NOTE: --branch used to be called --only-branch; use -b for
// compatibility.
$branch_arg = csprintf('-b %s', $drequest->getBranch());
}
list($stdout) = $repository->execxLocalCommand(
'log --debug --template %s --limit %d %C --rev %s %C',
'{node};{parents}\\n',
($offset + $limit), // No '--skip' in Mercurial.
$branch_arg,
hgsprintf('reverse(%s::%s)', '0', $commit_hash),
$path_arg);
+ $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout);
$lines = explode("\n", trim($stdout));
$lines = array_slice($lines, $offset);
$hash_list = array();
$parent_map = array();
$last = null;
foreach (array_reverse($lines) as $line) {
list($hash, $parents) = explode(';', $line);
$parents = trim($parents);
if (!$parents) {
if ($last === null) {
$parent_map[$hash] = array('...');
} else {
$parent_map[$hash] = array($last);
}
} else {
$parents = preg_split('/\s+/', $parents);
foreach ($parents as $parent) {
list($plocal, $phash) = explode(':', $parent);
if (!preg_match('/^0+$/', $phash)) {
$parent_map[$hash][] = $phash;
}
}
// This may happen for the zeroth commit in repository, both hashes
// are "000000000...".
if (empty($parent_map[$hash])) {
$parent_map[$hash] = array('...');
}
}
// The rendering code expects the first commit to be "mainline", like
// Git. Flip the order so it does the right thing.
$parent_map[$hash] = array_reverse($parent_map[$hash]);
$hash_list[] = $hash;
$last = $hash;
}
$hash_list = array_reverse($hash_list);
$this->parents = $parent_map;
return DiffusionQuery::loadHistoryForCommitIdentifiers(
$hash_list,
$drequest);
}
protected function getSVNResult(ConduitAPIRequest $request) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commit = $request->getValue('commit');
$path = $request->getValue('path');
$offset = $request->getValue('offset');
$limit = $request->getValue('limit');
$need_direct_changes = $request->getValue('needDirectChanges');
$need_child_changes = $request->getValue('needChildChanges');
$conn_r = $repository->establishConnection('r');
$paths = queryfx_all(
$conn_r,
'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',
PhabricatorRepository::TABLE_PATH,
array(md5('/'.trim($path, '/'))));
$paths = ipull($paths, 'id', 'path');
$path_id = idx($paths, '/'.trim($path, '/'));
if (!$path_id) {
return array();
}
$filter_query = '';
if ($need_direct_changes) {
if ($need_child_changes) {
$type = DifferentialChangeType::TYPE_CHILD;
$filter_query = 'AND (isDirect = 1 OR changeType = '.$type.')';
} else {
$filter_query = 'AND (isDirect = 1)';
}
}
$history_data = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE repositoryID = %d AND pathID = %d
AND commitSequence <= %d
%Q
ORDER BY commitSequence DESC
LIMIT %d, %d',
PhabricatorRepository::TABLE_PATHCHANGE,
$repository->getID(),
$path_id,
$commit ? $commit : 0x7FFFFFFF,
$filter_query,
$offset,
$limit);
$commits = array();
$commit_data = array();
$commit_ids = ipull($history_data, 'commitID');
if ($commit_ids) {
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'id IN (%Ld)',
$commit_ids);
if ($commits) {
$commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
'commitID in (%Ld)',
$commit_ids);
$commit_data = mpull($commit_data, null, 'getCommitID');
}
}
$history = array();
foreach ($history_data as $row) {
$item = new DiffusionPathChange();
$commit = idx($commits, $row['commitID']);
if ($commit) {
$item->setCommit($commit);
$item->setCommitIdentifier($commit->getCommitIdentifier());
$data = idx($commit_data, $commit->getID());
if ($data) {
$item->setCommitData($data);
}
}
$item->setChangeType($row['changeType']);
$item->setFileType($row['fileType']);
$history[] = $item;
}
return $history;
}
}
diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php
index cd1cbcd720..602b2b6123 100644
--- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php
+++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php
@@ -1,28 +1,29 @@
<?php
/**
* Execute and parse a low-level Mercurial branches query using `hg branches`.
*/
final class DiffusionLowLevelMercurialBranchesQuery
extends DiffusionLowLevelQuery {
protected function executeQuery() {
$repository = $this->getRepository();
// NOTE: `--debug` gives us 40-character hashes.
list($stdout) = $repository->execxLocalCommand(
'--debug branches');
+ $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout);
$branches = array();
$lines = ArcanistMercurialParser::parseMercurialBranches($stdout);
foreach ($lines as $name => $spec) {
$branches[] = id(new DiffusionBranchInformation())
->setName($name)
->setHeadCommitIdentifier($spec['rev']);
}
return $branches;
}
}
diff --git a/src/applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php b/src/applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php
index ec19f9cbce..dc9f900ce8 100644
--- a/src/applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php
+++ b/src/applications/diffusion/query/parents/DiffusionMercurialCommitParentsQuery.php
@@ -1,30 +1,31 @@
<?php
final class DiffusionMercurialCommitParentsQuery
extends DiffusionCommitParentsQuery {
protected function executeQuery() {
$drequest = $this->getRequest();
$repository = $drequest->getRepository();
list($stdout) = $repository->execxLocalCommand(
'log --debug --limit 1 --template={parents} --rev %s',
$drequest->getStableCommitName());
+ $stdout = PhabricatorRepository::filterMercurialDebugOutput($stdout);
$hashes = preg_split('/\s+/', trim($stdout));
foreach ($hashes as $key => $value) {
// Mercurial parents look like "23:ad9f769d6f786fad9f76d9a" -- we want
// to strip out the local rev part.
list($local, $global) = explode(':', $value);
$hashes[$key] = $global;
// With --debug we get 40-character hashes but also get the "000000..."
// hash for missing parents; ignore it.
if (preg_match('/^0+$/', $global)) {
unset($hashes[$key]);
}
}
return self::loadCommitsByIdentifiers($hashes, $drequest);
}
}
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
index 2ce03840a0..64ce761e72 100644
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -1,1057 +1,1080 @@
<?php
/**
* @task uri Repository URI Management
*/
final class PhabricatorRepository extends PhabricatorRepositoryDAO
implements
PhabricatorPolicyInterface,
PhabricatorFlaggableInterface,
PhabricatorMarkupInterface {
/**
* Shortest hash we'll recognize in raw "a829f32" form.
*/
const MINIMUM_UNQUALIFIED_HASH = 7;
/**
* Shortest hash we'll recognize in qualified "rXab7ef2f8" form.
*/
const MINIMUM_QUALIFIED_HASH = 5;
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';
const TABLE_LINTMESSAGE = 'repository_lintmessage';
const SERVE_OFF = 'off';
const SERVE_READONLY = 'readonly';
const SERVE_READWRITE = 'readwrite';
protected $name;
protected $callsign;
protected $uuid;
protected $viewPolicy;
protected $editPolicy;
protected $pushPolicy;
protected $versionControlSystem;
protected $details = array();
protected $credentialPHID;
private $commitCount = self::ATTACHABLE;
private $mostRecentCommit = self::ATTACHABLE;
public static function initializeNewRepository(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorApplicationDiffusion'))
->executeOne();
$view_policy = $app->getPolicy(DiffusionCapabilityDefaultView::CAPABILITY);
$edit_policy = $app->getPolicy(DiffusionCapabilityDefaultEdit::CAPABILITY);
$push_policy = $app->getPolicy(DiffusionCapabilityDefaultPush::CAPABILITY);
return id(new PhabricatorRepository())
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setPushPolicy($push_policy);
}
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(
PhabricatorRepositoryPHIDTypeRepository::TYPECONST);
}
public function toDictionary() {
return array(
'name' => $this->getName(),
'phid' => $this->getPHID(),
'callsign' => $this->getCallsign(),
'vcs' => $this->getVersionControlSystem(),
'uri' => PhabricatorEnv::getProductionURI($this->getURI()),
'remoteURI' => (string)$this->getRemoteURI(),
'tracking' => $this->getDetail('tracking-enabled'),
'description' => $this->getDetail('description'),
);
}
public function getDetail($key, $default = null) {
return idx($this->details, $key, $default);
}
public function getHumanReadableDetail($key, $default = null) {
$value = $this->getDetail($key, $default);
switch ($key) {
case 'branch-filter':
case 'close-commits-filter':
$value = array_keys($value);
$value = implode(', ', $value);
break;
}
return $value;
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
public function attachCommitCount($count) {
$this->commitCount = $count;
return $this;
}
public function getCommitCount() {
return $this->assertAttached($this->commitCount);
}
public function attachMostRecentCommit(
PhabricatorRepositoryCommit $commit = null) {
$this->mostRecentCommit = $commit;
return $this;
}
public function getMostRecentCommit() {
return $this->assertAttached($this->mostRecentCommit);
}
public function getDiffusionBrowseURIForPath(
PhabricatorUser $user,
$path,
$line = null,
$branch = null) {
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $user,
'repository' => $this,
'path' => $path,
'branch' => $branch,
));
return $drequest->generateURI(
array(
'action' => 'browse',
'line' => $line,
));
}
public function getLocalPath() {
return $this->getDetail('local-path');
}
public function getSubversionBaseURI($commit = null) {
$subpath = $this->getDetail('svn-subpath');
if (!strlen($subpath)) {
$subpath = null;
}
return $this->getSubversionPathURI($subpath, $commit);
}
public function getSubversionPathURI($path = null, $commit = null) {
$vcs = $this->getVersionControlSystem();
if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) {
throw new Exception("Not a subversion repository!");
}
if ($this->isHosted()) {
$uri = 'file://'.$this->getLocalPath();
} else {
$uri = $this->getDetail('remote-uri');
}
$uri = rtrim($uri, '/');
if (strlen($path)) {
$path = rawurlencode($path);
$path = str_replace('%2F', '/', $path);
$uri = $uri.'/'.ltrim($path, '/');
}
if ($path !== null || $commit !== null) {
$uri .= '@';
}
if ($commit !== null) {
$uri .= $commit;
}
return $uri;
}
/* -( Remote Command Execution )------------------------------------------- */
public function execRemoteCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newRemoteCommandFuture($args)->resolve();
}
public function execxRemoteCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newRemoteCommandFuture($args)->resolvex();
}
public function getRemoteCommandFuture($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newRemoteCommandFuture($args);
}
public function passthruRemoteCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newRemoteCommandPassthru($args)->execute();
}
private function newRemoteCommandFuture(array $argv) {
$argv = $this->formatRemoteCommand($argv);
$future = newv('ExecFuture', $argv);
$future->setEnv($this->getRemoteCommandEnvironment());
return $future;
}
private function newRemoteCommandPassthru(array $argv) {
$argv = $this->formatRemoteCommand($argv);
$passthru = newv('PhutilExecPassthru', $argv);
$passthru->setEnv($this->getRemoteCommandEnvironment());
return $passthru;
}
/* -( Local Command Execution )-------------------------------------------- */
public function execLocalCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newLocalCommandFuture($args)->resolve();
}
public function execxLocalCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newLocalCommandFuture($args)->resolvex();
}
public function getLocalCommandFuture($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newLocalCommandFuture($args);
}
public function passthruLocalCommand($pattern /* , $arg, ... */) {
$args = func_get_args();
return $this->newLocalCommandPassthru($args)->execute();
}
private function newLocalCommandFuture(array $argv) {
$this->assertLocalExists();
$argv = $this->formatLocalCommand($argv);
$future = newv('ExecFuture', $argv);
$future->setEnv($this->getLocalCommandEnvironment());
if ($this->usesLocalWorkingCopy()) {
$future->setCWD($this->getLocalPath());
}
return $future;
}
private function newLocalCommandPassthru(array $argv) {
$this->assertLocalExists();
$argv = $this->formatLocalCommand($argv);
$future = newv('PhutilExecPassthru', $argv);
$future->setEnv($this->getLocalCommandEnvironment());
if ($this->usesLocalWorkingCopy()) {
$future->setCWD($this->getLocalPath());
}
return $future;
}
/* -( Command Infrastructure )--------------------------------------------- */
private function getSSHWrapper() {
$root = dirname(phutil_get_library_root('phabricator'));
return $root.'/bin/ssh-connect';
}
private function getCommonCommandEnvironment() {
$env = array(
// NOTE: Force the language to "en_US.UTF-8", which overrides locale
// settings. This makes stuff print in English instead of, e.g., French,
// so we can parse the output of some commands, error messages, etc.
'LANG' => 'en_US.UTF-8',
// Propagate PHABRICATOR_ENV explicitly. For discussion, see T4155.
'PHABRICATOR_ENV' => PhabricatorEnv::getSelectedEnvironmentName(),
);
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
// NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if
// it can not read $HOME. For many users, $HOME points at /root (this
// seems to be a default result of Apache setup). Instead, explicitly
// point $HOME at a readable, empty directory so that Git looks for the
// config file it's after, fails to locate it, and moves on. This is
// really silly, but seems like the least damaging approach to
// mitigating the issue.
$root = dirname(phutil_get_library_root('phabricator'));
$env['HOME'] = $root.'/support/empty/';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
// NOTE: This overrides certain configuration, extensions, and settings
// which make Mercurial commands do random unusual things.
$env['HGPLAIN'] = 1;
break;
default:
throw new Exception("Unrecognized version control system.");
}
return $env;
}
private function getLocalCommandEnvironment() {
return $this->getCommonCommandEnvironment();
}
private function getRemoteCommandEnvironment() {
$env = $this->getCommonCommandEnvironment();
if ($this->shouldUseSSH()) {
// NOTE: This is read by `bin/ssh-connect`, and tells it which credentials
// to use.
$env['PHABRICATOR_CREDENTIAL'] = $this->getCredentialPHID();
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
// Force SVN to use `bin/ssh-connect`.
$env['SVN_SSH'] = $this->getSSHWrapper();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
// Force Git to use `bin/ssh-connect`.
$env['GIT_SSH'] = $this->getSSHWrapper();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
// We force Mercurial through `bin/ssh-connect` too, but it uses a
// command-line flag instead of an environmental variable.
break;
default:
throw new Exception("Unrecognized version control system.");
}
}
return $env;
}
private function formatRemoteCommand(array $args) {
$pattern = $args[0];
$args = array_slice($args, 1);
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) {
$flags = array();
$flag_args = array();
$flags[] = '--non-interactive';
$flags[] = '--no-auth-cache';
if ($this->shouldUseHTTP()) {
$flags[] = '--trust-server-cert';
}
$credential_phid = $this->getCredentialPHID();
if ($credential_phid) {
$key = PassphrasePasswordKey::loadFromPHID(
$credential_phid,
PhabricatorUser::getOmnipotentUser());
$flags[] = '--username %P';
$flags[] = '--password %P';
$flag_args[] = $key->getUsernameEnvelope();
$flag_args[] = $key->getPasswordEnvelope();
}
$flags = implode(' ', $flags);
$pattern = "svn {$flags} {$pattern}";
$args = array_mergev(array($flag_args, $args));
} else {
$pattern = "svn --non-interactive {$pattern}";
}
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$pattern = "git {$pattern}";
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
if ($this->shouldUseSSH()) {
$pattern = "hg --config ui.ssh=%s {$pattern}";
array_unshift(
$args,
$this->getSSHWrapper());
} else {
$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 = "svn --non-interactive {$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;
}
+ /**
+ * Sanitize output of an `hg` command invoked with the `--debug` flag to make
+ * it usable.
+ *
+ * @param string Output from `hg --debug ...`
+ * @return string Usable output.
+ */
+ public static function filterMercurialDebugOutput($stdout) {
+ // When hg commands are run with `--debug` and some config file isn't
+ // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011.
+ //
+ // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html
+
+ $lines = preg_split('/(?<=\n)/', $stdout);
+ $regex = '/ignoring untrusted configuration option .*\n$/';
+
+ foreach ($lines as $key => $line) {
+ $lines[$key] = preg_replace($regex, '', $line);
+ }
+
+ return implode('', $lines);
+ }
+
public function getURI() {
return '/diffusion/'.$this->getCallsign().'/';
}
public function isTracked() {
return $this->getDetail('tracking-enabled', false);
}
public function getDefaultBranch() {
$default = $this->getDetail('default-branch');
if (strlen($default)) {
return $default;
}
$default_branches = array(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'master',
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'default',
);
return idx($default_branches, $this->getVersionControlSystem());
}
public function getDefaultArcanistBranch() {
return coalesce($this->getDefaultBranch(), 'svn');
}
private function isBranchInFilter($branch, $filter_key) {
$vcs = $this->getVersionControlSystem();
$is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
$use_filter = ($is_git);
if ($use_filter) {
$filter = $this->getDetail($filter_key, array());
if ($filter && empty($filter[$branch])) {
return false;
}
}
// By default, all branches pass.
return true;
}
public function shouldTrackBranch($branch) {
return $this->isBranchInFilter($branch, 'branch-filter');
}
public function shouldAutocloseBranch($branch) {
if ($this->isImporting()) {
return false;
}
if ($this->getDetail('disable-autoclose', false)) {
return false;
}
return $this->isBranchInFilter($branch, 'close-commits-filter');
}
public function shouldAutocloseCommit(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data) {
if ($this->getDetail('disable-autoclose', false)) {
return false;
}
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
return true;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
return true;
default:
throw new Exception("Unrecognized version control system.");
}
$branches = $data->getCommitDetail('seenOnBranches', array());
foreach ($branches as $branch) {
if ($this->shouldAutocloseBranch($branch)) {
return true;
}
}
return false;
}
public function formatCommitName($commit_identifier) {
$vcs = $this->getVersionControlSystem();
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
$is_git = ($vcs == $type_git);
$is_hg = ($vcs == $type_hg);
if ($is_git || $is_hg) {
$short_identifier = substr($commit_identifier, 0, 12);
} else {
$short_identifier = $commit_identifier;
}
return 'r'.$this->getCallsign().$short_identifier;
}
public function isImporting() {
return (bool)$this->getDetail('importing', false);
}
/* -( Repository URI Management )------------------------------------------ */
/**
* Get the remote URI for this repository.
*
* @return string
* @task uri
*/
public function getRemoteURI() {
return (string)$this->getRemoteURIObject();
}
/**
* Get the remote URI for this repository, including credentials if they're
* used by this repository.
*
* @return PhutilOpaqueEnvelope URI, possibly including credentials.
* @task uri
*/
public function getRemoteURIEnvelope() {
$uri = $this->getRemoteURIObject();
$remote_protocol = $this->getRemoteProtocol();
if ($remote_protocol == 'http' || $remote_protocol == 'https') {
// For SVN, we use `--username` and `--password` flags separately, so
// don't add any credentials here.
if (!$this->isSVN()) {
$credential_phid = $this->getCredentialPHID();
if ($credential_phid) {
$key = PassphrasePasswordKey::loadFromPHID(
$credential_phid,
PhabricatorUser::getOmnipotentUser());
$uri->setUser($key->getUsernameEnvelope()->openEnvelope());
$uri->setPass($key->getPasswordEnvelope()->openEnvelope());
}
}
}
return new PhutilOpaqueEnvelope((string)$uri);
}
/**
* Get the remote URI for this repository, without authentication information.
*
* @return string Repository URI.
* @task uri
*/
public function getPublicRemoteURI() {
$uri = $this->getRemoteURIObject();
// Make sure we don't leak anything if this repo is using HTTP Basic Auth
// with the credentials in the URI or something zany like that.
// If repository is not accessed over SSH we remove both username and
// password.
if (!$this->shouldUseSSH()) {
$uri->setUser(null);
// This might be a Git URI or a normal URI. If it's Git, there's no
// password support.
if ($uri instanceof PhutilURI) {
$uri->setPass(null);
}
}
return (string)$uri;
}
/**
* Get the protocol for the repository's remote.
*
* @return string Protocol, like "ssh" or "git".
* @task uri
*/
public function getRemoteProtocol() {
$uri = $this->getRemoteURIObject();
if ($uri instanceof PhutilGitURI) {
return 'ssh';
} else {
return $uri->getProtocol();
}
}
/**
* Get a parsed object representation of the repository's remote URI. This
* may be a normal URI (returned as a @{class@libphutil:PhutilURI}) or a git
* URI (returned as a @{class@libphutil:PhutilGitURI}).
*
* @return wild A @{class@libphutil:PhutilURI} or
* @{class@libphutil:PhutilGitURI}.
* @task uri
*/
public function getRemoteURIObject() {
$raw_uri = $this->getDetail('remote-uri');
if (!$raw_uri) {
return new PhutilURI('');
}
if (!strncmp($raw_uri, '/', 1)) {
return new PhutilURI('file://'.$raw_uri);
}
$uri = new PhutilURI($raw_uri);
if ($uri->getProtocol()) {
return $uri;
}
$uri = new PhutilGitURI($raw_uri);
if ($uri->getDomain()) {
return $uri;
}
throw new Exception("Remote URI '{$raw_uri}' could not be parsed!");
}
/**
* Determine if we should connect to the remote using SSH flags and
* credentials.
*
* @return bool True to use the SSH protocol.
* @task uri
*/
private function shouldUseSSH() {
if ($this->isHosted()) {
return false;
}
$protocol = $this->getRemoteProtocol();
if ($this->isSSHProtocol($protocol)) {
return true;
}
return false;
}
/**
* Determine if we should connect to the remote using HTTP flags and
* credentials.
*
* @return bool True to use the HTTP protocol.
* @task uri
*/
private function shouldUseHTTP() {
if ($this->isHosted()) {
return false;
}
$protocol = $this->getRemoteProtocol();
return ($protocol == 'http' || $protocol == 'https');
}
/**
* Determine if we should connect to the remote using SVN flags and
* credentials.
*
* @return bool True to use the SVN protocol.
* @task uri
*/
private function shouldUseSVNProtocol() {
if ($this->isHosted()) {
return false;
}
$protocol = $this->getRemoteProtocol();
return ($protocol == 'svn');
}
/**
* Determine if a protocol is SSH or SSH-like.
*
* @param string A protocol string, like "http" or "ssh".
* @return bool True if the protocol is SSH-like.
* @task uri
*/
private function isSSHProtocol($protocol) {
return ($protocol == 'ssh' || $protocol == 'svn+ssh');
}
public function delete() {
$this->openTransaction();
$paths = id(new PhabricatorOwnersPath())
->loadAllWhere('repositoryPHID = %s', $this->getPHID());
foreach ($paths as $path) {
$path->delete();
}
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere('repositoryID = %d', $this->getID());
foreach ($projects as $project) {
// note each project deletes its PhabricatorRepositorySymbols
$project->delete();
}
$commits = id(new PhabricatorRepositoryCommit())
->loadAllWhere('repositoryID = %d', $this->getID());
foreach ($commits as $commit) {
// note PhabricatorRepositoryAuditRequests and
// PhabricatorRepositoryCommitData are deleted here too.
$commit->delete();
}
$mirrors = id(new PhabricatorRepositoryMirror())
->loadAllWhere('repositoryPHID = %s', $this->getPHID());
foreach ($mirrors as $mirror) {
$mirror->delete();
}
$conn_w = $this->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryID = %d',
self::TABLE_FILESYSTEM,
$this->getID());
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryID = %d',
self::TABLE_PATHCHANGE,
$this->getID());
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryID = %d',
self::TABLE_SUMMARY,
$this->getID());
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function isGit() {
$vcs = $this->getVersionControlSystem();
return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT);
}
public function isSVN() {
$vcs = $this->getVersionControlSystem();
return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN);
}
public function isHg() {
$vcs = $this->getVersionControlSystem();
return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL);
}
public function isHosted() {
return (bool)$this->getDetail('hosting-enabled', false);
}
public function setHosted($enabled) {
return $this->setDetail('hosting-enabled', $enabled);
}
public function getServeOverHTTP() {
if ($this->isSVN()) {
return self::SERVE_OFF;
}
$serve = $this->getDetail('serve-over-http', self::SERVE_OFF);
return $this->normalizeServeConfigSetting($serve);
}
public function setServeOverHTTP($mode) {
return $this->setDetail('serve-over-http', $mode);
}
public function getServeOverSSH() {
$serve = $this->getDetail('serve-over-ssh', self::SERVE_OFF);
return $this->normalizeServeConfigSetting($serve);
}
public function setServeOverSSH($mode) {
return $this->setDetail('serve-over-ssh', $mode);
}
public static function getProtocolAvailabilityName($constant) {
switch ($constant) {
case self::SERVE_OFF:
return pht('Off');
case self::SERVE_READONLY:
return pht('Read Only');
case self::SERVE_READWRITE:
return pht('Read/Write');
default:
return pht('Unknown');
}
}
private function normalizeServeConfigSetting($value) {
switch ($value) {
case self::SERVE_OFF:
case self::SERVE_READONLY:
return $value;
case self::SERVE_READWRITE:
if ($this->isHosted()) {
return self::SERVE_READWRITE;
} else {
return self::SERVE_READONLY;
}
default:
return self::SERVE_OFF;
}
}
/**
* Raise more useful errors when there are basic filesystem problems.
*/
private function assertLocalExists() {
if (!$this->usesLocalWorkingCopy()) {
return;
}
$local = $this->getLocalPath();
Filesystem::assertExists($local);
Filesystem::assertIsDirectory($local);
Filesystem::assertReadable($local);
}
/**
* Determine if the working copy is bare or not. In Git, this corresponds
* to `--bare`. In Mercurial, `--noupdate`.
*/
public function isWorkingCopyBare() {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
return false;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$local = $this->getLocalPath();
if (Filesystem::pathExists($local.'/.git')) {
return false;
} else {
return true;
}
}
}
public function usesLocalWorkingCopy() {
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
return $this->isHosted();
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
return true;
}
}
public function canDestroyWorkingCopy() {
if ($this->isHosted()) {
// Never destroy hosted working copies.
return false;
}
$default_path = PhabricatorEnv::getEnvConfig(
'repository.default-local-path');
return Filesystem::isDescendant($this->getLocalPath(), $default_path);
}
public function canMirror() {
if (!$this->isHosted()) {
return false;
}
if ($this->isGit()) {
return true;
}
return false;
}
public function canAllowDangerousChanges() {
if (!$this->isHosted()) {
return false;
}
if ($this->isGit()) {
return true;
}
return false;
}
public function shouldAllowDangerousChanges() {
return (bool)$this->getDetail('allow-dangerous-changes');
}
public function writeStatusMessage(
$status_type,
$status_code,
array $parameters = array()) {
$table = new PhabricatorRepositoryStatusMessage();
$conn_w = $table->establishConnection('w');
$table_name = $table->getTableName();
if ($status_code === null) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE repositoryID = %d AND statusType = %s',
$table_name,
$this->getID(),
$status_type);
} else {
queryfx(
$conn_w,
'INSERT INTO %T
(repositoryID, statusType, statusCode, parameters, epoch)
VALUES (%d, %s, %s, %s, %d)
ON DUPLICATE KEY UPDATE
statusCode = VALUES(statusCode),
parameters = VALUES(parameters),
epoch = VALUES(epoch)',
$table_name,
$this->getID(),
$status_type,
$status_code,
json_encode($parameters),
time());
}
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
DiffusionCapabilityPush::CAPABILITY,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
case DiffusionCapabilityPush::CAPABILITY:
return $this->getPushPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorMarkupInterface )----------------------------------------- */
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digestForIndex($this->getMarkupText($field));
return "repo:{$hash}";
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newMarkupEngine(array());
}
public function getMarkupText($field) {
return $this->getDetail('description');
}
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
require_celerity_resource('phabricator-remarkup-css');
return phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$output);
}
public function shouldUseMarkupCache($field) {
return true;
}
}
diff --git a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
index 69f18f454a..5b60b2968e 100644
--- a/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
+++ b/src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
@@ -1,111 +1,158 @@
<?php
final class PhabricatorRepositoryTestCase
extends PhabricatorTestCase {
public function testRepositoryURIProtocols() {
$tests = array(
'/path/to/repo' => 'file',
'file:///path/to/repo' => 'file',
'ssh://user@domain.com/path' => 'ssh',
'git@example.com:path' => 'ssh',
'git://git@example.com/path' => 'git',
'svn+ssh://example.com/path' => 'svn+ssh',
'https://example.com/repo/' => 'https',
'http://example.com/' => 'http',
'https://user@example.com/' => 'https',
);
foreach ($tests as $uri => $expect) {
$repository = new PhabricatorRepository();
$repository->setDetail('remote-uri', $uri);
$this->assertEqual(
$expect,
$repository->getRemoteProtocol(),
"Protocol for '{$uri}'.");
}
}
public function testBranchFilter() {
$git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repo = new PhabricatorRepository();
$repo->setVersionControlSystem($git);
$this->assertEqual(
true,
$repo->shouldTrackBranch('imaginary'),
'Track all branches by default.');
$repo->setDetail(
'branch-filter',
array(
'master' => true,
));
$this->assertEqual(
true,
$repo->shouldTrackBranch('master'),
'Track listed branches.');
$this->assertEqual(
false,
$repo->shouldTrackBranch('imaginary'),
'Do not track unlisted branches.');
}
public function testSubversionPathInfo() {
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
$repo = new PhabricatorRepository();
$repo->setVersionControlSystem($svn);
$repo->setDetail('remote-uri', 'http://svn.example.com/repo');
$this->assertEqual(
'http://svn.example.com/repo',
$repo->getSubversionPathURI());
$repo->setDetail('remote-uri', 'http://svn.example.com/repo/');
$this->assertEqual(
'http://svn.example.com/repo',
$repo->getSubversionPathURI());
$repo->setDetail('hosting-enabled', true);
$repo->setDetail('local-path', '/var/repo/SVN');
$this->assertEqual(
'file:///var/repo/SVN',
$repo->getSubversionPathURI());
$repo->setDetail('local-path', '/var/repo/SVN/');
$this->assertEqual(
'file:///var/repo/SVN',
$repo->getSubversionPathURI());
$this->assertEqual(
'file:///var/repo/SVN/a@',
$repo->getSubversionPathURI('a'));
$this->assertEqual(
'file:///var/repo/SVN/a@1',
$repo->getSubversionPathURI('a', 1));
$this->assertEqual(
'file:///var/repo/SVN/%3F@22',
$repo->getSubversionPathURI('?', 22));
$repo->setDetail('svn-subpath', 'quack/trunk/');
$this->assertEqual(
'file:///var/repo/SVN/quack/trunk/@',
$repo->getSubversionBaseURI());
$this->assertEqual(
'file:///var/repo/SVN/quack/trunk/@HEAD',
$repo->getSubversionBaseURI('HEAD'));
}
+ public function testFilterMercurialDebugOutput() {
+ $map = array(
+ "" => "",
+
+ "quack\n" => "quack\n",
+
+ "ignoring untrusted configuration option x.y = z\nquack\n" =>
+ "quack\n",
+
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "quack\n" =>
+ "quack\n",
+
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "quack\n" =>
+ "quack\n",
+
+ "quack\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n" =>
+ "quack\n",
+
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "duck\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "bread\n".
+ "ignoring untrusted configuration option x.y = z\n".
+ "quack\n" =>
+ "duck\nbread\nquack\n",
+
+ "ignoring untrusted configuration option x.y = z\n".
+ "duckignoring untrusted configuration option x.y = z\n".
+ "quack" =>
+ "duckquack",
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual = PhabricatorRepository::filterMercurialDebugOutput($input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Dec 3, 4:15 PM (5 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
433709
Default Alt Text
(46 KB)

Event Timeline