code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/commits.go (about) 1 // Copyright 2018 The Gogs Authors. All rights reserved. 2 // Copyright 2019 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package repo 6 7 import ( 8 "fmt" 9 "math" 10 "net/http" 11 "strconv" 12 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/context" 15 "code.gitea.io/gitea/modules/git" 16 "code.gitea.io/gitea/modules/setting" 17 api "code.gitea.io/gitea/modules/structs" 18 "code.gitea.io/gitea/routers/api/v1/utils" 19 "code.gitea.io/gitea/services/convert" 20 ) 21 22 // GetSingleCommit get a commit via sha 23 func GetSingleCommit(ctx *context.APIContext) { 24 // swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha} repository repoGetSingleCommit 25 // --- 26 // summary: Get a single commit from a repository 27 // produces: 28 // - application/json 29 // parameters: 30 // - name: owner 31 // in: path 32 // description: owner of the repo 33 // type: string 34 // required: true 35 // - name: repo 36 // in: path 37 // description: name of the repo 38 // type: string 39 // required: true 40 // - name: sha 41 // in: path 42 // description: a git ref or commit sha 43 // type: string 44 // required: true 45 // - name: stat 46 // in: query 47 // description: include diff stats for every commit (disable for speedup, default 'true') 48 // type: boolean 49 // - name: verification 50 // in: query 51 // description: include verification for every commit (disable for speedup, default 'true') 52 // type: boolean 53 // - name: files 54 // in: query 55 // description: include a list of affected files for every commit (disable for speedup, default 'true') 56 // type: boolean 57 // responses: 58 // "200": 59 // "$ref": "#/responses/Commit" 60 // "422": 61 // "$ref": "#/responses/validationError" 62 // "404": 63 // "$ref": "#/responses/notFound" 64 65 sha := ctx.Params(":sha") 66 if !git.IsValidRefPattern(sha) { 67 ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha)) 68 return 69 } 70 71 getCommit(ctx, sha, convert.ParseCommitOptions(ctx)) 72 } 73 74 func getCommit(ctx *context.APIContext, identifier string, toCommitOpts convert.ToCommitOptions) { 75 commit, err := ctx.Repo.GitRepo.GetCommit(identifier) 76 if err != nil { 77 if git.IsErrNotExist(err) { 78 ctx.NotFound(identifier) 79 return 80 } 81 ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err) 82 return 83 } 84 85 json, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, toCommitOpts) 86 if err != nil { 87 ctx.Error(http.StatusInternalServerError, "toCommit", err) 88 return 89 } 90 ctx.JSON(http.StatusOK, json) 91 } 92 93 // GetAllCommits get all commits via 94 func GetAllCommits(ctx *context.APIContext) { 95 // swagger:operation GET /repos/{owner}/{repo}/commits repository repoGetAllCommits 96 // --- 97 // summary: Get a list of all commits from a repository 98 // produces: 99 // - application/json 100 // parameters: 101 // - name: owner 102 // in: path 103 // description: owner of the repo 104 // type: string 105 // required: true 106 // - name: repo 107 // in: path 108 // description: name of the repo 109 // type: string 110 // required: true 111 // - name: sha 112 // in: query 113 // description: SHA or branch to start listing commits from (usually 'master') 114 // type: string 115 // - name: path 116 // in: query 117 // description: filepath of a file/dir 118 // type: string 119 // - name: stat 120 // in: query 121 // description: include diff stats for every commit (disable for speedup, default 'true') 122 // type: boolean 123 // - name: verification 124 // in: query 125 // description: include verification for every commit (disable for speedup, default 'true') 126 // type: boolean 127 // - name: files 128 // in: query 129 // description: include a list of affected files for every commit (disable for speedup, default 'true') 130 // type: boolean 131 // - name: page 132 // in: query 133 // description: page number of results to return (1-based) 134 // type: integer 135 // - name: limit 136 // in: query 137 // description: page size of results (ignored if used with 'path') 138 // type: integer 139 // - name: not 140 // in: query 141 // description: commits that match the given specifier will not be listed. 142 // type: string 143 // responses: 144 // "200": 145 // "$ref": "#/responses/CommitList" 146 // "404": 147 // "$ref": "#/responses/notFound" 148 // "409": 149 // "$ref": "#/responses/EmptyRepository" 150 151 if ctx.Repo.Repository.IsEmpty { 152 ctx.JSON(http.StatusConflict, api.APIError{ 153 Message: "Git Repository is empty.", 154 URL: setting.API.SwaggerURL, 155 }) 156 return 157 } 158 159 listOptions := utils.GetListOptions(ctx) 160 if listOptions.Page <= 0 { 161 listOptions.Page = 1 162 } 163 164 if listOptions.PageSize > setting.Git.CommitsRangeSize { 165 listOptions.PageSize = setting.Git.CommitsRangeSize 166 } 167 168 sha := ctx.FormString("sha") 169 path := ctx.FormString("path") 170 not := ctx.FormString("not") 171 172 var ( 173 commitsCountTotal int64 174 commits []*git.Commit 175 err error 176 ) 177 178 if len(path) == 0 { 179 var baseCommit *git.Commit 180 if len(sha) == 0 { 181 // no sha supplied - use default branch 182 head, err := ctx.Repo.GitRepo.GetHEADBranch() 183 if err != nil { 184 ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err) 185 return 186 } 187 188 baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name) 189 if err != nil { 190 ctx.Error(http.StatusInternalServerError, "GetCommit", err) 191 return 192 } 193 } else { 194 // get commit specified by sha 195 baseCommit, err = ctx.Repo.GitRepo.GetCommit(sha) 196 if err != nil { 197 ctx.Error(http.StatusInternalServerError, "GetCommit", err) 198 return 199 } 200 } 201 202 // Total commit count 203 commitsCountTotal, err = git.CommitsCount(ctx.Repo.GitRepo.Ctx, git.CommitsCountOptions{ 204 RepoPath: ctx.Repo.GitRepo.Path, 205 Not: not, 206 Revision: []string{baseCommit.ID.String()}, 207 }) 208 209 if err != nil { 210 ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err) 211 return 212 } 213 214 // Query commits 215 commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize, not) 216 if err != nil { 217 ctx.Error(http.StatusInternalServerError, "CommitsByRange", err) 218 return 219 } 220 } else { 221 if len(sha) == 0 { 222 sha = ctx.Repo.Repository.DefaultBranch 223 } 224 225 commitsCountTotal, err = git.CommitsCount(ctx, 226 git.CommitsCountOptions{ 227 RepoPath: ctx.Repo.GitRepo.Path, 228 Not: not, 229 Revision: []string{sha}, 230 RelPath: []string{path}, 231 }) 232 233 if err != nil { 234 ctx.Error(http.StatusInternalServerError, "FileCommitsCount", err) 235 return 236 } else if commitsCountTotal == 0 { 237 ctx.NotFound("FileCommitsCount", nil) 238 return 239 } 240 241 commits, err = ctx.Repo.GitRepo.CommitsByFileAndRange( 242 git.CommitsByFileAndRangeOptions{ 243 Revision: sha, 244 File: path, 245 Not: not, 246 Page: listOptions.Page, 247 }) 248 249 if err != nil { 250 ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err) 251 return 252 } 253 } 254 255 pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize))) 256 userCache := make(map[string]*user_model.User) 257 apiCommits := make([]*api.Commit, len(commits)) 258 259 for i, commit := range commits { 260 // Create json struct 261 apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx)) 262 if err != nil { 263 ctx.Error(http.StatusInternalServerError, "toCommit", err) 264 return 265 } 266 } 267 268 ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) 269 ctx.SetTotalCountHeader(commitsCountTotal) 270 271 // kept for backwards compatibility 272 ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page)) 273 ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) 274 ctx.RespHeader().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) 275 ctx.RespHeader().Set("X-PageCount", strconv.Itoa(pageCount)) 276 ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) 277 ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore") 278 279 ctx.JSON(http.StatusOK, &apiCommits) 280 } 281 282 // DownloadCommitDiffOrPatch render a commit's raw diff or patch 283 func DownloadCommitDiffOrPatch(ctx *context.APIContext) { 284 // swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha}.{diffType} repository repoDownloadCommitDiffOrPatch 285 // --- 286 // summary: Get a commit's diff or patch 287 // produces: 288 // - text/plain 289 // parameters: 290 // - name: owner 291 // in: path 292 // description: owner of the repo 293 // type: string 294 // required: true 295 // - name: repo 296 // in: path 297 // description: name of the repo 298 // type: string 299 // required: true 300 // - name: sha 301 // in: path 302 // description: SHA of the commit to get 303 // type: string 304 // required: true 305 // - name: diffType 306 // in: path 307 // description: whether the output is diff or patch 308 // type: string 309 // enum: [diff, patch] 310 // required: true 311 // responses: 312 // "200": 313 // "$ref": "#/responses/string" 314 // "404": 315 // "$ref": "#/responses/notFound" 316 sha := ctx.Params(":sha") 317 diffType := git.RawDiffType(ctx.Params(":diffType")) 318 319 if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil { 320 if git.IsErrNotExist(err) { 321 ctx.NotFound(sha) 322 return 323 } 324 ctx.Error(http.StatusInternalServerError, "DownloadCommitDiffOrPatch", err) 325 return 326 } 327 }