From cbe7f5296e0400a6327b484cf78d6ffb93c9dd2c Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Sun, 7 Feb 2021 15:43:40 +0100
Subject: [PATCH] [API] Add affected files of commits to commit struct (#14579)

* Add files affected by a commit to gitea API -- similar to github

* Add files affected by a commit to gitea API

* Fix stupid error

* Fix other stupid typo

* Generate swagger tmpl

* Comply with convert to git commit refacto

* update swagger docs

* extend test

* format code

* Update integrations/api_repo_git_commits_test.go

* Update modules/convert/git_commit.go

Co-authored-by: Laurent Cahour <laurent.cahour@dont-nod.com>
Co-authored-by: zeripath <art27@cantab.net>
---
 integrations/api_repo_git_commits_test.go | 24 +++++++++++++++++------
 modules/convert/git_commit.go             | 15 ++++++++++++++
 modules/structs/repo_commit.go            | 16 ++++++++++-----
 templates/swagger/v1_json.tmpl            | 18 +++++++++++++++++
 4 files changed, 62 insertions(+), 11 deletions(-)

diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go
index 5b0f82e854..d6bd5fc62e 100644
--- a/integrations/api_repo_git_commits_test.go
+++ b/integrations/api_repo_git_commits_test.go
@@ -14,6 +14,14 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func compareCommitFiles(t *testing.T, expect []string, files []*api.CommitAffectedFiles) {
+	var actual []string
+	for i := range files {
+		actual = append(actual, files[i].Filename)
+	}
+	assert.ElementsMatch(t, expect, actual)
+}
+
 func TestAPIReposGitCommits(t *testing.T) {
 	defer prepareTestEnv(t)()
 	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
@@ -56,10 +64,13 @@ func TestAPIReposGitCommitList(t *testing.T) {
 	var apiData []api.Commit
 	DecodeJSON(t, resp, &apiData)
 
-	assert.Equal(t, 3, len(apiData))
-	assert.Equal(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA)
-	assert.Equal(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA)
-	assert.Equal(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA)
+	assert.Len(t, apiData, 3)
+	assert.EqualValues(t, "69554a64c1e6030f051e5c3f94bfbd773cd6a324", apiData[0].CommitMeta.SHA)
+	compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files)
+	assert.EqualValues(t, "27566bd5738fc8b4e3fef3c5e72cce608537bd95", apiData[1].CommitMeta.SHA)
+	compareCommitFiles(t, []string{"readme.md"}, apiData[1].Files)
+	assert.EqualValues(t, "5099b81332712fe655e34e8dd63574f503f61811", apiData[2].CommitMeta.SHA)
+	compareCommitFiles(t, []string{"readme.md"}, apiData[2].Files)
 }
 
 func TestAPIReposGitCommitListPage2Empty(t *testing.T) {
@@ -76,7 +87,7 @@ func TestAPIReposGitCommitListPage2Empty(t *testing.T) {
 	var apiData []api.Commit
 	DecodeJSON(t, resp, &apiData)
 
-	assert.Equal(t, 0, len(apiData))
+	assert.Len(t, apiData, 0)
 }
 
 func TestAPIReposGitCommitListDifferentBranch(t *testing.T) {
@@ -93,6 +104,7 @@ func TestAPIReposGitCommitListDifferentBranch(t *testing.T) {
 	var apiData []api.Commit
 	DecodeJSON(t, resp, &apiData)
 
-	assert.Equal(t, 1, len(apiData))
+	assert.Len(t, apiData, 1)
 	assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData[0].CommitMeta.SHA)
+	compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files)
 }
diff --git a/modules/convert/git_commit.go b/modules/convert/git_commit.go
index 87dfb51e70..4e30ec2c0b 100644
--- a/modules/convert/git_commit.go
+++ b/modules/convert/git_commit.go
@@ -131,6 +131,20 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 		}
 	}
 
+	// Retrieve files affected by the commit
+	fileStatus, err := git.GetCommitFileStatus(repo.RepoPath(), commit.ID.String())
+	if err != nil {
+		return nil, err
+	}
+	affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified))
+	for _, files := range [][]string{fileStatus.Added, fileStatus.Removed, fileStatus.Modified} {
+		for _, filename := range files {
+			affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{
+				Filename: filename,
+			})
+		}
+	}
+
 	return &api.Commit{
 		CommitMeta: &api.CommitMeta{
 			URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
@@ -162,5 +176,6 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 		Author:    apiAuthor,
 		Committer: apiCommitter,
 		Parents:   apiParents,
+		Files:     affectedFileList,
 	}, nil
 }
diff --git a/modules/structs/repo_commit.go b/modules/structs/repo_commit.go
index b9607b185b..f5c5f1b940 100644
--- a/modules/structs/repo_commit.go
+++ b/modules/structs/repo_commit.go
@@ -42,11 +42,12 @@ type RepoCommit struct {
 // 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"`
+	HTMLURL    string                 `json:"html_url"`
+	RepoCommit *RepoCommit            `json:"commit"`
+	Author     *User                  `json:"author"`
+	Committer  *User                  `json:"committer"`
+	Parents    []*CommitMeta          `json:"parents"`
+	Files      []*CommitAffectedFiles `json:"files"`
 }
 
 // CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
@@ -56,3 +57,8 @@ type CommitDateOptions struct {
 	// swagger:strfmt date-time
 	Committer time.Time `json:"committer"`
 }
+
+// CommitAffectedFiles store information about files affected by the commit
+type CommitAffectedFiles struct {
+	Filename string `json:"filename"`
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index fd760a28e6..5a3be37b4a 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -11821,6 +11821,13 @@
           "format": "date-time",
           "x-go-name": "Created"
         },
+        "files": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/CommitAffectedFiles"
+          },
+          "x-go-name": "Files"
+        },
         "html_url": {
           "type": "string",
           "x-go-name": "HTMLURL"
@@ -11843,6 +11850,17 @@
       },
       "x-go-package": "code.gitea.io/gitea/modules/structs"
     },
+    "CommitAffectedFiles": {
+      "description": "CommitAffectedFiles store information about files affected by the commit",
+      "type": "object",
+      "properties": {
+        "filename": {
+          "type": "string",
+          "x-go-name": "Filename"
+        }
+      },
+      "x-go-package": "code.gitea.io/gitea/modules/structs"
+    },
     "CommitDateOptions": {
       "description": "CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE",
       "type": "object",