This commit is contained in:
yp05327 2023-07-24 02:34:06 +00:00
parent 6517ad975f
commit 9a33437cd6
5 changed files with 126 additions and 72 deletions

View File

@ -393,55 +393,74 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
return committer.Commit()
}
// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created
// doer should not be nil
// if commitAfterUnix is 0, will find the branches committed in recently 6 hours
// TODO use options to find the branches
func FindRecentlyPushedNewBranches(ctx context.Context, baseRepo *repo_model.Repository, doer *user_model.User, commitAfterUnix int64) (BranchList, error) {
baseBranch, err := GetBranch(ctx, baseRepo.ID, baseRepo.DefaultBranch)
if err != nil {
return nil, err
}
type FindRecentlyPushedNewBranchesOptions struct {
Actor *user_model.User
Repo *repo_model.Repository
BaseRepo *repo_model.Repository
CommitAfterUnix int64
}
// search all related repos
// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created
// opts.Actor should not be nil
// if opts.CommitAfterUnix is 0, we will find the branches committed in recently 6 hours
func FindRecentlyPushedNewBranches(ctx context.Context, opts *FindRecentlyPushedNewBranchesOptions) (BranchList, error) {
// find all related repo ids
repoOpts := repo_model.SearchRepoOptions{
Actor: doer,
Actor: opts.Actor,
Private: true,
AllPublic: false, // Include also all public repositories of users and public organisations
AllLimited: false, // Include also all public repositories of limited organisations
Fork: util.OptionalBoolTrue,
ForkID: baseRepo.ID,
ForkFrom: opts.BaseRepo.ID,
Archived: util.OptionalBoolFalse,
}
repoCond := repo_model.SearchRepositoryCondition(&repoOpts).
And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode)).
Or(builder.Eq{"id": baseRepo.ID})
repoIds := builder.Select("id").From("repository").Where(repoCond)
repoCond := repo_model.SearchRepositoryCondition(&repoOpts).And(repo_model.AccessibleRepositoryCondition(opts.Actor, unit.TypeCode))
if opts.Repo == opts.BaseRepo {
// should also include the brase repo's branches
repoCond = repoCond.Or(builder.Eq{"id": opts.BaseRepo.ID})
} else {
// in fork repo, we only detect the fork repo's branch
repoCond = repoCond.And(builder.Eq{"id": opts.Repo.ID})
}
repoIDs := builder.Select("id").From("repository").Where(repoCond)
// avoid check branches which have already created PRs
// TODO add head_branch_id in pull_request table then we can get the branch id from pull_request table directly
// find branches which have already created PRs
prBranchIds := builder.Select("branch.id").From("branch").
InnerJoin("pull_request", "branch.name = pull_request.head_branch AND branch.repo_id = pull_request.head_repo_id").
InnerJoin("issue", "issue.id = pull_request.issue_id").
Where(builder.Or(
builder.Eq{"pull_request.has_merged": true},
builder.In("pull_request.head_repo_id", repoIds),
builder.Eq{"branch.id": baseBranch.ID},
Where(builder.And(
builder.Eq{"pull_request.base_repo_id": opts.BaseRepo.ID},
builder.Eq{"pull_request.base_branch": opts.BaseRepo.DefaultBranch},
builder.In("pull_request.head_repo_id", repoIDs),
builder.Or(
builder.Eq{"issue.is_closed": true},
builder.Eq{"pull_request.has_merged": true},
),
))
if commitAfterUnix == 0 {
commitAfterUnix = time.Now().Add(-time.Hour * 6).Unix()
if opts.CommitAfterUnix == 0 {
opts.CommitAfterUnix = time.Now().Add(-time.Hour * 6).Unix()
}
opts := FindBranchOptions{
IDCond: builder.NotIn("id", prBranchIds),
RepoCond: builder.In("repo_id", repoIds),
CommitCond: builder.Neq{"commit_id": baseBranch.CommitID}, // newly created branch have no changes, so skip them,
PusherID: doer.ID,
baseBranch, err := GetBranch(ctx, opts.BaseRepo.ID, opts.BaseRepo.DefaultBranch)
if err != nil {
return nil, err
}
findBranchOpts := FindBranchOptions{
RepoCond: builder.In("branch.repo_id", repoIDs),
CommitCond: builder.Neq{"branch.commit_id": baseBranch.CommitID}, // newly created branch have no changes, so skip them,
PusherID: opts.Actor.ID,
IsDeletedBranch: util.OptionalBoolFalse,
CommitAfterUnix: commitAfterUnix,
CommitAfterUnix: opts.CommitAfterUnix,
OrderBy: "branch.updated_unix DESC",
// should not use branch name here, because if there are branches with same name in different repos,
// we can not detect them correctly
PullRequestCond: builder.NotIn("branch.id", prBranchIds),
}
opts.PageSize = 2
opts.Page = 1
return FindBranches(ctx, opts)
// only display top 2 latest branch
findBranchOpts.PageSize = 2
findBranchOpts.Page = 1
return FindBranches(ctx, findBranchOpts)
}

View File

@ -87,7 +87,6 @@ func (branches BranchList) LoadRepo(ctx context.Context) error {
type FindBranchOptions struct {
db.ListOptions
IDCond builder.Cond
RepoIDs []int64 // overwrites RepoCond if the length is not 0
RepoCond builder.Cond
ExcludeBranchNames []string
@ -97,13 +96,13 @@ type FindBranchOptions struct {
CommitAfterUnix int64
CommitBeforeUnix int64
OrderBy string
// find branch by pull request
PullRequestCond builder.Cond
}
func (opts *FindBranchOptions) Cond() builder.Cond {
cond := builder.NewCond()
if opts.IDCond != nil {
cond = cond.And(opts.IDCond)
}
if len(opts.RepoIDs) == 1 {
opts.RepoCond = builder.Eq{"branch.repo_id": opts.RepoIDs[0]}
@ -136,6 +135,11 @@ func (opts *FindBranchOptions) Cond() builder.Cond {
if opts.CommitBeforeUnix != 0 {
cond = cond.And(builder.Lte{"branch.commit_time": opts.CommitBeforeUnix})
}
if opts.PullRequestCond != nil {
cond = cond.And(opts.PullRequestCond)
}
return cond
}

View File

@ -189,35 +189,61 @@ func TestFindRecentlyPushedNewBranches(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
// test new branch of the repo and org fork repo
// user2 is the owner of the repo and the organization
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
branches, err := git_model.FindRecentlyPushedNewBranches(db.DefaultContext, repo, user2, 1689838760)
assert.NoError(t, err)
assert.Equal(t, 2, len(branches))
assert.Equal(t, "new-commit", branches[0].Name)
assert.Equal(t, "org-fork-new-commit", branches[1].Name)
// test new branch from user fork repo
user8 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 8})
branches, err = git_model.FindRecentlyPushedNewBranches(db.DefaultContext, repo, user8, 1689838760)
assert.NoError(t, err)
assert.Equal(t, 1, len(branches))
assert.Equal(t, "user-fork-new-commit", branches[0].Name)
// test new branch from private org with code permisstion repo
user18 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18})
branches, err = git_model.FindRecentlyPushedNewBranches(db.DefaultContext, repo, user18, 1689838760)
assert.NoError(t, err)
assert.Equal(t, 1, len(branches))
assert.Equal(t, "private-org-fork-new-commit", branches[0].Name)
// test new branch from private org with no code permisstion repo
user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
branches, err = git_model.FindRecentlyPushedNewBranches(db.DefaultContext, repo, user5, 1689838760)
assert.NoError(t, err)
assert.Equal(t, 0, len(branches))
user8 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 8})
user18 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18})
tests := []struct {
name string
actor *user_model.User
count int
want []string
}{
// user2 is the owner of the repo and the organization
{
name: "new branch of the repo and org fork repo",
actor: user2,
count: 2,
want: []string{"new-commit", "org-fork-new-commit"},
},
{
name: "new branch from user fork repo",
actor: user8,
count: 1,
want: []string{"user-fork-new-commit"},
},
{
name: "new branch from private org with code permisstion repo",
actor: user18,
count: 1,
want: []string{"private-org-fork-new-commit"},
},
{
name: "new branch from private org with no code permisstion repo",
actor: user5,
count: 0,
want: []string{"new-commit", "org-fork-new-commit"},
},
}
opts := &git_model.FindRecentlyPushedNewBranchesOptions{
Repo: repo,
BaseRepo: repo,
CommitAfterUnix: 1689838760,
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts.Actor = tt.actor
branches, err := git_model.FindRecentlyPushedNewBranches(db.DefaultContext, opts)
assert.NoError(t, err)
assert.Equal(t, tt.count, len(branches))
for i := 1; i < tt.count; i++ {
assert.Equal(t, tt.want[i], branches[i].Name)
}
})
}
// TODO:test pr branch
}

