From 585d0e581478cf716e1a23b407db961949e3e559 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 25 Jan 2019 16:09:13 +0800 Subject: [PATCH 1/6] add single commit API support --- routers/api/v1/api.go | 3 ++ routers/api/v1/repo/commits.go | 92 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 routers/api/v1/repo/commits.go diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2787d01a04c0..684b846cab54 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -506,6 +506,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", repo.ListBranches) m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) }, reqRepoReader(models.UnitTypeCode)) + m.Group("/commits", func() { + m.Get("/:sha", repo.GetSingleCommit) + }) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go new file mode 100644 index 000000000000..96938c998f55 --- /dev/null +++ b/routers/api/v1/repo/commits.go @@ -0,0 +1,92 @@ +// Copyright 2018 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "time" + + "code.gitea.io/git" + api "code.gitea.io/sdk/gitea" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" +) + +// GetSingleCommit get a commit via +func GetSingleCommit(c *context.APIContext) { + gitRepo, err := git.OpenRepository(c.Repo.Repository.RepoPath()) + if err != nil { + c.ServerError("OpenRepository", err) + return + } + commit, err := gitRepo.GetCommit(c.Params(":sha")) + if err != nil { + c.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) + return + } + + // Retrieve author and committer information + var apiAuthor, apiCommitter *api.User + author, err := models.GetUserByEmail(commit.Author.Email) + if err != nil && !models.IsErrUserNotExist(err) { + c.ServerError("Get user by author email", err) + return + } else if err == nil { + apiAuthor = author.APIFormat() + } + // Save one query if the author is also the committer + if commit.Committer.Email == commit.Author.Email { + apiCommitter = apiAuthor + } else { + committer, err := models.GetUserByEmail(commit.Committer.Email) + if err != nil && !models.IsErrUserNotExist(err) { + c.ServerError("Get user by committer email", err) + return + } else if err == nil { + apiCommitter = committer.APIFormat() + } + } + + // Retrieve parent(s) of the commit + apiParents := make([]*api.CommitMeta, commit.ParentCount()) + for i := 0; i < commit.ParentCount(); i++ { + sha, _ := commit.ParentID(i) + apiParents[i] = &api.CommitMeta{ + URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(), + SHA: sha.String(), + } + } + + c.JSON(200, &api.Commit{ + CommitMeta: &api.CommitMeta{ + URL: setting.AppURL + c.Link[1:], + SHA: commit.ID.String(), + }, + HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(), + RepoCommit: &api.RepoCommit{ + URL: setting.AppURL + c.Link[1:], + Author: &api.CommitUser{ + Name: commit.Author.Name, + Email: commit.Author.Email, + Date: commit.Author.When.Format(time.RFC3339), + }, + Committer: &api.CommitUser{ + Name: commit.Committer.Name, + Email: commit.Committer.Email, + Date: commit.Committer.When.Format(time.RFC3339), + }, + Message: commit.Summary(), + Tree: &api.CommitMeta{ + URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(), + SHA: commit.ID.String(), + }, + }, + Author: apiAuthor, + Committer: apiCommitter, + Parents: apiParents, + }) +} From 559884b3ea34fb156f479448416529e988aa0222 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 25 Jan 2019 18:18:50 +0800 Subject: [PATCH 2/6] update vendors --- Gopkg.lock | 13 +- routers/api/v1/api.go | 6 +- routers/api/v1/repo/commits.go | 53 +++-- templates/swagger/v1_json.tmpl | 64 ++++++ vendor/code.gitea.io/sdk/gitea/hook.go | 3 + vendor/code.gitea.io/sdk/gitea/repo_commit.go | 48 +++++ vendor/github.com/go-macaron/gzip/LICENSE | 191 ------------------ vendor/github.com/go-macaron/gzip/gzip.go | 121 ----------- 8 files changed, 160 insertions(+), 339 deletions(-) create mode 100644 vendor/code.gitea.io/sdk/gitea/repo_commit.go delete mode 100644 vendor/github.com/go-macaron/gzip/LICENSE delete mode 100644 vendor/github.com/go-macaron/gzip/gzip.go diff --git a/Gopkg.lock b/Gopkg.lock index 8f2a9d0ca7bd..5279636741b8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -11,11 +11,11 @@ [[projects]] branch = "master" - digest = "1:8a559f110defa54f847a3efa2734297571d960b476699579f2008e4a37b62a1a" + digest = "1:e89bf7ed97f3489e0b38a0e53db2b97b7750609ca9fc5c1b74a5a6079ede241b" name = "code.gitea.io/sdk" packages = ["gitea"] pruneopts = "NUT" - revision = "140e9fcba7583e1c6f22eb57676bb00794ef14a8" + revision = "85039742e2c0bfb3aa1cf8da39ba1800d11bbdd1" [[projects]] digest = "1:3fcef06a1a6561955c94af6c7757a6fa37605eb653f0d06ab960e5bb80092195" @@ -340,14 +340,6 @@ pruneopts = "NUT" revision = "503617c6b37257a55dff6293ec28556506c3a9a8" -[[projects]] - branch = "master" - digest = "1:6326b27f8e0c8e135c8674ddbc619fae879664ac832e8e6fa6a23ce0d279ed4d" - name = "github.com/go-macaron/gzip" - packages = ["."] - pruneopts = "NUT" - revision = "cad1c6580a07c56f5f6bc52d66002a05985c5854" - [[projects]] branch = "master" digest = "1:d09d3c94e1bd444464c221be961ba4005e119308f1bcd57aa5f49046fb6affd2" @@ -1155,7 +1147,6 @@ "github.com/go-macaron/cache/redis", "github.com/go-macaron/captcha", "github.com/go-macaron/csrf", - "github.com/go-macaron/gzip", "github.com/go-macaron/i18n", "github.com/go-macaron/inject", "github.com/go-macaron/session", diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 684b846cab54..7cba2790ef8d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -506,9 +506,6 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", repo.ListBranches) m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) }, reqRepoReader(models.UnitTypeCode)) - m.Group("/commits", func() { - m.Get("/:sha", repo.GetSingleCommit) - }) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) @@ -613,6 +610,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/statuses", repo.GetCommitStatusesByRef) }, reqRepoReader(models.UnitTypeCode)) m.Group("/git", func() { + m.Group("/commits", func() { + m.Get("/:sha", repo.GetSingleCommit) + }) m.Get("/refs", repo.GetGitAllRefs) m.Get("/refs/*", repo.GetGitRefs) m.Combo("/trees/:sha", context.RepoRef()).Get(repo.GetTree) diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 96938c998f55..a4cf5037d7ec 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -17,15 +17,42 @@ import ( ) // GetSingleCommit get a commit via -func GetSingleCommit(c *context.APIContext) { - gitRepo, err := git.OpenRepository(c.Repo.Repository.RepoPath()) +func GetSingleCommit(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha} repository repoGetSingleCommit + // --- + // summary: Get a single commit from a repository + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: sha + // in: path + // description: the commit hash + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/Commit" + // "404": + // "$ref": "#/responses/notFound" + + gitRepo, err := git.OpenRepository(ctx.Repo.Repository.RepoPath()) if err != nil { - c.ServerError("OpenRepository", err) + ctx.ServerError("OpenRepository", err) return } - commit, err := gitRepo.GetCommit(c.Params(":sha")) + commit, err := gitRepo.GetCommit(ctx.Params(":sha")) if err != nil { - c.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) + ctx.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err) return } @@ -33,7 +60,7 @@ func GetSingleCommit(c *context.APIContext) { var apiAuthor, apiCommitter *api.User author, err := models.GetUserByEmail(commit.Author.Email) if err != nil && !models.IsErrUserNotExist(err) { - c.ServerError("Get user by author email", err) + ctx.ServerError("Get user by author email", err) return } else if err == nil { apiAuthor = author.APIFormat() @@ -44,7 +71,7 @@ func GetSingleCommit(c *context.APIContext) { } else { committer, err := models.GetUserByEmail(commit.Committer.Email) if err != nil && !models.IsErrUserNotExist(err) { - c.ServerError("Get user by committer email", err) + ctx.ServerError("Get user by committer email", err) return } else if err == nil { apiCommitter = committer.APIFormat() @@ -56,19 +83,19 @@ func GetSingleCommit(c *context.APIContext) { for i := 0; i < commit.ParentCount(); i++ { sha, _ := commit.ParentID(i) apiParents[i] = &api.CommitMeta{ - URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/commits/" + sha.String(), + URL: ctx.Repo.Repository.APIURL() + "/git/commits/" + sha.String(), SHA: sha.String(), } } - c.JSON(200, &api.Commit{ + ctx.JSON(200, &api.Commit{ CommitMeta: &api.CommitMeta{ - URL: setting.AppURL + c.Link[1:], + URL: setting.AppURL + ctx.Link[1:], SHA: commit.ID.String(), }, - HTMLURL: c.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(), + HTMLURL: ctx.Repo.Repository.HTMLURL() + "/commits/" + commit.ID.String(), RepoCommit: &api.RepoCommit{ - URL: setting.AppURL + c.Link[1:], + URL: setting.AppURL + ctx.Link[1:], Author: &api.CommitUser{ Name: commit.Author.Name, Email: commit.Author.Email, @@ -81,7 +108,7 @@ func GetSingleCommit(c *context.APIContext) { }, Message: commit.Summary(), Tree: &api.CommitMeta{ - URL: c.BaseURL + "/repos/" + c.Repo.Repository.FullName() + "/tree/" + commit.ID.String(), + URL: ctx.Repo.Repository.APIURL() + "/trees/" + commit.ID.String(), SHA: commit.ID.String(), }, }, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 6989a05073e8..4422dcb39b77 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -1622,6 +1622,49 @@ } } }, + "/repos/{owner}/{repo}/git/commits/{sha}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get a single commit from a repository", + "operationId": "repoGetSingleCommit", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "the commit hash", + "name": "sha", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Commit" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/git/refs": { "get": { "produces": [ @@ -7570,6 +7613,13 @@ "description": "PayloadCommit represents a commit", "type": "object", "properties": { + "added": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Added" + }, "author": { "$ref": "#/definitions/PayloadUser" }, @@ -7585,6 +7635,20 @@ "type": "string", "x-go-name": "Message" }, + "modified": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Modified" + }, + "removed": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Removed" + }, "timestamp": { "type": "string", "format": "date-time", diff --git a/vendor/code.gitea.io/sdk/gitea/hook.go b/vendor/code.gitea.io/sdk/gitea/hook.go index ee6e2b79d50e..d36e4d4375ff 100644 --- a/vendor/code.gitea.io/sdk/gitea/hook.go +++ b/vendor/code.gitea.io/sdk/gitea/hook.go @@ -160,6 +160,9 @@ type PayloadCommit struct { Verification *PayloadCommitVerification `json:"verification"` // swagger:strfmt date-time Timestamp time.Time `json:"timestamp"` + Added []string `json:"added"` + Removed []string `json:"removed"` + Modified []string `json:"modified"` } // PayloadCommitVerification represents the GPG verification of a commit diff --git a/vendor/code.gitea.io/sdk/gitea/repo_commit.go b/vendor/code.gitea.io/sdk/gitea/repo_commit.go new file mode 100644 index 000000000000..2954f9fc1c36 --- /dev/null +++ b/vendor/code.gitea.io/sdk/gitea/repo_commit.go @@ -0,0 +1,48 @@ +// Copyright 2018 The Gogs Authors. All rights reserved. +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package gitea + +import ( + "fmt" +) + +// CommitMeta contains meta information of a commit in terms of API. +type CommitMeta struct { + URL string `json:"url"` + SHA string `json:"sha"` +} + +// CommitUser contains information of a user in the context of a commit. +type CommitUser struct { + Name string `json:"name"` + Email string `json:"email"` + Date string `json:"date"` +} + +// RepoCommit contains information of a commit in the context of a repository. +type RepoCommit struct { + URL string `json:"url"` + Author *CommitUser `json:"author"` + Committer *CommitUser `json:"committer"` + Message string `json:"message"` + Tree *CommitMeta `json:"tree"` +} + +// Commit contains information generated from a Git commit. +type Commit struct { + *CommitMeta + HTMLURL string `json:"html_url"` + RepoCommit *RepoCommit `json:"commit"` + Author *User `json:"author"` + Committer *User `json:"committer"` + Parents []*CommitMeta `json:"parents"` +} + +// GetSingleCommit returns a single commit +func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, error) { + commit := new(Commit) + return commit, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s", user, repo, commitID), nil, nil, &commit) +} diff --git a/vendor/github.com/go-macaron/gzip/LICENSE b/vendor/github.com/go-macaron/gzip/LICENSE deleted file mode 100644 index 37ec93a14fdc..000000000000 --- a/vendor/github.com/go-macaron/gzip/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/github.com/go-macaron/gzip/gzip.go b/vendor/github.com/go-macaron/gzip/gzip.go deleted file mode 100644 index 296c0e38b3fb..000000000000 --- a/vendor/github.com/go-macaron/gzip/gzip.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2013 Martini Authors -// Copyright 2015 The Macaron Authors -// -// 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 gzip - -import ( - "bufio" - "fmt" - "net" - "net/http" - "strings" - - "github.com/klauspost/compress/gzip" - "gopkg.in/macaron.v1" -) - -const ( - _HEADER_ACCEPT_ENCODING = "Accept-Encoding" - _HEADER_CONTENT_ENCODING = "Content-Encoding" - _HEADER_CONTENT_LENGTH = "Content-Length" - _HEADER_CONTENT_TYPE = "Content-Type" - _HEADER_VARY = "Vary" -) - -// Options represents a struct for specifying configuration options for the GZip middleware. -type Options struct { - // Compression level. Can be DefaultCompression(-1), ConstantCompression(-2) - // or any integer value between BestSpeed(1) and BestCompression(9) inclusive. - CompressionLevel int -} - -func isCompressionLevelValid(level int) bool { - return level == gzip.DefaultCompression || - level == gzip.ConstantCompression || - (level >= gzip.BestSpeed && level <= gzip.BestCompression) -} - -func prepareOptions(options []Options) Options { - var opt Options - if len(options) > 0 { - opt = options[0] - } - - if !isCompressionLevelValid(opt.CompressionLevel) { - // For web content, level 4 seems to be a sweet spot. - opt.CompressionLevel = 4 - } - return opt -} - -// Gziper returns a Handler that adds gzip compression to all requests. -// Make sure to include the Gzip middleware above other middleware -// that alter the response body (like the render middleware). -func Gziper(options ...Options) macaron.Handler { - opt := prepareOptions(options) - - return func(ctx *macaron.Context) { - if !strings.Contains(ctx.Req.Header.Get(_HEADER_ACCEPT_ENCODING), "gzip") { - return - } - - headers := ctx.Resp.Header() - headers.Set(_HEADER_CONTENT_ENCODING, "gzip") - headers.Set(_HEADER_VARY, _HEADER_ACCEPT_ENCODING) - - // We've made sure compression level is valid in prepareGzipOptions, - // no need to check same error again. - gz, err := gzip.NewWriterLevel(ctx.Resp, opt.CompressionLevel) - if err != nil { - panic(err.Error()) - } - defer gz.Close() - - gzw := gzipResponseWriter{gz, ctx.Resp} - ctx.Resp = gzw - ctx.MapTo(gzw, (*http.ResponseWriter)(nil)) - - // Check if render middleware has been registered, - // if yes, we need to modify ResponseWriter for it as well. - if _, ok := ctx.Render.(*macaron.DummyRender); !ok { - ctx.Render.SetResponseWriter(gzw) - } - - ctx.Next() - - // delete content length after we know we have been written to - gzw.Header().Del("Content-Length") - } -} - -type gzipResponseWriter struct { - w *gzip.Writer - macaron.ResponseWriter -} - -func (grw gzipResponseWriter) Write(p []byte) (int, error) { - if len(grw.Header().Get(_HEADER_CONTENT_TYPE)) == 0 { - grw.Header().Set(_HEADER_CONTENT_TYPE, http.DetectContentType(p)) - } - return grw.w.Write(p) -} - -func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hijacker, ok := grw.ResponseWriter.(http.Hijacker) - if !ok { - return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") - } - return hijacker.Hijack() -} From da2df5bb1f9acda7a2a25e7bfa61b955fc912d6e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 25 Jan 2019 18:26:07 +0800 Subject: [PATCH 3/6] add tests --- integrations/api_repo_git_commits_test.go | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 integrations/api_repo_git_commits_test.go diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go new file mode 100644 index 000000000000..587e9de5b267 --- /dev/null +++ b/integrations/api_repo_git_commits_test.go @@ -0,0 +1,32 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/models" +) + +func TestAPIReposGitCommits(t *testing.T) { + prepareTestEnv(t) + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + // Login as User2. + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + + for _, ref := range [...]string{ + "commits/master", // Branch + "commits/v1.1", // Tag + } { + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/%s?token="+token, user.Name, ref) + session.MakeRequest(t, req, http.StatusOK) + } + + // Test getting non-existent refs + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/unknown?token="+token, user.Name) + session.MakeRequest(t, req, http.StatusNotFound) +} From 28e8950f3bb22781fdaad27e64e51a6c5e93543f Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Sat, 2 Feb 2019 22:04:15 -0500 Subject: [PATCH 4/6] fix swagger --- routers/api/v1/swagger/repo.go | 7 +++ templates/swagger/v1_json.tmpl | 99 ++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 0c9f95f9627f..5b930e295e2c 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -140,3 +140,10 @@ type swaggerGitTreeResponse struct { //in: body Body api.GitTreeResponse `json:"body"` } + +// Commit +// swagger:response Commit +type swaggerCommit struct { + //in: body + Body api.Commit `json:"body"` +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4422dcb39b77..0ce6b805f773 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -6217,6 +6217,75 @@ }, "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "Commit": { + "type": "object", + "title": "Commit contains information generated from a Git commit.", + "properties": { + "author": { + "$ref": "#/definitions/User" + }, + "commit": { + "$ref": "#/definitions/RepoCommit" + }, + "committer": { + "$ref": "#/definitions/User" + }, + "html_url": { + "type": "string", + "x-go-name": "HTMLURL" + }, + "parents": { + "type": "array", + "items": { + "$ref": "#/definitions/CommitMeta" + }, + "x-go-name": "Parents" + }, + "sha": { + "type": "string", + "x-go-name": "SHA" + }, + "url": { + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, + "CommitMeta": { + "type": "object", + "title": "CommitMeta contains meta information of a commit in terms of API.", + "properties": { + "sha": { + "type": "string", + "x-go-name": "SHA" + }, + "url": { + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, + "CommitUser": { + "type": "object", + "title": "CommitUser contains information of a user in the context of a commit.", + "properties": { + "date": { + "type": "string", + "x-go-name": "Date" + }, + "email": { + "type": "string", + "x-go-name": "Email" + }, + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "CreateEmailOption": { "description": "CreateEmailOption options when creating email addresses", "type": "object", @@ -7995,6 +8064,30 @@ }, "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "RepoCommit": { + "type": "object", + "title": "RepoCommit contains information of a commit in the context of a repository.", + "properties": { + "author": { + "$ref": "#/definitions/CommitUser" + }, + "committer": { + "$ref": "#/definitions/CommitUser" + }, + "message": { + "type": "string", + "x-go-name": "Message" + }, + "tree": { + "$ref": "#/definitions/CommitMeta" + }, + "url": { + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "Repository": { "description": "Repository represents a repository", "type": "object", @@ -8425,6 +8518,12 @@ } } }, + "Commit": { + "description": "Commit", + "schema": { + "$ref": "#/definitions/Commit" + } + }, "DeployKey": { "description": "DeployKey", "schema": { From 50d2b14117982d56861a7942acc576a6d319217a Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Sat, 2 Feb 2019 22:13:04 -0500 Subject: [PATCH 5/6] Update api_repo_git_commits_test.go --- integrations/api_repo_git_commits_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go index 587e9de5b267..798a7c82029e 100644 --- a/integrations/api_repo_git_commits_test.go +++ b/integrations/api_repo_git_commits_test.go @@ -27,6 +27,6 @@ func TestAPIReposGitCommits(t *testing.T) { } // Test getting non-existent refs - req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/unknown?token="+token, user.Name) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/aaaaaaa?token="+token, user.Name) session.MakeRequest(t, req, http.StatusNotFound) } From fbb657d242a0cfcdecfe25ba55f754fb320833e5 Mon Sep 17 00:00:00 2001 From: Matti Ranta Date: Sat, 2 Feb 2019 22:25:45 -0500 Subject: [PATCH 6/6] dep ensure -update code.gitea.io/git --- Gopkg.lock | 4 +- integrations/api_repo_git_commits_test.go | 2 +- vendor/code.gitea.io/git/commit.go | 52 +++++++++++++++++++++++ vendor/code.gitea.io/git/repo_commit.go | 3 ++ vendor/code.gitea.io/git/submodule.go | 16 +++++-- vendor/code.gitea.io/git/tree.go | 22 +++++++--- 6 files changed, 87 insertions(+), 12 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 1727b91afa82..65cdf7efa36c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,11 +3,11 @@ [[projects]] branch = "master" - digest = "1:ab875622908a804a327a95a1701002b150806a3c5406df51ec231eac16d3a1ca" + digest = "1:8a6c3c311918c0f08fa2899feae2c938a9bf22b51378e3720d63b80aca4e80aa" name = "code.gitea.io/git" packages = ["."] pruneopts = "NUT" - revision = "389d3c803e12a30dffcbb54a15c2242521bc4333" + revision = "d04f81a6f8979be39da165fc034447a805071b97" [[projects]] branch = "master" diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go index 798a7c82029e..587e9de5b267 100644 --- a/integrations/api_repo_git_commits_test.go +++ b/integrations/api_repo_git_commits_test.go @@ -27,6 +27,6 @@ func TestAPIReposGitCommits(t *testing.T) { } // Test getting non-existent refs - req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/aaaaaaa?token="+token, user.Name) + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/git/commits/unknown?token="+token, user.Name) session.MakeRequest(t, req, http.StatusNotFound) } diff --git a/vendor/code.gitea.io/git/commit.go b/vendor/code.gitea.io/git/commit.go index 5e8c91d30343..227df09b7d18 100644 --- a/vendor/code.gitea.io/git/commit.go +++ b/vendor/code.gitea.io/git/commit.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -9,6 +10,7 @@ import ( "bytes" "container/list" "fmt" + "io" "net/http" "strconv" "strings" @@ -279,6 +281,56 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) { return nil, nil } +// CommitFileStatus represents status of files in a commit. +type CommitFileStatus struct { + Added []string + Removed []string + Modified []string +} + +// NewCommitFileStatus creates a CommitFileStatus +func NewCommitFileStatus() *CommitFileStatus { + return &CommitFileStatus{ + []string{}, []string{}, []string{}, + } +} + +// GetCommitFileStatus returns file status of commit in given repository. +func GetCommitFileStatus(repoPath, commitID string) (*CommitFileStatus, error) { + stdout, w := io.Pipe() + done := make(chan struct{}) + fileStatus := NewCommitFileStatus() + go func() { + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) < 2 { + continue + } + + switch fields[0][0] { + case 'A': + fileStatus.Added = append(fileStatus.Added, fields[1]) + case 'D': + fileStatus.Removed = append(fileStatus.Removed, fields[1]) + case 'M': + fileStatus.Modified = append(fileStatus.Modified, fields[1]) + } + } + done <- struct{}{} + }() + + stderr := new(bytes.Buffer) + err := NewCommand("show", "--name-status", "--pretty=format:''", commitID).RunInDirPipeline(repoPath, w, stderr) + w.Close() // Close writer to exit parsing goroutine + if err != nil { + return nil, concatenateError(err, stderr.String()) + } + + <-done + return fileStatus, nil +} + // GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository. func GetFullCommitID(repoPath, shortID string) (string, error) { if len(shortID) >= 40 { diff --git a/vendor/code.gitea.io/git/repo_commit.go b/vendor/code.gitea.io/git/repo_commit.go index d5cab8f8736e..484568585fe9 100644 --- a/vendor/code.gitea.io/git/repo_commit.go +++ b/vendor/code.gitea.io/git/repo_commit.go @@ -140,6 +140,9 @@ func (repo *Repository) GetCommit(commitID string) (*Commit, error) { var err error commitID, err = NewCommand("rev-parse", commitID).RunInDir(repo.Path) if err != nil { + if strings.Contains(err.Error(), "unknown revision or path") { + return nil, ErrNotExist{commitID, ""} + } return nil, err } } diff --git a/vendor/code.gitea.io/git/submodule.go b/vendor/code.gitea.io/git/submodule.go index a0fe7b4a5691..294df3986ade 100644 --- a/vendor/code.gitea.io/git/submodule.go +++ b/vendor/code.gitea.io/git/submodule.go @@ -29,13 +29,12 @@ func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile { } } -// RefURL guesses and returns reference URL. -func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string { - if sf.refURL == "" { +func getRefURL(refURL, urlPrefix, parentPath string) string { + if refURL == "" { return "" } - url := strings.TrimSuffix(sf.refURL, ".git") + url := strings.TrimSuffix(refURL, ".git") // git://xxx/user/repo if strings.HasPrefix(url, "git://") { @@ -67,12 +66,21 @@ func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string { if strings.Contains(urlPrefix, url[i+1:j]) { return urlPrefix + url[j+1:] } + if strings.HasPrefix(url, "ssh://") || strings.HasPrefix(url, "git+ssh://") { + k := strings.Index(url[j+1:], "/") + return "http://" + url[i+1:j] + "/" + url[j+1:][k+1:] + } return "http://" + url[i+1:j] + "/" + url[j+1:] } return url } +// RefURL guesses and returns reference URL. +func (sf *SubModuleFile) RefURL(urlPrefix string, parentPath string) string { + return getRefURL(sf.refURL, urlPrefix, parentPath) +} + // RefID returns reference ID. func (sf *SubModuleFile) RefID() string { return sf.refID diff --git a/vendor/code.gitea.io/git/tree.go b/vendor/code.gitea.io/git/tree.go index b67bf5584084..b65fe194096c 100644 --- a/vendor/code.gitea.io/git/tree.go +++ b/vendor/code.gitea.io/git/tree.go @@ -18,6 +18,9 @@ type Tree struct { entries Entries entriesParsed bool + + entriesRecursive Entries + entriesRecursiveParsed bool } // NewTree create a new tree according the repository and commit id @@ -67,20 +70,29 @@ func (t *Tree) ListEntries() (Entries, error) { if err != nil { return nil, err } + t.entries, err = parseTreeEntries(stdout, t) + if err == nil { + t.entriesParsed = true + } + return t.entries, err } // ListEntriesRecursive returns all entries of current tree recursively including all subtrees func (t *Tree) ListEntriesRecursive() (Entries, error) { - if t.entriesParsed { - return t.entries, nil + if t.entriesRecursiveParsed { + return t.entriesRecursive, nil } stdout, err := NewCommand("ls-tree", "-t", "-r", t.ID.String()).RunInDirBytes(t.repo.Path) - if err != nil { return nil, err } - t.entries, err = parseTreeEntries(stdout, t) - return t.entries, err + + t.entriesRecursive, err = parseTreeEntries(stdout, t) + if err == nil { + t.entriesRecursiveParsed = true + } + + return t.entriesRecursive, err }