Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API endpoint to get changed files of a PR #21177

Merged
merged 37 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5ff626e
add api endpoint to get changed files of a pr
anbraten Jan 10, 2022
fdf926f
Merge branch 'main' into pr-changed-files-api
anbraten Jan 10, 2022
0c807d1
update swagger definitions
anbraten Jan 10, 2022
30a9ed6
Merge branch 'main' into pr-changed-files-api
anbraten Jan 22, 2022
1eac29d
Merge branch 'main' into changed-pr-files
qwerty287 Sep 12, 2022
4f39f56
Iimprovements to changed files API
qwerty287 Sep 15, 2022
6956977
Undo change
qwerty287 Sep 18, 2022
c090514
Merge branch 'main' into changed-pr-files
qwerty287 Sep 18, 2022
0ac5d40
Merge branch 'main' into changed-pr-files
qwerty287 Sep 18, 2022
01b73fc
Update swagger struct
qwerty287 Sep 18, 2022
cf63a4b
Merge branch 'main' into changed-pr-files
6543 Sep 19, 2022
a6ec85d
support status "added"
6543 Sep 19, 2022
6579236
no duble call
6543 Sep 19, 2022
ea6644c
rm NameHash as it's just sha1(Filename)
6543 Sep 19, 2022
af1a798
Remove `files` opt
qwerty287 Sep 19, 2022
eb7af53
Merge remote-tracking branch 'origin/changed-pr-files' into changed-p…
qwerty287 Sep 19, 2022
8ce1e20
Add simple test and add renamed support
qwerty287 Sep 19, 2022
5334a80
Merge branch 'main' into changed-pr-files
qwerty287 Sep 19, 2022
6fbb0ed
Escape in converter
qwerty287 Sep 19, 2022
26af5f7
fix lint
6543 Sep 19, 2022
5d1691d
Update convert.go
wxiaoguang Sep 20, 2022
6cedaf0
Merge branch 'main' into changed-pr-files
6543 Sep 20, 2022
7b997ae
Merge branch 'main' into changed-pr-files
qwerty287 Sep 24, 2022
777f114
Always include urls; add previous_filename
qwerty287 Sep 24, 2022
5c268ae
Use head instead of base repo
qwerty287 Sep 24, 2022
dbf3f6b
Merge branch 'main' into changed-pr-files
qwerty287 Sep 25, 2022
77349b7
Merge branch 'main' into changed-pr-files
qwerty287 Sep 25, 2022
0d77cde
Merge branch 'main' into changed-pr-files
6543 Sep 26, 2022
93072ca
Merge branch 'main' into changed-pr-files
6543 Sep 27, 2022
8739158
Merge branch 'main' into changed-pr-files
qwerty287 Sep 28, 2022
b884d2e
Add `Changes` and `unchaged` status
qwerty287 Sep 28, 2022
d41fd55
Update URLs behavior
qwerty287 Sep 28, 2022
07735cf
Merge branch 'main' into changed-pr-files
lafriks Sep 28, 2022
6eca81a
Merge branch 'main' into changed-pr-files
6543 Sep 29, 2022
ac865e1
fix fixtures to match gitea-repositories-meta/user2/repo1.git/refs
6543 Sep 29, 2022
929a279
wow
6543 Sep 29, 2022
96dcc0a
start with declarative tests
6543 Sep 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/generate-go-licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ func main() {
}

entries = append(entries, LicenseEntry{
Name: name,
Path: path,
Name: name,
Path: path,
LicenseText: string(licenseText),
})
}
Expand Down
23 changes: 23 additions & 0 deletions modules/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
webhook_service "code.gitea.io/gitea/services/webhook"
)

Expand Down Expand Up @@ -414,3 +415,25 @@ func ToLFSLock(l *git_model.LFSLock) *api.LFSLock {
},
}
}

// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
status := "changed"
if f.IsDeleted {
status = "deleted"
}
6543 marked this conversation as resolved.
Show resolved Hide resolved
qwerty287 marked this conversation as resolved.
Show resolved Hide resolved
file := &api.ChangedFile{
NameHash: f.NameHash,
Filename: f.Name,
Status: status,
Additions: f.Addition,
Deletions: f.Deletion,
}

if f.Name != "" {
file.HTMLURL = fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", f.GetDiffFileName())
file.ContentsURL = fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", f.GetDiffFileName())
}
qwerty287 marked this conversation as resolved.
Show resolved Hide resolved

return file
}
11 changes: 11 additions & 0 deletions modules/structs/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,14 @@ type EditPullRequestOption struct {
RemoveDeadline *bool `json:"unset_due_date"`
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
}

