commits.go

  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	if sha == "" {
180		return nil, nil, fmt.Errorf("SHA must be a non-empty string")
181	}
182	u := fmt.Sprintf("projects/%s/repository/commits/%s", pathEscape(project), url.PathEscape(sha))
183
184	req, err := s.client.NewRequest("GET", u, nil, options)
185	if err != nil {
186		return nil, nil, err
187	}
188
189	c := new(Commit)
190	resp, err := s.client.Do(req, c)
191	if err != nil {
192		return nil, resp, err
193	}
194
195	return c, resp, err
196}
197
198// CreateCommitOptions represents the available options for a new commit.
199//
200// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
201type CreateCommitOptions struct {
202	Branch        *string         `url:"branch" json:"branch"`
203	CommitMessage *string         `url:"commit_message" json:"commit_message"`
204	StartBranch   *string         `url:"start_branch,omitempty" json:"start_branch,omitempty"`
205	StartSHA      *string         `url:"start_sha,omitempty" json:"start_sha,omitempty"`
206	StartProject  *string         `url:"start_project,omitempty" json:"start_project,omitempty"`
207	Actions       []*CommitAction `url:"actions" json:"actions"`
208	AuthorEmail   *string         `url:"author_email,omitempty" json:"author_email,omitempty"`
209	AuthorName    *string         `url:"author_name,omitempty" json:"author_name,omitempty"`
210	Stats         *bool           `url:"stats,omitempty" json:"stats,omitempty"`
211	Force         *bool           `url:"force,omitempty" json:"force,omitempty"`
212}
213
214// CreateCommit creates a commit with multiple files and actions.
215//
216// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
217func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
218	project, err := parseID(pid)
219	if err != nil {
220		return nil, nil, err
221	}
222	u := fmt.Sprintf("projects/%s/repository/commits", pathEscape(project))
223
224	req, err := s.client.NewRequest("POST", u, opt, options)
225	if err != nil {
226		return nil, nil, err
227	}
228
229	c := new(Commit)
230	resp, err := s.client.Do(req, &c)
231	if err != nil {
232		return nil, resp, err
233	}
234
235	return c, resp, err
236}
237
238// Diff represents a GitLab diff.
239//
240// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
241type Diff struct {
242	Diff        string `json:"diff"`
243	NewPath     string `json:"new_path"`
244	OldPath     string `json:"old_path"`
245	AMode       string `json:"a_mode"`
246	BMode       string `json:"b_mode"`
247	NewFile     bool   `json:"new_file"`
248	RenamedFile bool   `json:"renamed_file"`
249	DeletedFile bool   `json:"deleted_file"`
250}
251
252func (d Diff) String() string {
253	return Stringify(d)
254}
255
256// GetCommitDiffOptions represents the available GetCommitDiff() options.
257//
258// GitLab API docs:
259// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
260type GetCommitDiffOptions ListOptions
261
262// GetCommitDiff gets the diff of a commit in a project..
263//
264// GitLab API docs:
265// https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit
266func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) {
267	project, err := parseID(pid)
268	if err != nil {
269		return nil, nil, err
270	}
271	u := fmt.Sprintf("projects/%s/repository/commits/%s/diff", pathEscape(project), url.PathEscape(sha))
272
273	req, err := s.client.NewRequest("GET", u, opt, options)
274	if err != nil {
275		return nil, nil, err
276	}
277
278	var d []*Diff
279	resp, err := s.client.Do(req, &d)
280	if err != nil {
281		return nil, resp, err
282	}
283
284	return d, resp, err
285}
286
287// CommitComment represents a GitLab commit comment.
288//
289// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html
290type CommitComment struct {
291	Note     string `json:"note"`
292	Path     string `json:"path"`
293	Line     int    `json:"line"`
294	LineType string `json:"line_type"`
295	Author   Author `json:"author"`
296}
297
298// Author represents a GitLab commit author
299type Author struct {
300	ID        int        `json:"id"`
301	Username  string     `json:"username"`
302	Email     string     `json:"email"`
303	Name      string     `json:"name"`
304	State     string     `json:"state"`
305	Blocked   bool       `json:"blocked"`
306	CreatedAt *time.Time `json:"created_at"`
307}
308
309func (c CommitComment) String() string {
310	return Stringify(c)
311}
312
313// GetCommitCommentsOptions represents the available GetCommitComments() options.
314//
315// GitLab API docs:
316// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
317type GetCommitCommentsOptions ListOptions
318
319// GetCommitComments gets the comments of a commit in a project.
320//
321// GitLab API docs:
322// https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit
323func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) {
324	project, err := parseID(pid)
325	if err != nil {
326		return nil, nil, err
327	}
328	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", pathEscape(project), url.PathEscape(sha))
329
330	req, err := s.client.NewRequest("GET", u, opt, options)
331	if err != nil {
332		return nil, nil, err
333	}
334
335	var c []*CommitComment
336	resp, err := s.client.Do(req, &c)
337	if err != nil {
338		return nil, resp, err
339	}
340
341	return c, resp, err
342}
343
344// PostCommitCommentOptions represents the available PostCommitComment()
345// options.
346//
347// GitLab API docs:
348// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
349type PostCommitCommentOptions struct {
350	Note     *string `url:"note,omitempty" json:"note,omitempty"`
351	Path     *string `url:"path" json:"path"`
352	Line     *int    `url:"line" json:"line"`
353	LineType *string `url:"line_type" json:"line_type"`
354}
355
356// PostCommitComment adds a comment to a commit. Optionally you can post
357// comments on a specific line of a commit. Therefor both path, line_new and
358// line_old are required.
359//
360// GitLab API docs:
361// https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit
362func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) {
363	project, err := parseID(pid)
364	if err != nil {
365		return nil, nil, err
366	}
367	u := fmt.Sprintf("projects/%s/repository/commits/%s/comments", pathEscape(project), url.PathEscape(sha))
368
369	req, err := s.client.NewRequest("POST", u, opt, options)
370	if err != nil {
371		return nil, nil, err
372	}
373
374	c := new(CommitComment)
375	resp, err := s.client.Do(req, c)
376	if err != nil {
377		return nil, resp, err
378	}
379
380	return c, resp, err
381}
382
383// GetCommitStatusesOptions represents the available GetCommitStatuses() options.
384//
385// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
386type GetCommitStatusesOptions struct {
387	ListOptions
388	Ref   *string `url:"ref,omitempty" json:"ref,omitempty"`
389	Stage *string `url:"stage,omitempty" json:"stage,omitempty"`
390	Name  *string `url:"name,omitempty" json:"name,omitempty"`
391	All   *bool   `url:"all,omitempty" json:"all,omitempty"`
392}
393
394// CommitStatus represents a GitLab commit status.
395//
396// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
397type CommitStatus struct {
398	ID          int        `json:"id"`
399	SHA         string     `json:"sha"`
400	Ref         string     `json:"ref"`
401	Status      string     `json:"status"`
402	Name        string     `json:"name"`
403	TargetURL   string     `json:"target_url"`
404	Description string     `json:"description"`
405	CreatedAt   *time.Time `json:"created_at"`
406	StartedAt   *time.Time `json:"started_at"`
407	FinishedAt  *time.Time `json:"finished_at"`
408	Author      Author     `json:"author"`
409}
410
411// GetCommitStatuses gets the statuses of a commit in a project.
412//
413// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit
414func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) {
415	project, err := parseID(pid)
416	if err != nil {
417		return nil, nil, err
418	}
419	u := fmt.Sprintf("projects/%s/repository/commits/%s/statuses", pathEscape(project), url.PathEscape(sha))
420
421	req, err := s.client.NewRequest("GET", u, opt, options)
422	if err != nil {
423		return nil, nil, err
424	}
425
426	var cs []*CommitStatus
427	resp, err := s.client.Do(req, &cs)
428	if err != nil {
429		return nil, resp, err
430	}
431
432	return cs, resp, err
433}
434
435// SetCommitStatusOptions represents the available SetCommitStatus() options.
436//
437// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
438type SetCommitStatusOptions struct {
439	State       BuildStateValue `url:"state" json:"state"`
440	Ref         *string         `url:"ref,omitempty" json:"ref,omitempty"`
441	Name        *string         `url:"name,omitempty" json:"name,omitempty"`
442	Context     *string         `url:"context,omitempty" json:"context,omitempty"`
443	TargetURL   *string         `url:"target_url,omitempty" json:"target_url,omitempty"`
444	Description *string         `url:"description,omitempty" json:"description,omitempty"`
445}
446
447// SetCommitStatus sets the status of a commit in a project.
448//
449// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit
450func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) {
451	project, err := parseID(pid)
452	if err != nil {
453		return nil, nil, err
454	}
455	u := fmt.Sprintf("projects/%s/statuses/%s", pathEscape(project), url.PathEscape(sha))
456
457	req, err := s.client.NewRequest("POST", u, opt, options)
458	if err != nil {
459		return nil, nil, err
460	}
461
462	cs := new(CommitStatus)
463	resp, err := s.client.Do(req, &cs)
464	if err != nil {
465		return nil, resp, err
466	}
467
468	return cs, resp, err
469}
470
471// GetMergeRequestsByCommit gets merge request associated with a commit.
472//
473// GitLab API docs:
474// https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit
475func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
476	project, err := parseID(pid)
477	if err != nil {
478		return nil, nil, err
479	}
480	u := fmt.Sprintf("projects/%s/repository/commits/%s/merge_requests", pathEscape(project), url.PathEscape(sha))
481
482	req, err := s.client.NewRequest("GET", u, nil, options)
483	if err != nil {
484		return nil, nil, err
485	}
486
487	var mrs []*MergeRequest
488	resp, err := s.client.Do(req, &mrs)
489	if err != nil {
490		return nil, resp, err
491	}
492
493	return mrs, resp, err
494}
495
496// CherryPickCommitOptions represents the available CherryPickCommit() options.
497//
498// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
499type CherryPickCommitOptions struct {
500	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
501}
502
503// CherryPickCommit cherry picks a commit to a given branch.
504//
505// GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
506func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
507	project, err := parseID(pid)
508	if err != nil {
509		return nil, nil, err
510	}
511	u := fmt.Sprintf("projects/%s/repository/commits/%s/cherry_pick", pathEscape(project), url.PathEscape(sha))
512
513	req, err := s.client.NewRequest("POST", u, opt, options)
514	if err != nil {
515		return nil, nil, err
516	}
517
518	c := new(Commit)
519	resp, err := s.client.Do(req, &c)
520	if err != nil {
521		return nil, resp, err
522	}
523
524	return c, resp, err
525}
526
527// RevertCommitOptions represents the available RevertCommit() options.
528//
529// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
530type RevertCommitOptions struct {
531	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
532}
533
534// RevertCommit reverts a commit in a given branch.
535//
536// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
537func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
538	project, err := parseID(pid)
539	if err != nil {
540		return nil, nil, err
541	}
542	u := fmt.Sprintf("projects/%s/repository/commits/%s/revert", pathEscape(project), url.PathEscape(sha))
543
544	req, err := s.client.NewRequest("POST", u, opt, options)
545	if err != nil {
546		return nil, nil, err
547	}
548
549	c := new(Commit)
550	resp, err := s.client.Do(req, &c)
551	if err != nil {
552		return nil, resp, err
553	}
554
555	return c, resp, err
556}
557
558// GPGSignature represents a Gitlab commit's GPG Signature.
559//
560// GitLab API docs:
561// https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
562type GPGSignature struct {
563	KeyID              int    `json:"gpg_key_id"`
564	KeyPrimaryKeyID    string `json:"gpg_key_primary_keyid"`
565	KeyUserName        string `json:"gpg_key_user_name"`
566	KeyUserEmail       string `json:"gpg_key_user_email"`
567	VerificationStatus string `json:"verification_status"`
568	KeySubkeyID        int    `json:"gpg_key_subkey_id"`
569}
570
571// GetGPGSiganature gets a GPG signature of a commit.
572//
573// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit
574func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...OptionFunc) (*GPGSignature, *Response, error) {
575	project, err := parseID(pid)
576	if err != nil {
577		return nil, nil, err
578	}
579	u := fmt.Sprintf("projects/%s/repository/commits/%s/signature", pathEscape(project), url.PathEscape(sha))
580
581	req, err := s.client.NewRequest("GET", u, nil, options)
582	if err != nil {
583		return nil, nil, err
584	}
585
586	sig := new(GPGSignature)
587	resp, err := s.client.Do(req, &sig)
588	if err != nil {
589		return nil, resp, err
590	}
591
592	return sig, resp, err
593}