From 1eaf38380d74925650bee1593df74e1a1c0dc3a7 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Mon, 26 Aug 2013 00:57:42 +0200 Subject: [PATCH] implement repos_commits API --- github/git_commits.go | 1 + github/repos_collaborators.go | 1 + github/repos_commits.go | 161 ++++++++++++++++++++++++++++ github/repos_commits_test.go | 191 ++++++++++++++++++++++++++++++++++ github/strings_test.go | 4 + 5 files changed, 358 insertions(+) create mode 100644 github/repos_commits.go create mode 100644 github/repos_commits_test.go diff --git a/github/git_commits.go b/github/git_commits.go index 82a178d..0a39576 100644 --- a/github/git_commits.go +++ b/github/git_commits.go @@ -18,6 +18,7 @@ type Commit struct { Message *string `json:"message,omitempty"` Tree *Tree `json:"tree,omitempty"` Parents []Commit `json:"parents,omitempty"` + Stats *CommitStats `json:"stats,omitempty"` } func (c Commit) String() string { diff --git a/github/repos_collaborators.go b/github/repos_collaborators.go index 2972281..1dee956 100644 --- a/github/repos_collaborators.go +++ b/github/repos_collaborators.go @@ -36,6 +36,7 @@ func (s *RepositoriesService) IsCollaborator(owner, repo, user string) (bool, *R if err != nil { return false, nil, err } + resp, err := s.client.Do(req, nil) isCollab, err := parseBoolResponse(err) return isCollab, resp, err diff --git a/github/repos_commits.go b/github/repos_commits.go new file mode 100644 index 0000000..b61631b --- /dev/null +++ b/github/repos_commits.go @@ -0,0 +1,161 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/url" + "time" +) + +// RepositoryCommit represents a commit in a repo. +// Note that it's wrapping a Commit, so author/committer information is in two places, +// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". +type RepositoryCommit struct { + SHA *string `json:"sha,omitempty"` + Commit *Commit `json:"commit,omitempty"` + Author *User `json:"author,omitempty"` + Committer *User `json:"committer,omitempty"` + Parents []Commit `json:"parents,omitempty"` + Message *string `json:"message,omitempty"` + + // Details about how many changes were made in this commit. Only filled in during GetCommit! + Stats *CommitStats `json:"stats,omitempty"` + // Details about which files, and how this commit touched. Only filled in during GetCommit! + Files []CommitFile `json:"files,omitempty"` +} + +func (r RepositoryCommit) String() string { + return Stringify(r) +} + +// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit. +type CommitStats struct { + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + Total *int `json:"total,omitempty"` +} + +func (c CommitStats) String() string { + return Stringify(c) +} + +// CommitFile represents a file modified in a commit. +type CommitFile struct { + SHA *string `json:"sha,omitempty"` + Filename *string `json:"filename,omitempty"` + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + Changes *int `json:"changes,omitempty"` + Status *string `json:"status,omitempty"` + Patch *string `json:"patch,omitempty"` +} + +func (c CommitFile) String() string { + return Stringify(c) +} + +// CommitsComparison is the result of comparing two commits. +// See CompareCommits() for details. +type CommitsComparison struct { + BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` + + // Head can be 'behind' or 'ahead' + Status *string `json:"status,omitempty"` + AheadBy *int `json:"ahead_by,omitempty"` + BehindBy *int `json:"behind_by,omitempty"` + TotalCommits *int `json:"total_commits,omitempty"` + + Commits []RepositoryCommit `json:"commits,omitempty"` + + Files []CommitFile `json:"files,omitempty"` +} + +func (c CommitsComparison) String() string { + return Stringify(c) +} + +// CommitsListOptions specifies the optional parameters to the +// RepositoriesService.ListCommits method. +type CommitsListOptions struct { + // SHA or branch to start listing Commits from. + SHA string + // Path that should be touched by the returned Commits. + Path string + // Author of by which to filter Commits. + Author string + // Since when should Commits be included in the response. + Since time.Time + // Until when should Commits be included in the response. + Until time.Time +} + +// ListCommits lists the commits of a repository. +// +// GitHub API docs: http://developer.github.com/v3/repos/commits/#list +func (s *RepositoriesService) ListCommits(owner, repo string, opts *CommitsListOptions) ([]RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) + + if opts != nil { + params := url.Values{ + "sha": {opts.SHA}, + "path": {opts.Path}, + "author": {opts.Author}, + } + if !opts.Since.IsZero() { + params.Add("since", opts.Since.Format(time.RFC3339)) + } + if !opts.Until.IsZero() { + params.Add("until", opts.Until.Format(time.RFC3339)) + } + + u += "?" + params.Encode() + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + commits := new([]RepositoryCommit) + resp, err := s.client.Do(req, commits) + return *commits, resp, err +} + +// GetCommit fetches the specified commit, including all details about it. +// todo: support media formats - https://github.com/google/go-github/issues/6 +// +// GitHub API docs: http://developer.github.com/v3/repos/commits/#get-a-single-commit +// See also: http://developer.github.com//v3/git/commits/#get-a-single-commit provides the same functionality +func (s *RepositoriesService) GetCommit(owner, repo, sha string) (*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + commit := new(RepositoryCommit) + resp, err := s.client.Do(req, commit) + return commit, resp, err +} + +// CompareCommits compares a range of commits with each other. +// todo: support media formats - https://github.com/google/go-github/issues/6 +// +// GitHub API docs: http://developer.github.com/v3/repos/commits/index.html#compare-two-commits +func (s *RepositoriesService) CompareCommits(owner, repo string, base, head string) (*CommitsComparison, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + comp := new(CommitsComparison) + resp, err := s.client.Do(req, comp) + return comp, resp, err +} diff --git a/github/repos_commits_test.go b/github/repos_commits_test.go new file mode 100644 index 0000000..56ba8a5 --- /dev/null +++ b/github/repos_commits_test.go @@ -0,0 +1,191 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "reflect" + "testing" + "time" +) + +func TestRepositoriesService_ListCommits(t *testing.T) { + setup() + defer teardown() + + // given + mux.HandleFunc("/repos/o/r/commits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, + values{ + "sha": "s", + "path": "p", + "author": "a", + "since": "2013-08-01T00:00:00Z", + "until": "2013-09-03T00:00:00Z", + }) + fmt.Fprintf(w, `[{"sha": "s"}]`) + }) + + opt := &CommitsListOptions{ + SHA: "s", + Path: "p", + Author: "a", + Since: time.Date(2013, time.August, 1, 0, 0, 0, 0, time.UTC), + Until: time.Date(2013, time.September, 3, 0, 0, 0, 0, time.UTC), + } + commits, _, err := client.Repositories.ListCommits("o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCommits returned error: %v", err) + } + + want := []RepositoryCommit{{SHA: String("s")}} + if !reflect.DeepEqual(commits, want) { + t.Errorf("Repositories.ListCommits returned %+v, want %+v", commits, want) + } +} + +func TestRepositoriesService_GetCommit(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "sha": "s", + "commit": { "message": "m" }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ], + "stats": { "additions": 104, "deletions": 4, "total": 108 }, + "files": [ + { + "filename": "f", + "additions": 10, + "deletions": 2, + "changes": 12, + "status": "s", + "raw_url": "r", + "blob_url": "b", + "patch": "p" + } + ] + }`) + }) + + commit, _, err := client.Repositories.GetCommit("o", "r", "s") + if err != nil { + t.Errorf("Repositories.GetCommit returned error: %v", err) + } + + want := &RepositoryCommit{ + SHA: String("s"), + Commit: &Commit{ + Message: String("m"), + }, + Author: &User{ + Login: String("l"), + }, + Committer: &User{ + Login: String("l"), + }, + Parents: []Commit{ + { + SHA: String("s"), + }, + }, + Stats: &CommitStats{ + Additions: Int(104), + Deletions: Int(4), + Total: Int(108), + }, + Files: []CommitFile{ + { + Filename: String("f"), + Additions: Int(10), + Deletions: Int(2), + Changes: Int(12), + Status: String("s"), + Patch: String("p"), + }, + }, + } + if !reflect.DeepEqual(commit, want) { + t.Errorf("Repositories.GetCommit returned \n%+v, want \n%+v", commit, want) + } +} + +func TestRepositoriesService_CompareCommits(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/compare/b...h", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "base_commit": { + "sha": "s", + "commit": { + "author": { "name": "n" }, + "committer": { "name": "n" }, + "message": "m", + "tree": { "sha": "t" } + }, + "author": { "login": "n" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + }, + "status": "s", + "ahead_by": 1, + "behind_by": 2, + "total_commits": 1, + "commits": [ + { + "sha": "s", + "commit": { "author": { "name": "n" } }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + } + ], + "files": [ { "filename": "f" } ] + }`) + }) + + got, _, err := client.Repositories.CompareCommits("o", "r", "b", "h") + if err != nil { + t.Errorf("Repositories.CompareCommits returned error: %v", err) + } + + want := &CommitsComparison{ + Status: String("s"), + AheadBy: Int(1), + BehindBy: Int(2), + TotalCommits: Int(1), + BaseCommit: &RepositoryCommit{ + Commit: &Commit{ + Author: &CommitAuthor{Name: String("n")}, + }, + Author: &User{Login: String("l")}, + Committer: &User{Login: String("l")}, + Message: String("m"), + }, + Commits: []RepositoryCommit{ + { + SHA: String("s"), + }, + }, + Files: []CommitFile{ + { + Filename: String("f"), + }, + }, + } + + if reflect.DeepEqual(got, want) { + t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want) + } +} diff --git a/github/strings_test.go b/github/strings_test.go index 262fe0c..42dcc72 100644 --- a/github/strings_test.go +++ b/github/strings_test.go @@ -108,6 +108,10 @@ func TestString(t *testing.T) { {PullRequestComment{ID: Int(1)}, `github.PullRequestComment{ID:1}`}, {Repository{ID: Int(1)}, `github.Repository{ID:1}`}, {RepositoryComment{ID: Int(1)}, `github.RepositoryComment{ID:1}`}, + {RepositoryCommit{SHA: String("s")}, `github.RepositoryCommit{SHA:"s"}`}, + {CommitStats{Total: Int(1)}, `github.CommitStats{Total:1}`}, + {CommitFile{SHA: String("s")}, `github.CommitFile{SHA:"s"}`}, + {CommitsComparison{TotalCommits: Int(1)}, `github.CommitsComparison{TotalCommits:1}`}, {WebHookPayload{Ref: String("r")}, `github.WebHookPayload{Ref:"r"}`}, {WebHookCommit{ID: String("1")}, `github.WebHookCommit{ID:"1"}`}, {WebHookAuthor{Name: String("n")}, `github.WebHookAuthor{Name:"n"}`},