// ChangedFile store information about files affected by the pull request
type ChangedFile struct {
NameHash string `json:"name_hash"`
6543 marked this conversation as resolved.
Show resolved Hide resolved
Filename string `json:"filename"`
Status string `json:"status"`
Additions int `json:"additions"`
Deletions int `json:"deletions"`
HTMLURL string `json:"html_url,omitempty"`
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
ContentsURL string `json:"contents_url,omitempty"`
}
1 change: 1 addition & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,7 @@ func Routes(ctx gocontext.Context) *web.Route {
m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch)
m.Post("/update", reqToken(), repo.UpdatePullRequest)
m.Get("/commits", repo.GetPullRequestCommits)
m.Get("/files", context.ReferencesGitRepo(false), repo.GetPullRequestFiles)
6543 marked this conversation as resolved.
Show resolved Hide resolved
m.Combo("/merge").Get(repo.IsPullRequestMerged).
Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)
Expand Down
131 changes: 131 additions & 0 deletions routers/api/v1/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/automerge"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/gitdiff"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
Expand Down Expand Up @@ -1323,3 +1325,132 @@ func GetPullRequestCommits(ctx *context.APIContext) {

ctx.JSON(http.StatusOK, &apiCommits)
}

// GetPullRequestFiles gets all changed files associated with a given PR
func GetPullRequestFiles(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/files repository repoGetPullRequestFiles
// ---
// summary: Get changed files for a pull request
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the pull request to get
// type: integer
// format: int64
// required: true
// - name: skip-to
// in: query
// description: skip to given file
// type: string
// - name: whitespace
// in: query
// description: whitespace behavior
// type: string
// enum: [ignore-all, ignore-change, ignore-eol, show-all]
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/ChangedFileList"
// "404":
// "$ref": "#/responses/notFound"

pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
}
return
}

if err := pr.LoadBaseRepo(); err != nil {
ctx.InternalServerError(err)
return
}

baseGitRepo := ctx.Repo.GitRepo

var prInfo *git.CompareInfo
if pr.HasMerged {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false)
} else {
prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
}
if err != nil {
ctx.ServerError("GetCompareInfo", err)
return
}

headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
ctx.ServerError("GetRefCommitID", err)
return
}

startCommitID := prInfo.MergeBase
6543 marked this conversation as resolved.
Show resolved Hide resolved
endCommitID := headCommitID

maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles

diff, err := gitdiff.GetDiff(baseGitRepo,
&gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
AfterCommitID: endCommitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: maxLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: maxFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
}, ctx.FormStrings("files")...)
qwerty287 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
ctx.ServerError("GetDiff", err)
return
}

listOptions := utils.GetListOptions(ctx)

totalNumberOfFiles := diff.NumFiles
totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))

start, end := listOptions.GetStartEnd()

if end > totalNumberOfFiles {
end = totalNumberOfFiles
}

apiFiles := make([]*api.ChangedFile, 0, end-start)
for i := start; i < end; i++ {
apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], ctx.Repo.Repository, endCommitID))
}

ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
ctx.SetTotalCountHeader(int64(totalNumberOfFiles))

ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")

ctx.JSON(http.StatusOK, &apiFiles)
}
22 changes: 22 additions & 0 deletions routers/api/v1/swagger/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,28 @@ type swaggerCommitList struct {
Body []api.Commit `json:"body"`
}

// ChangedFileList
// swagger:response ChangedFileList
type swaggerChangedFileList struct {
6543 marked this conversation as resolved.
Show resolved Hide resolved
// The current page
Page int `json:"X-Page"`

// Commits per page
PerPage int `json:"X-PerPage"`

// Total commit count
Total int `json:"X-Total"`

// Total number of pages
PageCount int `json:"X-PageCount"`

// True if there is another page
HasMore bool `json:"X-HasMore"`

// in: body
Files []api.ChangedFile `json:"files"`
}

// Note
// swagger:response Note
type swaggerNote struct {
Expand Down
102 changes: 102 additions & 0 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -8019,6 +8019,62 @@
}
}
},
"/repos/{owner}/{repo}/pulls/{index}/files": {
"get": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Get changed files for a pull request",
"operationId": "repoGetPullRequestFiles",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"format": "int64",
"description": "index of the pull request to get",
"name": "index",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"$ref": "#/responses/ChangedFileList"
},
"404": {
"$ref": "#/responses/notFound"
}
}
}
},
"/repos/{owner}/{repo}/pulls/{index}/merge": {
"get": {
"produces": [
Expand Down Expand Up @@ -13715,6 +13771,17 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"ChangedFile": {
"description": "ChangedFile store information about files affected by the pull request",
"type": "object",
"properties": {
"filename": {
"type": "string",
"x-go-name": "Filename"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"CombinedStatus": {
"description": "CombinedStatus holds the combined state of several statuses for a single commit",
"type": "object",
Expand Down Expand Up @@ -19173,6 +19240,41 @@
}
}
},
"ChangedFileList": {
"description": "ChangedFileList",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/ChangedFile"
}
},
"headers": {
"X-HasMore": {
"type": "boolean",
"description": "True if there is another page"
},
"X-Page": {
"type": "integer",
"format": "int64",
"description": "The current page"
},
"X-PageCount": {
"type": "integer",
"format": "int64",
"description": "Total number of pages"
},
"X-PerPage": {
"type": "integer",
"format": "int64",
"description": "Commits per page"
},
"X-Total": {
"type": "integer",
"format": "int64",
"description": "Total commit count"
}
}
},
"CombinedStatus": {
"description": "CombinedStatus",
"schema": {
Expand Down