is_deleted: false
deleted_by_id: 0
deleted_unix: 0
+
+-
+ id: 5
+ repo_id: 10
+ name: 'master'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: 'Initial commit'
+ commit_time: 1489927679
+ pusher_id: 12
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 6
+ repo_id: 10
+ name: 'outdated-new-branch'
+ commit_id: 'cb24c347e328d83c1e0c3c908a6b2c0a2fcb8a3d'
+ commit_message: 'add'
+ commit_time: 1489927679
+ pusher_id: 12
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
+
+-
+ id: 14
+ repo_id: 11
+ name: 'master'
+ commit_id: '65f1bf27bc3bf70f64657658635e66094edbcb4d'
+ commit_message: 'Initial commit'
+ commit_time: 1489927679
+ pusher_id: 13
+ is_deleted: false
+ deleted_by_id: 0
+ deleted_unix: 0
-
group_id: 1
max_index: 5
+
-
group_id: 2
max_index: 2
+
-
group_id: 3
max_index: 2
+
-
group_id: 10
max_index: 1
+
-
group_id: 32
max_index: 2
+
-
group_id: 48
max_index: 1
+
-
group_id: 42
max_index: 1
+
-
group_id: 50
max_index: 1
+
-
group_id: 51
max_index: 1
uid: 40
org_id: 41
is_public: true
+
+-
+ id: 21
+ uid: 12
+ org_id: 25
+ is_public: true
+
+-
+ id: 22
+ uid: 2
+ org_id: 35
+ is_public: true
is_archived: false
is_mirror: false
status: 0
- is_fork: false
+ is_fork: true
fork_id: 10
is_template: false
template_id: 0
num_members: 2
includes_all_repositories: false
can_create_org_repo: false
+
+-
+ id: 23
+ org_id: 25
+ lower_name: owners
+ name: Owners
+ authorize: 4 # owner
+ num_repos: 0
+ num_members: 1
+ includes_all_repositories: false
+ can_create_org_repo: true
+
+-
+ id: 24
+ org_id: 35
+ lower_name: team24
+ name: team24
+ authorize: 2 # write
+ num_repos: 0
+ num_members: 1
+ includes_all_repositories: true
+ can_create_org_repo: false
team_id: 22
type: 3
access_mode: 1
+
+-
+ id: 55
+ team_id: 18
+ type: 1 # code
+ access_mode: 4
+
+-
+ id: 56
+ team_id: 23
+ type: 1 # code
+ access_mode: 4
+
+-
+ id: 57
+ team_id: 24
+ type: 1 # code
+ access_mode: 2
org_id: 41
team_id: 22
uid: 39
+
+-
+ id: 26
+ org_id: 25
+ team_id: 23
+ uid: 12
+
+-
+ id: 27
+ org_id: 35
+ team_id: 24
+ uid: 2
num_following: 0
num_stars: 0
num_repos: 0
- num_teams: 1
- num_members: 1
+ num_teams: 2
+ num_members: 2
visibility: 0
repo_admin_change_team_access: false
theme: ""
num_following: 0
num_stars: 0
num_repos: 0
- num_teams: 1
- num_members: 1
+ num_teams: 2
+ num_members: 2
visibility: 2
repo_admin_change_team_access: false
theme: ""
"code.gitea.io/gitea/models/db"
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/log"
+ "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
// for pagination, keyword search and filtering
type Branch struct {
ID int64
- RepoID int64 `xorm:"UNIQUE(s)"`
- Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
+ RepoID int64 `xorm:"UNIQUE(s)"`
+ Repo *repo_model.Repository `xorm:"-"`
+ Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment
CommitID string
CommitMessage string `xorm:"TEXT"` // it only stores the message summary (the first line)
PusherID int64
return err
}
+func (b *Branch) LoadRepo(ctx context.Context) (err error) {
+ if b.Repo != nil || b.RepoID == 0 {
+ return nil
+ }
+ b.Repo, err = repo_model.GetRepositoryByID(ctx, b.RepoID)
+ return err
+}
+
func init() {
db.RegisterModel(new(Branch))
db.RegisterModel(new(RenamedBranch))
return committer.Commit()
}
-// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created
-// except the indicate branch
-func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64, excludeBranchName string) (BranchList, error) {
- branches := make(BranchList, 0, 2)
- subQuery := builder.Select("head_branch").From("pull_request").
- InnerJoin("issue", "issue.id = pull_request.issue_id").
- Where(builder.Eq{
- "pull_request.head_repo_id": repoID,
- "issue.is_closed": false,
- })
- err := db.GetEngine(ctx).
- Where("pusher_id=? AND is_deleted=?", userID, false).
- And("name <> ?", excludeBranchName).
- And("repo_id = ?", repoID).
- And("commit_time >= ?", time.Now().Add(-time.Hour*6).Unix()).
- NotIn("name", subQuery).
- OrderBy("branch.commit_time DESC").
- Limit(2).
- Find(&branches)
- return branches, err
+type FindRecentlyPushedNewBranchesOptions struct {
+ Repo *repo_model.Repository
+ BaseRepo *repo_model.Repository
+ CommitAfterUnix int64
+ MaxCount int
+}
+
+type RecentlyPushedNewBranch struct {
+ BranchDisplayName string
+ BranchLink string
+ BranchCompareURL string
+ CommitTime timeutil.TimeStamp
+}
+
+// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 2 hours which has no opened PRs created
+// if opts.CommitAfterUnix is 0, we will find the branches that were committed to in the last 2 hours
+// if opts.ListOptions is not set, we will only display top 2 latest branch
+func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, opts *FindRecentlyPushedNewBranchesOptions) ([]*RecentlyPushedNewBranch, error) {
+ if doer == nil {
+ return []*RecentlyPushedNewBranch{}, nil
+ }
+
+ // find all related repo ids
+ repoOpts := repo_model.SearchRepoOptions{
+ Actor: doer,
+ 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: optional.Some(true),
+ ForkFrom: opts.BaseRepo.ID,
+ Archived: optional.Some(false),
+ }
+ repoCond := repo_model.SearchRepositoryCondition(&repoOpts).And(repo_model.AccessibleRepositoryCondition(doer, unit.TypeCode))
+ if opts.Repo.ID == opts.BaseRepo.ID {
+ // should also include the base 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)
+
+ if opts.CommitAfterUnix == 0 {
+ opts.CommitAfterUnix = time.Now().Add(-time.Hour * 2).Unix()
+ }
+
+ baseBranch, err := GetBranch(ctx, opts.BaseRepo.ID, opts.BaseRepo.DefaultBranch)
+ if err != nil {
+ return nil, err
+ }
+
+ // find all related branches, these branches may already created PRs, we will check later
+ var branches []*Branch
+ if err := db.GetEngine(ctx).
+ Where(builder.And(
+ builder.Eq{
+ "pusher_id": doer.ID,
+ "is_deleted": false,
+ },
+ builder.Gte{"commit_time": opts.CommitAfterUnix},
+ builder.In("repo_id", repoIDs),
+ // newly created branch have no changes, so skip them
+ builder.Neq{"commit_id": baseBranch.CommitID},
+ )).
+ OrderBy(db.SearchOrderByRecentUpdated.String()).
+ Find(&branches); err != nil {
+ return nil, err
+ }
+
+ newBranches := make([]*RecentlyPushedNewBranch, 0, len(branches))
+ if opts.MaxCount == 0 {
+ // by default we display 2 recently pushed new branch
+ opts.MaxCount = 2
+ }
+ for _, branch := range branches {
+ // whether branch have already created PR
+ count, err := db.GetEngine(ctx).Table("pull_request").
+ // we should not only use branch name here, because if there are branches with same name in other repos,
+ // we can not detect them correctly
+ Where(builder.Eq{"head_repo_id": branch.RepoID, "head_branch": branch.Name}).Count()
+ if err != nil {
+ return nil, err
+ }
+
+ // if no PR, we add to the result
+ if count == 0 {
+ if err := branch.LoadRepo(ctx); err != nil {
+ return nil, err
+ }
+
+ branchDisplayName := branch.Name
+ if branch.Repo.ID != opts.BaseRepo.ID && branch.Repo.ID != opts.Repo.ID {
+ branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName)
+ }
+ newBranches = append(newBranches, &RecentlyPushedNewBranch{
+ BranchDisplayName: branchDisplayName,
+ BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)),
+ BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name),
+ CommitTime: branch.CommitTime,
+ })
+ }
+ if len(newBranches) == opts.MaxCount {
+ break
+ }
+ }
+
+ return newBranches, nil
}
"context"
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/optional"
return nil
}
+func (branches BranchList) LoadRepo(ctx context.Context) error {
+ ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) {
+ return branch.RepoID, branch.RepoID > 0 && branch.Repo == nil
+ })
+
+ reposMap := make(map[int64]*repo_model.Repository, len(ids))
+ if err := db.GetEngine(ctx).In("id", ids).Find(&reposMap); err != nil {
+ return err
+ }
+ for _, branch := range branches {
+ if branch.RepoID <= 0 || branch.Repo != nil {
+ continue
+ }
+ branch.Repo = reposMap[branch.RepoID]
+ }
+ return nil
+}
+
type FindBranchOptions struct {
db.ListOptions
RepoID int64
{3, map[int64]bool{2: true, 4: false, 28: true}},
{6, map[int64]bool{5: true, 28: true}},
{7, map[int64]bool{5: false}},
- {25, map[int64]bool{24: true}},
+ {25, map[int64]bool{12: true, 24: true}},
{22, map[int64]bool{}},
}
for _, v := range tt {
{3, map[int64]bool{2: true, 4: false, 28: false}},
{6, map[int64]bool{5: true, 28: false}},
{7, map[int64]bool{5: true}},
- {25, map[int64]bool{24: false}}, // ErrTeamNotExist
- {22, map[int64]bool{}}, // No member
+ {25, map[int64]bool{12: true, 24: false}}, // ErrTeamNotExist
+ {22, map[int64]bool{}}, // No member
}
for _, v := range tt {
t.Run(fmt.Sprintf("IsUserOrgOwnerOfOrgId%d", v.orgid), func(t *testing.T) {
// True -> include just forks
// False -> include just non-forks
Fork optional.Option[bool]
+ // 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
cond = cond.And(builder.Eq{"is_fork": false})
} else {
cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()})
+
+ if opts.ForkFrom > 0 && opts.Fork.Value() {
+ cond = cond.And(builder.Eq{"fork_id": opts.ForkFrom})
+ }
}
}
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issue_model "code.gitea.io/gitea/models/issues"
+ access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
return
}
- showRecentlyPushedNewBranches := true
- if ctx.Repo.Repository.IsMirror ||
- !ctx.Repo.Repository.UnitEnabled(ctx, unit_model.TypePullRequests) {
- showRecentlyPushedNewBranches = false
+ opts := &git_model.FindRecentlyPushedNewBranchesOptions{
+ Repo: ctx.Repo.Repository,
+ BaseRepo: ctx.Repo.Repository,
}
- if showRecentlyPushedNewBranches {
- ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch)
+ if ctx.Repo.Repository.IsFork {
+ opts.BaseRepo = ctx.Repo.Repository.BaseRepo
+ }
+
+ baseRepoPerm, err := access_model.GetUserRepoPermission(ctx, opts.BaseRepo, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserRepoPermission", err)
+ return
+ }
+
+ if !opts.Repo.IsMirror && !opts.BaseRepo.IsMirror &&
+ opts.BaseRepo.UnitEnabled(ctx, unit_model.TypePullRequests) &&
+ baseRepoPerm.CanRead(unit_model.TypePullRequests) {
+ ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Doer, opts)
if err != nil {
- ctx.ServerError("GetRecentlyPushedBranches", err)
+ ctx.ServerError("FindRecentlyPushedNewBranches", err)
return
}
}
<div class="ui positive message tw-flex tw-items-center">
<div class="tw-flex-1">
{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}}
- {{$branchLink := HTMLFormat `<a href="%s/src/branch/%s">%s</a>` $.RepoLink (PathEscapeSegments .Name) .Name}}
+ {{$branchLink := HTMLFormat `<a href="%s">%s</a>` .BranchLink .BranchDisplayName}}
{{ctx.Locale.Tr "repo.pulls.recently_pushed_new_branches" $branchLink $timeSince}}
</div>
- <a role="button" class="ui compact green button tw-m-0" href="{{$.Repository.ComposeBranchCompareURL $.Repository.BaseRepo .Name}}">
+ <a role="button" class="ui compact green button tw-m-0" href="{{.BranchCompareURL}}">
{{ctx.Locale.Tr "repo.pulls.compare_changes"}}
</a>
</div>
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
org17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org17"})
+ org35 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "private_org35"})
assert.Equal(t, []*api.Organization{
{
Location: "",
Visibility: "public",
},
+ {
+ ID: 35,
+ Name: org35.Name,
+ UserName: org35.Name,
+ FullName: org35.FullName,
+ Email: org35.Email,
+ AvatarURL: org35.AvatarLink(db.DefaultContext),
+ Description: "",
+ Website: "",
+ Location: "",
+ Visibility: "private",
+ },
}, orgs)
// user itself should get it's org's he is a member of
DecodeJSON(t, resp, &orgs)
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"})
org17 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org17"})
+ org35 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "private_org35"})
assert.Equal(t, []*api.Organization{
{
Location: "",
Visibility: "public",
},
+ {
+ ID: 35,
+ Name: org35.Name,
+ UserName: org35.Name,
+ FullName: org35.FullName,
+ Email: org35.Email,
+ AvatarURL: org35.AvatarLink(db.DefaultContext),
+ Description: "",
+ Website: "",
+ Location: "",
+ Visibility: "private",
+ },
}, orgs)
}
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session = loginUser(t, user2.Name)
- testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork")
+ testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork", "")
testCreateBranch(t, session, user2.Name, "test_blob_excerpt-fork", "branch/main", "forked-branch", http.StatusSeeOther)
testEditFile(t, session, user2.Name, "test_blob_excerpt-fork", "forked-branch", "README.md", strings.Repeat("a\n", 15)+"CHANGED\n"+strings.Repeat("a\n", 15))
import (
"bytes"
"encoding/base64"
+ "fmt"
"io"
"mime/multipart"
"net/http"
+ "net/http/httptest"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"github.com/stretchr/testify/assert"
)
+func testAPINewFile(t *testing.T, session *TestSession, user, repo, branch, treePath, content string) *httptest.ResponseRecorder {
+ url := fmt.Sprintf("/%s/%s/_new/%s", user, repo, branch)
+ req := NewRequestWithValues(t, "POST", url, map[string]string{
+ "_csrf": GetCSRF(t, session, "/user/settings"),
+ "commit_choice": "direct",
+ "tree_path": treePath,
+ "content": content,
+ })
+ return session.MakeRequest(t, req, http.StatusSeeOther)
+}
+
func TestEmptyRepo(t *testing.T) {
defer tests.PrepareTestEnv(t)()
subPaths := []string{
assert.True(t, result.Valid())
}
+// GetCSRF returns CSRF token from body
func GetCSRF(t testing.TB, session *TestSession, urlStr string) string {
t.Helper()
req := NewRequest(t, "GET", urlStr)
doc := NewHTMLParser(t, resp.Body)
return doc.GetCSRF()
}
+
+// GetCSRFFrom returns CSRF token from body
+func GetCSRFFromCookie(t testing.TB, session *TestSession, urlStr string) string {
+ t.Helper()
+ req := NewRequest(t, "GET", urlStr)
+ session.MakeRequest(t, req, http.StatusOK)
+ return session.GetCookie("_csrf").Value
+}
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
func TestPullCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
func TestPullCreate_TitleEscape(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "<i>XSS PR</i>")
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "master1", http.StatusSeeOther)
testEditFile(t, session, "user1", "repo1", "master1", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master1", "This is a pull title")
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
hookTasksLenBefore := len(hookTasks)
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited!)\n")
func TestPullCleanUpAfterMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited - TestPullCleanUpAfterMerge)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "feature/test", "This is a pull title")
func TestCantMergeWorkInProgress(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "[wip] This is a pull title")
func TestCantMergeConflict(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "conflict", "README.md", "Hello, World (Edited Once)\n")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
func TestCantMergeUnrelated(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base", "README.md", "Hello, World (Edited Twice)\n")
// Now we want to create a commit on a branch that is totally unrelated to our current head
func TestFastForwardOnlyMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "update", "README.md", "Hello, World 2\n")
// Use API to create a pr from update to master
func TestCantFastForwardOnlyMergeDiverging(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "diverging", "README.md", "Hello, World diverged\n")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World 2\n")
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
testEditFileToNewBranch(t, session, "user2", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "base-pr", "child-pr", "README.md", "Hello, World\n(Edited - TestPullRetargetOnCleanup - base PR)\n(Edited - TestPullRetargetOnCleanup - child PR)")
respBasePR := testPullCreate(t, session, "user2", "repo1", true, "master", "base-pr", "Base Pull Request")
func TestPullDontRetargetChildOnWrongRepo(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "base-pr", "README.md", "Hello, World\n(Edited - TestPullDontRetargetChildOnWrongRepo - base PR)\n")
testEditFileToNewBranch(t, session, "user1", "repo1", "base-pr", "child-pr", "README.md", "Hello, World\n(Edited - TestPullDontRetargetChildOnWrongRepo - base PR)\n(Edited - TestPullDontRetargetChildOnWrongRepo - child PR)")
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
// create a pull request
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull")
session := loginUser(t, "user1")
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
forkedName := "repo1-1"
- testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+ testRepoFork(t, session, "user2", "repo1", "user1", forkedName, "")
defer func() {
testDeleteRepository(t, session, "user1", forkedName)
}()
session := loginUser(t, "user1")
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
forkedName := "repo1-2"
- testRepoFork(t, session, "user2", "repo1", "user1", forkedName)
+ testRepoFork(t, session, "user2", "repo1", "user1", forkedName, "")
defer func() {
testDeleteRepository(t, session, "user1", forkedName)
}()
user2Session := loginUser(t, "user2")
// Have user1 create a fork of repo1.
- testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1", "")
t.Run("Submit approve/reject review on merged PR", func(t *testing.T) {
// Create a merged PR (made by user1) in the upstream repo1.
func TestPullCreate_CommitStatus(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
url := path.Join("user1", "repo1", "compare", "master...status1")
// so we need to have this meta commit also in develop branch.
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFileToNewBranch(t, session, "user1", "repo1", "master", "status1", "README.md", "status1")
testEditFileToNewBranch(t, session, "user1", "repo1", "status1", "status1", "README.md", "# repo1\n\nDescription for repo1")
func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "status1", http.StatusSeeOther)
url := path.Join("user1", "repo1", "compare", "master...status1")
req := NewRequestWithValues(t, "POST", url,
session := loginUser(t, "user1")
// Create PRs (1 merged & 2 proposed)
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
package integration
import (
+ "fmt"
"net/http"
"net/url"
"path"
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
+ org_model "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/tests"
+ "github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
)
func testCreateBranch(t testing.TB, session *TestSession, user, repo, oldRefSubURL, newBranchName string, expectedStatus int) string {
var csrf string
if expectedStatus == http.StatusNotFound {
- csrf = GetCSRF(t, session, path.Join(user, repo, "src/branch/master"))
+ // src/branch/branch_name may not container "_csrf" input,
+ // so we need to get it from cookies not from body
+ csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src/branch/master"))
} else {
- csrf = GetCSRF(t, session, path.Join(user, repo, "src", oldRefSubURL))
+ csrf = GetCSRFFromCookie(t, session, path.Join(user, repo, "src", oldRefSubURL))
}
req := NewRequestWithValues(t, "POST", path.Join(user, repo, "branches/_new", oldRefSubURL), map[string]string{
"_csrf": csrf,
strings.TrimSpace(htmlDoc.doc.Find(".ui.message").Text()),
)
}
+
+func prepareBranch(t *testing.T, session *TestSession, repo *repo_model.Repository) {
+ baseRefSubURL := fmt.Sprintf("branch/%s", repo.DefaultBranch)
+
+ // create branch with no new commit
+ testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "no-commit", http.StatusSeeOther)
+
+ // create branch with commit
+ testCreateBranch(t, session, repo.OwnerName, repo.Name, baseRefSubURL, "new-commit", http.StatusSeeOther)
+ testAPINewFile(t, session, repo.OwnerName, repo.Name, "new-commit", "new-commit.txt", "new-commit")
+
+ // create deleted branch
+ testCreateBranch(t, session, repo.OwnerName, repo.Name, "branch/new-commit", "deleted-branch", http.StatusSeeOther)
+ testUIDeleteBranch(t, session, repo.OwnerName, repo.Name, "deleted-branch")
+}
+
+func testCreatePullToDefaultBranch(t *testing.T, session *TestSession, baseRepo, headRepo *repo_model.Repository, headBranch, title string) string {
+ srcRef := headBranch
+ if baseRepo.ID != headRepo.ID {
+ srcRef = fmt.Sprintf("%s/%s:%s", headRepo.OwnerName, headRepo.Name, headBranch)
+ }
+ resp := testPullCreate(t, session, baseRepo.OwnerName, baseRepo.Name, false, baseRepo.DefaultBranch, srcRef, title)
+ elem := strings.Split(test.RedirectURL(resp), "/")
+ // return pull request ID
+ return elem[4]
+}
+
+func prepareRepoPR(t *testing.T, baseSession, headSession *TestSession, baseRepo, headRepo *repo_model.Repository) {
+ // create opening PR
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "opening-pr", http.StatusSeeOther)
+ testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "opening-pr", "opening pr")
+
+ // create closed PR
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr", http.StatusSeeOther)
+ prID := testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr", "closed pr")
+ testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID)
+
+ // create closed PR with deleted branch
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "closed-pr-deleted", http.StatusSeeOther)
+ prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "closed-pr-deleted", "closed pr with deleted branch")
+ testIssueClose(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID)
+ testUIDeleteBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "closed-pr-deleted")
+
+ // create merged PR
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr", http.StatusSeeOther)
+ prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr", "merged pr")
+ testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr", fmt.Sprintf("new-commit-%s.txt", headRepo.Name), "new-commit")
+ testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, false)
+
+ // create merged PR with deleted branch
+ testCreateBranch(t, headSession, headRepo.OwnerName, headRepo.Name, "branch/new-commit", "merged-pr-deleted", http.StatusSeeOther)
+ prID = testCreatePullToDefaultBranch(t, baseSession, baseRepo, headRepo, "merged-pr-deleted", "merged pr with deleted branch")
+ testAPINewFile(t, headSession, headRepo.OwnerName, headRepo.Name, "merged-pr-deleted", fmt.Sprintf("new-commit-%s-2.txt", headRepo.Name), "new-commit")
+ testPullMerge(t, baseSession, baseRepo.OwnerName, baseRepo.Name, prID, repo_model.MergeStyleRebaseMerge, true)
+}
+
+func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath string, expected []string) {
+ branches := make([]string, 0, 2)
+ req := NewRequest(t, "GET", repoPath)
+ resp := session.MakeRequest(t, req, http.StatusOK)
+ doc := NewHTMLParser(t, resp.Body)
+ doc.doc.Find(".ui.positive.message div a").Each(func(index int, branch *goquery.Selection) {
+ branches = append(branches, branch.Text())
+ })
+ assert.Equal(t, expected, branches)
+}
+
+func TestRecentlyPushedNewBranches(t *testing.T) {
+ defer tests.PrepareTestEnv(t)()
+
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ user1Session := loginUser(t, "user1")
+ user2Session := loginUser(t, "user2")
+ user12Session := loginUser(t, "user12")
+ user13Session := loginUser(t, "user13")
+
+ // prepare branch and PRs in original repo
+ repo10 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
+ prepareBranch(t, user12Session, repo10)
+ prepareRepoPR(t, user12Session, user12Session, repo10, repo10)
+
+ // outdated new branch should not be displayed
+ checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"new-commit"})
+
+ // create a fork repo in public org
+ testRepoFork(t, user12Session, repo10.OwnerName, repo10.Name, "org25", "org25_fork_repo10", "new-commit")
+ orgPublicForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 25, Name: "org25_fork_repo10"})
+ prepareRepoPR(t, user12Session, user12Session, repo10, orgPublicForkRepo)
+
+ // user12 is the owner of the repo10 and the organization org25
+ // in repo10, user12 has opening/closed/merged pr and closed/merged pr with deleted branch
+ checkRecentlyPushedNewBranches(t, user12Session, "user12/repo10", []string{"org25/org25_fork_repo10:new-commit", "new-commit"})
+
+ userForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 11})
+ testCtx := NewAPITestContext(t, repo10.OwnerName, repo10.Name, auth_model.AccessTokenScopeWriteRepository)
+ t.Run("AddUser13AsCollaborator", doAPIAddCollaborator(testCtx, "user13", perm.AccessModeWrite))
+ prepareBranch(t, user13Session, userForkRepo)
+ prepareRepoPR(t, user13Session, user13Session, repo10, userForkRepo)
+
+ // create branch with same name in different repo by user13
+ testCreateBranch(t, user13Session, repo10.OwnerName, repo10.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
+ testCreateBranch(t, user13Session, userForkRepo.OwnerName, userForkRepo.Name, "branch/new-commit", "same-name-branch", http.StatusSeeOther)
+ testCreatePullToDefaultBranch(t, user13Session, repo10, userForkRepo, "same-name-branch", "same name branch pr")
+
+ // user13 pushed 2 branches with the same name in repo10 and repo11
+ // and repo11's branch has a pr, but repo10's branch doesn't
+ // in this case, we should get repo10's branch but not repo11's branch
+ checkRecentlyPushedNewBranches(t, user13Session, "user12/repo10", []string{"same-name-branch", "user13/repo11:new-commit"})
+
+ // create a fork repo in private org
+ testRepoFork(t, user1Session, repo10.OwnerName, repo10.Name, "private_org35", "org35_fork_repo10", "new-commit")
+ orgPrivateForkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: 35, Name: "org35_fork_repo10"})
+ prepareRepoPR(t, user1Session, user1Session, repo10, orgPrivateForkRepo)
+
+ // user1 is the owner of private_org35 and no write permission to repo10
+ // so user1 can only see the branch in org35_fork_repo10
+ checkRecentlyPushedNewBranches(t, user1Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:new-commit"})
+
+ // user2 push a branch in private_org35
+ testCreateBranch(t, user2Session, orgPrivateForkRepo.OwnerName, orgPrivateForkRepo.Name, "branch/new-commit", "user-read-permission", http.StatusSeeOther)
+ // convert write permission to read permission for code unit
+ token := getTokenForLoggedInUser(t, user1Session, auth_model.AccessTokenScopeWriteOrganization)
+ req := NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d", 24), &api.EditTeamOption{
+ Name: "team24",
+ UnitsMap: map[string]string{"repo.code": "read"},
+ }).AddTokenAuth(token)
+ MakeRequest(t, req, http.StatusOK)
+ teamUnit := unittest.AssertExistsAndLoadBean(t, &org_model.TeamUnit{TeamID: 24, Type: unit.TypeCode})
+ assert.Equal(t, perm.AccessModeRead, teamUnit.AccessMode)
+ // user2 can see the branch as it is created by user2
+ checkRecentlyPushedNewBranches(t, user2Session, "user12/repo10", []string{"private_org35/org35_fork_repo10:user-read-permission"})
+ })
+}
"github.com/stretchr/testify/assert"
)
-func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName string) *httptest.ResponseRecorder {
+func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName, forkBranch string) *httptest.ResponseRecorder {
forkOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: forkOwnerName})
// Step0: check the existence of the to-fork repo
_, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", forkOwner.ID)).Attr("data-value")
assert.True(t, exists, fmt.Sprintf("Fork owner '%s' is not present in select box", forkOwnerName))
req = NewRequestWithValues(t, "POST", link, map[string]string{
- "_csrf": htmlDoc.GetCSRF(),
- "uid": fmt.Sprintf("%d", forkOwner.ID),
- "repo_name": forkRepoName,
+ "_csrf": htmlDoc.GetCSRF(),
+ "uid": fmt.Sprintf("%d", forkOwner.ID),
+ "repo_name": forkRepoName,
+ "fork_single_branch": forkBranch,
})
session.MakeRequest(t, req, http.StatusSeeOther)
func TestRepoFork(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user1")
- testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "")
}
func TestRepoForkToOrg(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
- testRepoFork(t, session, "user2", "repo1", "org3", "repo1")
+ testRepoFork(t, session, "user2", "repo1", "org3", "repo1", "")
// Check that no more forking is allowed as user2 owns repository
// and org3 organization that owner user2 is also now has forked this repository