Pull Requests¶
The pr package provides functions for creating and managing pull requests. All functions take a
clientv1.Client and return stable gogithub.* types.
Creating Pull Requests¶
Basic PR Creation¶
import "github.com/grokify/gogithub/pr"
pullRequest, err := pr.CreatePR(ctx, client,
"upstream-owner", "upstream-repo", // Base repository
"fork-owner", "feature-branch", // Head (your fork and branch)
"main", // Base branch to merge into
"Add new feature", // PR title
"This PR adds a new feature...", // PR body
)
if err != nil {
return err
}
fmt.Printf("PR created: %s\n", pullRequest.HTMLURL)
Cross-Fork PRs¶
When creating a PR from a fork to an upstream repository, repo.EnsureFork creates the fork if it
doesn't already exist and returns its owner/repo names:
// First, ensure you have a fork
forkOwner, forkRepo, err := repo.EnsureFork(ctx, client, "upstream-owner", "upstream-repo", "your-username")
if err != nil {
return err
}
// Create a branch in your fork
sha, _ := repo.GetBranchSHA(ctx, client, forkOwner, forkRepo, "main")
repo.CreateBranch(ctx, client, forkOwner, forkRepo, "my-feature", sha)
// Make commits to your fork's branch
// ...
// Create PR from fork to upstream
pullRequest, err := pr.CreatePR(ctx, client,
"upstream-owner", "upstream-repo", // base
forkOwner, "my-feature", // head
"main",
"My contribution",
"Description of changes",
)
Listing Pull Requests¶
List PRs for a Repository¶
prs, err := pr.ListPRs(ctx, client, "owner", "repo", &clientv1.ListPullRequestsOptions{
State: "open",
Sort: "created",
Direction: "desc",
})
for _, p := range prs {
fmt.Printf("#%d: %s\n", p.Number, p.Title)
}
Get Single PR¶
pullRequest, err := pr.GetPR(ctx, client, "owner", "repo", 123)
fmt.Printf("State: %s\n", pullRequest.State)
if pullRequest.Mergeable != nil {
fmt.Printf("Mergeable: %v\n", *pullRequest.Mergeable)
}
Managing Pull Requests¶
Merge a PR¶
result, err := pr.MergePR(ctx, client, "owner", "repo", 123, "Merge PR #123", &clientv1.MergePullRequestOptions{
MergeMethod: "squash", // "merge", "squash", or "rebase"
})
if result.Merged {
fmt.Println("PR merged successfully")
}
Close a PR¶
pullRequest, err := pr.ClosePR(ctx, client, "owner", "repo", 123)
fmt.Printf("PR state: %s\n", pullRequest.State) // "closed"
Reviews and Comments¶
Get PR Diff¶
Retrieve the diff content for a pull request:
diff, err := pr.GetPRDiff(ctx, client, "owner", "repo", 123)
if err != nil {
return err
}
fmt.Println(diff) // Raw diff output
List Reviews¶
reviews, err := pr.ListPRReviews(ctx, client, "owner", "repo", 123)
for _, review := range reviews {
fmt.Printf("%s: %s - %s\n",
review.User.Login,
review.State,
review.Body)
}
Submit a Review¶
Use CreateReview to submit a formal review:
// Approve the PR
review, err := pr.CreateReview(ctx, client, "owner", "repo", 123,
pr.ReviewEventApprove,
"LGTM! Great work.",
)
// Request changes
review, err := pr.CreateReview(ctx, client, "owner", "repo", 123,
pr.ReviewEventRequestChanges,
"Please address the comments below.",
)
// Add a comment review (neither approve nor request changes)
review, err := pr.CreateReview(ctx, client, "owner", "repo", 123,
pr.ReviewEventComment,
"Some observations about the implementation...",
)
Review events:
| Event | Description |
|---|---|
pr.ReviewEventApprove |
Approve the PR |
pr.ReviewEventRequestChanges |
Request changes before merging |
pr.ReviewEventComment |
General comment without approval status |
pr.ApprovePR, pr.RequestChangesPR, and pr.CommentPR wrap CreateReview with the matching
event for convenience.
Add Comments¶
General PR comment (appears in the conversation):
comment, err := pr.CreateIssueComment(ctx, client, "owner", "repo", 123,
"Thanks for the contribution! I have a few suggestions.",
)
Line-level comment (appears on specific code):
comment, err := pr.CreateLineComment(ctx, client, "owner", "repo", 123,
"abc123def", // Commit SHA
"src/main.go", // File path
"Consider using a constant here for better readability.",
42, // Line number
)
List PR Comments¶
comments, err := pr.ListPRComments(ctx, client, "owner", "repo", 123)
for _, c := range comments {
fmt.Printf("%s at %s:%d: %s\n",
c.User.Login,
c.Path,
c.Line,
c.Body)
}
Complete Workflow Example¶
Here's a complete example of creating a contribution via PR:
package main
import (
"context"
"fmt"
"github.com/grokify/gogithub/clientv1"
"github.com/grokify/gogithub/pr"
"github.com/grokify/gogithub/repo"
)
func main() {
ctx := context.Background()
client, err := clientv1.NewClient(ctx, "your-token")
if err != nil {
panic(err)
}
upstreamOwner := "upstream-owner"
upstreamRepo := "upstream-repo"
// 1. Ensure a fork exists
forkOwner, forkRepo, err := repo.EnsureFork(ctx, client, upstreamOwner, upstreamRepo, "your-username")
if err != nil {
panic(err)
}
// 2. Create a feature branch
sha, err := repo.GetBranchSHA(ctx, client, forkOwner, forkRepo, "main")
if err != nil {
panic(err)
}
branchName := "add-documentation"
err = repo.CreateBranch(ctx, client, forkOwner, forkRepo, branchName, sha)
if err != nil {
panic(err)
}
// 3. Make changes
files := []repo.FileContent{
{Path: "CONTRIBUTING.md", Content: []byte("# Contributing\n\nWelcome!")},
}
_, err = repo.CreateCommit(ctx, client, forkOwner, forkRepo, branchName, "Add contributing guide", files)
if err != nil {
panic(err)
}
// 4. Create pull request
pullRequest, err := pr.CreatePR(ctx, client,
upstreamOwner, upstreamRepo,
forkOwner, branchName,
"main",
"Add contributing guide",
"This PR adds a CONTRIBUTING.md file to help new contributors.",
)
if err != nil {
panic(err)
}
fmt.Printf("PR created: %s\n", pullRequest.HTMLURL)
}
Error Handling¶
PRError¶
pullRequest, err := pr.CreatePR(ctx, client, baseOwner, baseRepo, headOwner, headBranch, baseBranch, title, body)
if err != nil {
var prErr *pr.PRError
if errors.As(err, &prErr) {
fmt.Printf("PR operation failed for %s/%s: %v\n",
prErr.Owner, prErr.Repo, prErr.Err)
}
}
API Reference¶
See pkg.go.dev/github.com/grokify/gogithub/pr for complete API documentation.