Repository: xanzy/go-gitlab
Pull request #644 (2019-06-25 19:07:19)

Add support for revert a commit
Hi,

the client library lacks support for "revert a commit" operation, that Gitlab introduced in 11.5 version. This PR allows for such operation.

/cc @svanharmelen 


Patch:
diff --git a/commits.go b/commits.go
index 7556394..85af094 100644
--- a/commits.go
+++ b/commits.go
@@ -485,14 +485,14 @@ func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, o
 	return mrs, resp, err
 }
 
-// CherryPickCommitOptions represents the available options for cherry-picking a commit.
+// CherryPickCommitOptions represents the available CherryPickCommit() options.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
 type CherryPickCommitOptions struct {
-	TargetBranch *string `url:"branch" json:"branch,omitempty"`
+	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
 }
 
-// CherryPickCommit sherry picks a commit to a given branch.
+// CherryPickCommit cherry picks a commit to a given branch.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit
 func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
@@ -515,3 +515,34 @@ func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *Cher
 
 	return c, resp, err
 }
+
+// RevertCommitOptions represents the available RevertCommit() options.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
+type RevertCommitOptions struct {
+	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
+}
+
+// RevertCommit reverts a commit in a given branch.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit
+func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...OptionFunc) (*Commit, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/repository/commits/%s/revert", pathEscape(project), sha)
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var c *Commit
+	resp, err := s.client.Do(req, &c)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return c, resp, err
+}
diff --git a/gitlab.go b/gitlab.go
index c8ac26f..8734af0 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -341,7 +341,6 @@ type Client struct {
 	Releases              *ReleasesService
 	Repositories          *RepositoriesService
 	RepositoryFiles       *RepositoryFilesService
-	ResourceLabelEvents   *ResourceLabelEventsService
 	Runners               *RunnersService
 	Search                *SearchService
 	Services              *ServicesService
@@ -491,7 +490,6 @@ func newClient(httpClient *http.Client) *Client {
 	c.Releases = &ReleasesService{client: c}
 	c.Repositories = &RepositoriesService{client: c}
 	c.RepositoryFiles = &RepositoryFilesService{client: c}
-	c.ResourceLabelEvents = &ResourceLabelEventsService{client: c}
 	c.Runners = &RunnersService{client: c}
 	c.Search = &SearchService{client: c}
 	c.Services = &ServicesService{client: c}
diff --git a/issues.go b/issues.go
index 1b10782..2f031a7 100644
--- a/issues.go
+++ b/issues.go
@@ -162,7 +162,6 @@ type ListGroupIssuesOptions struct {
 	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
 	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
 	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
-	In              *string    `url:"in,omitempty" json:"in,omitempty"`
 	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
 	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
 	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
@@ -210,7 +209,6 @@ type ListProjectIssuesOptions struct {
 	OrderBy         *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
 	Sort            *string    `url:"sort,omitempty" json:"sort,omitempty"`
 	Search          *string    `url:"search,omitempty" json:"search,omitempty"`
-	In              *string    `url:"in,omitempty" json:"in,omitempty"`
 	CreatedAfter    *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
 	CreatedBefore   *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
 	UpdatedAfter    *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
diff --git a/resource_label_events.go b/resource_label_events.go
deleted file mode 100644
index e9ff66a..0000000
--- a/resource_label_events.go
+++ /dev/null
@@ -1,219 +0,0 @@
-//
-// Copyright 2017, Sander van Harmelen
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package gitlab
-
-import (
-	"fmt"
-	"time"
-)
-
-// ResourceLabelEventsService handles communication with the event related
-// methods of the GitLab API.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/resource_label_events.html
-type ResourceLabelEventsService struct {
-	client *Client
-}
-
-// LabelEvent represents a resource label event.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-issue-label-event
-type LabelEvent struct {
-	ID           int        `json:"id"`
-	Action       string     `json:"action"`
-	CreatedAt    *time.Time `json:"created_at"`
-	ResourceType string     `json:"resource_type"`
-	ResourceID   int        `json:"resource_id"`
-	User         struct {
-		ID        int    `json:"id"`
-		Name      string `json:"name"`
-		Username  string `json:"username"`
-		State     string `json:"state"`
-		AvatarURL string `json:"avatar_url"`
-		WebURL    string `json:"web_url"`
-	} `json:"user"`
-	Label struct {
-		ID          int    `json:"id"`
-		Name        string `json:"name"`
-		Color       string `json:"color"`
-		TextColor   string `json:"text_color"`
-		Description string `json:"description"`
-	} `json:"label"`
-}
-
-// ListLabelEventsOptions represents the options for all resource label events
-// list methods.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-issue-label-events
-type ListLabelEventsOptions struct {
-	ListOptions
-}
-
-// ListIssueLabelEvents retrieves resource label events for the
-// specified project and issue.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-issue-label-events
-func (s *ResourceLabelEventsService) ListIssueLabelEvents(pid interface{}, issue int, opt *ListLabelEventsOptions, options ...OptionFunc) ([]*LabelEvent, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/issues/%d/resource_label_events", pathEscape(project), issue)
-
-	req, err := s.client.NewRequest("GET", u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var ls []*LabelEvent
-	resp, err := s.client.Do(req, &ls)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return ls, resp, err
-}
-
-// GetIssueLabelEvent gets a single issue-label-event.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-issue-label-event
-func (s *ResourceLabelEventsService) GetIssueLabelEvent(pid interface{}, issue int, event int, options ...OptionFunc) (*LabelEvent, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/issues/%d/resource_label_events/%d", pathEscape(project), issue, event)
-
-	req, err := s.client.NewRequest("GET", u, nil, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	l := new(LabelEvent)
-	resp, err := s.client.Do(req, l)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return l, resp, err
-}
-
-// ListGroupEpicLabelEvents retrieves resource label events for the specified
-// group and epic.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#list-group-epic-label-events
-func (s *ResourceLabelEventsService) ListGroupEpicLabelEvents(gid interface{}, epic int, opt *ListLabelEventsOptions, options ...OptionFunc) ([]*LabelEvent, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/epics/%d/resource_label_events", pathEscape(group), epic)
-
-	req, err := s.client.NewRequest("GET", u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var ls []*LabelEvent
-	resp, err := s.client.Do(req, &ls)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return ls, resp, err
-}
-
-// GetGroupEpicLabelEvent gets a single group epic label event.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-epic-label-event
-func (s *ResourceLabelEventsService) GetGroupEpicLabelEvent(gid interface{}, epic int, event int, options ...OptionFunc) (*LabelEvent, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/epics/%d/resource_label_events/%d", pathEscape(group), epic, event)
-
-	req, err := s.client.NewRequest("GET", u, nil, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	l := new(LabelEvent)
-	resp, err := s.client.Do(req, l)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return l, resp, err
-}
-
-// ListMergeLabelEvents retrieves resource label events for the specified
-// project and merge request.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#list-project-merge-request-label-events
-func (s *ResourceLabelEventsService) ListMergeLabelEvents(pid interface{}, request int, opt *ListLabelEventsOptions, options ...OptionFunc) ([]*LabelEvent, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_label_events", pathEscape(project), request)
-
-	req, err := s.client.NewRequest("GET", u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var ls []*LabelEvent
-	resp, err := s.client.Do(req, &ls)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return ls, resp, err
-}
-
-// GetMergeRequestLabelEvent gets a single merge request label event.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/resource_label_events.html#get-single-merge-request-label-event
-func (s *ResourceLabelEventsService) GetMergeRequestLabelEvent(pid interface{}, request int, event int, options ...OptionFunc) (*LabelEvent, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/merge_requests/%d/resource_label_events/%d", pathEscape(project), request, event)
-
-	req, err := s.client.NewRequest("GET", u, nil, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	l := new(LabelEvent)
-	resp, err := s.client.Do(req, l)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return l, resp, err
-}


Test patch:
diff --git a/commits_test.go b/commits_test.go
index e3ccb70..a407df0 100644
--- a/commits_test.go
+++ b/commits_test.go
@@ -9,6 +9,8 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+var testRevertCommitTargetBranch = "release"
+
 func TestGetCommit(t *testing.T) {
 	mux, server, client := setup()
 	defer teardown(server)
@@ -91,3 +93,84 @@ func TestSetCommitStatus(t *testing.T) {
 		t.Errorf("Commits.SetCommitStatus returned %+v, want %+v", status, want)
 	}
 }
+
+func TestRevertCommit_NoOptions(t *testing.T) {
+	mux, server, client := setup()
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/revert", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		mustWriteHTTPResponse(t, w, "testdata/get_commit.json")
+	})
+
+	commit, resp, err := client.Commits.RevertCommit("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", nil)
+	if err != nil {
+		t.Fatalf("Commits.RevertCommit returned error: %v, response: %v", err, resp)
+	}
+
+	want := &Commit{
+		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
+		ShortID:        "6104942438c",
+		Title:          "Sanitize for network graph",
+		AuthorName:     "randx",
+		AuthorEmail:    "dmitriy.zaporozhets@gmail.com",
+		CommitterName:  "Dmitriy",
+		CommitterEmail: "dmitriy.zaporozhets@gmail.com",
+		Message:        "Sanitize for network graph",
+		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
+		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
+		Status:         BuildState(Running),
+		LastPipeline: &PipelineInfo{
+			ID:     8,
+			Ref:    "master",
+			SHA:    "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
+			Status: "created",
+			WebURL: "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
+		},
+		ProjectID: 13083,
+	}
+
+	assert.Equal(t, want, commit)
+}
+
+func TestRevertCommit_WithOptions(t *testing.T) {
+	mux, server, client := setup()
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/repository/commits/b0b3a907f41409829b307a28b82fdbd552ee5a27/revert", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "POST")
+		testBody(t, r, `{"branch":"release"}`)
+		mustWriteHTTPResponse(t, w, "testdata/get_commit.json")
+	})
+
+	commit, resp, err := client.Commits.RevertCommit("1", "b0b3a907f41409829b307a28b82fdbd552ee5a27", &RevertCommitOptions{
+		Branch: &testRevertCommitTargetBranch,
+	})
+	if err != nil {
+		t.Fatalf("Commits.RevertCommit returned error: %v, response: %v", err, resp)
+	}
+
+	want := &Commit{
+		ID:             "6104942438c14ec7bd21c6cd5bd995272b3faff6",
+		ShortID:        "6104942438c",
+		Title:          "Sanitize for network graph",
+		AuthorName:     "randx",
+		AuthorEmail:    "dmitriy.zaporozhets@gmail.com",
+		CommitterName:  "Dmitriy",
+		CommitterEmail: "dmitriy.zaporozhets@gmail.com",
+		Message:        "Sanitize for network graph",
+		ParentIDs:      []string{"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"},
+		Stats:          &CommitStats{Additions: 15, Deletions: 10, Total: 25},
+		Status:         BuildState(Running),
+		LastPipeline: &PipelineInfo{
+			ID:     8,
+			Ref:    "master",
+			SHA:    "2dc6aa325a317eda67812f05600bdf0fcdc70ab0",
+			Status: "created",
+			WebURL: "https://gitlab.com/gitlab-org/gitlab-ce/pipelines/54268416",
+		},
+		ProjectID: 13083,
+	}
+
+	assert.Equal(t, want, commit)
+}