code.gitea.io/gitea@v1.22.3/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 issues_model "code.gitea.io/gitea/models/issues" 14 user_model "code.gitea.io/gitea/models/user" 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/context" 20 "code.gitea.io/gitea/services/convert" 21 ) 22 23 // GetSingleCommit get a commit via sha 24 func GetSingleCommit(ctx *context.APIContext) { 25 // swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha} repository repoGetSingleCommit 26 // --- 27 // summary: Get a single commit from a repository 28 // produces: 29 // - application/json 30 // parameters: 31 // - name: owner 32 // in: path 33 // description: owner of the repo 34 // type: string 35 // required: true 36 // - name: repo 37 // in: path 38 // description: name of the repo 39 // type: string 40 // required: true 41 // - name: sha 42 // in: path 43 // description: a git ref or commit sha 44 // type: string 45 // required: true 46 // - name: stat 47 // in: query 48 // description: include diff stats for every commit (disable for speedup, default 'true') 49 // type: boolean 50 // - name: verification 51 // in: query 52 // description: include verification for every commit (disable for speedup, default 'true') 53 // type: boolean 54 // - name: files 55 // in: query 56 // description: include a list of affected files for every commit (disable for speedup, default 'true') 57 // type: boolean 58 // responses: 59 // "200": 60 // "$ref": "#/responses/Commit" 61 // "422": 62 // "$ref": "#/responses/validationError" 63 // "404": 64 // "$ref": "#/responses/notFound" 65 66 sha := ctx.Params(":sha") 67 if !git.IsValidRefPattern(sha) { 68 ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha)) 69 return 70 } 71 72 getCommit(ctx, sha, convert.ParseCommitOptions(ctx)) 73 } 74 75 func getCommit(ctx *context.APIContext, identifier string, toCommitOpts convert.ToCommitOptions) { 76 commit, err := ctx.Repo.GitRepo.GetCommit(identifier) 77 if err != nil { 78 if git.IsErrNotExist(err) { 79 ctx.NotFound(identifier) 80 return 81 } 82 ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err) 83 return 84 } 85 86 json, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, toCommitOpts) 87 if err != nil { 88 ctx.Error(http.StatusInternalServerError, "toCommit", err) 89 return 90 } 91 ctx.JSON(http.StatusOK, json) 92 } 93 94 // GetAllCommits get all commits via 95 func GetAllCommits(ctx *context.APIContext) { 96 // swagger:operation GET /repos/{owner}/{repo}/commits repository repoGetAllCommits 97 // --- 98 // summary: Get a list of all commits from a repository 99 // produces: 100 // - application/json 101 // parameters: 102 // - name: owner 103 // in: path 104 // description: owner of the repo 105 // type: string 106 // required: true 107 // - name: repo 108 // in: path 109 // description: name of the repo 110 // type: string 111 // required: true 112 // - name: sha 113 // in: query 114 // description: SHA or branch to start listing commits from (usually 'master') 115 // type: string 116 // - name: path 117 // in: query 118 // description: filepath of a file/dir 119 // type: string 120 // - name: stat 121 // in: query 122 // description: include diff stats for every commit (disable for speedup, default 'true') 123 // type: boolean 124 // - name: verification 125 // in: query 126 // description: include verification for every commit (disable for speedup, default 'true') 127 // type: boolean 128 // - name: files 129 // in: query 130 // description: include a list of affected files for every commit (disable for speedup, default 'true') 131 // type: boolean 132 // - name: page 133 // in: query 134 // description: page number of results to return (1-based) 135 // type: integer 136 // - name: limit 137 // in: query 138 // description: page size of results (ignored if used with 'path') 139 // type: integer 140 // - name: not 141 // in: query 142 // description: commits that match the given specifier will not be listed. 143 // type: string 144 // responses: 145 // "200": 146 // "$ref": "#/responses/CommitList" 147 // "404": 148 // "$ref": "#/responses/notFound" 149 // "409": 150 // "$ref": "#/responses/EmptyRepository" 151 152 if ctx.Repo.Repository.IsEmpty { 153 ctx.JSON(http.StatusConflict, api.APIError{ 154 Message: "Git Repository is empty.", 155 URL: setting.API.SwaggerURL, 156 }) 157 return 158 } 159 160 listOptions := utils.GetListOptions(ctx) 161 if listOptions.Page <= 0 { 162 listOptions.Page = 1 163 } 164 165 if listOptions.PageSize > setting.Git.CommitsRangeSize { 166 listOptions.PageSize = setting.Git.CommitsRangeSize 167 } 168 169 sha := ctx.FormString("sha") 170 path := ctx.FormString("path") 171 not := ctx.FormString("not") 172 173 var ( 174 commitsCountTotal int64 175 commits []*git.Commit 176 err error 177 ) 178 179 if len(path) == 0 { 180 var baseCommit *git.Commit 181 if len(sha) == 0 { 182 // no sha supplied - use default branch 183 head, err := ctx.Repo.GitRepo.GetHEADBranch() 184 if err != nil { 185 ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err) 186 return 187 } 188 189 baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name) 190 if err != nil { 191 ctx.Error(http.StatusInternalServerError, "GetCommit", err) 192 return 193 } 194 } else { 195 // get commit specified by sha 196 baseCommit, err = ctx.Repo.GitRepo.GetCommit(sha) 197 if err != nil { 198 ctx.Error(http.StatusInternalServerError, "GetCommit", err) 199 return 200 } 201 } 202 203 // Total commit count 204 commitsCountTotal, err = git.CommitsCount(ctx.Repo.GitRepo.Ctx, git.CommitsCountOptions{ 205 RepoPath: ctx.Repo.GitRepo.Path, 206 Not: not, 207 Revision: []string{baseCommit.ID.String()}, 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 if err != nil { 249 ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err) 250 return 251 } 252 } 253 254 pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize))) 255 userCache := make(map[string]*user_model.User) 256 apiCommits := make([]*api.Commit, len(commits)) 257 258 for i, commit := range commits { 259 // Create json struct 260 apiCommits[i], err = convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, convert.ParseCommitOptions(ctx)) 261 if err != nil { 262 ctx.Error(http.StatusInternalServerError, "toCommit", err) 263 return 264 } 265 } 266 267 ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) 268 ctx.SetTotalCountHeader(commitsCountTotal) 269 270 // kept for backwards compatibility 271 ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page)) 272 ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) 273 ctx.RespHeader().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) 274 ctx.RespHeader().Set("X-PageCount", strconv.Itoa(pageCount)) 275 ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) 276 ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-Total", "X-PageCount", "X-HasMore") 277 278 ctx.JSON(http.StatusOK, &apiCommits) 279 } 280 281 // DownloadCommitDiffOrPatch render a commit's raw diff or patch 282 func DownloadCommitDiffOrPatch(ctx *context.APIContext) { 283 // swagger:operation GET /repos/{owner}/{repo}/git/commits/{sha}.{diffType} repository repoDownloadCommitDiffOrPatch 284 // --- 285 // summary: Get a commit's diff or patch 286 // produces: 287 // - text/plain 288 // parameters: 289 // - name: owner 290 // in: path 291 // description: owner of the repo 292 // type: string 293 // required: true 294 // - name: repo 295 // in: path 296 // description: name of the repo 297 // type: string 298 // required: true 299 // - name: sha 300 // in: path 301 // description: SHA of the commit to get 302 // type: string 303 // required: true 304 // - name: diffType 305 // in: path 306 // description: whether the output is diff or patch 307 // type: string 308 // enum: [diff, patch] 309 // required: true 310 // responses: 311 // "200": 312 // "$ref": "#/responses/string" 313 // "404": 314 // "$ref": "#/responses/notFound" 315 sha := ctx.Params(":sha") 316 diffType := git.RawDiffType(ctx.Params(":diffType")) 317 318 if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil { 319 if git.IsErrNotExist(err) { 320 ctx.NotFound(sha) 321 return 322 } 323 ctx.Error(http.StatusInternalServerError, "DownloadCommitDiffOrPatch", err) 324 return 325 } 326 } 327 328 // GetCommitPullRequest returns the merged pull request of the commit 329 func GetCommitPullRequest(ctx *context.APIContext) { 330 // swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/pull repository repoGetCommitPullRequest 331 // --- 332 // summary: Get the merged pull request of the commit 333 // produces: 334 // - application/json 335 // parameters: 336 // - name: owner 337 // in: path 338 // description: owner of the repo 339 // type: string 340 // required: true 341 // - name: repo 342 // in: path 343 // description: name of the repo 344 // type: string 345 // required: true 346 // - name: sha 347 // in: path 348 // description: SHA of the commit to get 349 // type: string 350 // required: true 351 // responses: 352 // "200": 353 // "$ref": "#/responses/PullRequest" 354 // "404": 355 // "$ref": "#/responses/notFound" 356 357 pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.Params("sha")) 358 if err != nil { 359 if issues_model.IsErrPullRequestNotExist(err) { 360 ctx.Error(http.StatusNotFound, "GetPullRequestByMergedCommit", err) 361 } else { 362 ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err) 363 } 364 return 365 } 366 367 if err = pr.LoadBaseRepo(ctx); err != nil { 368 ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) 369 return 370 } 371 if err = pr.LoadHeadRepo(ctx); err != nil { 372 ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) 373 return 374 } 375 ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer)) 376 }