diff --git a/resources/sql/autopatches/20150425.isclosed.sql b/resources/sql/autopatches/20150425.isclosed.sql new file mode 100644 index 0000000000..3d8586b565 --- /dev/null +++ b/resources/sql/autopatches/20150425.isclosed.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_repository.repository_refcursor + ADD isClosed BOOL NOT NULL; diff --git a/src/applications/diffusion/controller/DiffusionRefTableController.php b/src/applications/diffusion/controller/DiffusionRefTableController.php index f3f005594d..340d6d5e63 100644 --- a/src/applications/diffusion/controller/DiffusionRefTableController.php +++ b/src/applications/diffusion/controller/DiffusionRefTableController.php @@ -75,7 +75,7 @@ final class DiffusionRefTableController extends DiffusionController { $cached_hash = idx($cache, 'identifier'); if ($cached_hash !== null) { - $cache_hash = DiffusionView::linkCommit( + $cached_hash = DiffusionView::linkCommit( $repository, $cached_hash); } diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php index 015f0b1c8e..1e040e21e9 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelMercurialBranchesQuery.php @@ -16,29 +16,57 @@ final class DiffusionLowLevelMercurialBranchesQuery protected function executeQuery() { $repository = $this->getRepository(); + $specs = array(); if ($this->contains !== null) { - $spec = hgsprintf('(descendants(%s) and head())', $this->contains); + $specs['all'] = hgsprintf( + '(descendants(%s) and head())', + $this->contains); + $specs['open'] = hgsprintf( + '(descendants(%s) and head() and not closed())', + $this->contains); } else { - $spec = hgsprintf('head()'); + $specs['all'] = hgsprintf('head()'); + $specs['open'] = hgsprintf('head() and not closed()'); } - list($stdout) = $repository->execxLocalCommand( - 'log --template %s --rev %s', - '{node}\1{branch}\2', - $spec); + $futures = array(); + foreach ($specs as $key => $spec) { + $futures[$key] = $repository->getLocalCommandFuture( + 'log --template %s --rev %s', + '{node}\1{branch}\2', + $spec); + } $branches = array(); + $open = array(); + foreach (new FutureIterator($futures) as $key => $future) { + list($stdout) = $future->resolvex(); - $lines = explode("\2", $stdout); - $lines = array_filter($lines); - foreach ($lines as $line) { - list($node, $branch) = explode("\1", $line); - $branches[] = id(new DiffusionRepositoryRef()) - ->setShortName($branch) - ->setCommitIdentifier($node); + $lines = explode("\2", $stdout); + $lines = array_filter($lines); + foreach ($lines as $line) { + list($node, $branch) = explode("\1", $line); + $id = $node.'/'.$branch; + if (empty($branches[$id])) { + $branches[$id] = id(new DiffusionRepositoryRef()) + ->setShortName($branch) + ->setCommitIdentifier($node); + } + + if ($key == 'open') { + $open[$id] = true; + } + } } - return $branches; + foreach ($branches as $id => $branch) { + $branch->setRawFields( + array( + 'closed' => (empty($open[$id])), + )); + } + + return array_values($branches); } } diff --git a/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php b/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php index 153d7767a3..a393a6be69 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php @@ -19,6 +19,8 @@ final class PhabricatorRepositoryRefEngine $repository = $this->getRepository(); + $branches_may_close = false; + $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: @@ -31,6 +33,7 @@ final class PhabricatorRepositoryRefEngine $branches = $this->loadMercurialBranchPositions($repository); $bookmarks = $this->loadMercurialBookmarkPositions($repository); $tags = array(); + $branches_may_close = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $branches = $this->loadGitBranchPositions($repository); @@ -87,6 +90,51 @@ final class PhabricatorRepositoryRefEngine $this->newRefs = array(); $this->deadRefs = array(); } + + if ($branches && $branches_may_close) { + $this->updateBranchStates($repository, $branches); + } + } + + private function updateBranchStates( + PhabricatorRepository $repository, + array $branches) { + + assert_instances_of($branches, 'DiffusionRepositoryRef'); + + $all_cursors = id(new PhabricatorRepositoryRefCursorQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withRepositoryPHIDs(array($repository->getPHID())) + ->execute(); + + $state_map = array(); + $type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH; + foreach ($all_cursors as $cursor) { + if ($cursor->getRefType() !== $type_branch) { + continue; + } + $raw_name = $cursor->getRefNameRaw(); + $hash = $cursor->getCommitIdentifier(); + + $state_map[$raw_name][$hash] = $cursor; + } + + foreach ($branches as $branch) { + $cursor = idx($state_map, $branch->getShortName(), array()); + $cursor = idx($cursor, $branch->getCommitIdentifier()); + if (!$cursor) { + continue; + } + + $fields = $branch->getRawFields(); + + $cursor_state = (bool)$cursor->getIsClosed(); + $branch_state = (bool)idx($fields, 'closed'); + + if ($cursor_state != $branch_state) { + $cursor->setIsClosed((int)$branch_state)->save(); + } + } } private function markRefNew(PhabricatorRepositoryRefCursor $cursor) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index 13ce78e54b..ae1b3a86ea 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -544,9 +544,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO // trusted, Mercurial prints out a warning to stdout, twice, after Feb 2011. // // http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html + // + // After Jan 2015, it may also fail to write to a revision branch cache. + + $ignore = array( + 'ignoring untrusted configuration option', + "couldn't write revision branch cache:", + ); + + foreach ($ignore as $key => $pattern) { + $ignore[$key] = preg_quote($pattern, '/'); + } + + $ignore = '('.implode('|', $ignore).')'; $lines = preg_split('/(?<=\n)/', $stdout); - $regex = '/ignoring untrusted configuration option .*\n$/'; + $regex = '/'.$ignore.'.*\n$/'; foreach ($lines as $key => $line) { $lines[$key] = preg_replace($regex, '', $line); diff --git a/src/applications/repository/storage/PhabricatorRepositoryRefCursor.php b/src/applications/repository/storage/PhabricatorRepositoryRefCursor.php index efdcdfc1ba..c7cdc21dbd 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryRefCursor.php +++ b/src/applications/repository/storage/PhabricatorRepositoryRefCursor.php @@ -18,6 +18,7 @@ final class PhabricatorRepositoryRefCursor extends PhabricatorRepositoryDAO protected $refNameRaw; protected $refNameEncoding; protected $commitIdentifier; + protected $isClosed = 0; private $repository = self::ATTACHABLE; @@ -35,6 +36,7 @@ final class PhabricatorRepositoryRefCursor extends PhabricatorRepositoryDAO // T6203/NULLABILITY // This probably should not be nullable; refNameRaw is not nullable. 'refNameEncoding' => 'text16?', + 'isClosed' => 'bool', ), self::CONFIG_KEY_SCHEMA => array( 'key_cursor' => array(