Skip to content

Commit 25b5901

Browse files
committed
make resolveGitReference function more robust, add comments to follow the logic
1 parent 8aa3379 commit 25b5901

File tree

1 file changed

+74
-14
lines changed

1 file changed

+74
-14
lines changed

pkg/github/repositories.go

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,36 +1358,96 @@ func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []str
13581358
return matchedPaths
13591359
}
13601360

1361-
// resolveGitReference resolves git references with the following logic:
1362-
// 1. If SHA is provided, it takes precedence
1363-
// 2. If neither is provided, use the default branch as ref
1364-
// 3. Get commit SHA from the ref
1365-
// Refs can look like `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head`
1366-
// The function returns the resolved ref, commit SHA and any error.
1361+
// resolveGitReference takes a user-provided ref and sha and resolves them into a
1362+
// definitive commit SHA and its corresponding fully-qualified reference.
1363+
//
1364+
// The resolution logic follows a clear priority:
1365+
//
1366+
// 1. If a specific commit `sha` is provided, it takes precedence and is used directly,
1367+
// and all reference resolution is skipped.
1368+
//
1369+
// 2. If no `sha` is provided, the function resolves the `ref`
1370+
// string into a fully-qualified format (e.g., "refs/heads/main") by trying
1371+
// the following steps in order:
1372+
// a. **Empty Ref:** If `ref` is empty, the repository's default branch is used.
1373+
// b. **Fully-Qualified:** If `ref` already starts with "refs/", it's considered fully
1374+
// qualified and used as-is.
1375+
// c. **Partially-Qualified:** If `ref` starts with "heads/" or "tags/", it is
1376+
// prefixed with "refs/" to make it fully-qualified.
1377+
// d. **Short Name:** Otherwise, the `ref` is treated as a short name. The function
1378+
// first attempts to resolve it as a branch ("refs/heads/<ref>"). If that
1379+
// returns a 404 Not Found error, it then attempts to resolve it as a tag
1380+
// ("refs/tags/<ref>").
1381+
//
1382+
// 3. **Final Lookup:** Once a fully-qualified ref is determined, a final API call
1383+
// is made to fetch that reference's definitive commit SHA.
1384+
//
1385+
// Any unexpected (non-404) errors during the resolution process are returned
1386+
// immediately. All API errors are logged with rich context to aid diagnostics.
13671387
func resolveGitReference(ctx context.Context, githubClient *github.Client, owner, repo, ref, sha string) (*raw.ContentOpts, error) {
1368-
// 1. If SHA is provided, use it directly
1388+
// 1) If SHA explicitly provided, it's the highest priority.
13691389
if sha != "" {
13701390
return &raw.ContentOpts{Ref: "", SHA: sha}, nil
13711391
}
13721392

1373-
// 2. If neither provided, use the default branch as ref
1393+
// 2) If no SHA is provided, we try to resolve the ref into a fully-qualified format.
1394+
// 2a) If ref is empty, determine the default branch.
13741395
if ref == "" {
13751396
repoInfo, resp, err := githubClient.Repositories.Get(ctx, owner, repo)
13761397
if err != nil {
13771398
_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get repository info", resp, err)
13781399
return nil, fmt.Errorf("failed to get repository info: %w", err)
13791400
}
1380-
ref = fmt.Sprintf("refs/heads/%s", repoInfo.GetDefaultBranch())
1401+
ref = repoInfo.GetDefaultBranch()
13811402
}
13821403

1383-
// 3. Get the SHA from the ref
1404+
originalRef := ref // Keep original ref for clearer error messages down the line.
1405+
1406+
if strings.HasPrefix(ref, "refs/") {
1407+
// 2b) Already fully qualified. We will use it.
1408+
} else if strings.HasPrefix(ref, "heads/") || strings.HasPrefix(ref, "tags/") {
1409+
ref = "refs/" + ref // 2c) Partially qualified. Make it fully qualified.
1410+
} else {
1411+
// 2d) Short name. Try to resolve it as a branch or tag.
1412+
_, resp, err := githubClient.Git.GetRef(ctx, owner, repo, "refs/heads/"+ref)
1413+
1414+
// try to resolve the ref as a branch first.
1415+
if err == nil {
1416+
ref = "refs/heads/" + ref // It's a branch.
1417+
} else {
1418+
// The branch lookup failed. Check if it was a 404 Not Found error.
1419+
// If it was, we will try to resolve it as a tag.
1420+
ghErr, isGhErr := err.(*github.ErrorResponse)
1421+
if isGhErr && ghErr.Response.StatusCode == http.StatusNotFound {
1422+
// The branch wasn't found, so try as a tag.
1423+
_, resp2, err2 := githubClient.Git.GetRef(ctx, owner, repo, "refs/tags/"+ref)
1424+
if err2 == nil {
1425+
ref = "refs/tags/" + ref // It's a tag.
1426+
} else {
1427+
// The tag lookup also failed. Check if it was a 404 Not Found error.
1428+
ghErr2, isGhErr2 := err2.(*github.ErrorResponse)
1429+
if isGhErr2 && ghErr2.Response.StatusCode == http.StatusNotFound {
1430+
return nil, fmt.Errorf("could not resolve ref %q as a branch or a tag", originalRef)
1431+
}
1432+
// The tag lookup failed for a different reason.
1433+
_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get reference (tag)", resp2, err2)
1434+
return nil, fmt.Errorf("failed to get reference for tag '%s': %w", originalRef, err2)
1435+
}
1436+
} else {
1437+
// The branch lookup failed for a different reason.
1438+
_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get reference (branch)", resp, err)
1439+
return nil, fmt.Errorf("failed to get reference for branch '%s': %w", originalRef, err)
1440+
}
1441+
}
1442+
}
1443+
1444+
// Now that 'ref' is a valid, fully-qualified name, we get the definitive reference object.
13841445
reference, resp, err := githubClient.Git.GetRef(ctx, owner, repo, ref)
13851446
if err != nil {
1386-
_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get reference", resp, err)
1387-
return nil, fmt.Errorf("failed to get reference: %w", err)
1447+
_, _ = ghErrors.NewGitHubAPIErrorToCtx(ctx, "failed to get final reference", resp, err)
1448+
return nil, fmt.Errorf("failed to get final reference for %q: %w", ref, err)
13881449
}
1389-
sha = reference.GetObject().GetSHA()
13901450

1391-
// Use provided ref, or it will be empty which defaults to the default branch
1451+
sha = reference.GetObject().GetSHA()
13921452
return &raw.ContentOpts{Ref: ref, SHA: sha}, nil
13931453
}

0 commit comments

Comments
 (0)