@@ -1358,36 +1358,100 @@ func filterPaths(entries []*github.TreeEntry, path string, maxResults int) []str
1358
1358
return matchedPaths
1359
1359
}
1360
1360
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.
1367
1387
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.
1369
1389
if sha != "" {
1370
1390
return & raw.ContentOpts {Ref : "" , SHA : sha }, nil
1371
1391
}
1372
1392
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.
1375
1403
repoInfo , resp , err := githubClient .Repositories .Get (ctx , owner , repo )
1376
1404
if err != nil {
1377
1405
_ , _ = ghErrors .NewGitHubAPIErrorToCtx (ctx , "failed to get repository info" , resp , err )
1378
1406
return nil , fmt .Errorf ("failed to get repository info: %w" , err )
1379
1407
}
1380
1408
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
+ }
1381
1445
}
1382
1446
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
+ }
1388
1453
}
1389
- sha = reference .GetObject ().GetSHA ()
1390
1454
1391
- // Use provided ref, or it will be empty which defaults to the default branch
1455
+ sha = reference . GetObject (). GetSHA ()
1392
1456
return & raw.ContentOpts {Ref : ref , SHA : sha }, nil
1393
1457
}
0 commit comments