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	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}