1// 2// Copyright 2017, Sander van Harmelen 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17package gitlab 18 19import ( 20 "fmt" 21 "net/url" 22 "time" 23) 24 25// CommitsService handles communication with the commit related methods 26// of the GitLab API. 27// 28// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html 29type CommitsService struct { 30 client *Client 31} 32 33// Commit represents a GitLab commit. 34// 35// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html 36type Commit struct { 37 ID string `json:"id"` 38 ShortID string `json:"short_id"` 39 Title string `json:"title"` 40 AuthorName string `json:"author_name"` 41 AuthorEmail string `json:"author_email"` 42 AuthoredDate *time.Time `json:"authored_date"` 43 CommitterName string `json:"committer_name"` 44 CommitterEmail string `json:"committer_email"` 45 CommittedDate *time.Time `json:"committed_date"` 46 CreatedAt *time.Time `json:"created_at"` 47 Message string `json:"message"` 48 ParentIDs []string `json:"parent_ids"` 49 Stats *CommitStats `json:"stats"` 50 Status *BuildStateValue `json:"status"` 51} 52 53// CommitStats represents the number of added and deleted files in a commit. 54// 55// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html 56type CommitStats struct { 57 Additions int `json:"additions"` 58 Deletions int `json:"deletions"` 59 Total int `json:"total"` 60} 61 62func (c Commit) String() string { 63 return Stringify(c) 64} 65 66// ListCommitsOptions represents the available ListCommits() options. 67// 68// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits 69type ListCommitsOptions struct { 70 ListOptions 71 RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"` 72 Since *time.Time `url:"since,omitempty" json:"since,omitempty"` 73 Until *time.Time `url:"until,omitempty" json:"until,omitempty"` 74 Path *string `url:"path,omitempty" json:"path,omitempty"` 75 All *bool `url:"all,omitempty" json:"all,omitempty"` 76 WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"` 77} 78 79// ListCommits gets a list of repository commits in a project. 80// 81// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits 82func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) { 83 project, err := parseID(pid) 84 if err != nil { 85 return nil, nil, err 86 } 87 u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project)) 88 89 req, err := s.client.NewRequest("GET", u, opt, options) 90 if err != nil { 91 return nil, nil, err 92 } 93 94 var c []*Commit 95 resp, err := s.client.Do(req, &c) 96 if err != nil { 97 return nil, resp, err 98 } 99 100 return c, resp, err 101} 102 103// FileAction represents the available actions that can be performed on a file. 104// 105// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions 106type FileAction string 107 108// The available file actions. 109const ( 110 FileCreate FileAction = "create" 111 FileDelete FileAction = "delete" 112 FileMove FileAction = "move" 113 FileUpdate FileAction = "update" 114) 115 116// CommitAction represents a single file action within a commit. 117type CommitAction struct { 118 Action FileAction `url:"action" json:"action"` 119 FilePath string `url:"file_path" json:"file_path"` 120 PreviousPath string `url:"previous_path,omitempty" json:"previous_path,omitempty"` 121 Content string `url:"content,omitempty" json:"content,omitempty"` 122 Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"` 123} 124 125// CommitRef represents the reference of branches/tags in a commit. 126// 127// GitLab API docs: 128// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to 129type CommitRef struct { 130 Type string `json:"type"` 131 Name string `json:"name"` 132} 133 134// GetCommitRefsOptions represents the available GetCommitRefs() options. 135// 136// GitLab API docs: 137// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to 138type GetCommitRefsOptions struct { 139 ListOptions 140 Type *string `url:"type,omitempty" json:"type,omitempty"` 141} 142 143// GetCommitRefs gets all references (from branches or tags) a commit is pushed to 144// 145// GitLab API docs: 146// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to 147func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...OptionFunc) ([]CommitRef, *Response, error) { 148 project, err := parseID(pid) 149 if err != nil { 150 return nil, nil, err 151 } 152 u := fmt.Sprintf("projects/%s/repository/commits/%s/refs", url.QueryEscape(project), sha) 153 154 req, err := s.client.NewRequest("GET", u, opt, options) 155 if err != nil { 156 return nil, nil, err 157 } 158 159 var cs []CommitRef 160 resp, err := s.client.Do(req, &cs) 161 if err != nil { 162 return nil, resp, err 163 } 164 165 return cs, resp, err 166} 167 168// GetCommit gets a specific commit identified by the commit hash or name of a 169// branch or tag. 170// 171// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit 172func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) { 173 project, err := parseID(pid) 174 if err != nil { 175 return nil, nil, err 176 } 177 u := fmt.Sprintf("projects/%s/repository/commits/%s", url.QueryEscape(project), sha) 178 179 req, err := s.client.NewRequest("GET", u, nil, options) 180 if err != nil { 181 return nil, nil, err 182 } 183 184 c := new(Commit) 185 resp, err := s.client.Do(req, c) 186 if err != nil { 187 return nil, resp, err 188 } 189 190 return c, resp, err 191} 192 193// CreateCommitOptions represents the available options for a new commit. 194// 195// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions 196type CreateCommitOptions struct { 197 Branch *string `url:"branch" json:"branch"` 198 CommitMessage *string `url:"commit_message" json:"commit_message"` 199 StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"` 200 Actions []*CommitAction `url:"actions" json:"actions"` 201 AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"` 202 AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"` 203} 204 205// CreateCommit creates a commit with multiple files and actions. 206// 207// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions 208func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) { 209 project, err := parseID(pid) 210 if err != nil { 211 return nil, nil, err 212 } 213 u := fmt.Sprintf("projects/%s/repository/commits", url.QueryEscape(project)) 214 215 req, err := s.client.NewRequest("POST", u, opt, options) 216 if err != nil { 217 return nil, nil, err 218 } 219 220 var c *Commit 221 resp, err := s.client.Do(req, &c) 222 if err != nil { 223 return nil, resp, err 224 } 225 226 return c, resp, err 227} 228 229// Diff represents a GitLab diff. 230// 231// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html 232type Diff struct { 233 Diff string `json:"diff"` 234 NewPath string `json:"new_path"` 235 OldPath string `json:"old_path"` 236 AMode string `json:"a_mode"` 237 BMode string `json:"b_mode"` 238 NewFile bool `json:"new_file"` 239 RenamedFile bool `json:"renamed_file"` 240 DeletedFile bool `json:"deleted_file"` 241} 242 243func (d Diff) String() string { 244 return Stringify(d) 245} 246 247// GetCommitDiffOptions represents the available GetCommitDiff() options. 248// 249// GitLab API docs: 250// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit 251type GetCommitDiffOptions ListOptions 252 253// GetCommitDiff gets the diff of a commit in a project.. 254// 255// GitLab API docs: 256// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit 257func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) { 258 project, err := parseID(pid) 259 if err != nil { 260 return nil, nil, err 261 } 262 u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", url.QueryEscape(project), sha) 263 264 req, err := s.client.NewRequest("GET", u, opt, options) 265 if err != nil { 266 return nil, nil, err 267 } 268 269 var d []*Diff 270 resp, err := s.client.Do(req, &d) 271 if err != nil { 272 return nil, resp, err 273 } 274 275 return d, resp, err 276} 277 278// CommitComment represents a GitLab commit comment. 279// 280// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html 281type CommitComment struct { 282 Note string `json:"note"` 283 Path string `json:"path"` 284 Line int `json:"line"` 285 LineType string `json:"line_type"` 286 Author Author `json:"author"` 287} 288 289// Author represents a GitLab commit author 290type Author struct { 291 ID int `json:"id"` 292 Username string `json:"username"` 293 Email string `json:"email"` 294 Name string `json:"name"` 295 State string `json:"state"` 296 Blocked bool `json:"blocked"` 297 CreatedAt *time.Time `json:"created_at"` 298} 299 300func (c CommitComment) String() string { 301 return Stringify(c) 302} 303 304// GetCommitCommentsOptions represents the available GetCommitComments() options. 305// 306// GitLab API docs: 307// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit 308type GetCommitCommentsOptions ListOptions 309 310// GetCommitComments gets the comments of a commit in a project. 311// 312// GitLab API docs: 313// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit 314func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) { 315 project, err := parseID(pid) 316 if err != nil { 317 return nil, nil, err 318 } 319 u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha) 320 321 req, err := s.client.NewRequest("GET", u, opt, options) 322 if err != nil { 323 return nil, nil, err 324 } 325 326 var c []*CommitComment 327 resp, err := s.client.Do(req, &c) 328 if err != nil { 329 return nil, resp, err 330 } 331 332 return c, resp, err 333} 334 335// PostCommitCommentOptions represents the available PostCommitComment() 336// options. 337// 338// GitLab API docs: 339// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit 340type PostCommitCommentOptions struct { 341 Note *string `url:"note,omitempty" json:"note,omitempty"` 342 Path *string `url:"path" json:"path"` 343 Line *int `url:"line" json:"line"` 344 LineType *string `url:"line_type" json:"line_type"` 345} 346 347// PostCommitComment adds a comment to a commit. Optionally you can post 348// comments on a specific line of a commit. Therefor both path, line_new and 349// line_old are required. 350// 351// GitLab API docs: 352// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit 353func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) { 354 project, err := parseID(pid) 355 if err != nil { 356 return nil, nil, err 357 } 358 u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", url.QueryEscape(project), sha) 359 360 req, err := s.client.NewRequest("POST", u, opt, options) 361 if err != nil { 362 return nil, nil, err 363 } 364 365 c := new(CommitComment) 366 resp, err := s.client.Do(req, c) 367 if err != nil { 368 return nil, resp, err 369 } 370 371 return c, resp, err 372} 373 374// GetCommitStatusesOptions represents the available GetCommitStatuses() options. 375// 376// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 377type GetCommitStatusesOptions struct { 378 ListOptions 379 Ref *string `url:"ref,omitempty" json:"ref,omitempty"` 380 Stage *string `url:"stage,omitempty" json:"stage,omitempty"` 381 Name *string `url:"name,omitempty" json:"name,omitempty"` 382 All *bool `url:"all,omitempty" json:"all,omitempty"` 383} 384 385// CommitStatus represents a GitLab commit status. 386// 387// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 388type CommitStatus struct { 389 ID int `json:"id"` 390 SHA string `json:"sha"` 391 Ref string `json:"ref"` 392 Status string `json:"status"` 393 Name string `json:"name"` 394 TargetURL string `json:"target_url"` 395 Description string `json:"description"` 396 CreatedAt *time.Time `json:"created_at"` 397 StartedAt *time.Time `json:"started_at"` 398 FinishedAt *time.Time `json:"finished_at"` 399 Author Author `json:"author"` 400} 401 402// GetCommitStatuses gets the statuses of a commit in a project. 403// 404// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit 405func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) { 406 project, err := parseID(pid) 407 if err != nil { 408 return nil, nil, err 409 } 410 u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", url.QueryEscape(project), sha) 411 412 req, err := s.client.NewRequest("GET", u, opt, options) 413 if err != nil { 414 return nil, nil, err 415 } 416 417 var cs []*CommitStatus 418 resp, err := s.client.Do(req, &cs) 419 if err != nil { 420 return nil, resp, err 421 } 422 423 return cs, resp, err 424} 425 426// SetCommitStatusOptions represents the available SetCommitStatus() options. 427// 428// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit 429type SetCommitStatusOptions struct { 430 State BuildStateValue `url:"state" json:"state"` 431 Ref *string `url:"ref,omitempty" json:"ref,omitempty"` 432 Name *string `url:"name,omitempty" json:"name,omitempty"` 433 Context *string `url:"context,omitempty" json:"context,omitempty"` 434 TargetURL *string `url:"target_url,omitempty" json:"target_url,omitempty"` 435 Description *string `url:"description,omitempty" json:"description,omitempty"` 436} 437 438// SetCommitStatus sets the status of a commit in a project. 439// 440// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit 441func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) { 442 project, err := parseID(pid) 443 if err != nil { 444 return nil, nil, err 445 } 446 u := fmt.Sprintf("projects/%s/statuses/%s", url.QueryEscape(project), sha) 447 448 req, err := s.client.NewRequest("POST", u, opt, options) 449 if err != nil { 450 return nil, nil, err 451 } 452 453 var cs *CommitStatus 454 resp, err := s.client.Do(req, &cs) 455 if err != nil { 456 return nil, resp, err 457 } 458 459 return cs, resp, err 460} 461 462// GetMergeRequestsByCommit gets merge request associated with a commit. 463// 464// GitLab API docs: 465// https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit 466func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...OptionFunc) ([]*MergeRequest, *Response, error) { 467 project, err := parseID(pid) 468 if err != nil { 469 return nil, nil, err 470 } 471 u := fmt.Sprintf("projects/%s/repository/commits/%s/merge_requests", 472 url.QueryEscape(project), url.QueryEscape(sha)) 473 474 req, err := s.client.NewRequest("GET", u, nil, options) 475 if err != nil { 476 return nil, nil, err 477 } 478 479 var mrs []*MergeRequest 480 resp, err := s.client.Do(req, &mrs) 481 if err != nil { 482 return nil, resp, err 483 } 484 485 return mrs, resp, err 486} 487 488// CherryPickCommitOptions represents the available options for cherry-picking a commit. 489// 490// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit 491type CherryPickCommitOptions struct { 492 TargetBranch *string `url:"branch" json:"branch,omitempty"` 493} 494 495// CherryPickCommit sherry picks a commit to a given branch. 496// 497// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit 498func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) { 499 project, err := parseID(pid) 500 if err != nil { 501 return nil, nil, err 502 } 503 u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick", 504 url.QueryEscape(project), url.QueryEscape(sha)) 505 506 req, err := s.client.NewRequest("POST", u, opt, options) 507 if err != nil { 508 return nil, nil, err 509 } 510 511 var c *Commit 512 resp, err := s.client.Do(req, &c) 513 if err != nil { 514 return nil, resp, err 515 } 516 517 return c, resp, err 518} 519