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