Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
11 changes: 5 additions & 6 deletions models/migrations/v1_12/v136.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ package v1_12
import (
"fmt"
"math"
"path/filepath"
"strings"
"time"

"code.gitea.io/gitea/modules/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
Expand Down Expand Up @@ -85,12 +84,12 @@ func AddCommitDivergenceToPulls(x *xorm.Engine) error {
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
continue
}
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")

gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)

divergence, err := git.GetDivergingCommits(graceful.GetManager().HammerContext(), repoPath, pr.BaseBranch, gitRefName)
divergence, err := gitrepo.GetDivergingCommits(graceful.GetManager().HammerContext(),
repo_model.StorageRepo(repo_model.RelativePath(baseRepo.OwnerName, baseRepo.Name)),
pr.BaseBranch, gitRefName)
if err != nil {
log.Warn("Could not recalculate Divergence for pull: %d", pr.ID)
pr.CommitsAhead = 0
Expand Down
30 changes: 0 additions & 30 deletions modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,36 +243,6 @@ func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error
return time.Parse("Mon Jan _2 15:04:05 2006 -0700", commitTime)
}

// DivergeObject represents commit count diverging commits
type DivergeObject struct {
Ahead int
Behind int
}

// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath})
if err != nil {
return do, err
}
left, right, found := strings.Cut(strings.Trim(stdout, "\n"), "\t")
if !found {
return do, fmt.Errorf("git rev-list output is missing a tab: %q", stdout)
}

do.Behind, err = strconv.Atoi(left)
if err != nil {
return do, err
}
do.Ahead, err = strconv.Atoi(right)
if err != nil {
return do, err
}
return do, nil
}

// CreateBundle create bundle content to the target path
func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.Writer) error {
tmp, cleanup, err := setting.AppDataTempDir("git-repo-content").MkdirTempRandom("gitea-bundle")
Expand Down
24 changes: 0 additions & 24 deletions modules/git/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,3 @@ func TestRepoIsEmpty(t *testing.T) {
assert.NoError(t, err)
assert.True(t, isEmpty)
}

func TestRepoGetDivergingCommits(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
do, err := GetDivergingCommits(t.Context(), bareRepo1Path, "master", "branch2")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 1,
Behind: 5,
}, do)

do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "master")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
Behind: 0,
}, do)

do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "test")
assert.NoError(t, err)
assert.Equal(t, DivergeObject{
Ahead: 0,
Behind: 2,
}, do)
}
44 changes: 44 additions & 0 deletions modules/gitrepo/compare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"context"
"fmt"
"strconv"
"strings"

"code.gitea.io/gitea/modules/git/gitcmd"
)

// DivergeObject represents commit count diverging commits
type DivergeObject struct {
Ahead int
Behind int
}

// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(ctx context.Context, repo Repository, baseBranch, targetBranch string) (*DivergeObject, error) {
cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
stdout, _, err1 := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)})
if err1 != nil {
return nil, err1
}

left, right, found := strings.Cut(strings.Trim(stdout, "\n"), "\t")
if !found {
return nil, fmt.Errorf("git rev-list output is missing a tab: %q", stdout)
}

behind, err := strconv.Atoi(left)
if err != nil {
return nil, err
}
ahead, err := strconv.Atoi(right)
if err != nil {
return nil, err
}
return &DivergeObject{Ahead: ahead, Behind: behind}, nil
}
42 changes: 42 additions & 0 deletions modules/gitrepo/compare_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"testing"

"github.com/stretchr/testify/assert"
)

type mockRepository struct {
path string
}

func (r *mockRepository) RelativePath() string {
return r.path
}

func TestRepoGetDivergingCommits(t *testing.T) {
repo := &mockRepository{path: "repo1_bare"}
do, err := GetDivergingCommits(t.Context(), repo, "master", "branch2")
assert.NoError(t, err)
assert.Equal(t, &DivergeObject{
Ahead: 1,
Behind: 5,
}, do)

do, err = GetDivergingCommits(t.Context(), repo, "master", "master")
assert.NoError(t, err)
assert.Equal(t, &DivergeObject{
Ahead: 0,
Behind: 0,
}, do)

do, err = GetDivergingCommits(t.Context(), repo, "master", "test")
assert.NoError(t, err)
assert.Equal(t, &DivergeObject{
Ahead: 0,
Behind: 2,
}, do)
}
4 changes: 2 additions & 2 deletions modules/gitrepo/gitrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ type Repository interface {
RelativePath() string // We don't assume how the directory structure of the repository is, so we only need the relative path
}

