merge_requests.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	"time"
 22)
 23
 24// MergeRequestsService handles communication with the merge requests related
 25// methods of the GitLab API.
 26//
 27// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
 28type MergeRequestsService struct {
 29	client    *Client
 30	timeStats *timeStatsService
 31}
 32
 33// MergeRequest represents a GitLab merge request.
 34//
 35// GitLab API docs: https://docs.gitlab.com/ce/api/merge_requests.html
 36type MergeRequest struct {
 37	ID                        int          `json:"id"`
 38	IID                       int          `json:"iid"`
 39	TargetBranch              string       `json:"target_branch"`
 40	SourceBranch              string       `json:"source_branch"`
 41	ProjectID                 int          `json:"project_id"`
 42	Title                     string       `json:"title"`
 43	State                     string       `json:"state"`
 44	CreatedAt                 *time.Time   `json:"created_at"`
 45	UpdatedAt                 *time.Time   `json:"updated_at"`
 46	Upvotes                   int          `json:"upvotes"`
 47	Downvotes                 int          `json:"downvotes"`
 48	Author                    *BasicUser   `json:"author"`
 49	Assignee                  *BasicUser   `json:"assignee"`
 50	Assignees                 []*BasicUser `json:"assignees"`
 51	SourceProjectID           int          `json:"source_project_id"`
 52	TargetProjectID           int          `json:"target_project_id"`
 53	Labels                    Labels       `json:"labels"`
 54	Description               string       `json:"description"`
 55	WorkInProgress            bool         `json:"work_in_progress"`
 56	Milestone                 *Milestone   `json:"milestone"`
 57	MergeWhenPipelineSucceeds bool         `json:"merge_when_pipeline_succeeds"`
 58	MergeStatus               string       `json:"merge_status"`
 59	MergeError                string       `json:"merge_error"`
 60	MergedBy                  *BasicUser   `json:"merged_by"`
 61	MergedAt                  *time.Time   `json:"merged_at"`
 62	ClosedBy                  *BasicUser   `json:"closed_by"`
 63	ClosedAt                  *time.Time   `json:"closed_at"`
 64	Subscribed                bool         `json:"subscribed"`
 65	SHA                       string       `json:"sha"`
 66	MergeCommitSHA            string       `json:"merge_commit_sha"`
 67	UserNotesCount            int          `json:"user_notes_count"`
 68	ChangesCount              string       `json:"changes_count"`
 69	ShouldRemoveSourceBranch  bool         `json:"should_remove_source_branch"`
 70	ForceRemoveSourceBranch   bool         `json:"force_remove_source_branch"`
 71	WebURL                    string       `json:"web_url"`
 72	DiscussionLocked          bool         `json:"discussion_locked"`
 73	Changes                   []struct {
 74		OldPath     string `json:"old_path"`
 75		NewPath     string `json:"new_path"`
 76		AMode       string `json:"a_mode"`
 77		BMode       string `json:"b_mode"`
 78		Diff        string `json:"diff"`
 79		NewFile     bool   `json:"new_file"`
 80		RenamedFile bool   `json:"renamed_file"`
 81		DeletedFile bool   `json:"deleted_file"`
 82	} `json:"changes"`
 83	TimeStats    *TimeStats    `json:"time_stats"`
 84	Squash       bool          `json:"squash"`
 85	Pipeline     *PipelineInfo `json:"pipeline"`
 86	HeadPipeline *Pipeline     `json:"head_pipeline"`
 87	DiffRefs     struct {
 88		BaseSha  string `json:"base_sha"`
 89		HeadSha  string `json:"head_sha"`
 90		StartSha string `json:"start_sha"`
 91	} `json:"diff_refs"`
 92	DivergedCommitsCount int    `json:"diverged_commits_count"`
 93	RebaseInProgress     bool   `json:"rebase_in_progress"`
 94	ApprovalsBeforeMerge int    `json:"approvals_before_merge"`
 95	Reference            string `json:"reference"`
 96	TaskCompletionStatus struct {
 97		Count          int `json:"count"`
 98		CompletedCount int `json:"completed_count"`
 99	} `json:"task_completion_status"`
100}
101
102func (m MergeRequest) String() string {
103	return Stringify(m)
104}
105
106// MergeRequestDiffVersion represents Gitlab merge request version.
107//
108// Gitlab API docs:
109// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
110type MergeRequestDiffVersion struct {
111	ID             int        `json:"id"`
112	HeadCommitSHA  string     `json:"head_commit_sha,omitempty"`
113	BaseCommitSHA  string     `json:"base_commit_sha,omitempty"`
114	StartCommitSHA string     `json:"start_commit_sha,omitempty"`
115	CreatedAt      *time.Time `json:"created_at,omitempty"`
116	MergeRequestID int        `json:"merge_request_id,omitempty"`
117	State          string     `json:"state,omitempty"`
118	RealSize       string     `json:"real_size,omitempty"`
119	Commits        []*Commit  `json:"commits,omitempty"`
120	Diffs          []*Diff    `json:"diffs,omitempty"`
121}
122
123func (m MergeRequestDiffVersion) String() string {
124	return Stringify(m)
125}
126
127// ListMergeRequestsOptions represents the available ListMergeRequests()
128// options.
129//
130// GitLab API docs:
131// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
132type ListMergeRequestsOptions struct {
133	ListOptions
134	State           *string    `url:"state,omitempty" json:"state,omitempty"`
135	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
136	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
137	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
138	View            *string    `url:"view,omitempty" json:"view,omitempty"`
139	Labels          Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
140	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
141	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
142	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
143	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
144	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
145	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
146	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
147	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
148	SourceBranch    *string    `url:"source_branch,omitempty" json:"source_branch,omitempty"`
149	TargetBranch    *string    `url:"target_branch,omitempty" json:"target_branch,omitempty"`
150	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
151	In              *string    `url:"in,omitempty" json:"in,omitempty"`
152	WIP             *string    `url:"wip,omitempty" json:"wip,omitempty"`
153}
154
155// ListMergeRequests gets all merge requests. The state parameter can be used
156// to get only merge requests with a given state (opened, closed, or merged)
157// or all of them (all). The pagination parameters page and per_page can be
158// used to restrict the list of merge requests.
159//
160// GitLab API docs:
161// https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests
162func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
163	req, err := s.client.NewRequest("GET", "merge_requests", opt, options)
164	if err != nil {
165		return nil, nil, err
166	}
167
168	var m []*MergeRequest
169	resp, err := s.client.Do(req, &m)
170	if err != nil {
171		return nil, resp, err
172	}
173
174	return m, resp, err
175}
176
177// ListGroupMergeRequestsOptions represents the available ListGroupMergeRequests()
178// options.
179//
180// GitLab API docs:
181// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
182type ListGroupMergeRequestsOptions struct {
183	ListOptions
184	State           *string    `url:"state,omitempty" json:"state,omitempty"`
185	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
186	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
187	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
188	View            *string    `url:"view,omitempty" json:"view,omitempty"`
189	Labels          *Labels    `url:"labels,omitempty" json:"labels,omitempty"`
190	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
191	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
192	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
193	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
194	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
195	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
196	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
197	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
198	SourceBranch    *string    `url:"source_branch,omitempty" json:"source_branch,omitempty"`
199	TargetBranch    *string    `url:"target_branch,omitempty" json:"target_branch,omitempty"`
200	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
201}
202
203// ListGroupMergeRequests gets all merge requests for this group.
204//
205// GitLab API docs:
206// https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests
207func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
208	group, err := parseID(gid)
209	if err != nil {
210		return nil, nil, err
211	}
212	u := fmt.Sprintf("groups/%s/merge_requests", pathEscape(group))
213
214	req, err := s.client.NewRequest("GET", u, opt, options)
215	if err != nil {
216		return nil, nil, err
217	}
218
219	var m []*MergeRequest
220	resp, err := s.client.Do(req, &m)
221	if err != nil {
222		return nil, resp, err
223	}
224
225	return m, resp, err
226}
227
228// ListProjectMergeRequestsOptions represents the available ListMergeRequests()
229// options.
230//
231// GitLab API docs:
232// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
233type ListProjectMergeRequestsOptions struct {
234	ListOptions
235	IIDs            []int      `url:"iids[],omitempty" json:"iids,omitempty"`
236	State           *string    `url:"state,omitempty" json:"state,omitempty"`
237	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
238	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
239	Milestone       *string    `url:"milestone,omitempty" json:"milestone,omitempty"`
240	View            *string    `url:"view,omitempty" json:"view,omitempty"`
241	Labels          *Labels    `url:"labels,omitempty" json:"labels,omitempty"`
242	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
243	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
244	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
245	UpdatedBefore   *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
246	Scope           *string    `url:"scope,omitempty" json:"scope,omitempty"`
247	AuthorID        *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
248	AssigneeID      *int       `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
249	MyReactionEmoji *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
250	SourceBranch    *string    `url:"source_branch,omitempty" json:"source_branch,omitempty"`
251	TargetBranch    *string    `url:"target_branch,omitempty" json:"target_branch,omitempty"`
252	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
253	WIP             *string    `url:"wip,omitempty" json:"wip,omitempty"`
254}
255
256// ListProjectMergeRequests gets all merge requests for this project.
257//
258// GitLab API docs:
259// https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests
260func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) {
261	project, err := parseID(pid)
262	if err != nil {
263		return nil, nil, err
264	}
265	u := fmt.Sprintf("projects/%s/merge_requests", pathEscape(project))
266
267	req, err := s.client.NewRequest("GET", u, opt, options)
268	if err != nil {
269		return nil, nil, err
270	}
271
272	var m []*MergeRequest
273	resp, err := s.client.Do(req, &m)
274	if err != nil {
275		return nil, resp, err
276	}
277
278	return m, resp, err
279}
280
281// GetMergeRequestsOptions represents the available GetMergeRequests()
282// options.
283//
284// GitLab API docs:
285// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
286type GetMergeRequestsOptions struct {
287	RenderHTML                  *bool `url:"render_html,omitempty" json:"render_html,omitempty"`
288	IncludeDivergedCommitsCount *bool `url:"include_diverged_commits_count,omitempty" json:"include_diverged_commits_count,omitempty"`
289	IncludeRebaseInProgress     *bool `url:"include_rebase_in_progress,omitempty" json:"include_rebase_in_progress,omitempty"`
290}
291
292// GetMergeRequest shows information about a single merge request.
293//
294// GitLab API docs:
295// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr
296func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
297	project, err := parseID(pid)
298	if err != nil {
299		return nil, nil, err
300	}
301	u := fmt.Sprintf("projects/%s/merge_requests/%d", pathEscape(project), mergeRequest)
302
303	req, err := s.client.NewRequest("GET", u, opt, options)
304	if err != nil {
305		return nil, nil, err
306	}
307
308	m := new(MergeRequest)
309	resp, err := s.client.Do(req, m)
310	if err != nil {
311		return nil, resp, err
312	}
313
314	return m, resp, err
315}
316
317// GetMergeRequestApprovals gets information about a merge requests approvals
318//
319// GitLab API docs:
320// https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals
321func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) {
322	project, err := parseID(pid)
323	if err != nil {
324		return nil, nil, err
325	}
326	u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequest)
327
328	req, err := s.client.NewRequest("GET", u, nil, options)
329	if err != nil {
330		return nil, nil, err
331	}
332
333	a := new(MergeRequestApprovals)
334	resp, err := s.client.Do(req, a)
335	if err != nil {
336		return nil, resp, err
337	}
338
339	return a, resp, err
340}
341
342// GetMergeRequestCommitsOptions represents the available GetMergeRequestCommits()
343// options.
344//
345// GitLab API docs:
346// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
347type GetMergeRequestCommitsOptions ListOptions
348
349// GetMergeRequestCommits gets a list of merge request commits.
350//
351// GitLab API docs:
352// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits
353func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) {
354	project, err := parseID(pid)
355	if err != nil {
356		return nil, nil, err
357	}
358	u := fmt.Sprintf("projects/%s/merge_requests/%d/commits", pathEscape(project), mergeRequest)
359
360	req, err := s.client.NewRequest("GET", u, opt, options)
361	if err != nil {
362		return nil, nil, err
363	}
364
365	var c []*Commit
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// GetMergeRequestChanges shows information about the merge request including
375// its files and changes.
376//
377// GitLab API docs:
378// https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes
379func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
380	project, err := parseID(pid)
381	if err != nil {
382		return nil, nil, err
383	}
384	u := fmt.Sprintf("projects/%s/merge_requests/%d/changes", pathEscape(project), mergeRequest)
385
386	req, err := s.client.NewRequest("GET", u, nil, options)
387	if err != nil {
388		return nil, nil, err
389	}
390
391	m := new(MergeRequest)
392	resp, err := s.client.Do(req, m)
393	if err != nil {
394		return nil, resp, err
395	}
396
397	return m, resp, err
398}
399
400// ListMergeRequestPipelines gets all pipelines for the provided merge request.
401//
402// GitLab API docs:
403// https://docs.gitlab.com/ce/api/merge_requests.html#list-mr-pipelines
404func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...OptionFunc) ([]*PipelineInfo, *Response, error) {
405	project, err := parseID(pid)
406	if err != nil {
407		return nil, nil, err
408	}
409	u := fmt.Sprintf("projects/%s/merge_requests/%d/pipelines", pathEscape(project), mergeRequest)
410
411	req, err := s.client.NewRequest("GET", u, nil, options)
412	if err != nil {
413		return nil, nil, err
414	}
415
416	var p []*PipelineInfo
417	resp, err := s.client.Do(req, &p)
418	if err != nil {
419		return nil, resp, err
420	}
421
422	return p, resp, err
423}
424
425// GetIssuesClosedOnMergeOptions represents the available GetIssuesClosedOnMerge()
426// options.
427//
428// GitLab API docs:
429// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
430type GetIssuesClosedOnMergeOptions ListOptions
431
432// GetIssuesClosedOnMerge gets all the issues that would be closed by merging the
433// provided merge request.
434//
435// GitLab API docs:
436// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
437func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...OptionFunc) ([]*Issue, *Response, error) {
438	project, err := parseID(pid)
439	if err != nil {
440		return nil, nil, err
441	}
442	u := fmt.Sprintf("projects/%s/merge_requests/%d/closes_issues", pathEscape(project), mergeRequest)
443
444	req, err := s.client.NewRequest("GET", u, opt, options)
445	if err != nil {
446		return nil, nil, err
447	}
448
449	var i []*Issue
450	resp, err := s.client.Do(req, &i)
451	if err != nil {
452		return nil, resp, err
453	}
454
455	return i, resp, err
456}
457
458// CreateMergeRequestOptions represents the available CreateMergeRequest()
459// options.
460//
461// GitLab API docs:
462// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
463type CreateMergeRequestOptions struct {
464	Title              *string `url:"title,omitempty" json:"title,omitempty"`
465	Description        *string `url:"description,omitempty" json:"description,omitempty"`
466	SourceBranch       *string `url:"source_branch,omitempty" json:"source_branch,omitempty"`
467	TargetBranch       *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
468	Labels             *Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
469	AssigneeID         *int    `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
470	AssigneeIDs        []int   `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
471	TargetProjectID    *int    `url:"target_project_id,omitempty" json:"target_project_id,omitempty"`
472	MilestoneID        *int    `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
473	RemoveSourceBranch *bool   `url:"remove_source_branch,omitempty" json:"remove_source_branch,omitempty"`
474	Squash             *bool   `url:"squash,omitempty" json:"squash,omitempty"`
475	AllowCollaboration *bool   `url:"allow_collaboration,omitempty" json:"allow_collaboration,omitempty"`
476}
477
478// CreateMergeRequest creates a new merge request.
479//
480// GitLab API docs:
481// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr
482func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
483	project, err := parseID(pid)
484	if err != nil {
485		return nil, nil, err
486	}
487	u := fmt.Sprintf("projects/%s/merge_requests", pathEscape(project))
488
489	req, err := s.client.NewRequest("POST", u, opt, options)
490	if err != nil {
491		return nil, nil, err
492	}
493
494	m := new(MergeRequest)
495	resp, err := s.client.Do(req, m)
496	if err != nil {
497		return nil, resp, err
498	}
499
500	return m, resp, err
501}
502
503// UpdateMergeRequestOptions represents the available UpdateMergeRequest()
504// options.
505//
506// GitLab API docs:
507// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
508type UpdateMergeRequestOptions struct {
509	Title              *string `url:"title,omitempty" json:"title,omitempty"`
510	Description        *string `url:"description,omitempty" json:"description,omitempty"`
511	TargetBranch       *string `url:"target_branch,omitempty" json:"target_branch,omitempty"`
512	AssigneeID         *int    `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
513	AssigneeIDs        []int   `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
514	Labels             *Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
515	MilestoneID        *int    `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
516	StateEvent         *string `url:"state_event,omitempty" json:"state_event,omitempty"`
517	RemoveSourceBranch *bool   `url:"remove_source_branch,omitempty" json:"remove_source_branch,omitempty"`
518	Squash             *bool   `url:"squash,omitempty" json:"squash,omitempty"`
519	DiscussionLocked   *bool   `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
520	AllowCollaboration *bool   `url:"allow_collaboration,omitempty" json:"allow_collaboration,omitempty"`
521}
522
523// UpdateMergeRequest updates an existing project milestone.
524//
525// GitLab API docs:
526// https://docs.gitlab.com/ce/api/merge_requests.html#update-mr
527func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
528	project, err := parseID(pid)
529	if err != nil {
530		return nil, nil, err
531	}
532	u := fmt.Sprintf("projects/%s/merge_requests/%d", pathEscape(project), mergeRequest)
533
534	req, err := s.client.NewRequest("PUT", u, opt, options)
535	if err != nil {
536		return nil, nil, err
537	}
538
539	m := new(MergeRequest)
540	resp, err := s.client.Do(req, m)
541	if err != nil {
542		return nil, resp, err
543	}
544
545	return m, resp, err
546}
547
548// DeleteMergeRequest deletes a merge request.
549//
550// GitLab API docs:
551// https://docs.gitlab.com/ce/api/merge_requests.html#delete-a-merge-request
552func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) {
553	project, err := parseID(pid)
554	if err != nil {
555		return nil, err
556	}
557	u := fmt.Sprintf("projects/%s/merge_requests/%d", pathEscape(project), mergeRequest)
558
559	req, err := s.client.NewRequest("DELETE", u, nil, options)
560	if err != nil {
561		return nil, err
562	}
563
564	return s.client.Do(req, nil)
565}
566
567// AcceptMergeRequestOptions represents the available AcceptMergeRequest()
568// options.
569//
570// GitLab API docs:
571// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
572type AcceptMergeRequestOptions struct {
573	MergeCommitMessage        *string `url:"merge_commit_message,omitempty" json:"merge_commit_message,omitempty"`
574	SquashCommitMessage       *string `url:"squash_commit_message,omitempty" json:"squash_commit_message,omitempty"`
575	Squash                    *bool   `url:"squash,omitempty" json:"squash,omitempty"`
576	ShouldRemoveSourceBranch  *bool   `url:"should_remove_source_branch,omitempty" json:"should_remove_source_branch,omitempty"`
577	MergeWhenPipelineSucceeds *bool   `url:"merge_when_pipeline_succeeds,omitempty" json:"merge_when_pipeline_succeeds,omitempty"`
578	SHA                       *string `url:"sha,omitempty" json:"sha,omitempty"`
579}
580
581// AcceptMergeRequest merges changes submitted with MR using this API. If merge
582// success you get 200 OK. If it has some conflicts and can not be merged - you
583// get 405 and error message 'Branch cannot be merged'. If merge request is
584// already merged or closed - you get 405 and error message 'Method Not Allowed'
585//
586// GitLab API docs:
587// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
588func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) {
589	project, err := parseID(pid)
590	if err != nil {
591		return nil, nil, err
592	}
593	u := fmt.Sprintf("projects/%s/merge_requests/%d/merge", pathEscape(project), mergeRequest)
594
595	req, err := s.client.NewRequest("PUT", u, opt, options)
596	if err != nil {
597		return nil, nil, err
598	}
599
600	m := new(MergeRequest)
601	resp, err := s.client.Do(req, m)
602	if err != nil {
603		return nil, resp, err
604	}
605
606	return m, resp, err
607}
608
609// CancelMergeWhenPipelineSucceeds cancels a merge when pipeline succeeds. If
610// you don't have permissions to accept this merge request - you'll get a 401.
611// If the merge request is already merged or closed - you get 405 and error
612// message 'Method Not Allowed'. In case the merge request is not set to be
613// merged when the pipeline succeeds, you'll also get a 406 error.
614//
615// GitLab API docs:
616// https://docs.gitlab.com/ce/api/merge_requests.html#cancel-merge-when-pipeline-succeeds
617func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
618	project, err := parseID(pid)
619	if err != nil {
620		return nil, nil, err
621	}
622	u := fmt.Sprintf("projects/%s/merge_requests/%d/cancel_merge_when_pipeline_succeeds", pathEscape(project), mergeRequest)
623
624	req, err := s.client.NewRequest("PUT", u, nil, options)
625	if err != nil {
626		return nil, nil, err
627	}
628
629	m := new(MergeRequest)
630	resp, err := s.client.Do(req, m)
631	if err != nil {
632		return nil, resp, err
633	}
634
635	return m, resp, err
636}
637
638// RebaseMergeRequest automatically rebases the source_branch of the merge
639// request against its target_branch. If you don’t have permissions to push
640// to the merge request’s source branch, you’ll get a 403 Forbidden response.
641//
642// GitLab API docs:
643// https://docs.gitlab.com/ce/api/merge_requests.html#rebase-a-merge-request
644func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) {
645	project, err := parseID(pid)
646	if err != nil {
647		return nil, err
648	}
649	u := fmt.Sprintf("projects/%s/merge_requests/%d/rebase", pathEscape(project), mergeRequest)
650
651	req, err := s.client.NewRequest("PUT", u, nil, options)
652	if err != nil {
653		return nil, err
654	}
655
656	return s.client.Do(req, nil)
657}
658
659// GetMergeRequestDiffVersionsOptions represents the available
660// GetMergeRequestDiffVersions() options.
661//
662// GitLab API docs:
663// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
664type GetMergeRequestDiffVersionsOptions ListOptions
665
666// GetMergeRequestDiffVersions get a list of merge request diff versions.
667//
668// GitLab API docs:
669// https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions
670func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...OptionFunc) ([]*MergeRequestDiffVersion, *Response, error) {
671	project, err := parseID(pid)
672	if err != nil {
673		return nil, nil, err
674	}
675	u := fmt.Sprintf("projects/%s/merge_requests/%d/versions", pathEscape(project), mergeRequest)
676
677	req, err := s.client.NewRequest("GET", u, opt, options)
678	if err != nil {
679		return nil, nil, err
680	}
681
682	var v []*MergeRequestDiffVersion
683	resp, err := s.client.Do(req, &v)
684	if err != nil {
685		return nil, resp, err
686	}
687
688	return v, resp, err
689}
690
691// GetSingleMergeRequestDiffVersion get a single MR diff version
692//
693// GitLab API docs:
694// https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version
695func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...OptionFunc) (*MergeRequestDiffVersion, *Response, error) {
696	project, err := parseID(pid)
697	if err != nil {
698		return nil, nil, err
699	}
700	u := fmt.Sprintf("projects/%s/merge_requests/%d/versions/%d", pathEscape(project), mergeRequest, version)
701
702	req, err := s.client.NewRequest("GET", u, nil, options)
703	if err != nil {
704		return nil, nil, err
705	}
706
707	var v = new(MergeRequestDiffVersion)
708	resp, err := s.client.Do(req, v)
709	if err != nil {
710		return nil, resp, err
711	}
712
713	return v, resp, err
714}
715
716// SubscribeToMergeRequest subscribes the authenticated user to the given merge
717// request to receive notifications. If the user is already subscribed to the
718// merge request, the status code 304 is returned.
719//
720// GitLab API docs:
721// https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request
722func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
723	project, err := parseID(pid)
724	if err != nil {
725		return nil, nil, err
726	}
727	u := fmt.Sprintf("projects/%s/merge_requests/%d/subscribe", pathEscape(project), mergeRequest)
728
729	req, err := s.client.NewRequest("POST", u, nil, options)
730	if err != nil {
731		return nil, nil, err
732	}
733
734	m := new(MergeRequest)
735	resp, err := s.client.Do(req, m)
736	if err != nil {
737		return nil, resp, err
738	}
739
740	return m, resp, err
741}
742
743// UnsubscribeFromMergeRequest unsubscribes the authenticated user from the
744// given merge request to not receive notifications from that merge request.
745// If the user is not subscribed to the merge request, status code 304 is
746// returned.
747//
748// GitLab API docs:
749// https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request
750func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) {
751	project, err := parseID(pid)
752	if err != nil {
753		return nil, nil, err
754	}
755	u := fmt.Sprintf("projects/%s/merge_requests/%d/unsubscribe", pathEscape(project), mergeRequest)
756
757	req, err := s.client.NewRequest("POST", u, nil, options)
758	if err != nil {
759		return nil, nil, err
760	}
761
762	m := new(MergeRequest)
763	resp, err := s.client.Do(req, m)
764	if err != nil {
765		return nil, resp, err
766	}
767
768	return m, resp, err
769}
770
771// CreateTodo manually creates a todo for the current user on a merge request.
772// If there already exists a todo for the user on that merge request,
773// status code 304 is returned.
774//
775// GitLab API docs:
776// https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo
777func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...OptionFunc) (*Todo, *Response, error) {
778	project, err := parseID(pid)
779	if err != nil {
780		return nil, nil, err
781	}
782	u := fmt.Sprintf("projects/%s/merge_requests/%d/todo", pathEscape(project), mergeRequest)
783
784	req, err := s.client.NewRequest("POST", u, nil, options)
785	if err != nil {
786		return nil, nil, err
787	}
788
789	t := new(Todo)
790	resp, err := s.client.Do(req, t)
791	if err != nil {
792		return nil, resp, err
793	}
794
795	return t, resp, err
796}
797
798// SetTimeEstimate sets the time estimate for a single project merge request.
799//
800// GitLab API docs:
801// https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request
802func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
803	return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...)
804}
805
806// ResetTimeEstimate resets the time estimate for a single project merge request.
807//
808// GitLab API docs:
809// https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request
810func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
811	return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...)
812}
813
814// AddSpentTime adds spent time for a single project merge request.
815//
816// GitLab API docs:
817// https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request
818func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) {
819	return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...)
820}
821
822// ResetSpentTime resets the spent time for a single project merge request.
823//
824// GitLab API docs:
825// https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request
826func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
827	return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...)
828}
829
830// GetTimeSpent gets the spent time for a single project merge request.
831//
832// GitLab API docs:
833// https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats
834func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) {
835	return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...)
836}