@@ -1358,36 +1358,100 @@ 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.
13671387func 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
1374- if ref == "" {
1393+ originalRef := ref // Keep original ref for clearer error messages down the line.
1394+
1395+ // 2) If no SHA is provided, we try to resolve the ref into a fully-qualified format.
1396+ var reference * github.Reference
1397+ var resp * github.Response
1398+ var err error
1399+
1400+ switch {
1401+ case originalRef == "" :
1402+ // 2a) If ref is empty, determine the default branch.
13751403 repoInfo , resp , err := githubClient .Repositories .Get (ctx , owner , repo )
13761404 if err != nil {
13771405 _ , _ = ghErrors .NewGitHubAPIErrorToCtx (ctx , "failed to get repository info" , resp , err )
13781406 return nil , fmt .Errorf ("failed to get repository info: %w" , err )
13791407 }
13801408 ref = fmt .Sprintf ("refs/heads/%s" , repoInfo .GetDefaultBranch ())
1409+ case strings .HasPrefix (originalRef , "refs/" ):
1410+ // 2b) Already fully qualified. The reference will be fetched at the end.
1411+ case strings .HasPrefix (originalRef , "heads/" ) || strings .HasPrefix (originalRef , "tags/" ):
1412+ // 2c) Partially qualified. Make it fully qualified.
1413+ ref = "refs/" + originalRef
1414+ default :
1415+ // 2d) It's a short name, so we try to resolve it to either a branch or a tag.
1416+ branchRef := "refs/heads/" + originalRef
1417+ reference , resp , err = githubClient .Git .GetRef (ctx , owner , repo , branchRef )
1418+
1419+ if err == nil {
1420+ ref = branchRef // It's a branch.
1421+ } else {
1422+ // The branch lookup failed. Check if it was a 404 Not Found error.
1423+ ghErr , isGhErr := err .(* github.ErrorResponse )
1424+ if isGhErr && ghErr .Response .StatusCode == http .StatusNotFound {
1425+ tagRef := "refs/tags/" + originalRef
1426+ reference , resp , err = githubClient .Git .GetRef (ctx , owner , repo , tagRef )
1427+ if err == nil {
1428+ ref = tagRef // It's a tag.
1429+ } else {
1430+ // The tag lookup also failed. Check if it was a 404 Not Found error.
1431+ ghErr2 , isGhErr2 := err .(* github.ErrorResponse )
1432+ if isGhErr2 && ghErr2 .Response .StatusCode == http .StatusNotFound {
1433+ return nil , fmt .Errorf ("could not resolve ref %q as a branch or a tag" , originalRef )
1434+ }
1435+ // The tag lookup failed for a different reason.
1436+ _ , _ = ghErrors .NewGitHubAPIErrorToCtx (ctx , "failed to get reference (tag)" , resp , err )
1437+ return nil , fmt .Errorf ("failed to get reference for tag '%s': %w" , originalRef , err )
1438+ }
1439+ } else {
1440+ // The branch lookup failed for a different reason.
1441+ _ , _ = ghErrors .NewGitHubAPIErrorToCtx (ctx , "failed to get reference (branch)" , resp , err )
1442+ return nil , fmt .Errorf ("failed to get reference for branch '%s': %w" , originalRef , err )
1443+ }
1444+ }
13811445 }
13821446
1383- // 3. Get the SHA from the ref
1384- reference , resp , err := githubClient .Git .GetRef (ctx , owner , repo , ref )
1385- 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+ if reference == nil {
1448+ reference , resp , err = githubClient .Git .GetRef (ctx , owner , repo , ref )
1449+ if err != nil {
1450+ _ , _ = ghErrors .NewGitHubAPIErrorToCtx (ctx , "failed to get final reference" , resp , err )
1451+ return nil , fmt .Errorf ("failed to get final reference for %q: %w" , ref , err )
1452+ }
13881453 }
1389- sha = reference .GetObject ().GetSHA ()
13901454
1391- // Use provided ref, or it will be empty which defaults to the default branch
1455+ sha = reference . GetObject (). GetSHA ()
13921456 return & raw.ContentOpts {Ref : ref , SHA : sha }, nil
13931457}
0 commit comments