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