Skip to content

Commit

Permalink
Add commits dropdown in PR files view and allow commit by commit revi…
Browse files Browse the repository at this point in the history
…ew (#25528)

This PR adds a new dropdown to select a commit or a commit range
(shift-click like github) of a Pull Request.
After selection of a commit only the changes of this commit will be shown.
When selecting a range of commits the diff of this range is shown.

This allows to review a PR commit by commit or by viewing only commit ranges.
The "Show changes since your last review" mechanism github uses is implemented, too.
When reviewing a single commit or a commit range the "Viewed" functionality is disabled.

## Screenshots

### The commit dropdown

![image](https://github.com/go-gitea/gitea/assets/51889757/0db3ae62-1272-436c-be64-4730c5d611e3)

### Selecting a commit range

![image](https://github.com/go-gitea/gitea/assets/51889757/ad81eedb-8437-42b0-8073-2d940c25fe8f)

### Show changes of a single commit only

![image](https://github.com/go-gitea/gitea/assets/51889757/6b1a113b-73ef-4ecc-adf6-bc2340bb8f97)

### Show changes of a commit range

![image](https://github.com/go-gitea/gitea/assets/51889757/6401b358-cd66-4c09-8baa-6cf6177f23a7)


Fixes #20989
Fixes #19263

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
  • Loading branch information
5 people committed Jul 28, 2023
1 parent 4971a10 commit 5553206
Show file tree
Hide file tree
Showing 71 changed files with 748 additions and 35 deletions.
17 changes: 17 additions & 0 deletions models/fixtures/issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,20 @@
created_unix: 946684830
updated_unix: 978307200
is_locked: false

-
id: 19
repo_id: 58
index: 1
poster_id: 2
original_author_id: 0
name: issue for pr
content: content
milestone_id: 0
priority: 0
is_closed: false
is_pull: true
num_comments: 0
created_unix: 946684830
updated_unix: 978307200
is_locked: false
13 changes: 13 additions & 0 deletions models/fixtures/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,16 @@
base_branch: master
merge_base: 2a47ca4b614a9f5a
has_merged: false

-
id: 7
type: 0 # gitea pull request
status: 2 # mergable
issue_id: 19
index: 1
head_repo_id: 58
base_repo_id: 58
head_branch: branch1
base_branch: main
merge_base: cbff181af4c9c7fee3cf6c106699e07d9a3f54e6
has_merged: false
30 changes: 30 additions & 0 deletions models/fixtures/repo_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -607,3 +607,33 @@
repo_id: 52
type: 1
created_unix: 946684810

-
id: 91
repo_id: 58
type: 1
created_unix: 946684810

-
id: 92
repo_id: 58
type: 2
created_unix: 946684810

-
id: 93
repo_id: 58
type: 3
created_unix: 946684810

-
id: 94
repo_id: 58
type: 4
created_unix: 946684810

-
id: 95
repo_id: 58
type: 5
created_unix: 946684810
31 changes: 31 additions & 0 deletions models/fixtures/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1662,3 +1662,34 @@
is_private: false
status: 0
num_issues: 0

-
id: 58 # org public repo
owner_id: 2
owner_name: user2
lower_name: commitsonpr
name: commitsonpr
default_branch: main
num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 1
num_closed_pulls: 0
num_milestones: 0
num_closed_milestones: 0
num_projects: 0
num_closed_projects: 0
is_private: false
is_empty: false
is_archived: false
is_mirror: false
status: 0
is_fork: false
fork_id: 0
is_template: false
template_id: 0
size: 0
is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false
2 changes: 1 addition & 1 deletion models/fixtures/user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
num_followers: 2
num_following: 1
num_stars: 2
num_repos: 13
num_repos: 14
num_teams: 0
num_members: 0
visibility: 0
Expand Down
2 changes: 1 addition & 1 deletion models/issues/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ func TestCountIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
count, err := issues_model.CountIssues(db.DefaultContext, &issues_model.IssuesOptions{})
assert.NoError(t, err)
assert.EqualValues(t, 18, count)
assert.EqualValues(t, 19, count)
}

func TestIssueLoadAttributes(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion models/issues/review_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func FindLatestReviews(ctx context.Context, opts FindReviewOptions) (ReviewList,
}

sess.In("id", builder.
Select("max ( id ) ").
Select("max(id)").
From("review").
Where(cond).
GroupBy("reviewer_id"))
Expand Down
6 changes: 3 additions & 3 deletions models/repo/repo_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,12 @@ func TestSearchRepository(t *testing.T) {
{
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
count: 30,
count: 31,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
count: 35,
count: 36,
},
{
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
Expand All @@ -255,7 +255,7 @@ func TestSearchRepository(t *testing.T) {
{
name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
count: 30,
count: 31,
},
{
name: "AllTemplates",
Expand Down
7 changes: 7 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,13 @@ pulls.switch_comparison_type = Switch comparison type
pulls.switch_head_and_base = Switch head and base
pulls.filter_branch = Filter branch
pulls.no_results = No results found.
pulls.show_all_commits = Show all commits
pulls.show_changes_since_your_last_review = Show changes since your last review
pulls.showing_only_single_commit = Showing only changes of commit %[1]s
pulls.showing_specified_commit_range = Showing only changes between %[1]s..%[2]s
pulls.select_commit_hold_shift_for_range = Select commit. Hold shift + click to select a range
pulls.review_only_possible_for_full_diff = Review is only possible when viewing the full diff
pulls.filter_changes_by_commit = Filter by commit
pulls.nothing_to_compare = These branches are equal. There is no need to create a pull request.
pulls.nothing_to_compare_and_allow_empty_pr = These branches are equal. This PR will be empty.
pulls.has_pull_request = `A pull request between these branches already exists: <a href="%[1]s">%[2]s#%[3]d</a>`
Expand Down
116 changes: 111 additions & 5 deletions routers/web/repo/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,42 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
return compareInfo
}

type pullCommitList struct {
Commits []pull_service.CommitInfo `json:"commits"`
LastReviewCommitSha string `json:"last_review_commit_sha"`
Locale map[string]string `json:"locale"`
}

// GetPullCommits get all commits for given pull request
func GetPullCommits(ctx *context.Context) {
issue := checkPullInfo(ctx)
if ctx.Written() {
return
}
resp := &pullCommitList{}

commits, lastReviewCommitSha, err := pull_service.GetPullCommits(ctx, issue)
if err != nil {
ctx.JSON(http.StatusInternalServerError, err)
return
}

// Get the needed locale
resp.Locale = map[string]string{
"lang": ctx.Locale.Language(),
"filter_changes_by_commit": ctx.Tr("repo.pulls.filter_changes_by_commit"),
"show_all_commits": ctx.Tr("repo.pulls.show_all_commits"),
"stats_num_commits": ctx.TrN(len(commits), "repo.activity.git_stats_commit_1", "repo.activity.git_stats_commit_n", len(commits)),
"show_changes_since_your_last_review": ctx.Tr("repo.pulls.show_changes_since_your_last_review"),
"select_commit_hold_shift_for_range": ctx.Tr("repo.pulls.select_commit_hold_shift_for_range"),
}

resp.Commits = commits
resp.LastReviewCommitSha = lastReviewCommitSha

ctx.JSON(http.StatusOK, resp)
}

// ViewPullCommits show commits for a pull request
func ViewPullCommits(ctx *context.Context) {
ctx.Data["PageIsPullList"] = true
Expand Down Expand Up @@ -739,7 +775,7 @@ func ViewPullCommits(ctx *context.Context) {
}

// ViewPullFiles render pull request changed files list page
func ViewPullFiles(ctx *context.Context) {
func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommit string, willShowSpecifiedCommitRange, willShowSpecifiedCommit bool) {
ctx.Data["PageIsPullList"] = true
ctx.Data["PageIsPullFiles"] = true

Expand All @@ -762,6 +798,33 @@ func ViewPullFiles(ctx *context.Context) {
prInfo = PrepareViewPullInfo(ctx, issue)
}

// Validate the given commit sha to show (if any passed)
if willShowSpecifiedCommit || willShowSpecifiedCommitRange {

foundStartCommit := len(specifiedStartCommit) == 0
foundEndCommit := len(specifiedEndCommit) == 0

if !(foundStartCommit && foundEndCommit) {
for _, commit := range prInfo.Commits {
if commit.ID.String() == specifiedStartCommit {
foundStartCommit = true
}
if commit.ID.String() == specifiedEndCommit {
foundEndCommit = true
}

if foundStartCommit && foundEndCommit {
break
}
}
}

if !(foundStartCommit && foundEndCommit) {
ctx.NotFound("Given SHA1 not found for this PR", nil)
return
}
}

if ctx.Written() {
return
} else if prInfo == nil {
Expand All @@ -775,12 +838,30 @@ func ViewPullFiles(ctx *context.Context) {
return
}

startCommitID = prInfo.MergeBase
endCommitID = headCommitID
ctx.Data["IsShowingOnlySingleCommit"] = willShowSpecifiedCommit

if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
if len(specifiedEndCommit) > 0 {
endCommitID = specifiedEndCommit
} else {
endCommitID = headCommitID
}
if len(specifiedStartCommit) > 0 {
startCommitID = specifiedStartCommit
} else {
startCommitID = prInfo.MergeBase
}
ctx.Data["IsShowingAllCommits"] = false
} else {
endCommitID = headCommitID
startCommitID = prInfo.MergeBase
ctx.Data["IsShowingAllCommits"] = true
}

ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["AfterCommitID"] = endCommitID
ctx.Data["BeforeCommitID"] = startCommitID

fileOnly := ctx.FormBool("file-only")

Expand All @@ -789,8 +870,8 @@ func ViewPullFiles(ctx *context.Context) {
if fileOnly && (len(files) == 2 || len(files) == 1) {
maxLines, maxFiles = -1, -1
}

diffOptions := &gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
AfterCommitID: endCommitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: maxLines,
Expand All @@ -799,9 +880,18 @@ func ViewPullFiles(ctx *context.Context) {
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
}

if !willShowSpecifiedCommit {
diffOptions.BeforeCommitID = startCommitID
}

var methodWithError string
var diff *gitdiff.Diff
if !ctx.IsSigned {

// if we're not logged in or only a single commit (or commit range) is shown we
// have to load only the diff and not get the viewed information
// as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in.
if !ctx.IsSigned || willShowSpecifiedCommit || willShowSpecifiedCommitRange {
diff, err = gitdiff.GetDiff(gitRepo, diffOptions, files...)
methodWithError = "GetDiff"
} else {
Expand Down Expand Up @@ -908,6 +998,22 @@ func ViewPullFiles(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplPullFiles)
}

func ViewPullFilesForSingleCommit(ctx *context.Context) {
viewPullFiles(ctx, "", ctx.Params("sha"), true, true)
}

func ViewPullFilesForRange(ctx *context.Context) {
viewPullFiles(ctx, ctx.Params("shaFrom"), ctx.Params("shaTo"), true, false)
}

func ViewPullFilesStartingFromCommit(ctx *context.Context) {
viewPullFiles(ctx, "", ctx.Params("sha"), true, false)
}

func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) {
viewPullFiles(ctx, "", "", false, false)
}

// UpdatePullRequest merge PR's baseBranch into headBranch
func UpdatePullRequest(ctx *context.Context) {
issue := checkPullInfo(ctx)
Expand Down
2 changes: 1 addition & 1 deletion routers/web/user/home_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestPulls(t *testing.T) {
Pulls(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())

assert.Len(t, ctx.Data["Issues"], 4)
assert.Len(t, ctx.Data["Issues"], 5)
}

func TestMilestones(t *testing.T) {
Expand Down
10 changes: 8 additions & 2 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1279,14 +1279,20 @@ func registerRoutes(m *web.Route) {
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
m.Get(".diff", repo.DownloadPullDiff)
m.Get(".patch", repo.DownloadPullPatch)
m.Get("/commits", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
m.Group("/commits", func() {
m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
m.Get("/list", context.RepoRef(), repo.GetPullCommits)
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
})
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
m.Post("/update", repo.UpdatePullRequest)
m.Post("/set_allow_maintainer_edit", web.Bind(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits)
m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
m.Group("/files", func() {
m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFiles)
m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Group("/reviews", func() {
m.Get("/new_comment", repo.RenderNewCodeCommentForm)
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
Expand Down
Loading

0 comments on commit 5553206

Please sign in to comment.