View File

@ -133,8 +133,9 @@ type SearchRepoOptions struct {
// None -> include forks AND non-forks
// True -> include just forks
// False -> include just non-forks
Fork util.OptionalBool
ForkID int64 // If Fork option is True, you can use this option to limit the forks of a special repo by repo id.
Fork util.OptionalBool
// If Fork option is True, you can use this option to limit the forks of a special repo by repo id.
ForkFrom int64
// None -> include templates AND non-templates
// True -> include just templates
// False -> include just non-templates
@ -469,8 +470,8 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
} else {
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
if opts.ForkID > 0 && opts.Fork == util.OptionalBoolTrue {
cond = cond.And(builder.Eq{"fork_id": opts.ForkID})
if opts.ForkFrom > 0 && opts.Fork == util.OptionalBoolTrue {
cond = cond.And(builder.Eq{"fork_id": opts.ForkFrom})
}
}
}

View File

@ -983,11 +983,15 @@ func renderCode(ctx *context.Context) {
return
}
baseRepo := ctx.Repo.Repository
if ctx.Repo.Repository.IsFork {
baseRepo = ctx.Repo.Repository.BaseRepo
opts := &git_model.FindRecentlyPushedNewBranchesOptions{
Actor: ctx.Doer,
Repo: ctx.Repo.Repository,
BaseRepo: ctx.Repo.Repository,
}
branches, err := git_model.FindRecentlyPushedNewBranches(ctx, baseRepo, ctx.Doer, 0)
if ctx.Repo.Repository.IsFork {
opts.BaseRepo = ctx.Repo.Repository.BaseRepo
}
branches, err := git_model.FindRecentlyPushedNewBranches(ctx, opts)
if err != nil {
ctx.ServerError("FindRecentlyPushedNewBranches", err)
return