Add pull request capability

Change-Id: Ib54054cc9b32930764cc2110203742c3948f9ea3
diff --git a/server/git/PULL_REQUEST_README.md b/server/git/PULL_REQUEST_README.md
new file mode 100644
index 0000000..b6664f2
--- /dev/null
+++ b/server/git/PULL_REQUEST_README.md
@@ -0,0 +1,323 @@
+# Pull Request Capabilities
+
+This package now includes comprehensive pull request (PR) capabilities that support both GitHub and Gerrit platforms. The implementation provides a unified interface for managing pull requests across different code hosting platforms.
+
+## Features
+
+- **Unified Interface**: Same API for both GitHub and Gerrit
+- **Full CRUD Operations**: Create, read, update, delete pull requests
+- **Advanced Filtering**: List pull requests with various filters
+- **Merge Operations**: Support for different merge strategies
+- **Error Handling**: Comprehensive error handling with detailed messages
+- **Authentication**: Support for token-based and basic authentication
+
+## Supported Platforms
+
+### GitHub
+- Uses GitHub REST API v3
+- Supports personal access tokens for authentication
+- Full support for all pull request operations
+- Handles GitHub-specific features like draft PRs, labels, assignees, and reviewers
+
+### Gerrit
+- Uses Gerrit REST API
+- Supports HTTP password or API token authentication
+- Maps Gerrit "changes" to pull requests
+- Handles Gerrit-specific features like topics and review workflows
+
+## Quick Start
+
+### GitHub Example
+
+```go
+package main
+
+import (
+    "context"
+    "github.com/iomodo/staff/git"
+    "net/http"
+    "time"
+)
+
+func main() {
+    ctx := context.Background()
+    
+    // Create GitHub configuration
+    githubConfig := git.GitHubConfig{
+        Token: "your-github-token",
+        BaseURL: "https://api.github.com",
+        HTTPClient: &http.Client{Timeout: 30 * time.Second},
+    }
+    
+    // Create GitHub provider
+    githubProvider := git.NewGitHubPullRequestProvider("owner", "repo", githubConfig)
+    
+    // Create Git instance with pull request capabilities
+    git := git.NewGitWithPullRequests("/path/to/repo", git.GitConfig{}, githubProvider)
+    
+    // Create a pull request
+    prOptions := git.PullRequestOptions{
+        Title:       "Add new feature",
+        Description: "This PR adds a new feature to the application.",
+        BaseBranch:  "main",
+        HeadBranch:  "feature/new-feature",
+        Labels:      []string{"enhancement", "feature"},
+        Assignees:   []string{"username1", "username2"},
+        Reviewers:   []string{"reviewer1", "reviewer2"},
+        Draft:       false,
+    }
+    
+    pr, err := git.CreatePullRequest(ctx, prOptions)
+    if err != nil {
+        log.Fatal(err)
+    }
+    
+    fmt.Printf("Created pull request: %s (#%d)\n", pr.Title, pr.Number)
+}
+```
+
+### Gerrit Example
+
+```go
+package main
+
+import (
+    "context"
+    "github.com/iomodo/staff/git"
+    "net/http"
+    "time"
+)
+
+func main() {
+    ctx := context.Background()
+    
+    // Create Gerrit configuration
+    gerritConfig := git.GerritConfig{
+        Username: "your-username",
+        Password: "your-http-password-or-api-token",
+        BaseURL:  "https://gerrit.example.com",
+        HTTPClient: &http.Client{Timeout: 30 * time.Second},
+    }
+    
+    // Create Gerrit provider
+    gerritProvider := git.NewGerritPullRequestProvider("project-name", gerritConfig)
+    
+    // Create Git instance with pull request capabilities
+    git := git.NewGitWithPullRequests("/path/to/repo", git.GitConfig{}, gerritProvider)
+    
+    // Create a change (pull request)
+    prOptions := git.PullRequestOptions{
+        Title:       "Add new feature",
+        Description: "This change adds a new feature to the application.",
+        BaseBranch:  "master",
+        HeadBranch:  "feature/new-feature",
+    }
+    
+    pr, err := git.CreatePullRequest(ctx, prOptions)
+    if err != nil {
+        log.Fatal(err)
+    }
+    
+    fmt.Printf("Created change: %s (#%d)\n", pr.Title, pr.Number)
+}
+```
+
+## API Reference
+
+### Types
+
+#### PullRequest
+Represents a pull request or merge request across platforms.
+
+```go
+type PullRequest struct {
+    ID          string
+    Number      int
+    Title       string
+    Description string
+    State       string // "open", "closed", "merged"
+    Author      Author
+    CreatedAt   time.Time
+    UpdatedAt   time.Time
+    BaseBranch  string
+    HeadBranch  string
+    BaseRepo    string
+    HeadRepo    string
+    Labels      []string
+    Assignees   []Author
+    Reviewers   []Author
+    Commits     []Commit
+    Comments    []PullRequestComment
+}
+```
+
+#### PullRequestOptions
+Options for creating or updating pull requests.
+
+```go
+type PullRequestOptions struct {
+    Title       string
+    Description string
+    BaseBranch  string
+    HeadBranch  string
+    BaseRepo    string
+    HeadRepo    string
+    Labels      []string
+    Assignees   []string
+    Reviewers   []string
+    Draft       bool
+}
+```
+
+#### ListPullRequestOptions
+Options for listing pull requests.
+
+```go
+type ListPullRequestOptions struct {
+    State      string // "open", "closed", "all"
+    Author     string
+    Assignee   string
+    BaseBranch string
+    HeadBranch string
+    Labels     []string
+    Limit      int
+}
+```
+
+#### MergePullRequestOptions
+Options for merging pull requests.
+
+```go
+type MergePullRequestOptions struct {
+    MergeMethod string // "merge", "squash", "rebase"
+    CommitTitle string
+    CommitMsg   string
+}
+```
+
+### Methods
+
+#### CreatePullRequest
+Creates a new pull request.
+
+```go
+func (g *Git) CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error)
+```
+
+#### GetPullRequest
+Retrieves a pull request by ID.
+
+```go
+func (g *Git) GetPullRequest(ctx context.Context, id string) (*PullRequest, error)
+```
+
+#### ListPullRequests
+Lists pull requests with optional filtering.
+
+```go
+func (g *Git) ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error)
+```
+
+#### UpdatePullRequest
+Updates an existing pull request.
+
+```go
+func (g *Git) UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error)
+```
+
+#### ClosePullRequest
+Closes a pull request.
+
+```go
+func (g *Git) ClosePullRequest(ctx context.Context, id string) error
+```
+
+#### MergePullRequest
+Merges a pull request.
+
+```go
+func (g *Git) MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error
+```
+
+## Configuration
+
+### GitHub Configuration
+
+```go
+type GitHubConfig struct {
+    Token      string        // GitHub personal access token
+    BaseURL    string        // GitHub API base URL (default: https://api.github.com)
+    HTTPClient *http.Client  // Custom HTTP client (optional)
+}
+```
+
+### Gerrit Configuration
+
+```go
+type GerritConfig struct {
+    Username    string        // Gerrit username
+    Password    string        // HTTP password or API token
+    BaseURL     string        // Gerrit instance URL
+    HTTPClient  *http.Client  // Custom HTTP client (optional)
+}
+```
+
+## Error Handling
+
+All pull request operations return detailed error information through the `GitError` type:
+
+```go
+type GitError struct {
+    Command string
+    Output  string
+    Err     error
+}
+```
+
+Common error scenarios:
+- Authentication failures
+- Invalid repository or project names
+- Network connectivity issues
+- API rate limiting
+- Invalid pull request data
+
+## Platform-Specific Notes
+
+### GitHub
+- Requires a personal access token with appropriate permissions
+- Supports draft pull requests
+- Full support for labels, assignees, and reviewers
+- Uses GitHub's REST API v3
+
+### Gerrit
+- Requires HTTP password or API token
+- Uses "changes" instead of "pull requests"
+- Topics are used to group related changes
+- Review workflow is more structured
+- Uses Gerrit's REST API
+
+## Examples
+
+See `pull_request_example.go` for comprehensive examples of using both GitHub and Gerrit providers.
+
+## Testing
+
+Run the tests to ensure everything works correctly:
+
+```bash
+go test ./git/... -v
+```
+
+## Contributing
+
+When adding support for new platforms:
+
+1. Implement the `PullRequestProvider` interface
+2. Add platform-specific configuration types
+3. Create conversion functions to map platform-specific data to our unified types
+4. Add comprehensive tests
+5. Update this documentation
+
+## License
+
+This code is part of the staff project and follows the same licensing terms. 
\ No newline at end of file
diff --git a/server/git/gerrit.go b/server/git/gerrit.go
new file mode 100644
index 0000000..6ce60e9
--- /dev/null
+++ b/server/git/gerrit.go
@@ -0,0 +1,454 @@
+package git
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// GerritConfig holds configuration for Gerrit operations
+type GerritConfig struct {
+	Username   string
+	Password   string // Can be HTTP password or API token
+	BaseURL    string
+	HTTPClient *http.Client
+}
+
+// GerritPullRequestProvider implements PullRequestProvider for Gerrit
+type GerritPullRequestProvider struct {
+	config  GerritConfig
+	project string
+}
+
+// NewGerritPullRequestProvider creates a new Gerrit pull request provider
+func NewGerritPullRequestProvider(project string, config GerritConfig) PullRequestProvider {
+	if config.HTTPClient == nil {
+		config.HTTPClient = &http.Client{Timeout: 30 * time.Second}
+	}
+
+	return &GerritPullRequestProvider{
+		config:  config,
+		project: project,
+	}
+}
+
+// Gerrit API response types
+type gerritChange struct {
+	ID              string                     `json:"id"`
+	Number          int                        `json:"_number"`
+	Subject         string                     `json:"subject"`
+	Description     string                     `json:"description"`
+	Status          string                     `json:"status"`
+	Owner           gerritAccount              `json:"owner"`
+	Created         time.Time                  `json:"created"`
+	Updated         time.Time                  `json:"updated"`
+	Branch          string                     `json:"branch"`
+	Topic           string                     `json:"topic"`
+	Labels          map[string]gerritLabelInfo `json:"labels"`
+	Reviewers       map[string][]gerritAccount `json:"reviewers"`
+	CurrentRevision string                     `json:"current_revision"`
+	Revisions       map[string]gerritRevision  `json:"revisions"`
+	Messages        []gerritMessage            `json:"messages"`
+}
+
+type gerritAccount struct {
+	AccountID int    `json:"_account_id"`
+	Name      string `json:"name"`
+	Email     string `json:"email"`
+	Username  string `json:"username"`
+}
+
+type gerritLabelInfo struct {
+	All []gerritApproval `json:"all"`
+}
+
+type gerritApproval struct {
+	AccountID int    `json:"_account_id"`
+	Name      string `json:"name"`
+	Email     string `json:"email"`
+	Value     int    `json:"value"`
+}
+
+type gerritRevision struct {
+	Number int                        `json:"_number"`
+	Ref    string                     `json:"ref"`
+	Fetch  map[string]gerritFetchInfo `json:"fetch"`
+	Commit gerritCommit               `json:"commit"`
+	Files  map[string]gerritFileInfo  `json:"files"`
+}
+
+type gerritFetchInfo struct {
+	URL string `json:"url"`
+	Ref string `json:"ref"`
+}
+
+type gerritCommit struct {
+	Subject string       `json:"subject"`
+	Message string       `json:"message"`
+	Author  gerritPerson `json:"author"`
+}
+
+type gerritPerson struct {
+	Name  string `json:"name"`
+	Email string `json:"email"`
+	Date  string `json:"date"`
+}
+
+type gerritFileInfo struct {
+	Status        string `json:"status"`
+	LinesInserted int    `json:"lines_inserted"`
+	LinesDeleted  int    `json:"lines_deleted"`
+}
+
+type gerritMessage struct {
+	ID             string        `json:"id"`
+	Author         gerritAccount `json:"author"`
+	Message        string        `json:"message"`
+	Date           time.Time     `json:"date"`
+	RevisionNumber int           `json:"_revision_number"`
+}
+
+type gerritCreateChangeRequest struct {
+	Project     string `json:"project"`
+	Subject     string `json:"subject"`
+	Description string `json:"description"`
+	Branch      string `json:"branch"`
+	Topic       string `json:"topic,omitempty"`
+	Base        string `json:"base,omitempty"`
+}
+
+type gerritUpdateChangeRequest struct {
+	Subject     string `json:"subject,omitempty"`
+	Description string `json:"description,omitempty"`
+	Topic       string `json:"topic,omitempty"`
+	Status      string `json:"status,omitempty"`
+}
+
+type gerritReviewRequest struct {
+	Message   string                     `json:"message,omitempty"`
+	Labels    map[string]int             `json:"labels,omitempty"`
+	Reviewers []string                   `json:"reviewers,omitempty"`
+	Comments  map[string][]gerritComment `json:"comments,omitempty"`
+}
+
+type gerritComment struct {
+	Line    int    `json:"line"`
+	Message string `json:"message"`
+}
+
+// CreatePullRequest creates a new change (pull request) on Gerrit
+func (g *GerritPullRequestProvider) CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error) {
+	reqBody := gerritCreateChangeRequest{
+		Project:     g.project,
+		Subject:     options.Title,
+		Description: options.Description,
+		Branch:      options.BaseBranch,
+		Topic:       options.HeadBranch, // Use head branch as topic
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/a/changes/", g.config.BaseURL)
+	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusCreated {
+		return nil, fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	// Gerrit returns the change ID in the response body
+	var changeID string
+	if err := json.NewDecoder(resp.Body).Decode(&changeID); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	// Remove the ")]}'" prefix that Gerrit adds to responses
+	changeID = strings.TrimPrefix(changeID, ")]}'")
+
+	// Fetch the created change
+	return g.GetPullRequest(ctx, changeID)
+}
+
+// GetPullRequest retrieves a change by ID
+func (g *GerritPullRequestProvider) GetPullRequest(ctx context.Context, id string) (*PullRequest, error) {
+	url := fmt.Sprintf("%s/a/changes/%s?o=DETAILED_ACCOUNTS&o=DETAILED_LABELS&o=MESSAGES&o=CURRENT_REVISION&o=CURRENT_COMMIT", g.config.BaseURL, id)
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	// Read and remove Gerrit's ")]}'" prefix
+	body := make([]byte, 4)
+	if _, err := resp.Body.Read(body); err != nil {
+		return nil, fmt.Errorf("failed to read response prefix: %w", err)
+	}
+	if string(body) != ")]}'" {
+		return nil, fmt.Errorf("unexpected response prefix: %s", string(body))
+	}
+
+	var gerritChange gerritChange
+	if err := json.NewDecoder(resp.Body).Decode(&gerritChange); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	return g.convertGerritChange(gerritChange), nil
+}
+
+// ListPullRequests lists changes
+func (g *GerritPullRequestProvider) ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error) {
+	url := fmt.Sprintf("%s/a/changes/", g.config.BaseURL)
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	// Add query parameters
+	q := req.URL.Query()
+	q.Add("q", fmt.Sprintf("project:%s", g.project))
+
+	if options.State != "" {
+		switch options.State {
+		case "open":
+			q.Add("q", "status:open")
+		case "closed":
+			q.Add("q", "status:closed")
+		}
+	}
+	if options.Author != "" {
+		q.Add("q", fmt.Sprintf("owner:%s", options.Author))
+	}
+	if options.Assignee != "" {
+		q.Add("q", fmt.Sprintf("reviewer:%s", options.Assignee))
+	}
+	if options.BaseBranch != "" {
+		q.Add("q", fmt.Sprintf("branch:%s", options.BaseBranch))
+	}
+	if options.Limit > 0 {
+		q.Add("n", strconv.Itoa(options.Limit))
+	}
+	req.URL.RawQuery = q.Encode()
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	// Read and remove Gerrit's ")]}'" prefix
+	body := make([]byte, 4)
+	if _, err := resp.Body.Read(body); err != nil {
+		return nil, fmt.Errorf("failed to read response prefix: %w", err)
+	}
+	if string(body) != ")]}'" {
+		return nil, fmt.Errorf("unexpected response prefix: %s", string(body))
+	}
+
+	var gerritChanges []gerritChange
+	if err := json.NewDecoder(resp.Body).Decode(&gerritChanges); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	prs := make([]PullRequest, len(gerritChanges))
+	for i, change := range gerritChanges {
+		prs[i] = *g.convertGerritChange(change)
+	}
+
+	return prs, nil
+}
+
+// UpdatePullRequest updates a change
+func (g *GerritPullRequestProvider) UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error) {
+	reqBody := gerritUpdateChangeRequest{
+		Subject:     options.Title,
+		Description: options.Description,
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/a/changes/%s", g.config.BaseURL, id)
+	req, err := http.NewRequestWithContext(ctx, "PUT", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	// Fetch the updated change
+	return g.GetPullRequest(ctx, id)
+}
+
+// ClosePullRequest closes a change
+func (g *GerritPullRequestProvider) ClosePullRequest(ctx context.Context, id string) error {
+	reqBody := gerritUpdateChangeRequest{
+		Status: "ABANDONED",
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/a/changes/%s/abandon", g.config.BaseURL, id)
+	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	return nil
+}
+
+// MergePullRequest merges a change
+func (g *GerritPullRequestProvider) MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error {
+	url := fmt.Sprintf("%s/a/changes/%s/submit", g.config.BaseURL, id)
+	req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
+	if err != nil {
+		return fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.SetBasicAuth(g.config.Username, g.config.Password)
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("Gerrit API error: %d", resp.StatusCode)
+	}
+
+	return nil
+}
+
+// convertGerritChange converts a Gerrit change to our PullRequest type
+func (g *GerritPullRequestProvider) convertGerritChange(change gerritChange) *PullRequest {
+	// Extract labels
+	var labels []string
+	for labelName := range change.Labels {
+		labels = append(labels, labelName)
+	}
+
+	// Extract reviewers
+	var reviewers []Author
+	if reviewersMap, exists := change.Reviewers["REVIEWER"]; exists {
+		for _, reviewer := range reviewersMap {
+			reviewers = append(reviewers, Author{
+				Name:  reviewer.Name,
+				Email: reviewer.Email,
+			})
+		}
+	}
+
+	// Extract comments from messages
+	var comments []PullRequestComment
+	for _, message := range change.Messages {
+		if message.Message != "" {
+			comments = append(comments, PullRequestComment{
+				ID: message.ID,
+				Author: Author{
+					Name:  message.Author.Name,
+					Email: message.Author.Email,
+				},
+				Content:   message.Message,
+				CreatedAt: message.Date,
+				UpdatedAt: message.Date,
+			})
+		}
+	}
+
+	// Determine state
+	state := "open"
+	switch change.Status {
+	case "MERGED":
+		state = "merged"
+	case "ABANDONED":
+		state = "closed"
+	}
+
+	return &PullRequest{
+		ID:          change.ID,
+		Number:      change.Number,
+		Title:       change.Subject,
+		Description: change.Description,
+		State:       state,
+		Author: Author{
+			Name:  change.Owner.Name,
+			Email: change.Owner.Email,
+		},
+		CreatedAt:  change.Created,
+		UpdatedAt:  change.Updated,
+		BaseBranch: change.Branch,
+		HeadBranch: change.Topic, // Use topic as head branch
+		BaseRepo:   g.project,
+		HeadRepo:   g.project, // Gerrit changes are within the same project
+		Labels:     labels,
+		Assignees:  []Author{}, // Gerrit doesn't have assignees in the same way
+		Reviewers:  reviewers,
+		Commits:    []Commit{}, // Would need additional API call to populate
+		Comments:   comments,
+	}
+}
diff --git a/server/git/git.go b/server/git/git.go
index fdcd467..dc3885f 100644
--- a/server/git/git.go
+++ b/server/git/git.go
@@ -52,6 +52,14 @@
 	SetConfig(ctx context.Context, key, value string) error
 	GetUserConfig(ctx context.Context) (*UserConfig, error)
 	SetUserConfig(ctx context.Context, config UserConfig) error
+
+	// Pull Request operations
+	CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error)
+	GetPullRequest(ctx context.Context, id string) (*PullRequest, error)
+	ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error)
+	UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error)
+	ClosePullRequest(ctx context.Context, id string) error
+	MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error
 }
 
 // Status represents the current state of the repository
