Skip to content

Commit 3e0687f

Browse files
committed
Fix the wrong push commits in the pull request when force push
1 parent 89cd373 commit 3e0687f

File tree

4 files changed

+119
-11
lines changed

4 files changed

+119
-11
lines changed

services/git/compare.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,38 @@ func (ci *CompareInfo) DirectComparison() bool {
4242
return ci.CompareSeparator == ".."
4343
}
4444

45+
// GetCompareCommitIDsWithMergeBase get commit IDs from repo in between oldCommitID and newCommitID
46+
// Commit on baseBranch will skip
47+
func GetCompareCommitIDsWithMergeBase(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID string) (commitIDs []string, err error) {
48+
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
49+
if err != nil {
50+
return nil, err
51+
}
52+
defer closer.Close()
53+
54+
newCommit, err := gitRepo.GetCommit(newCommitID)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
mergeBase, err := gitrepo.MergeBase(ctx, repo, oldCommitID, newCommitID)
60+
if err != nil {
61+
return nil, fmt.Errorf("MergeBase: %w", err)
62+
}
63+
64+
commits, err := gitRepo.ShowPrettyFormatLogToList(ctx, mergeBase+".."+newCommit.ID.String())
65+
if err != nil {
66+
return nil, fmt.Errorf("ShowPrettyFormatLogToList: %w", err)
67+
}
68+
69+
commitIDs = make([]string, 0, len(commits))
70+
for i := len(commits) - 1; i >= 0; i-- {
71+
commitIDs = append(commitIDs, commits[i].ID.String())
72+
}
73+
74+
return commitIDs, err
75+
}
76+
4577
// GetCompareInfo generates and returns compare information between base and head branches of repositories.
4678
func GetCompareInfo(ctx context.Context, baseRepo, headRepo *repo_model.Repository, headGitRepo *git.Repository, baseRef, headRef git.RefName, directComparison, fileOnly bool) (_ *CompareInfo, err error) {
4779
compareInfo := &CompareInfo{

services/git/compare_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package git
5+
6+
import (
7+
"testing"
8+
9+
issues_model "code.gitea.io/gitea/models/issues"
10+
"code.gitea.io/gitea/models/unittest"
11+
"code.gitea.io/gitea/modules/gitrepo"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestGetCompareCommitIDsWithMergeBase(t *testing.T) {
17+
assert.NoError(t, unittest.PrepareTestDatabase())
18+
19+
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
20+
assert.NoError(t, pr.LoadBaseRepo(t.Context()))
21+
22+
gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo)
23+
assert.NoError(t, err)
24+
defer gitRepo.Close()
25+
26+
headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch)
27+
assert.NoError(t, err)
28+
29+
baseCommit, err := gitRepo.GetBranchCommit(pr.BaseBranch)
30+
assert.NoError(t, err)
31+
32+
commitIDs, err := GetCompareCommitIDsWithMergeBase(t.Context(), pr.BaseRepo, pr.BaseBranch, headCommit.ID.String())
33+
assert.NoError(t, err)
34+
assert.NotEmpty(t, commitIDs)
35+
36+
commits, err := gitRepo.CommitsBetweenNotBase(headCommit, baseCommit, pr.BaseBranch)
37+
assert.NoError(t, err)
38+
39+
expected := make([]string, 0, len(commits))
40+
for i := len(commits) - 1; i >= 0; i-- {
41+
expected = append(expected, commits[i].ID.String())
42+
}
43+
44+
assert.Equal(t, expected, commitIDs)
45+
}

services/pull/comment.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"code.gitea.io/gitea/modules/gitrepo"
1414
"code.gitea.io/gitea/modules/json"
1515
"code.gitea.io/gitea/modules/log"
16+
git_service "code.gitea.io/gitea/services/git"
1617
)
1718

1819
// getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID
@@ -62,19 +63,24 @@ func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *iss
6263
}
6364

6465
var data issues_model.PushActionContent
65-
data.CommitIDs, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch)
66-
if err != nil {
67-
// For force-push events, a missing/unreachable old commit should not prevent
68-
// deleting stale push comments or creating the force-push timeline entry.
69-
if !isForcePush {
66+
if isForcePush {
67+
// if it's a force push, we need to get the whole pull request commits
68+
data.CommitIDs, err = git_service.GetCompareCommitIDsWithMergeBase(ctx, pr.BaseRepo, pr.BaseBranch, newCommitID)
69+
if err != nil {
70+
// For force-push events, a missing/unreachable old commit should not prevent
71+
// deleting stale push comments or creating the force-push timeline entry.
72+
log.Error("GetCompareCommitIDsWithMergeBase: %v", err)
73+
}
74+
} else {
75+
data.CommitIDs, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch)
76+
if err != nil {
7077
return nil, err
7178
}
72-
log.Error("getCommitIDsFromRepo: %v", err)
73-
}
74-
// It maybe an empty pull request. Only non-empty pull request need to create push comment
75-
// for force push, we always need to delete the old push comment so don't return here.
76-
if len(data.CommitIDs) == 0 && !isForcePush {
77-
return nil, nil //nolint:nilnil // return nil because no comment needs to be created
79+
// It maybe an empty pull request. Only non-empty pull request need to create push comment
80+
// for force push, we always need to delete the old push comment so don't return here.
81+
if len(data.CommitIDs) == 0 {
82+
return nil, nil //nolint:nilnil // return nil because no comment needs to be created
83+
}
7884
}
7985

8086
return db.WithTx2(ctx, func(ctx context.Context) (*issues_model.Comment, error) {

services/pull/comment_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,31 @@ func TestCreatePushPullCommentForcePushDeletesOldComments(t *testing.T) {
9393
assert.Equal(t, 1, forcePushCount)
9494
})
9595

96+
t.Run("force-push-ignores-missing-old-commit", func(t *testing.T) {
97+
assert.NoError(t, unittest.PrepareTestDatabase())
98+
99+
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
100+
assert.NoError(t, pr.LoadIssue(t.Context()))
101+
assert.NoError(t, pr.LoadBaseRepo(t.Context()))
102+
103+
pusher := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
104+
105+
gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo)
106+
assert.NoError(t, err)
107+
defer gitRepo.Close()
108+
109+
headCommit, err := gitRepo.GetBranchCommit(pr.HeadBranch)
110+
assert.NoError(t, err)
111+
112+
comment, err := CreatePushPullComment(t.Context(), pusher, pr, "0000000000000000000000000000000000000000", headCommit.ID.String(), true)
113+
assert.NoError(t, err)
114+
assert.NotNil(t, comment)
115+
var createdData issues_model.PushActionContent
116+
assert.NoError(t, json.Unmarshal([]byte(comment.Content), &createdData))
117+
assert.True(t, createdData.IsForcePush)
118+
assert.NotEmpty(t, createdData.CommitIDs)
119+
})
120+
96121
t.Run("head-vs-base-branch", func(t *testing.T) {
97122
assert.NoError(t, unittest.PrepareTestDatabase())
98123

0 commit comments

Comments
 (0)