diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index c5eafc77a..17f95d016 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -747,6 +747,10 @@ func Routes() *web.Route {
 					})
 				}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true))
 
+				m.Group("", func() {
+					// m.Get("/graph", repo.Graph)
+					m.Get("/commits/{sha:([a-f0-9]{7,40})$}/diff", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
+				}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
 				m.Group("/commits/count", func() {
 					m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.GetCommitsCount)
 					m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.GetCommitsCount)
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index e4dd16095..282798c54 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -16,10 +16,12 @@ import (
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/validation"
 	"code.gitea.io/gitea/routers/api/v1/utils"
+	"code.gitea.io/gitea/services/gitdiff"
 )
 
 // GetSingleCommit get a commit via sha
@@ -535,3 +537,40 @@ func GetFileAllCommits(ctx *context.APIContext) {
 
 	ctx.JSON(http.StatusOK, apiCommits)
 }
+
+// 获取 commit diff
+func Diff(ctx *context.APIContext) {
+
+	commitID := ctx.Params(":sha")
+
+	gitRepo := ctx.Repo.GitRepo
+
+	log.Info("gitRepo = ", gitRepo)
+
+	commit, err := gitRepo.GetCommit(commitID)
+	log.Info("commit=\n", commit)
+
+	if err != nil {
+		if git.IsErrNotExist(err) {
+			ctx.NotFound("Repo.GitRepo.GetCommit", err)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "Repo.GitRepo.GetCommit", err)
+		}
+		return
+	}
+	if len(commitID) != 40 {
+		commitID = commit.ID.String()
+	}
+	diff, err := gitdiff.GetDiffCommit(gitRepo,
+		commitID, setting.Git.MaxGitDiffLines,
+		setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
+	if err != nil {
+		ctx.NotFound("GetDiffCommitWithWhitespaceBehavior", err)
+		return
+	}
+	if err != nil {
+		ctx.NotFound("GetDiffCommit", err)
+		return
+	}
+	ctx.JSON(http.StatusOK, &diff)
+}
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index e4517fe77..687259efa 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -1217,6 +1217,11 @@ func GetDiffRange(gitRepo *git.Repository, beforeCommitID, afterCommitID string,
 	return GetDiffRangeWithWhitespaceBehavior(gitRepo, beforeCommitID, afterCommitID, maxLines, maxLineCharacters, maxFiles, "")
 }
 
+// GetDiffCommit builds a Diff representing the given commitID.
+func GetDiffCommit(gitRepo *git.Repository, commitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) {
+	return GetDiffRange(gitRepo, "", commitID, maxLines, maxLineCharacters, maxFiles)
+}
+
 // GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
 // Passing the empty string as beforeCommitID returns a diff from the parent commit.
 // The whitespaceBehavior is either an empty string or a git flag