@@ -178,14 +186,16 @@
 
 // Git implementation using os/exec to call git commands
 type Git struct {
-	repoPath string
-	config   GitConfig
+	repoPath   string
+	config     GitConfig
+	prProvider PullRequestProvider
 }
 
 // GitConfig holds configuration for Git operations
 type GitConfig struct {
-	Timeout time.Duration
-	Env     map[string]string
+	Timeout             time.Duration
+	Env                 map[string]string
+	PullRequestProvider PullRequestProvider
 }
 
 // NewGit creates a new Git instance
@@ -195,8 +205,9 @@
 	}
 
 	return &Git{
-		repoPath: repoPath,
-		config:   config,
+		repoPath:   repoPath,
+		config:     config,
+		prProvider: config.PullRequestProvider,
 	}
 }
 
@@ -208,6 +219,12 @@
 	})
 }
 
+// NewGitWithPullRequests creates a Git instance with pull request capabilities
+func NewGitWithPullRequests(repoPath string, config GitConfig, prProvider PullRequestProvider) GitInterface {
+	config.PullRequestProvider = prProvider
+	return NewGit(repoPath, config)
+}
+
 // Ensure Git implements GitInterface
 var _ GitInterface = (*Git)(nil)
 
@@ -567,6 +584,56 @@
 	return g.SetConfig(ctx, "user.email", config.Email)
 }
 
