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);
     }
 }
 private function loadMercurialParents()
 {
     $repository = $this->getRepository();
     list($stdout) = $repository->execxLocalCommand('log --debug --limit 1 --template={parents} --rev %s', $this->identifier);
     $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 $hashes;
 }
 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, $branch_arg, hgsprintf('reverse(ancestors(%s))', $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);
 }