// RelativePath should be an unix style path like username/reponame.git
// RelativePath should be a unix style path like username/reponame.git
// This method should change it according to the current OS.
func repoPath(repo Repository) string {
var repoPath = func(repo Repository) string {
return filepath.Join(setting.RepoRootPath, filepath.FromSlash(repo.RelativePath()))
}

Expand Down
30 changes: 30 additions & 0 deletions modules/gitrepo/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package gitrepo

import (
"os"
"path/filepath"
"testing"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/tempdir"
)

func TestMain(m *testing.M) {
gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home")
if err != nil {
log.Fatal("Unable to create temp dir: %v", err)
}
defer cleanup()

// resolve repository path relative to the test directory
repoPath = func(repo Repository) string {
return filepath.Join("../git/tests/repos", repo.RelativePath())
}

setting.Git.HomePath = gitHomePath
os.Exit(m.Run())
}
35 changes: 20 additions & 15 deletions services/pull/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,6 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
return err
}

divergence, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
if err != nil {
return err
}
pr.CommitsAhead = divergence.Ahead
pr.CommitsBehind = divergence.Behind

assigneeCommentMap := make(map[int64]*issues_model.Comment)

var reviewNotifiers []*issue_service.ReviewRequestNotifier
Expand Down Expand Up @@ -134,6 +127,19 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
return err
}

// Update Commit Divergence
divergence, err := GetDiverging(ctx, pr)
if err != nil {
return err
}
pr.CommitsAhead = divergence.Ahead
pr.CommitsBehind = divergence.Behind

err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind)
if err != nil {
return err
}

// add first push codes comment
if _, err := CreatePushPullComment(ctx, issue.Poster, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName(), false); err != nil {
return err
Expand Down Expand Up @@ -295,13 +301,6 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
pr.CommitsAhead = divergence.Ahead
pr.CommitsBehind = divergence.Behind

// add first push codes comment
baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil {
return err
}
defer baseGitRepo.Close()

return db.WithTx(ctx, func(ctx context.Context) error {
if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil {
return err
Expand Down Expand Up @@ -464,7 +463,13 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", opts.RepoID, opts.Branch, err)
return
}
baseRepo, err := repo_model.GetRepositoryByID(ctx, opts.RepoID)
if err != nil {
log.Error("GetRepositoryByID: %v", err)
return
}
for _, pr := range prs {
pr.BaseRepo = baseRepo // avoid loading again // FIXME: why only here does so but the code above doesn't do so
divergence, err := GetDiverging(ctx, pr)
if err != nil {
if git_model.IsErrBranchNotExist(err) && !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) {
Expand All @@ -486,7 +491,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
// checkIfPRContentChanged checks if diff to target branch has changed by push
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr) // FIXME: why it still needs to create a temp repo, since the alongside calls like GetDiverging doesn't do so anymore
if err != nil {
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
return false, err
Expand Down
32 changes: 15 additions & 17 deletions services/pull/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"
Expand All @@ -34,17 +34,20 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.
}
defer releaser()

diffCount, err := GetDiverging(ctx, pr)
if err != nil {
return err
} else if diffCount.Behind == 0 {
return fmt.Errorf("HeadBranch of PR %d is up to date", pr.Index)
}

if err := pr.LoadBaseRepo(ctx); err != nil {
log.Error("unable to load BaseRepo for %-v during update-by-merge: %v", pr, err)
return fmt.Errorf("unable to load BaseRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
}

if pr.ID > 0 { // only a real PR needs to check this FIXME: why it needs this check?
diffCount, err := GetDiverging(ctx, pr)
if err != nil {
return err
} else if diffCount.Behind == 0 {
return fmt.Errorf("HeadBranch of PR %d is up to date", pr.Index)
}
}

if err := pr.LoadHeadRepo(ctx); err != nil {
log.Error("unable to load HeadRepo for PR %-v during update-by-merge: %v", pr, err)
return fmt.Errorf("unable to load HeadRepo for PR[%d] during update-by-merge: %w", pr.ID, err)
Expand Down Expand Up @@ -173,17 +176,12 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
}

// GetDiverging determines how many commits a PR is ahead or behind the PR base branch
func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*git.DivergeObject, error) {
func GetDiverging(ctx context.Context, pr *issues_model.PullRequest) (*gitrepo.DivergeObject, error) {
log.Trace("GetDiverging[%-v]: compare commits", pr)
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
if err != nil {
if !git_model.IsErrBranchNotExist(err) {
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
}

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

diff, err := git.GetDivergingCommits(ctx, prCtx.tmpBasePath, baseBranch, trackingBranch)
return &diff, err
return gitrepo.GetDivergingCommits(ctx, pr.BaseRepo, pr.BaseBranch, pr.GetGitHeadRefName())
}
Loading
Loading