diff --git a/src/land/engine/ArcanistGitLandEngine.php b/src/land/engine/ArcanistGitLandEngine.php --- a/src/land/engine/ArcanistGitLandEngine.php +++ b/src/land/engine/ArcanistGitLandEngine.php @@ -876,6 +876,8 @@ } protected function confirmOntoRefs(array $onto_refs) { + $api = $this->getRepositoryAPI(); + foreach ($onto_refs as $onto_ref) { if (!strlen($onto_ref)) { throw new PhutilArgumentUsageException( @@ -886,10 +888,61 @@ } } - // TODO: Check that these refs really exist in the remote? Checking the - // remote is expensive and users probably rarely specify "--onto" manually, - // but if "arc land" creates branches without prompting when you make typos - // that also seems questionable. + $markers = $api->newMarkerRefQuery() + ->withRemotes(array($this->getOntoRemoteRef())) + ->withNames($onto_refs) + ->execute(); + + $markers = mgroup($markers, 'getName'); + + $new_markers = array(); + foreach ($onto_refs as $onto_ref) { + if (isset($markers[$onto_ref])) { + // Remote already has a branch with this name, so we're fine: we + // aren't creatinga new branch. + continue; + } + + $new_markers[] = id(new ArcanistMarkerRef()) + ->setMarkerType(ArcanistMarkerRef::TYPE_BRANCH) + ->setName($onto_ref); + } + + if ($new_markers) { + echo tsprintf( + "\n%!\n%W\n\n", + pht('CREATE %s BRANCHE(S)', phutil_count($new_markers)), + pht( + 'These %s symbol(s) do not exist in the remote. They will be '. + 'created as new branches:', + phutil_count($new_markers))); + + foreach ($new_markers as $new_marker) { + echo tsprintf('%s', $new_marker->newRefView()); + } + + echo tsprintf("\n"); + + $is_hold = $this->getShouldHold(); + if ($is_hold) { + echo tsprintf( + "%?\n", + pht( + 'You are using "--hold", so execution will stop before the '. + '%s branche(s) are actually created. You will be given '. + 'instructions to create the branches.', + phutil_count($new_markers))); + } + + $query = pht( + 'Create %s new branche(s) in the remote?', + phutil_count($new_markers)); + + $this->getWorkflow() + ->getPrompt('arc.land.create') + ->setQuery($query) + ->execute(); + } } protected function selectOntoRefs(array $symbols) { @@ -1238,6 +1291,7 @@ $api = $this->getRepositoryAPI(); // Make sure that our "into" target is valid. $log = $this->getLogEngine(); + $api = $this->getRepositoryAPI(); if ($this->getIntoEmpty()) { // If we're running under "--into-empty", we don't have to do anything. diff --git a/src/land/engine/ArcanistLandEngine.php b/src/land/engine/ArcanistLandEngine.php --- a/src/land/engine/ArcanistLandEngine.php +++ b/src/land/engine/ArcanistLandEngine.php @@ -1569,4 +1569,9 @@ return $command->execute(); } + final protected function getOntoRemoteRef() { + return id(new ArcanistRemoteRef()) + ->setRemoteName($this->getOntoRemote()); + } + } diff --git a/src/land/engine/ArcanistMercurialLandEngine.php b/src/land/engine/ArcanistMercurialLandEngine.php --- a/src/land/engine/ArcanistMercurialLandEngine.php +++ b/src/land/engine/ArcanistMercurialLandEngine.php @@ -270,8 +270,7 @@ } } - $remote_ref = id(new ArcanistRemoteRef()) - ->setRemoteName($this->getOntoRemote()); + $remote_ref = $this->getOntoRemoteRef(); $markers = $api->newMarkerRefQuery() ->withRemotes(array($remote_ref)) @@ -346,8 +345,8 @@ "\n%!\n%W\n\n", pht('CREATE %s BOOKMARK(S)', phutil_count($new_markers)), pht( - 'These %s symbol(s) do not exist in the remote. They will be created '. - 'as new bookmarks:', + 'These %s symbol(s) do not exist in the remote. They will be '. + 'created as new bookmarks:', phutil_count($new_markers))); @@ -357,6 +356,17 @@ echo tsprintf("\n"); + $is_hold = $this->getShouldHold(); + if ($is_hold) { + echo tsprintf( + "%?\n", + pht( + 'You are using "--hold", so execution will stop before the '. + '%s bookmark(s) are actually created. You will be given '. + 'instructions to create the bookmarks.', + phutil_count($new_markers))); + } + $query = pht( 'Create %s new remote bookmark(s)?', phutil_count($new_markers)); diff --git a/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php b/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php --- a/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php +++ b/src/repository/marker/ArcanistGitRepositoryMarkerQuery.php @@ -122,7 +122,59 @@ } protected function newRemoteRefMarkers(ArcanistRemoteRef $remote) { - throw new PhutilMethodNotImplementedException(); + $api = $this->getRepositoryAPI(); + + // NOTE: Since we only care about branches today, we only list branches. + + $future = $api->newFuture( + 'ls-remote --refs %s %s', + $remote->getRemoteName(), + 'refs/heads/*'); + list($stdout) = $future->resolve(); + + $branch_prefix = 'refs/heads/'; + $branch_length = strlen($branch_prefix); + + $pattern = '(^(?P\S+)\t(?P\S+)\z)'; + $markers = array(); + + $lines = phutil_split_lines($stdout, false); + foreach ($lines as $line) { + $matches = null; + $ok = preg_match($pattern, $line, $matches); + if (!$ok) { + throw new Exception( + pht( + 'Failed to match "ls-remote" pattern against line "%s".', + $line)); + } + + $hash = $matches['hash']; + $ref = $matches['ref']; + + if (!strncmp($ref, $branch_prefix, $branch_length)) { + $type = ArcanistMarkerRef::TYPE_BRANCH; + $name = substr($ref, $branch_length); + } else { + // For now, discard other refs. + continue; + } + + $marker = id(new ArcanistMarkerRef()) + ->setName($name) + ->setMarkerType($type) + ->setMarkerHash($hash) + ->setCommitHash($hash); + + $commit_ref = $api->newCommitRef() + ->setCommitHash($hash); + + $marker->attachCommitRef($commit_ref); + + $markers[] = $marker; + } + + return $markers; } } diff --git a/src/xsprintf/tsprintf.php b/src/xsprintf/tsprintf.php --- a/src/xsprintf/tsprintf.php +++ b/src/xsprintf/tsprintf.php @@ -51,6 +51,7 @@ case '?': $value = tsprintf('** ? ** %s', $value); $value = PhutilTerminalString::escapeStringValue($value, false); + $value = phutil_console_wrap($value, 6, false); $type = 's'; break; case '>':