Skip to content

Commit bb42584

Browse files
authored
Merge pull request #110 from utopia-php/feature/github-branch-search
Add server-side branch prefix search
2 parents 59b6483 + 5a93859 commit bb42584

2 files changed

Lines changed: 39 additions & 12 deletions

File tree

src/VCS/Adapter/Git/GitHub.php

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -742,19 +742,42 @@ public function getPullRequestFromBranch(string $owner, string $repositoryName,
742742
}
743743

744744
/**
745-
* Lists branches for a given repository
745+
* Lists branches for a given repository, optionally filtered by a search prefix.
746746
*
747-
* @param string $owner Owner name of the repository
748-
* @param string $repositoryName Name of the GitHub repository
749-
* @param int $perPage Number of branches to fetch per page
750-
* @param int $page Page number to start fetching from
751-
* @return array<string> List of branch names as array
747+
* When $search is provided, uses GET /repos/{owner}/{repo}/git/matching-refs/heads/{prefix}
748+
* to perform server-side prefix filtering. This endpoint ignores per_page/page params and
749+
* always returns all matching refs in one call; results are then paginated client-side.
750+
* When $search is empty, uses GET /repos/{owner}/{repo}/branches with GitHub's native pagination.
751+
*
752+
* @param string $owner
753+
* @param string $repositoryName
754+
* @param int $perPage Clamped to [1, 100]
755+
* @param int $page Page number (1-based)
756+
* @param string $search Prefix filter; empty returns all branches
757+
* @return array<string> List of branch names
752758
*/
753-
public function listBranches(string $owner, string $repositoryName, int $perPage = 100, int $page = 1): array
759+
public function listBranches(string $owner, string $repositoryName, int $perPage = 100, int $page = 1, string $search = ''): array
754760
{
755-
$url = "/repos/$owner/$repositoryName/branches";
756761
$perPage = min(max($perPage, 1), 100);
757762

763+
if ($search !== '') {
764+
$url = "/repos/$owner/$repositoryName/git/matching-refs/heads/" . \str_replace('%2F', '/', \rawurlencode($search));
765+
$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);
766+
767+
$statusCode = $response['headers']['status-code'] ?? 0;
768+
$responseBody = $response['body'] ?? [];
769+
770+
if ($statusCode < 200 || $statusCode >= 300 || !is_array($responseBody)) {
771+
return [];
772+
}
773+
774+
$branches = array_map(fn ($ref) => str_replace('refs/heads/', '', $ref['ref'] ?? ''), $responseBody);
775+
$offset = ($page - 1) * $perPage;
776+
777+
return array_values(array_slice($branches, $offset, $perPage));
778+
}
779+
780+
$url = "/repos/$owner/$repositoryName/branches";
758781
$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
759782
'page' => $page,
760783
'per_page' => $perPage,
@@ -831,15 +854,13 @@ public function getLatestCommit(string $owner, string $repositoryName, string $b
831854
$responseBody = $response['body'] ?? [];
832855
$responseBodyCommit = $responseBody['commit'] ?? [];
833856
$responseBodyCommitAuthor = $responseBodyCommit['author'] ?? [];
834-
$responseBodyAuthor = $responseBody['author'] ?? [];
857+
$responseBodyAuthor = is_array($responseBody['author'] ?? null) ? $responseBody['author'] : [];
835858

836859
if (
837860
!array_key_exists('name', $responseBodyCommitAuthor) ||
838861
!array_key_exists('message', $responseBodyCommit) ||
839862
!array_key_exists('sha', $responseBody) ||
840-
!array_key_exists('html_url', $responseBody) ||
841-
!array_key_exists('avatar_url', $responseBodyAuthor) ||
842-
!array_key_exists('html_url', $responseBodyAuthor)
863+
!array_key_exists('html_url', $responseBody)
843864
) {
844865
throw new Exception("Latest commit response is missing required information.");
845866
}

tests/VCS/Adapter/GitHubTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,12 @@ public function testListBranchesPagination(): void
548548

549549
$all = $adapter->listBranches(static::$owner, $repositoryName, 100, 1);
550550
$this->assertEqualsCanonicalizing([static::$defaultBranch, 'branch-a', 'branch-b'], $all);
551+
552+
$searchResults = $adapter->listBranches(static::$owner, $repositoryName, 100, 1, 'branch');
553+
$this->assertEqualsCanonicalizing(['branch-a', 'branch-b'], $searchResults);
554+
555+
$noMatch = $adapter->listBranches(static::$owner, $repositoryName, 100, 1, 'xyz');
556+
$this->assertEmpty($noMatch);
551557
} finally {
552558
$this->vcsAdapter->deleteRepository(static::$owner, $repositoryName);
553559
}

0 commit comments

Comments
 (0)