mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add API to get file commit history (#17652)
Adds an API endpoint `api/v1/repos/{owner}/{repo}/git/history/{filepath}` to get the commits affecting the given file or directory.
Closes https://github.com/go-gitea/gitea/issues/16206 and closes https://github.com/go-gitea/gitea/issues/16703
			
			
This commit is contained in:
		@@ -132,3 +132,21 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) {
 | 
				
			|||||||
		resp.Body.String())
 | 
							resp.Body.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetFileHistory(t *testing.T) {
 | 
				
			||||||
 | 
						defer prepareTestEnv(t)()
 | 
				
			||||||
 | 
						user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 | 
				
			||||||
 | 
						// Login as User2.
 | 
				
			||||||
 | 
						session := loginUser(t, user.Name)
 | 
				
			||||||
 | 
						token := getTokenForLoggedInUser(t, session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?path=readme.md&token="+token+"&sha=good-sign", user.Name)
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var apiData []api.Commit
 | 
				
			||||||
 | 
						DecodeJSON(t, resp, &apiData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Len(t, apiData, 1)
 | 
				
			||||||
 | 
						assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData[0].CommitMeta.SHA)
 | 
				
			||||||
 | 
						compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -108,13 +108,17 @@ func GetAllCommits(ctx *context.APIContext) {
 | 
				
			|||||||
	//   in: query
 | 
						//   in: query
 | 
				
			||||||
	//   description: SHA or branch to start listing commits from (usually 'master')
 | 
						//   description: SHA or branch to start listing commits from (usually 'master')
 | 
				
			||||||
	//   type: string
 | 
						//   type: string
 | 
				
			||||||
 | 
						// - name: path
 | 
				
			||||||
 | 
						//   in: query
 | 
				
			||||||
 | 
						//   description: filepath of a file/dir
 | 
				
			||||||
 | 
						//   type: string
 | 
				
			||||||
	// - name: page
 | 
						// - name: page
 | 
				
			||||||
	//   in: query
 | 
						//   in: query
 | 
				
			||||||
	//   description: page number of results to return (1-based)
 | 
						//   description: page number of results to return (1-based)
 | 
				
			||||||
	//   type: integer
 | 
						//   type: integer
 | 
				
			||||||
	// - name: limit
 | 
						// - name: limit
 | 
				
			||||||
	//   in: query
 | 
						//   in: query
 | 
				
			||||||
	//   description: page size of results
 | 
						//   description: page size of results (ignored if used with 'path')
 | 
				
			||||||
	//   type: integer
 | 
						//   type: integer
 | 
				
			||||||
	// responses:
 | 
						// responses:
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
@@ -149,46 +153,73 @@ func GetAllCommits(ctx *context.APIContext) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sha := ctx.FormString("sha")
 | 
						sha := ctx.FormString("sha")
 | 
				
			||||||
 | 
						path := ctx.FormString("path")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var baseCommit *git.Commit
 | 
						var (
 | 
				
			||||||
	if len(sha) == 0 {
 | 
							commitsCountTotal int64
 | 
				
			||||||
		// no sha supplied - use default branch
 | 
							commits           []*git.Commit
 | 
				
			||||||
		head, err := gitRepo.GetHEADBranch()
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(path) == 0 {
 | 
				
			||||||
 | 
							var baseCommit *git.Commit
 | 
				
			||||||
 | 
							if len(sha) == 0 {
 | 
				
			||||||
 | 
								// no sha supplied - use default branch
 | 
				
			||||||
 | 
								head, err := gitRepo.GetHEADBranch()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								baseCommit, err = gitRepo.GetBranchCommit(head.Name)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.Error(http.StatusInternalServerError, "GetCommit", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// get commit specified by sha
 | 
				
			||||||
 | 
								baseCommit, err = gitRepo.GetCommit(sha)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									ctx.Error(http.StatusInternalServerError, "GetCommit", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Total commit count
 | 
				
			||||||
 | 
							commitsCountTotal, err = baseCommit.CommitsCount()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err)
 | 
								ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		baseCommit, err = gitRepo.GetBranchCommit(head.Name)
 | 
							// Query commits
 | 
				
			||||||
 | 
							commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "GetCommit", err)
 | 
								ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		// get commit specified by sha
 | 
							if len(sha) == 0 {
 | 
				
			||||||
		baseCommit, err = gitRepo.GetCommit(sha)
 | 
								sha = ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							commitsCountTotal, err = gitRepo.FileCommitsCount(sha, path)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "GetCommit", err)
 | 
								ctx.Error(http.StatusInternalServerError, "FileCommitsCount", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							} else if commitsCountTotal == 0 {
 | 
				
			||||||
 | 
								ctx.NotFound("FileCommitsCount", nil)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							commits, err = gitRepo.CommitsByFileAndRange(sha, path, listOptions.Page)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Total commit count
 | 
					 | 
				
			||||||
	commitsCountTotal, err := baseCommit.CommitsCount()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
 | 
						pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Query commits
 | 
					 | 
				
			||||||
	commits, err := baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	userCache := make(map[string]*user_model.User)
 | 
						userCache := make(map[string]*user_model.User)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiCommits := make([]*api.Commit, len(commits))
 | 
						apiCommits := make([]*api.Commit, len(commits))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2950,6 +2950,12 @@
 | 
				
			|||||||
            "name": "sha",
 | 
					            "name": "sha",
 | 
				
			||||||
            "in": "query"
 | 
					            "in": "query"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "type": "string",
 | 
				
			||||||
 | 
					            "description": "filepath of a file/dir",
 | 
				
			||||||
 | 
					            "name": "path",
 | 
				
			||||||
 | 
					            "in": "query"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "type": "integer",
 | 
					            "type": "integer",
 | 
				
			||||||
            "description": "page number of results to return (1-based)",
 | 
					            "description": "page number of results to return (1-based)",
 | 
				
			||||||
@@ -2958,7 +2964,7 @@
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "type": "integer",
 | 
					            "type": "integer",
 | 
				
			||||||
            "description": "page size of results",
 | 
					            "description": "page size of results (ignored if used with 'path')",
 | 
				
			||||||
            "name": "limit",
 | 
					            "name": "limit",
 | 
				
			||||||
            "in": "query"
 | 
					            "in": "query"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user