public function testCommandByVersion()
 {
     $cases = array(array('name' => pht('Versions which should not use `files`'), 'versions' => array('2.6.2', '2.9', '3.1'), 'match' => false), array('name' => pht('Versions which should use `files`'), 'versions' => array('3.2', '3.3', '3.5.2'), 'match' => true));
     foreach ($cases as $case) {
         foreach ($case['versions'] as $version) {
             $actual = PhabricatorRepositoryVersion::isMercurialFilesCommandAvailable($version);
             $expect = $case['match'];
             $this->assertEqual($expect, $actual, $case['name']);
         }
     }
 }
 protected function executeQuery()
 {
     $repository = $this->getRepository();
     $path = $this->path;
     $commit = $this->commit;
     $hg_paths_command = 'locate --print0 --rev %s -I %s';
     $hg_version = PhabricatorRepositoryVersion::getMercurialVersion();
     if (PhabricatorRepositoryVersion::isMercurialFilesCommandAvailable($hg_version)) {
         $hg_paths_command = 'files --print0 --rev %s -I %s';
     }
     $match_against = trim($path, '/');
     $prefix = trim('./' . $match_against, '/');
     list($entire_manifest) = $repository->execxLocalCommand($hg_paths_command, hgsprintf('%s', $commit), $prefix);
     return explode("", $entire_manifest);
 }
 /**
  * @task hg
  */
 private function executeMercurialCreate()
 {
     $repository = $this->getRepository();
     $path = rtrim($repository->getLocalPath(), '/');
     if ($repository->isHosted()) {
         $repository->execxRemoteCommand('init -- %s', $path);
     } else {
         $remote = $repository->getRemoteURIEnvelope();
         // NOTE: Mercurial prior to 3.2.4 has an severe command injection
         // vulnerability. See: <http://bit.ly/19B58E9>
         // On vulnerable versions of Mercurial, we refuse to clone remotes which
         // contain characters which may be interpreted by the shell.
         $hg_version = PhabricatorRepositoryVersion::getMercurialVersion();
         $is_vulnerable = version_compare($hg_version, '3.2.4', '<');
         if ($is_vulnerable) {
             $cleartext = $remote->openEnvelope();
             // The use of "%R" here is an attempt to limit collateral damage
             // for normal URIs because it isn't clear how long this vulnerability
             // has been around for.
             $escaped = csprintf('%R', $cleartext);
             if ((string) $escaped !== (string) $cleartext) {
                 throw new Exception(pht('You have an old version of Mercurial (%s) which has a severe ' . 'command injection security vulnerability. The remote URI for ' . 'this repository (%s) is potentially unsafe. Upgrade Mercurial ' . 'to at least 3.2.4 to clone it.', $hg_version, $repository->getMonogram()));
             }
         }
         try {
             $repository->execxRemoteCommand('clone --noupdate -- %P %s', $remote, $path);
         } catch (Exception $ex) {
             $message = $ex->getMessage();
             $message = $this->censorMercurialErrorMessage($message);
             throw new Exception($message);
         }
     }
 }
 protected function executeChecks()
 {
     if (phutil_is_windows()) {
         $bin_name = 'where';
     } else {
         $bin_name = 'which';
     }
     if (!Filesystem::binaryExists($bin_name)) {
         $message = pht("Without '%s', Phabricator can not test for the availability " . "of other binaries.", $bin_name);
         $this->raiseWarning($bin_name, $message);
         // We need to return here if we can't find the 'which' / 'where' binary
         // because the other tests won't be valid.
         return;
     }
     if (!Filesystem::binaryExists('diff')) {
         $message = pht("Without '%s', Phabricator will not be able to generate or render " . "diffs in multiple applications.", 'diff');
         $this->raiseWarning('diff', $message);
     } else {
         $tmp_a = new TempFile();
         $tmp_b = new TempFile();
         $tmp_c = new TempFile();
         Filesystem::writeFile($tmp_a, 'A');
         Filesystem::writeFile($tmp_b, 'A');
         Filesystem::writeFile($tmp_c, 'B');
         list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_b);
         if ($err) {
             $this->newIssue('bin.diff.same')->setName(pht("Unexpected '%s' Behavior", 'diff'))->setMessage(pht("The '%s' binary on this system has unexpected behavior: " . "it was expected to exit without an error code when passed " . "identical files, but exited with code %d.", 'diff', $err));
         }
         list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_c);
         if (!$err) {
             $this->newIssue('bin.diff.diff')->setName(pht("Unexpected 'diff' Behavior"))->setMessage(pht("The '%s' binary on this system has unexpected behavior: " . "it was expected to exit with a nonzero error code when passed " . "differing files, but did not.", 'diff'));
         }
     }
     $table = new PhabricatorRepository();
     $vcses = queryfx_all($table->establishConnection('r'), 'SELECT DISTINCT versionControlSystem FROM %T', $table->getTableName());
     foreach ($vcses as $vcs) {
         switch ($vcs['versionControlSystem']) {
             case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                 $binary = 'git';
                 break;
             case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                 $binary = 'svn';
                 break;
             case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                 $binary = 'hg';
                 break;
             default:
                 $binary = null;
                 break;
         }
         if (!$binary) {
             continue;
         }
         if (!Filesystem::binaryExists($binary)) {
             $message = pht('You have at least one repository configured which uses this ' . 'version control system. It will not work without the VCS binary.');
             $this->raiseWarning($binary, $message);
             continue;
         }
         $version = null;
         switch ($vcs['versionControlSystem']) {
             case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                 $minimum_version = null;
                 $bad_versions = array();
                 list($err, $stdout, $stderr) = exec_manual('git --version');
                 $version = trim(substr($stdout, strlen('git version ')));
                 break;
             case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                 $minimum_version = '1.5';
                 $bad_versions = array('1.7.1' => pht('This version of Subversion has a bug where `%s` does not work ' . 'for files added in rN (Subversion issue #2873), fixed in 1.7.2.', 'svn diff -c N'));
                 list($err, $stdout, $stderr) = exec_manual('svn --version --quiet');
                 $version = trim($stdout);
                 break;
             case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                 $minimum_version = '1.9';
                 $bad_versions = array('2.1' => pht('This version of Mercurial returns a bad exit code ' . 'after a successful pull.'), '2.2' => pht('This version of Mercurial has a significant memory leak, fixed ' . 'in 2.2.1. Pushing fails with this version as well; see %s.', 'T3046#54922'));
                 $version = PhabricatorRepositoryVersion::getMercurialVersion();
                 break;
         }
         if ($version === null) {
             $this->raiseUnknownVersionWarning($binary);
         } else {
             if ($minimum_version && version_compare($version, $minimum_version, '<')) {
                 $this->raiseMinimumVersionWarning($binary, $minimum_version, $version);
             }
             foreach ($bad_versions as $bad_version => $details) {
                 if ($bad_version === $version) {
                     $this->raiseBadVersionWarning($binary, $bad_version);
                 }
             }
         }
     }
 }