+// Pull Request operations
+
+// CreatePullRequest creates a new pull request
+func (g *Git) CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error) {
+	if g.prProvider == nil {
+		return nil, &GitError{Command: "CreatePullRequest", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.CreatePullRequest(ctx, options)
+}
+
+// GetPullRequest retrieves a pull request by ID
+func (g *Git) GetPullRequest(ctx context.Context, id string) (*PullRequest, error) {
+	if g.prProvider == nil {
+		return nil, &GitError{Command: "GetPullRequest", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.GetPullRequest(ctx, id)
+}
+
+// ListPullRequests lists pull requests
+func (g *Git) ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error) {
+	if g.prProvider == nil {
+		return nil, &GitError{Command: "ListPullRequests", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.ListPullRequests(ctx, options)
+}
+
+// UpdatePullRequest updates a pull request
+func (g *Git) UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error) {
+	if g.prProvider == nil {
+		return nil, &GitError{Command: "UpdatePullRequest", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.UpdatePullRequest(ctx, id, options)
+}
+
+// ClosePullRequest closes a pull request
+func (g *Git) ClosePullRequest(ctx context.Context, id string) error {
+	if g.prProvider == nil {
+		return &GitError{Command: "ClosePullRequest", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.ClosePullRequest(ctx, id)
+}
+
+// MergePullRequest merges a pull request
+func (g *Git) MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error {
+	if g.prProvider == nil {
+		return &GitError{Command: "MergePullRequest", Output: "no pull request provider configured"}
+	}
+	return g.prProvider.MergePullRequest(ctx, id, options)
+}
+
 // Helper methods
 
 func (g *Git) runCommand(cmd *exec.Cmd, command string) error {
@@ -795,3 +862,77 @@
 
 	return remotes, nil
 }
+
+// PullRequest represents a pull request or merge request
+type PullRequest struct {
+	ID          string
+	Number      int
+	Title       string
+	Description string
+	State       string // "open", "closed", "merged"
+	Author      Author
+	CreatedAt   time.Time
+	UpdatedAt   time.Time
+	BaseBranch  string
+	HeadBranch  string
+	BaseRepo    string
+	HeadRepo    string
+	Labels      []string
+	Assignees   []Author
+	Reviewers   []Author
+	Commits     []Commit
+	Comments    []PullRequestComment
+}
+
+// PullRequestComment represents a comment on a pull request
+type PullRequestComment struct {
+	ID        string
+	Author    Author
+	Content   string
+	CreatedAt time.Time
+	UpdatedAt time.Time
+	Path      string
+	Line      int
+}
+
+// PullRequestOptions defines options for creating/updating pull requests
+type PullRequestOptions struct {
+	Title       string
+	Description string
+	BaseBranch  string
+	HeadBranch  string
+	BaseRepo    string
+	HeadRepo    string
+	Labels      []string
+	Assignees   []string
+	Reviewers   []string
+	Draft       bool
+}
+
+// ListPullRequestOptions defines options for listing pull requests
+type ListPullRequestOptions struct {
+	State      string // "open", "closed", "all"
+	Author     string
+	Assignee   string
+	BaseBranch string
+	HeadBranch string
+	Labels     []string
+	Limit      int
+}
+
+// MergePullRequestOptions defines options for merging pull requests
+type MergePullRequestOptions struct {
+	MergeMethod string // "merge", "squash", "rebase"
+	CommitTitle string
+	CommitMsg   string
+}
+
+// PullRequestProvider defines the interface for pull request operations
+type PullRequestProvider interface {
+	CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error)
+	GetPullRequest(ctx context.Context, id string) (*PullRequest, error)
+	ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error)
+	UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error)
+	ClosePullRequest(ctx context.Context, id string) error
+	MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error
+}
diff --git a/server/git/github.go b/server/git/github.go
new file mode 100644
index 0000000..8f15c68
--- /dev/null
+++ b/server/git/github.go
@@ -0,0 +1,397 @@
+package git
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+)
+
+// GitHubConfig holds configuration for GitHub operations
+type GitHubConfig struct {
+	Token      string
+	BaseURL    string // Default: https://api.github.com
+	HTTPClient *http.Client
+}
+
+// GitHubPullRequestProvider implements PullRequestProvider for GitHub
+type GitHubPullRequestProvider struct {
+	config GitHubConfig
+	owner  string
+	repo   string
+}
+
+// NewGitHubPullRequestProvider creates a new GitHub pull request provider
+func NewGitHubPullRequestProvider(owner, repo string, config GitHubConfig) PullRequestProvider {
+	if config.BaseURL == "" {
+		config.BaseURL = "https://api.github.com"
+	}
+	if config.HTTPClient == nil {
+		config.HTTPClient = &http.Client{Timeout: 30 * time.Second}
+	}
+
+	return &GitHubPullRequestProvider{
+		config: config,
+		owner:  owner,
+		repo:   repo,
+	}
+}
+
+// GitHub API response types
+type githubPullRequest struct {
+	ID                 int           `json:"id"`
+	Number             int           `json:"number"`
+	Title              string        `json:"title"`
+	Body               string        `json:"body"`
+	State              string        `json:"state"`
+	User               githubUser    `json:"user"`
+	CreatedAt          time.Time     `json:"created_at"`
+	UpdatedAt          time.Time     `json:"updated_at"`
+	Base               githubBranch  `json:"base"`
+	Head               githubBranch  `json:"head"`
+	Labels             []githubLabel `json:"labels"`
+	Assignees          []githubUser  `json:"assignees"`
+	RequestedReviewers []githubUser  `json:"requested_reviewers"`
+	CommitsURL         string        `json:"commits_url"`
+	CommentsURL        string        `json:"comments_url"`
+}
+
+type githubUser struct {
+	Login string `json:"login"`
+	ID    int    `json:"id"`
+	Type  string `json:"type"`
+}
+
+type githubBranch struct {
+	Ref  string     `json:"ref"`
+	SHA  string     `json:"sha"`
+	Repo githubRepo `json:"repo"`
+}
+
+type githubRepo struct {
+	FullName string `json:"full_name"`
+}
+
+type githubLabel struct {
+	Name  string `json:"name"`
+	Color string `json:"color"`
+}
+
+type githubCreatePRRequest struct {
+	Title     string   `json:"title"`
+	Body      string   `json:"body"`
+	Head      string   `json:"head"`
+	Base      string   `json:"base"`
+	Labels    []string `json:"labels,omitempty"`
+	Assignees []string `json:"assignees,omitempty"`
+	Reviewers []string `json:"reviewers,omitempty"`
+	Draft     bool     `json:"draft,omitempty"`
+}
+
+type githubUpdatePRRequest struct {
+	Title     string   `json:"title,omitempty"`
+	Body      string   `json:"body,omitempty"`
+	State     string   `json:"state,omitempty"`
+	Base      string   `json:"base,omitempty"`
+	Labels    []string `json:"labels,omitempty"`
+	Assignees []string `json:"assignees,omitempty"`
+}
+
+type githubMergePRRequest struct {
+	CommitTitle string `json:"commit_title,omitempty"`
+	CommitMsg   string `json:"commit_message,omitempty"`
+	MergeMethod string `json:"merge_method,omitempty"`
+}
+
+// CreatePullRequest creates a new pull request on GitHub
+func (g *GitHubPullRequestProvider) CreatePullRequest(ctx context.Context, options PullRequestOptions) (*PullRequest, error) {
+	reqBody := githubCreatePRRequest{
+		Title:     options.Title,
+		Body:      options.Description,
+		Head:      options.HeadBranch,
+		Base:      options.BaseBranch,
+		Labels:    options.Labels,
+		Assignees: options.Assignees,
+		Reviewers: options.Reviewers,
+		Draft:     options.Draft,
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls", g.config.BaseURL, g.owner, g.repo)
+	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusCreated {
+		return nil, fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	var githubPR githubPullRequest
+	if err := json.NewDecoder(resp.Body).Decode(&githubPR); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	return g.convertGitHubPR(githubPR), nil
+}
+
+// GetPullRequest retrieves a pull request by number
+func (g *GitHubPullRequestProvider) GetPullRequest(ctx context.Context, id string) (*PullRequest, error) {
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls/%s", g.config.BaseURL, g.owner, g.repo, id)
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	var githubPR githubPullRequest
+	if err := json.NewDecoder(resp.Body).Decode(&githubPR); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	return g.convertGitHubPR(githubPR), nil
+}
+
+// ListPullRequests lists pull requests
+func (g *GitHubPullRequestProvider) ListPullRequests(ctx context.Context, options ListPullRequestOptions) ([]PullRequest, error) {
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls", g.config.BaseURL, g.owner, g.repo)
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	// Add query parameters
+	q := req.URL.Query()
+	if options.State != "" {
+		q.Add("state", options.State)
+	}
+	if options.Author != "" {
+		q.Add("author", options.Author)
+	}
+	if options.Assignee != "" {
+		q.Add("assignee", options.Assignee)
+	}
+	if options.BaseBranch != "" {
+		q.Add("base", options.BaseBranch)
+	}
+	if options.HeadBranch != "" {
+		q.Add("head", options.HeadBranch)
+	}
+	if options.Limit > 0 {
+		q.Add("per_page", fmt.Sprintf("%d", options.Limit))
+	}
+	req.URL.RawQuery = q.Encode()
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	var githubPRs []githubPullRequest
+	if err := json.NewDecoder(resp.Body).Decode(&githubPRs); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	prs := make([]PullRequest, len(githubPRs))
+	for i, githubPR := range githubPRs {
+		prs[i] = *g.convertGitHubPR(githubPR)
+	}
+
+	return prs, nil
+}
+
+// UpdatePullRequest updates a pull request
+func (g *GitHubPullRequestProvider) UpdatePullRequest(ctx context.Context, id string, options PullRequestOptions) (*PullRequest, error) {
+	reqBody := githubUpdatePRRequest{
+		Title:     options.Title,
+		Body:      options.Description,
+		Base:      options.BaseBranch,
+		Labels:    options.Labels,
+		Assignees: options.Assignees,
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls/%s", g.config.BaseURL, g.owner, g.repo, id)
+	req, err := http.NewRequestWithContext(ctx, "PATCH", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	var githubPR githubPullRequest
+	if err := json.NewDecoder(resp.Body).Decode(&githubPR); err != nil {
+		return nil, fmt.Errorf("failed to decode response: %w", err)
+	}
+
+	return g.convertGitHubPR(githubPR), nil
+}
+
+// ClosePullRequest closes a pull request
+func (g *GitHubPullRequestProvider) ClosePullRequest(ctx context.Context, id string) error {
+	reqBody := githubUpdatePRRequest{
+		State: "closed",
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls/%s", g.config.BaseURL, g.owner, g.repo, id)
+	req, err := http.NewRequestWithContext(ctx, "PATCH", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	return nil
+}
+
+// MergePullRequest merges a pull request
+func (g *GitHubPullRequestProvider) MergePullRequest(ctx context.Context, id string, options MergePullRequestOptions) error {
+	reqBody := githubMergePRRequest{
+		CommitTitle: options.CommitTitle,
+		CommitMsg:   options.CommitMsg,
+		MergeMethod: options.MergeMethod,
+	}
+
+	jsonBody, err := json.Marshal(reqBody)
+	if err != nil {
+		return fmt.Errorf("failed to marshal request body: %w", err)
+	}
+
+	url := fmt.Sprintf("%s/repos/%s/%s/pulls/%s/merge", g.config.BaseURL, g.owner, g.repo, id)
+	req, err := http.NewRequestWithContext(ctx, "PUT", url, bytes.NewBuffer(jsonBody))
+	if err != nil {
+		return fmt.Errorf("failed to create request: %w", err)
+	}
+
+	req.Header.Set("Authorization", "token "+g.config.Token)
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+	resp, err := g.config.HTTPClient.Do(req)
+	if err != nil {
+		return fmt.Errorf("failed to make request: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return fmt.Errorf("GitHub API error: %d", resp.StatusCode)
+	}
+
+	return nil
+}
+
+// convertGitHubPR converts a GitHub pull request to our PullRequest type
+func (g *GitHubPullRequestProvider) convertGitHubPR(githubPR githubPullRequest) *PullRequest {
+	labels := make([]string, len(githubPR.Labels))
+	for i, label := range githubPR.Labels {
+		labels[i] = label.Name
+	}
+
+	assignees := make([]Author, len(githubPR.Assignees))
+	for i, assignee := range githubPR.Assignees {
+		assignees[i] = Author{
+			Name:  assignee.Login,
+			Email: "", // GitHub API doesn't provide email in this context
+		}
+	}
+
+	reviewers := make([]Author, len(githubPR.RequestedReviewers))
+	for i, reviewer := range githubPR.RequestedReviewers {
+		reviewers[i] = Author{
+			Name:  reviewer.Login,
+			Email: "", // GitHub API doesn't provide email in this context
+		}
+	}
+
+	return &PullRequest{
+		ID:          fmt.Sprintf("%d", githubPR.ID),
+		Number:      githubPR.Number,
+		Title:       githubPR.Title,
+		Description: githubPR.Body,
+		State:       githubPR.State,
+		Author: Author{
+			Name:  githubPR.User.Login,
+			Email: "", // GitHub API doesn't provide email in this context
+		},
+		CreatedAt:  githubPR.CreatedAt,
+		UpdatedAt:  githubPR.UpdatedAt,
+		BaseBranch: githubPR.Base.Ref,
+		HeadBranch: githubPR.Head.Ref,
+		BaseRepo:   githubPR.Base.Repo.FullName,
+		HeadRepo:   githubPR.Head.Repo.FullName,
+		Labels:     labels,
+		Assignees:  assignees,
+		Reviewers:  reviewers,
+		Commits:    []Commit{},             // Would need additional API call to populate
+		Comments:   []PullRequestComment{}, // Would need additional API call to populate
+	}
+}
diff --git a/server/git/pull_request_example.go b/server/git/pull_request_example.go
new file mode 100644
index 0000000..5e34dc4
--- /dev/null
+++ b/server/git/pull_request_example.go
@@ -0,0 +1,248 @@
+package git
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net/http"
+	"time"
+)
+
+// ExamplePullRequestUsage demonstrates how to use the pull request capabilities
+func ExamplePullRequestUsage() {
+	ctx := context.Background()
+
+	// Example 1: GitHub Pull Requests
+	exampleGitHubPullRequests(ctx)
+
+	// Example 2: Gerrit Pull Requests
+	exampleGerritPullRequests(ctx)
+}
+
+func exampleGitHubPullRequests(ctx context.Context) {
+	fmt.Println("=== GitHub Pull Request Example ===")
+
+	// Create GitHub configuration
+	githubConfig := GitHubConfig{
+		Token:      "your-github-token-here",
+		BaseURL:    "https://api.github.com",
+		HTTPClient: &http.Client{Timeout: 30 * time.Second},
+	}
+
+	// Create GitHub pull request provider
+	githubProvider := NewGitHubPullRequestProvider("owner", "repo", githubConfig)
+
+	// Create Git instance with GitHub pull request capabilities
+	git := NewGitWithPullRequests("/path/to/repo", GitConfig{
+		Timeout: 30 * time.Second,
+	}, githubProvider)
+
+	// Create a new pull request
+	prOptions := PullRequestOptions{
+		Title:       "Add new feature",
+		Description: "This PR adds a new feature to the application.",
+		BaseBranch:  "main",
+		HeadBranch:  "feature/new-feature",
+		Labels:      []string{"enhancement", "feature"},
+		Assignees:   []string{"username1", "username2"},
+		Reviewers:   []string{"reviewer1", "reviewer2"},
+		Draft:       false,
+	}
+
+	pr, err := git.CreatePullRequest(ctx, prOptions)
+	if err != nil {
+		log.Printf("Failed to create pull request: %v", err)
+		return
+	}
+
+	fmt.Printf("Created pull request: %s (#%d)\n", pr.Title, pr.Number)
+
+	// List pull requests
+	listOptions := ListPullRequestOptions{
+		State:      "open",
+		Author:     "username",
+		BaseBranch: "main",
+		Limit:      10,
+	}
+
+	prs, err := git.ListPullRequests(ctx, listOptions)
+	if err != nil {
+		log.Printf("Failed to list pull requests: %v", err)
+		return
+	}
+
+	fmt.Printf("Found %d pull requests\n", len(prs))
+
+	// Get a specific pull request
+	pr, err = git.GetPullRequest(ctx, pr.ID)
+	if err != nil {
+		log.Printf("Failed to get pull request: %v", err)
+		return
+	}
+
+	fmt.Printf("Pull request status: %s\n", pr.State)
+
+	// Update a pull request
+	updateOptions := PullRequestOptions{
+		Title:       "Updated title",
+		Description: "Updated description",
+		Labels:      []string{"bug", "urgent"},
+	}
+
+	updatedPR, err := git.UpdatePullRequest(ctx, pr.ID, updateOptions)
+	if err != nil {
+		log.Printf("Failed to update pull request: %v", err)
+		return
+	}
+
+	fmt.Printf("Updated pull request: %s\n", updatedPR.Title)
+
+	// Merge a pull request
+	mergeOptions := MergePullRequestOptions{
+		MergeMethod: "squash",
+		CommitTitle: "Merge pull request #123",
+		CommitMsg:   "This merges the feature branch into main",
+	}
+
+	err = git.MergePullRequest(ctx, pr.ID, mergeOptions)
+	if err != nil {
+		log.Printf("Failed to merge pull request: %v", err)
+		return
+	}
+
+	fmt.Println("Pull request merged successfully")
+}
+
+func exampleGerritPullRequests(ctx context.Context) {
+	fmt.Println("=== Gerrit Pull Request Example ===")
+
+	// Create Gerrit configuration
+	gerritConfig := GerritConfig{
+		Username:   "your-username",
+		Password:   "your-http-password-or-api-token",
+		BaseURL:    "https://gerrit.example.com",
+		HTTPClient: &http.Client{Timeout: 30 * time.Second},
+	}
+
+	// Create Gerrit pull request provider
+	gerritProvider := NewGerritPullRequestProvider("project-name", gerritConfig)
+
+	// Create Git instance with Gerrit pull request capabilities
+	git := NewGitWithPullRequests("/path/to/repo", GitConfig{
+		Timeout: 30 * time.Second,
+	}, gerritProvider)
+
+	// Create a new change (pull request)
+	prOptions := PullRequestOptions{
+		Title:       "Add new feature",
+		Description: "This change adds a new feature to the application.",
+		BaseBranch:  "master",
+		HeadBranch:  "feature/new-feature",
+	}
+
+	pr, err := git.CreatePullRequest(ctx, prOptions)
+	if err != nil {
+		log.Printf("Failed to create change: %v", err)
+		return
+	}
+
+	fmt.Printf("Created change: %s (#%d)\n", pr.Title, pr.Number)
+
+	// List changes
+	listOptions := ListPullRequestOptions{
+		State:      "open",
+		Author:     "username",
+		BaseBranch: "master",
+		Limit:      10,
+	}
+
+	prs, err := git.ListPullRequests(ctx, listOptions)
+	if err != nil {
+		log.Printf("Failed to list changes: %v", err)
+		return
+	}
+
+	fmt.Printf("Found %d changes\n", len(prs))
+
+	// Get a specific change
+	pr, err = git.GetPullRequest(ctx, pr.ID)
+	if err != nil {
+		log.Printf("Failed to get change: %v", err)
+		return
+	}
+
+	fmt.Printf("Change status: %s\n", pr.State)
+
+	// Update a change
+	updateOptions := PullRequestOptions{
+		Title:       "Updated title",
+		Description: "Updated description",
+	}
+
+	updatedPR, err := git.UpdatePullRequest(ctx, pr.ID, updateOptions)
+	if err != nil {
+		log.Printf("Failed to update change: %v", err)
+		return
+	}
+
+	fmt.Printf("Updated change: %s\n", updatedPR.Title)
+
+	// Submit a change (merge)
+	mergeOptions := MergePullRequestOptions{
+		CommitTitle: "Submit change",
+		CommitMsg:   "This submits the change to master",
+	}
+
+	err = git.MergePullRequest(ctx, pr.ID, mergeOptions)
+	if err != nil {
+		log.Printf("Failed to submit change: %v", err)
+		return
+	}
+
+	fmt.Println("Change submitted successfully")
+}
+
+// Example of using both providers in the same application
+func ExampleMultiProviderUsage() {
+	ctx := context.Background()
+
+	// Determine which provider to use based on configuration
+	useGitHub := true // This could come from config
+
+	var git GitInterface
+
+	if useGitHub {
+		// Use GitHub
+		githubConfig := GitHubConfig{
+			Token:   "github-token",
+			BaseURL: "https://api.github.com",
+		}
+		githubProvider := NewGitHubPullRequestProvider("owner", "repo", githubConfig)
+		git = NewGitWithPullRequests("/path/to/repo", GitConfig{}, githubProvider)
+	} else {
+		// Use Gerrit
+		gerritConfig := GerritConfig{
+			Username: "gerrit-username",
+			Password: "gerrit-password",
+			BaseURL:  "https://gerrit.example.com",
+		}
+		gerritProvider := NewGerritPullRequestProvider("project", gerritConfig)
+		git = NewGitWithPullRequests("/path/to/repo", GitConfig{}, gerritProvider)
+	}
+
+	// Use the same interface regardless of provider
+	prOptions := PullRequestOptions{
+		Title:       "Cross-platform PR",
+		Description: "This works with both GitHub and Gerrit",
+		BaseBranch:  "main",
+		HeadBranch:  "feature/cross-platform",
+	}
+
+	pr, err := git.CreatePullRequest(ctx, prOptions)
+	if err != nil {
+		log.Printf("Failed to create pull request: %v", err)
+		return
+	}
+
+	fmt.Printf("Created pull request: %s\n", pr.Title)
+}