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 LastPipeline *PipelineInfo `json:"last_pipeline"`
52 ProjectID int `json:"project_id"`
53}
54
55// CommitStats represents the number of added and deleted files in a commit.
56//
57// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
58type CommitStats struct {
59 Additions int `json:"additions"`
60 Deletions int `json:"deletions"`
61 Total int `json:"total"`
62}
63
64func (c Commit) String() string {
65 return Stringify(c)
66}
67
68// ListCommitsOptions represents the available ListCommits() options.
69//
70// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits
71type ListCommitsOptions struct {
72 ListOptions
73 RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"`
74 Since *time.Time `url:"since,omitempty" json:"since,omitempty"`
75 Until *time.Time `url:"until,omitempty" json:"until,omitempty"`
76 Path *string `url:"path,omitempty" json:"path,omitempty"`
77 All *bool `url:"all,omitempty" json:"all,omitempty"`
78 WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"`
79}
80
81// ListCommits gets a list of repository commits in a project.
82//
83// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits
84func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
85 project, err := parseID(pid)
86 if err != nil {
87 return nil, nil, err
88 }
89 u := fmt.Sprintf("projects/%s/repository/commits", pathEscape(project))
90
91 req, err := s.client.NewRequest("GET", u, opt, options)
92 if err != nil {
93 return nil, nil, err
94 }
95
96 var c []*Commit
97 resp, err := s.client.Do(req, &c)
98 if err != nil {
99 return nil, resp, err
100 }
101
102 return c, resp, err
103}
104
105// FileAction represents the available actions that can be performed on a file.
106//
107// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
108type FileAction string
109
110// The available file actions.
111const (
112 FileCreate FileAction = "create"
113 FileDelete FileAction = "delete"
114 FileMove FileAction = "move"
115 FileUpdate FileAction = "update"
116)
117
118// CommitAction represents a single file action within a commit.
119type CommitAction struct {
120 Action FileAction `url:"action" json:"action"`
121 FilePath string `url:"file_path" json:"file_path"`
122 PreviousPath string `url:"previous_path,omitempty" json:"previous_path,omitempty"`
123 Content string `url:"content,omitempty" json:"content,omitempty"`
124 Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"`
125}
126
127// CommitRef represents the reference of branches/tags in a commit.
128//
129// GitLab API docs:
130// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to
131type CommitRef struct {
132 Type string `json:"type"`
133 Name string `json:"name"`
134}
135
136// GetCommitRefsOptions represents the available GetCommitRefs() options.
137//
138// GitLab API docs:
139// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to
140type GetCommitRefsOptions struct {
141 ListOptions
142 Type *string `url:"type,omitempty" json:"type,omitempty"`
143}
144
145// GetCommitRefs gets all references (from branches or tags) a commit is pushed to
146//
147// GitLab API docs:
148// https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to
149func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...OptionFunc) ([]*CommitRef, *Response, error) {
150 project, err := parseID(pid)
151 if err != nil {
152 return nil, nil, err
153 }
154 u := fmt.Sprintf("projects/%s/repository/commits/%s/refs", pathEscape(project), url.PathEscape(sha))
155
156 req, err := s.client.NewRequest("GET", u, opt, options)
157 if err != nil {
158 return nil, nil, err
159 }
160
161 var cs []*CommitRef
162 resp, err := s.client.Do(req, &cs)
163 if err != nil {
164 return nil, resp, err
165 }
166
167 return cs, resp, err
168}
169
170// GetCommit gets a specific commit identified by the commit hash or name of a
171// branch or tag.
172//
173// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit
174func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) {
175 project, err := parseID(pid)
176 if err != nil {
177 return nil, nil, err
178 }
179 u := fmt.Sprintf("projects/%s/repository/commits/%s", pathEscape(project), url.PathEscape(sha))
180
181 req, err := s.client.NewRequest("GET", u, nil, options)
182 if err != nil {
183 return nil, nil, err
184 }
185
186 c := new(Commit)
187 resp, err := s.client.Do(req, c)
188 if err != nil {
189 return nil, resp, err
190 }
191
192 return c, resp, err
193}
194
195// CreateCommitOptions represents the available options for a new commit.
196//
197// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
198type CreateCommitOptions struct {
199 Branch *string `url:"branch" json:"branch"`
200 CommitMessage *string `url:"commit_message" json:"commit_message"`
201 StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"`
202 Actions []*CommitAction `url:"actions" json:"actions"`
203 AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"`
204 AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"`
205}
206
207// CreateCommit creates a commit with multiple files and actions.
208//
209// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
210func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
211 project, err := parseID(pid)
212 if err != nil {
213 return nil, nil, err
214 }
215 u := fmt.Sprintf("projects/%s/repository/commits", pathEscape(project))
216
217 req, err := s.client.NewRequest("POST", u, opt, options)
218 if err != nil {
219 return nil, nil, err
220 }
221
222 c := new(Commit)
223 resp, err := s.client.Do(req, &c)
224 if err != nil {
225 return nil, resp, err
226 }
227
228 return c, resp, err
229}
230
231// Diff represents a GitLab diff.
232//
233// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
234type Diff struct {
235 Diff string `json:"diff"`
236 NewPath string `json:"new_path"`
237 OldPath string `json:"old_path"`
238 AMode string `json:"a_mode"`
239 BMode string `json:"b_mode"`
240 NewFile bool `json:"new_file"`
241 RenamedFile bool `json:"renamed_file"`
242 DeletedFile bool `json:"deleted_file"`
243}
244
245func (d Diff) String() string {
246 return Stringify(d)
247}
248
249// GetCommitDiffOptions represents the available GetCommitDiff() options.
250//
251// GitLab API docs:
252// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
253type GetCommitDiffOptions ListOptions
254
255// GetCommitDiff gets the diff of a commit in a project..
256//
257// GitLab API docs:
258// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
259func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) {
260 project, err := parseID(pid)
261 if err != nil {
262 return nil, nil, err
263 }
264 u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", pathEscape(project), url.PathEscape(sha))
265
266 req, err := s.client.NewRequest("GET", u, opt, options)
267 if err != nil {
268 return nil, nil, err
269 }
270
271 var d []*Diff
272 resp, err := s.client.Do(req, &d)
273 if err != nil {
274 return nil, resp, err
275 }
276
277 return d, resp, err
278}
279
280// CommitComment represents a GitLab commit comment.
281//
282// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
283type CommitComment struct {
284 Note string `json:"note"`
285 Path string `json:"path"`
286 Line int `json:"line"`
287 LineType string `json:"line_type"`
288 Author Author `json:"author"`
289}
290
291// Author represents a GitLab commit author
292type Author struct {
293 ID int `json:"id"`
294 Username string `json:"username"`
295 Email string `json:"email"`
296 Name string `json:"name"`
297 State string `json:"state"`
298 Blocked bool `json:"blocked"`
299 CreatedAt *time.Time `json:"created_at"`
300}
301
302func (c CommitComment) String() string {
303 return Stringify(c)
304}
305
306// GetCommitCommentsOptions represents the available GetCommitComments() options.
307//
308// GitLab API docs:
309// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
310type GetCommitCommentsOptions ListOptions
311
312// GetCommitComments gets the comments of a commit in a project.
313//
314// GitLab API docs:
315// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
316func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) {
317 project, err := parseID(pid)
318 if err != nil {
319 return nil, nil, err
320 }
321 u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", pathEscape(project), url.PathEscape(sha))
322
323 req, err := s.client.NewRequest("GET", u, opt, options)
324 if err != nil {
325 return nil, nil, err
326 }
327
328 var c []*CommitComment
329 resp, err := s.client.Do(req, &c)
330 if err != nil {
331 return nil, resp, err
332 }
333
334 return c, resp, err
335}
336
337// PostCommitCommentOptions represents the available PostCommitComment()
338// options.
339//
340// GitLab API docs:
341// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
342type PostCommitCommentOptions struct {
343 Note *string `url:"note,omitempty" json:"note,omitempty"`
344 Path *string `url:"path" json:"path"`
345 Line *int `url:"line" json:"line"`
346 LineType *string `url:"line_type" json:"line_type"`
347}
348
349// PostCommitComment adds a comment to a commit. Optionally you can post
350// comments on a specific line of a commit. Therefor both path, line_new and
351// line_old are required.
352//
353// GitLab API docs:
354// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
355func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
356 project, err := parseID(pid)
357 if err != nil {
358 return nil, nil, err
359 }
360 u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", pathEscape(project), url.PathEscape(sha))
361
362 req, err := s.client.NewRequest("POST", u, opt, options)
363 if err != nil {
364 return nil, nil, err
365 }
366
367 c := new(CommitComment)
368 resp, err := s.client.Do(req, c)
369 if err != nil {
370 return nil, resp, err
371 }
372
373 return c, resp, err
374}
375
376// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
377//
378// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
379type GetCommitStatusesOptions struct {
380 ListOptions
381 Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
382 Stage *string `url:"stage,omitempty" json:"stage,omitempty"`
383 Name *string `url:"name,omitempty" json:"name,omitempty"`
384 All *bool `url:"all,omitempty" json:"all,omitempty"`
385}
386
387// CommitStatus represents a GitLab commit status.
388//
389// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
390type CommitStatus struct {
391 ID int `json:"id"`
392 SHA string `json:"sha"`
393 Ref string `json:"ref"`
394 Status string `json:"status"`
395 Name string `json:"name"`
396 TargetURL string `json:"target_url"`
397 Description string `json:"description"`
398 CreatedAt *time.Time `json:"created_at"`
399 StartedAt *time.Time `json:"started_at"`
400 FinishedAt *time.Time `json:"finished_at"`
401 Author Author `json:"author"`
402}
403
404// GetCommitStatuses gets the statuses of a commit in a project.
405//
406// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
407func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
408 project, err := parseID(pid)
409 if err != nil {
410 return nil, nil, err
411 }
412 u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", pathEscape(project), url.PathEscape(sha))
413
414 req, err := s.client.NewRequest("GET", u, opt, options)
415 if err != nil {
416 return nil, nil, err
417 }
418
419 var cs []*CommitStatus
420 resp, err := s.client.Do(req, &cs)
421 if err != nil {
422 return nil, resp, err
423 }
424
425 return cs, resp, err
426}
427
428// SetCommitStatusOptions represents the available SetCommitStatus() options.
429//
430// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
431type SetCommitStatusOptions struct {
432 State BuildStateValue `url:"state" json:"state"`
433 Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
434 Name *string `url:"name,omitempty" json:"name,omitempty"`
435 Context *string `url:"context,omitempty" json:"context,omitempty"`
436 TargetURL *string `url:"target_url,omitempty" json:"target_url,omitempty"`
437 Description *string `url:"description,omitempty" json:"description,omitempty"`
438}
439
440// SetCommitStatus sets the status of a commit in a project.
441//
442// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
443func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
444 project, err := parseID(pid)
445 if err != nil {
446 return nil, nil, err
447 }
448 u := fmt.Sprintf("projects/%s/statuses/%s", pathEscape(project), url.PathEscape(sha))
449
450 req, err := s.client.NewRequest("POST", u, opt, options)
451 if err != nil {
452 return nil, nil, err
453 }
454
455 cs := new(CommitStatus)
456 resp, err := s.client.Do(req, &cs)
457 if err != nil {
458 return nil, resp, err
459 }
460
461 return cs, resp, err
462}
463
464// GetMergeRequestsByCommit gets merge request associated with a commit.
465//
466// GitLab API docs:
467// https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit
468func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
469 project, err := parseID(pid)
470 if err != nil {
471 return nil, nil, err
472 }
473 u := fmt.Sprintf("projects/%s/repository/commits/%s/merge_requests", pathEscape(project), url.PathEscape(sha))
474
475 req, err := s.client.NewRequest("GET", u, nil, options)
476 if err != nil {
477 return nil, nil, err
478 }
479
480 var mrs []*MergeRequest
481 resp, err := s.client.Do(req, &mrs)
482 if err != nil {
483 return nil, resp, err
484 }
485
486 return mrs, resp, err
487}
488
489// CherryPickCommitOptions represents the available CherryPickCommit() options.
490//
491// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
492type CherryPickCommitOptions struct {
493 Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
494}
495
496// CherryPickCommit cherry picks a commit to a given branch.
497//
498// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
499func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
500 project, err := parseID(pid)
501 if err != nil {
502 return nil, nil, err
503 }
504 u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick", pathEscape(project), url.PathEscape(sha))
505
506 req, err := s.client.NewRequest("POST", u, opt, options)
507 if err != nil {
508 return nil, nil, err
509 }
510
511 c := new(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
520// RevertCommitOptions represents the available RevertCommit() options.
521//
522// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
523type RevertCommitOptions struct {
524 Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
525}
526
527// RevertCommit reverts a commit in a given branch.
528//
529// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
530func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
531 project, err := parseID(pid)
532 if err != nil {
533 return nil, nil, err
534 }
535 u := fmt.Sprintf("projects/%s/repository/commits/%s/revert", pathEscape(project), url.PathEscape(sha))
536
537 req, err := s.client.NewRequest("POST", u, opt, options)
538 if err != nil {
539 return nil, nil, err
540 }
541
542 c := new(Commit)
543 resp, err := s.client.Do(req, &c)
544 if err != nil {
545 return nil, resp, err
546 }
547
548 return c, resp, err
549}
550
551// GPGSignature represents a Gitlab commit's GPG Signature.
552//
553// GitLab API docs:
554// https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
555type GPGSignature struct {
556 KeyID int `json:"gpg_key_id"`
557 KeyPrimaryKeyID string `json:"gpg_key_primary_keyid"`
558 KeyUserName string `json:"gpg_key_user_name"`
559 KeyUserEmail string `json:"gpg_key_user_email"`
560 VerificationStatus string `json:"verification_status"`
561 KeySubkeyID int `json:"gpg_key_subkey_id"`
562}
563
564// GetGPGSiganature gets a GPG signature of a commit.
565//
566// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
567func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...OptionFunc) (*GPGSignature, *Response, error) {
568 project, err := parseID(pid)
569 if err != nil {
570 return nil, nil, err
571 }
572 u := fmt.Sprintf("projects/%s/repository/commits/%s/signature", pathEscape(project), url.PathEscape(sha))
573
574 req, err := s.client.NewRequest("GET", u, nil, options)
575 if err != nil {
576 return nil, nil, err
577 }
578
579 sig := new(GPGSignature)
580 resp, err := s.client.Do(req, &sig)
581 if err != nil {
582 return nil, resp, err
583 }
584
585 return sig, resp, err
586}