github.com/google/go-github/v49@v49.1.0/github/repos_commits.go (about) 1 // Copyright 2013 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "bytes" 10 "context" 11 "fmt" 12 "net/url" 13 "time" 14 ) 15 16 // RepositoryCommit represents a commit in a repo. 17 // Note that it's wrapping a Commit, so author/committer information is in two places, 18 // but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". 19 type RepositoryCommit struct { 20 NodeID *string `json:"node_id,omitempty"` 21 SHA *string `json:"sha,omitempty"` 22 Commit *Commit `json:"commit,omitempty"` 23 Author *User `json:"author,omitempty"` 24 Committer *User `json:"committer,omitempty"` 25 Parents []*Commit `json:"parents,omitempty"` 26 HTMLURL *string `json:"html_url,omitempty"` 27 URL *string `json:"url,omitempty"` 28 CommentsURL *string `json:"comments_url,omitempty"` 29 30 // Details about how many changes were made in this commit. Only filled in during GetCommit! 31 Stats *CommitStats `json:"stats,omitempty"` 32 // Details about which files, and how this commit touched. Only filled in during GetCommit! 33 Files []*CommitFile `json:"files,omitempty"` 34 } 35 36 func (r RepositoryCommit) String() string { 37 return Stringify(r) 38 } 39 40 // CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit. 41 type CommitStats struct { 42 Additions *int `json:"additions,omitempty"` 43 Deletions *int `json:"deletions,omitempty"` 44 Total *int `json:"total,omitempty"` 45 } 46 47 func (c CommitStats) String() string { 48 return Stringify(c) 49 } 50 51 // CommitFile represents a file modified in a commit. 52 type CommitFile struct { 53 SHA *string `json:"sha,omitempty"` 54 Filename *string `json:"filename,omitempty"` 55 Additions *int `json:"additions,omitempty"` 56 Deletions *int `json:"deletions,omitempty"` 57 Changes *int `json:"changes,omitempty"` 58 Status *string `json:"status,omitempty"` 59 Patch *string `json:"patch,omitempty"` 60 BlobURL *string `json:"blob_url,omitempty"` 61 RawURL *string `json:"raw_url,omitempty"` 62 ContentsURL *string `json:"contents_url,omitempty"` 63 PreviousFilename *string `json:"previous_filename,omitempty"` 64 } 65 66 func (c CommitFile) String() string { 67 return Stringify(c) 68 } 69 70 // CommitsComparison is the result of comparing two commits. 71 // See CompareCommits() for details. 72 type CommitsComparison struct { 73 BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` 74 MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"` 75 76 // Head can be 'behind' or 'ahead' 77 Status *string `json:"status,omitempty"` 78 AheadBy *int `json:"ahead_by,omitempty"` 79 BehindBy *int `json:"behind_by,omitempty"` 80 TotalCommits *int `json:"total_commits,omitempty"` 81 82 Commits []*RepositoryCommit `json:"commits,omitempty"` 83 84 Files []*CommitFile `json:"files,omitempty"` 85 86 HTMLURL *string `json:"html_url,omitempty"` 87 PermalinkURL *string `json:"permalink_url,omitempty"` 88 DiffURL *string `json:"diff_url,omitempty"` 89 PatchURL *string `json:"patch_url,omitempty"` 90 URL *string `json:"url,omitempty"` // API URL. 91 } 92 93 func (c CommitsComparison) String() string { 94 return Stringify(c) 95 } 96 97 // CommitsListOptions specifies the optional parameters to the 98 // RepositoriesService.ListCommits method. 99 type CommitsListOptions struct { 100 // SHA or branch to start listing Commits from. 101 SHA string `url:"sha,omitempty"` 102 103 // Path that should be touched by the returned Commits. 104 Path string `url:"path,omitempty"` 105 106 // Author of by which to filter Commits. 107 Author string `url:"author,omitempty"` 108 109 // Since when should Commits be included in the response. 110 Since time.Time `url:"since,omitempty"` 111 112 // Until when should Commits be included in the response. 113 Until time.Time `url:"until,omitempty"` 114 115 ListOptions 116 } 117 118 // BranchCommit is the result of listing branches with commit SHA. 119 type BranchCommit struct { 120 Name *string `json:"name,omitempty"` 121 Commit *Commit `json:"commit,omitempty"` 122 Protected *bool `json:"protected,omitempty"` 123 } 124 125 // ListCommits lists the commits of a repository. 126 // 127 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#list-commits 128 func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opts *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { 129 u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) 130 u, err := addOptions(u, opts) 131 if err != nil { 132 return nil, nil, err 133 } 134 135 req, err := s.client.NewRequest("GET", u, nil) 136 if err != nil { 137 return nil, nil, err 138 } 139 140 var commits []*RepositoryCommit 141 resp, err := s.client.Do(ctx, req, &commits) 142 if err != nil { 143 return nil, resp, err 144 } 145 146 return commits, resp, nil 147 } 148 149 // GetCommit fetches the specified commit, including all details about it. 150 // 151 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#get-a-single-commit 152 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#get-a-commit 153 func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string, opts *ListOptions) (*RepositoryCommit, *Response, error) { 154 u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) 155 u, err := addOptions(u, opts) 156 if err != nil { 157 return nil, nil, err 158 } 159 160 req, err := s.client.NewRequest("GET", u, nil) 161 if err != nil { 162 return nil, nil, err 163 } 164 165 commit := new(RepositoryCommit) 166 resp, err := s.client.Do(ctx, req, commit) 167 if err != nil { 168 return nil, resp, err 169 } 170 171 return commit, resp, nil 172 } 173 174 // GetCommitRaw fetches the specified commit in raw (diff or patch) format. 175 // 176 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#get-a-commit 177 func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opts RawOptions) (string, *Response, error) { 178 u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) 179 req, err := s.client.NewRequest("GET", u, nil) 180 if err != nil { 181 return "", nil, err 182 } 183 184 switch opts.Type { 185 case Diff: 186 req.Header.Set("Accept", mediaTypeV3Diff) 187 case Patch: 188 req.Header.Set("Accept", mediaTypeV3Patch) 189 default: 190 return "", nil, fmt.Errorf("unsupported raw type %d", opts.Type) 191 } 192 193 var buf bytes.Buffer 194 resp, err := s.client.Do(ctx, req, &buf) 195 if err != nil { 196 return "", resp, err 197 } 198 199 return buf.String(), resp, nil 200 } 201 202 // GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is 203 // supplied and no new commits have occurred, a 304 Unmodified response is returned. 204 // 205 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#get-a-commit 206 func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) { 207 u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, refURLEscape(ref)) 208 209 req, err := s.client.NewRequest("GET", u, nil) 210 if err != nil { 211 return "", nil, err 212 } 213 if lastSHA != "" { 214 req.Header.Set("If-None-Match", `"`+lastSHA+`"`) 215 } 216 217 req.Header.Set("Accept", mediaTypeV3SHA) 218 219 var buf bytes.Buffer 220 resp, err := s.client.Do(ctx, req, &buf) 221 if err != nil { 222 return "", resp, err 223 } 224 225 return buf.String(), resp, nil 226 } 227 228 // CompareCommits compares a range of commits with each other. 229 // 230 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#compare-two-commits 231 func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string, opts *ListOptions) (*CommitsComparison, *Response, error) { 232 escapedBase := url.QueryEscape(base) 233 escapedHead := url.QueryEscape(head) 234 235 u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, escapedBase, escapedHead) 236 u, err := addOptions(u, opts) 237 if err != nil { 238 return nil, nil, err 239 } 240 241 req, err := s.client.NewRequest("GET", u, nil) 242 if err != nil { 243 return nil, nil, err 244 } 245 246 comp := new(CommitsComparison) 247 resp, err := s.client.Do(ctx, req, comp) 248 if err != nil { 249 return nil, resp, err 250 } 251 252 return comp, resp, nil 253 } 254 255 // CompareCommitsRaw compares a range of commits with each other in raw (diff or patch) format. 256 // 257 // Both "base" and "head" must be branch names in "repo". 258 // To compare branches across other repositories in the same network as "repo", 259 // use the format "<USERNAME>:branch". 260 // 261 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#compare-two-commits 262 func (s *RepositoriesService) CompareCommitsRaw(ctx context.Context, owner, repo, base, head string, opts RawOptions) (string, *Response, error) { 263 escapedBase := url.QueryEscape(base) 264 escapedHead := url.QueryEscape(head) 265 266 u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, escapedBase, escapedHead) 267 268 req, err := s.client.NewRequest("GET", u, nil) 269 if err != nil { 270 return "", nil, err 271 } 272 273 switch opts.Type { 274 case Diff: 275 req.Header.Set("Accept", mediaTypeV3Diff) 276 case Patch: 277 req.Header.Set("Accept", mediaTypeV3Patch) 278 default: 279 return "", nil, fmt.Errorf("unsupported raw type %d", opts.Type) 280 } 281 282 var buf bytes.Buffer 283 resp, err := s.client.Do(ctx, req, &buf) 284 if err != nil { 285 return "", resp, err 286 } 287 288 return buf.String(), resp, nil 289 } 290 291 // ListBranchesHeadCommit gets all branches where the given commit SHA is the HEAD, 292 // or latest commit for the branch. 293 // 294 // GitHub API docs: https://docs.github.com/en/rest/commits/commits#list-branches-for-head-commit 295 func (s *RepositoriesService) ListBranchesHeadCommit(ctx context.Context, owner, repo, sha string) ([]*BranchCommit, *Response, error) { 296 u := fmt.Sprintf("repos/%v/%v/commits/%v/branches-where-head", owner, repo, sha) 297 298 req, err := s.client.NewRequest("GET", u, nil) 299 if err != nil { 300 return nil, nil, err 301 } 302 303 // TODO: remove custom Accept header when this API fully launches. 304 req.Header.Set("Accept", mediaTypeListPullsOrBranchesForCommitPreview) 305 var branchCommits []*BranchCommit 306 resp, err := s.client.Do(ctx, req, &branchCommits) 307 if err != nil { 308 return nil, resp, err 309 } 310 311 return branchCommits, resp, nil 312 }