diff --git a/src/repository/marker/ArcanistMarkerRef.php b/src/repository/marker/ArcanistMarkerRef.php --- a/src/repository/marker/ArcanistMarkerRef.php +++ b/src/repository/marker/ArcanistMarkerRef.php @@ -9,6 +9,8 @@ const TYPE_BRANCH = 'branch'; const TYPE_BOOKMARK = 'bookmark'; + const TYPE_COMMIT_STATE = 'commit-state'; + const TYPE_BRANCH_STATE = 'branch-state'; private $name; private $markerType; @@ -148,6 +150,14 @@ return ($this->getMarkerType() === self::TYPE_BRANCH); } + public function isCommitState() { + return ($this->getMarkerType() === self::TYPE_COMMIT_STATE); + } + + public function isBranchState() { + return ($this->getMarkerType() === self::TYPE_BRANCH_STATE); + } + public function attachCommitRef(ArcanistCommitRef $ref) { return $this->attachHardpoint(self::HARDPOINT_COMMITREF, $ref); } diff --git a/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php b/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php --- a/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php +++ b/src/repository/marker/ArcanistMercurialRepositoryMarkerQuery.php @@ -71,10 +71,6 @@ } $node = $item['node']; - if (!$node) { - // NOTE: For now, we ignore the virtual "current branch" marker. - continue; - } switch ($item['type']) { case 'branch': @@ -83,8 +79,11 @@ case 'bookmark': $marker_type = ArcanistMarkerRef::TYPE_BOOKMARK; break; - case 'commit': - $marker_type = null; + case 'commit-state': + $marker_type = ArcanistMarkerRef::TYPE_COMMIT_STATE; + break; + case 'branch-state': + $marker_type = ArcanistMarkerRef::TYPE_BRANCH_STATE; break; default: throw new Exception( @@ -94,11 +93,6 @@ $item['type'])); } - if ($marker_type === null) { - // NOTE: For now, we ignore the virtual "head" marker. - continue; - } - $commit_ref = $api->newCommitRef() ->setCommitHash($node); diff --git a/src/repository/state/ArcanistMercurialLocalState.php b/src/repository/state/ArcanistMercurialLocalState.php --- a/src/repository/state/ArcanistMercurialLocalState.php +++ b/src/repository/state/ArcanistMercurialLocalState.php @@ -5,40 +5,105 @@ private $localCommit; private $localBranch; + private $localBookmark; protected function executeSaveLocalState() { $api = $this->getRepositoryAPI(); $log = $this->getWorkflow()->getLogEngine(); - // TODO: Both of these can be pulled from "hg arc-ls-markers" more - // efficiently. - - $this->localCommit = $api->getCanonicalRevisionName('.'); - - list($branch) = $api->execxLocal('branch'); - $this->localBranch = trim($branch); - - $log->writeTrace( - pht('SAVE STATE'), - pht( + $markers = $api->newMarkerRefQuery() + ->execute(); + + $local_commit = null; + foreach ($markers as $marker) { + if ($marker->isCommitState()) { + $local_commit = $marker->getCommitHash(); + } + } + + if ($local_commit === null) { + throw new Exception( + pht( + 'Unable to identify the current commit in the working copy.')); + } + + $this->localCommit = $local_commit; + + $local_branch = null; + foreach ($markers as $marker) { + if ($marker->isBranchState()) { + $local_branch = $marker->getName(); + break; + } + } + + if ($local_branch === null) { + throw new Exception( + pht( + 'Unable to identify the current branch in the working copy.')); + } + + if ($local_branch !== null) { + $this->localBranch = $local_branch; + } + + $local_bookmark = null; + foreach ($markers as $marker) { + if ($marker->isBookmark()) { + if ($marker->getIsActive()) { + $local_bookmark = $marker->getName(); + break; + } + } + } + + if ($local_bookmark !== null) { + $this->localBookmark = $local_bookmark; + } + + $has_bookmark = ($this->localBookmark !== null); + + if ($has_bookmark) { + $location = pht( + 'Saving local state (at "%s" on branch "%s", bookmarked as "%s").', + $api->getDisplayHash($this->localCommit), + $this->localBranch, + $this->localBookmark); + } else { + $location = pht( 'Saving local state (at "%s" on branch "%s").', $api->getDisplayHash($this->localCommit), - $this->localBranch)); + $this->localBranch); + } + + $log->writeTrace(pht('SAVE STATE'), $location); } protected function executeRestoreLocalState() { $api = $this->getRepositoryAPI(); $log = $this->getWorkflow()->getLogEngine(); - $log->writeStatus( - pht('LOAD STATE'), - pht( + if ($this->localBookmark !== null) { + $location = pht( + 'Restoring local state (at "%s" on branch "%s", bookmarked as "%s").', + $api->getDisplayHash($this->localCommit), + $this->localBranch, + $this->localBookmark); + } else { + $location = pht( 'Restoring local state (at "%s" on branch "%s").', $api->getDisplayHash($this->localCommit), - $this->localBranch)); + $this->localBranch); + } + + $log->writeStatus(pht('LOAD STATE'), $location); $api->execxLocal('update -- %s', $this->localCommit); $api->execxLocal('branch --force -- %s', $this->localBranch); + + if ($this->localBookmark !== null) { + $api->execxLocal('bookmark --force -- %s', $this->localBookmark); + } } protected function executeDiscardLocalState() { @@ -70,6 +135,12 @@ 'hg branch --force -- %s', $this->localBranch); + if ($this->localBookmark !== null) { + $commands[] = csprintf( + 'hg bookmark --force -- %s', + $this->localBookmark); + } + return $commands; } diff --git a/support/hg/arc-hg.py b/support/hg/arc-hg.py --- a/support/hg/arc-hg.py +++ b/support/hg/arc-hg.py @@ -85,21 +85,17 @@ active_node = repo[b'.'].node() all_heads = set(repo.heads()) current_name = repo.dirstate.branch() - saw_current = False - saw_active = False branch_list = repo.branchmap().iterbranches() for branch_name, branch_heads, tip_node, is_closed in branch_list: for head_node in branch_heads: - is_active = (head_node == active_node) - is_tip = (head_node == tip_node) - is_current = (branch_name == current_name) - if is_current: - saw_current = True + is_active = False + if branch_name == current_name: + if head_node == active_node: + is_active = True - if is_active: - saw_active = True + is_tip = (head_node == tip_node) if is_closed: head_closed = True @@ -115,26 +111,9 @@ 'isActive': is_active, 'isClosed': head_closed, 'isTip': is_tip, - 'isCurrent': is_current, 'description': description, }) - # If the current branch (selected with "hg branch X") is not reflected in - # the list of heads we selected, add a virtual head for it so callers get - # a complete picture of repository marker state. - - if not saw_current: - markers.append({ - 'type': 'branch', - 'name': current_name, - 'node': None, - 'isActive': False, - 'isClosed': False, - 'isTip': False, - 'isCurrent': True, - 'description': None, - }) - bookmarks = repo._bookmarks active_bookmark = repo._activebookmark @@ -142,9 +121,6 @@ is_active = (active_bookmark == bookmark_name) description = repo[bookmark_node].description() - if is_active: - saw_active = True - markers.append({ 'type': 'bookmark', 'name': bookmark_name, @@ -153,21 +129,36 @@ 'description': description, }) - # If the current working copy state is not the head of a branch and there is - # also no active bookmark, add a virtual marker for it so callers can figure - # out exactly where we are. - - if not saw_active: - markers.append({ - 'type': 'commit', - 'name': None, - 'node': node.hex(active_node), - 'isActive': False, - 'isClosed': False, - 'isTip': False, - 'isCurrent': True, - 'description': repo[b'.'].description(), - }) + # Add virtual markers for the current commit state and current branch state + # so callers can figure out exactly where we are. + + # Common cases where this matters include: + + # You run "hg update 123" to update to an older revision. Your working + # copy commit will not be a branch head or a bookmark. + + # You run "hg branch X" to create a new branch, but have not made any commits + # yet. Your working copy branch will not be reflected in any commits. + + markers.append({ + 'type': 'branch-state', + 'name': current_name, + 'node': None, + 'isActive': True, + 'isClosed': False, + 'isTip': False, + 'description': None, + }) + + markers.append({ + 'type': 'commit-state', + 'name': None, + 'node': node.hex(active_node), + 'isActive': True, + 'isClosed': False, + 'isTip': False, + 'description': repo[b'.'].description(), + }) return markers