for p, v := range baseBranchName {
if v == '/' && gitRepo.IsBranchExist(baseBranchName[:p]) && p != len(baseBranchName)-1 {
- curentTopicBranch = baseBranchName[p+1:]
+ currentTopicBranch = baseBranchName[p+1:]
baseBranchName = baseBranchName[:p]
break
}
}
}
- if len(topicBranch) == 0 && len(curentTopicBranch) == 0 {
+ if len(topicBranch) == 0 && len(currentTopicBranch) == 0 {
results = append(results, private.HookProcReceiveRefResult{
OriginalRef: opts.RefFullNames[i],
OldOID: opts.OldCommitIDs[i],
@@ -78,18 +77,18 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
continue
}
- if len(curentTopicBranch) == 0 {
- curentTopicBranch = topicBranch
+ if len(currentTopicBranch) == 0 {
+ currentTopicBranch = topicBranch
}
// because different user maybe want to use same topic,
// So it's better to make sure the topic branch name
- // has user name prefix
+ // has username prefix
var headBranch string
- if !strings.HasPrefix(curentTopicBranch, userName+"/") {
- headBranch = userName + "/" + curentTopicBranch
+ if !strings.HasPrefix(currentTopicBranch, userName+"/") {
+ headBranch = userName + "/" + currentTopicBranch
} else {
- headBranch = curentTopicBranch
+ headBranch = currentTopicBranch
}
pr, err := issues_model.GetUnmergedPullRequest(ctx, repo.ID, repo.ID, headBranch, baseBranchName, issues_model.PullRequestFlowAGit)
@@ -178,7 +177,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
continue
}
- if !forcePush {
+ if !forcePush.Value() {
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").
AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).
RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()})
diff --git a/services/context/permission.go b/services/context/permission.go
index 14a9801dccba0..9338587257cdc 100644
--- a/services/context/permission.go
+++ b/services/context/permission.go
@@ -58,6 +58,9 @@ func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
return func(ctx *Context) {
if !ctx.Repo.CanRead(unitType) {
+ if unitType == unit.TypeCode && canWriteAsMaintainer(ctx) {
+ return
+ }
if log.IsTrace() {
if ctx.IsSigned {
log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
diff --git a/services/context/repo.go b/services/context/repo.go
index 0072b63b7c992..2df2b7ea40387 100644
--- a/services/context/repo.go
+++ b/services/context/repo.go
@@ -374,7 +374,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
return
}
- if !ctx.Repo.Permission.HasAnyUnitAccessOrEveryoneAccess() {
+ if !ctx.Repo.Permission.HasAnyUnitAccessOrEveryoneAccess() && !canWriteAsMaintainer(ctx) {
if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return
@@ -1058,3 +1058,11 @@ func GitHookService() func(ctx *Context) {
}
}
}
+
+// canWriteAsMaintainer check if the doer can write to a branch as a maintainer
+func canWriteAsMaintainer(ctx *Context) bool {
+ branchName := getRefNameFromPath(ctx.Repo, ctx.PathParam("*"), func(branchName string) bool {
+ return issues_model.CanMaintainerWriteToBranch(ctx, ctx.Repo.Permission, branchName, ctx.Doer)
+ })
+ return len(branchName) > 0
+}
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index 4c8e036f057b3..eb21b6534b8f4 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -760,10 +760,15 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
pr.Updated = pr.Created
}
+ prTitle := pr.Title
+ if pr.IsDraft && !issues_model.HasWorkInProgressPrefix(pr.Title) {
+ prTitle = fmt.Sprintf("%s %s", setting.Repository.PullRequest.WorkInProgressPrefixes[0], pr.Title)
+ }
+
issue := issues_model.Issue{
RepoID: g.repo.ID,
Repo: g.repo,
- Title: pr.Title,
+ Title: prTitle,
Index: pr.Number,
Content: pr.Content,
MilestoneID: milestoneID,
diff --git a/services/migrations/github.go b/services/migrations/github.go
index a36b02ca8b220..604ab84b39645 100644
--- a/services/migrations/github.go
+++ b/services/migrations/github.go
@@ -737,6 +737,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
PatchURL: pr.GetPatchURL(), // see below for SECURITY related issues here
Reactions: reactions,
ForeignIndex: int64(*pr.Number),
+ IsDraft: pr.GetDraft(),
})
// SECURITY: Ensure that the PR is safe
diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go
index 065b687fa6877..295bc7c29f517 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -722,6 +722,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
PatchURL: pr.WebURL + ".patch",
ForeignIndex: int64(pr.IID),
Context: gitlabIssueContext{IsMergeRequest: true},
+ IsDraft: pr.Draft,
})
// SECURITY: Ensure that the PR is safe
diff --git a/services/user/user.go b/services/user/user.go
index 2287e36c716ac..9aded62a51af8 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -32,6 +32,10 @@ import (
// RenameUser renames a user
func RenameUser(ctx context.Context, u *user_model.User, newUserName string) error {
+ if newUserName == u.Name {
+ return nil
+ }
+
// Non-local users are not allowed to change their username.
if !u.IsOrganization() && !u.IsLocal() {
return user_model.ErrUserIsNotLocal{
@@ -40,10 +44,6 @@ func RenameUser(ctx context.Context, u *user_model.User, newUserName string) err
}
}
- if newUserName == u.Name {
- return nil
- }
-
if err := user_model.IsUsableUsername(newUserName); err != nil {
return err
}
diff --git a/templates/devtest/fomantic-dropdown.tmpl b/templates/devtest/fomantic-dropdown.tmpl
index 57a7c1313ea63..0b9d227220bdb 100644
--- a/templates/devtest/fomantic-dropdown.tmpl
+++ b/templates/devtest/fomantic-dropdown.tmpl
@@ -29,15 +29,16 @@
empty multiple dropdown
-
-
-
- {{svg "octicon-triangle-down" 14 "dropdown icon"}}
- {{svg "octicon-x" 14 "remove icon"}}
-
clearable search dropdown
-
@@ -50,6 +51,27 @@
+
+
+
+ {{svg "octicon-triangle-down" 14 "dropdown icon"}}
+ {{svg "octicon-x" 14 "remove icon"}}
+
clearable search dropdown
+
+
+
Selection
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/GIT_COLA_MSG b/tests/gitea-repositories-meta/org42/search-by-path.git/GIT_COLA_MSG
new file mode 100644
index 0000000000000..8b137891791fe
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/GIT_COLA_MSG
@@ -0,0 +1 @@
+
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/HEAD b/tests/gitea-repositories-meta/org42/search-by-path.git/HEAD
new file mode 100644
index 0000000000000..cb089cd89a7d7
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/config b/tests/gitea-repositories-meta/org42/search-by-path.git/config
new file mode 100644
index 0000000000000..07d359d07cf1e
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/config
@@ -0,0 +1,4 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/description b/tests/gitea-repositories-meta/org42/search-by-path.git/description
new file mode 100644
index 0000000000000..382e2d7f10128
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/description
@@ -0,0 +1,8 @@
+This repository will be used to test code search. The snippet below shows its directory structure
+
+.
+├── avocado.md
+├── cucumber.md
+├── ham.md
+└── potato
+ └── ham.md
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive
new file mode 100755
index 0000000000000..4b3d452abcce2
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+ORI_DIR=`pwd`
+SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
+cd "$ORI_DIR"
+for i in `ls "$SHELL_FOLDER/post-receive.d"`; do
+ sh "$SHELL_FOLDER/post-receive.d/$i"
+done
\ No newline at end of file
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive.d/gitea b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive.d/gitea
new file mode 100755
index 0000000000000..43a948da3a983
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/post-receive.d/gitea
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive
new file mode 100755
index 0000000000000..412701305369c
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+ORI_DIR=`pwd`
+SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
+cd "$ORI_DIR"
+for i in `ls "$SHELL_FOLDER/pre-receive.d"`; do
+ sh "$SHELL_FOLDER/pre-receive.d/$i"
+done
\ No newline at end of file
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive.d/gitea b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive.d/gitea
new file mode 100755
index 0000000000000..49d09406364a5
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/pre-receive.d/gitea
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive
new file mode 100755
index 0000000000000..af2808b03702f
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+ORI_DIR=`pwd`
+SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
+cd "$ORI_DIR"
+for i in `ls "$SHELL_FOLDER/proc-receive.d"`; do
+ sh "$SHELL_FOLDER/proc-receive.d/$i"
+done
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive.d/gitea b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive.d/gitea
new file mode 100755
index 0000000000000..97521c62115db
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/proc-receive.d/gitea
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" proc-receive
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update
new file mode 100755
index 0000000000000..c186fe4a18b0f
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+ORI_DIR=`pwd`
+SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
+cd "$ORI_DIR"
+for i in `ls "$SHELL_FOLDER/update.d"`; do
+ sh "$SHELL_FOLDER/update.d/$i" $1 $2 $3
+done
\ No newline at end of file
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update.d/gitea b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update.d/gitea
new file mode 100755
index 0000000000000..38101c242664a
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/hooks/update.d/gitea
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/info/exclude b/tests/gitea-repositories-meta/org42/search-by-path.git/info/exclude
new file mode 100644
index 0000000000000..a5196d1be8fb5
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/info/refs b/tests/gitea-repositories-meta/org42/search-by-path.git/info/refs
new file mode 100644
index 0000000000000..6b948c96a8351
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/info/refs
@@ -0,0 +1,13 @@
+90c1019714259b24fb81711d4416ac0f18667dfa refs/heads/DefaultBranch
+985f0301dba5e7b34be866819cd15ad3d8f508ee refs/heads/branch2
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/heads/develop
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/heads/feature/1
+78fb907e3a3309eae4fe8fef030874cebbf1cd5e refs/heads/home-md-img-check
+3731fe53b763859aaf83e703ee731f6b9447ff1e refs/heads/master
+62fb502a7172d4453f0322a2cc85bddffa57f07a refs/heads/pr-to-update
+4649299398e4d39a5c09eb4f534df6f1e1eb87cc refs/heads/sub-home-md-img-check
+3fa2f829675543ecfc16b2891aebe8bf0608a8f4 refs/notes/commits
+4a357436d925b5c974181ff12a994538ddc5a269 refs/pull/2/head
+5f22f7d0d95d614d25a5b68592adb345a4b5c7fd refs/pull/3/head
+62fb502a7172d4453f0322a2cc85bddffa57f07a refs/pull/5/head
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/tags/v1.1
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/logs/refs/heads/master b/tests/gitea-repositories-meta/org42/search-by-path.git/logs/refs/heads/master
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/commit-graph b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/commit-graph
new file mode 100644
index 0000000000000..b38715bb92b03
Binary files /dev/null and b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/commit-graph differ
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/packs b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/packs
new file mode 100644
index 0000000000000..b2af8c8378a44
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-393dc29256bc27cb2ec73898507df710be7a3cf5.pack
+
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.bitmap b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.bitmap
new file mode 100644
index 0000000000000..1fdef225e830c
Binary files /dev/null and b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.bitmap differ
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.idx b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.idx
new file mode 100644
index 0000000000000..0d930e7499f5e
Binary files /dev/null and b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.idx differ
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.pack b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.pack
new file mode 100644
index 0000000000000..f1aac1e7404fe
Binary files /dev/null and b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.pack differ
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.rev b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.rev
new file mode 100644
index 0000000000000..869860ba611c4
Binary files /dev/null and b/tests/gitea-repositories-meta/org42/search-by-path.git/objects/pack/pack-393dc29256bc27cb2ec73898507df710be7a3cf5.rev differ
diff --git a/tests/gitea-repositories-meta/org42/search-by-path.git/packed-refs b/tests/gitea-repositories-meta/org42/search-by-path.git/packed-refs
new file mode 100644
index 0000000000000..70e69af1e1018
--- /dev/null
+++ b/tests/gitea-repositories-meta/org42/search-by-path.git/packed-refs
@@ -0,0 +1,14 @@
+# pack-refs with: peeled fully-peeled sorted
+90c1019714259b24fb81711d4416ac0f18667dfa refs/heads/DefaultBranch
+985f0301dba5e7b34be866819cd15ad3d8f508ee refs/heads/branch2
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/heads/develop
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/heads/feature/1
+78fb907e3a3309eae4fe8fef030874cebbf1cd5e refs/heads/home-md-img-check
+3731fe53b763859aaf83e703ee731f6b9447ff1e refs/heads/master
+62fb502a7172d4453f0322a2cc85bddffa57f07a refs/heads/pr-to-update
+4649299398e4d39a5c09eb4f534df6f1e1eb87cc refs/heads/sub-home-md-img-check
+3fa2f829675543ecfc16b2891aebe8bf0608a8f4 refs/notes/commits
+4a357436d925b5c974181ff12a994538ddc5a269 refs/pull/2/head
+5f22f7d0d95d614d25a5b68592adb345a4b5c7fd refs/pull/3/head
+62fb502a7172d4453f0322a2cc85bddffa57f07a refs/pull/5/head
+65f1bf27bc3bf70f64657658635e66094edbcb4d refs/tags/v1.1
diff --git a/tests/integration/api_org_test.go b/tests/integration/api_org_test.go
index 70d3a446f7688..fff121490c9ca 100644
--- a/tests/integration/api_org_test.go
+++ b/tests/integration/api_org_test.go
@@ -177,7 +177,7 @@ func TestAPIGetAll(t *testing.T) {
var apiOrgList []*api.Organization
DecodeJSON(t, resp, &apiOrgList)
- assert.Len(t, apiOrgList, 12)
+ assert.Len(t, apiOrgList, 13)
assert.Equal(t, "Limited Org 36", apiOrgList[1].FullName)
assert.Equal(t, "limited", apiOrgList[1].Visibility)
@@ -186,7 +186,7 @@ func TestAPIGetAll(t *testing.T) {
resp = MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &apiOrgList)
- assert.Len(t, apiOrgList, 8)
+ assert.Len(t, apiOrgList, 9)
assert.Equal(t, "org 17", apiOrgList[0].FullName)
assert.Equal(t, "public", apiOrgList[0].Visibility)
}
diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go
index 716da762e542d..93c9ca0920d49 100644
--- a/tests/integration/api_repo_test.go
+++ b/tests/integration/api_repo_test.go
@@ -94,9 +94,9 @@ func TestAPISearchRepo(t *testing.T) {
}{
{
name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{
- nil: {count: 35},
- user: {count: 35},
- user2: {count: 35},
+ nil: {count: 36},
+ user: {count: 36},
+ user2: {count: 36},
},
},
{
diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go
index f024d22c4a197..76db3c69324b4 100644
--- a/tests/integration/git_test.go
+++ b/tests/integration/git_test.go
@@ -5,6 +5,7 @@ package integration
import (
"bytes"
+ "context"
"crypto/rand"
"encoding/hex"
"fmt"
@@ -943,3 +944,59 @@ func TestDataAsync_Issue29101(t *testing.T) {
defer r2.Close()
})
}
+
+func TestAgitPullPush(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ baseAPITestContext := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
+
+ u.Path = baseAPITestContext.GitPath()
+ u.User = url.UserPassword("user2", userPassword)
+
+ dstPath := t.TempDir()
+ doGitClone(dstPath, u)(t)
+
+ gitRepo, err := git.OpenRepository(context.Background(), dstPath)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+
+ doGitCreateBranch(dstPath, "test-agit-push")
+
+ // commit 1
+ _, err = generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-")
+ assert.NoError(t, err)
+
+ // push to create an agit pull request
+ err = git.NewCommand(git.DefaultContext, "push", "origin",
+ "-o", "title=test-title", "-o", "description=test-description",
+ "HEAD:refs/for/master/test-agit-push",
+ ).Run(&git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+
+ // check pull request exist
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: 1, Flow: issues_model.PullRequestFlowAGit, HeadBranch: "user2/test-agit-push"})
+ assert.NoError(t, pr.LoadIssue(db.DefaultContext))
+ assert.Equal(t, "test-title", pr.Issue.Title)
+ assert.Equal(t, "test-description", pr.Issue.Content)
+
+ // commit 2
+ _, err = generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-2-")
+ assert.NoError(t, err)
+
+ // push 2
+ err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push").Run(&git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+
+ // reset to first commit
+ err = git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+
+ // test force push without confirm
+ _, stderr, err := git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push").RunStdString(&git.RunOpts{Dir: dstPath})
+ assert.Error(t, err)
+ assert.Contains(t, stderr, "[remote rejected] HEAD -> refs/for/master/test-agit-push (request `force-push` push option)")
+
+ // test force push with confirm
+ err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/master/test-agit-push", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath})
+ assert.NoError(t, err)
+ })
+}
diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go
index aed699fd20018..def6506253f90 100644
--- a/tests/integration/pull_compare_test.go
+++ b/tests/integration/pull_compare_test.go
@@ -14,6 +14,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/test"
repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
@@ -73,3 +74,80 @@ func TestPullCompare(t *testing.T) {
assert.EqualValues(t, editButtonCount, 0, "Expected not to find a button to edit a file in the PR diff view because head repository has been deleted")
})
}
+
+func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
+ onGiteaRun(t, func(t *testing.T, u *url.URL) {
+ // repo3 is private
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
+ assert.True(t, repo3.IsPrivate)
+
+ // user4 forks repo3
+ user4Session := loginUser(t, "user4")
+ forkedRepoName := "user4-forked-repo3"
+ testRepoFork(t, user4Session, repo3.OwnerName, repo3.Name, "user4", forkedRepoName, "")
+ forkedRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user4", Name: forkedRepoName})
+ assert.True(t, forkedRepo.IsPrivate)
+
+ // user4 creates a new branch and a PR
+ testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n")
+ resp := testPullCreateDirectly(t, user4Session, repo3.OwnerName, repo3.Name, "master", "user4", forkedRepoName, "user4/update-readme", "PR for user4 forked repo3")
+ prURL := test.RedirectURL(resp)
+
+ // user2 (admin of repo3) goes to the PR files page
+ user2Session := loginUser(t, "user2")
+ resp = user2Session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("%s/files", prURL)), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ nodes := htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .dropdown .menu a")
+ if assert.Equal(t, 1, nodes.Length()) {
+ // there is only "View File" button, no "Edit File" button
+ assert.Equal(t, "View File", nodes.First().Text())
+ viewFileLink, exists := nodes.First().Attr("href")
+ if assert.True(t, exists) {
+ user2Session.MakeRequest(t, NewRequest(t, "GET", viewFileLink), http.StatusOK)
+ }
+ }
+
+ // user4 goes to the PR page and enable "Allow maintainers to edit"
+ resp = user4Session.MakeRequest(t, NewRequest(t, "GET", prURL), http.StatusOK)
+ htmlDoc = NewHTMLParser(t, resp.Body)
+ dataURL, exists := htmlDoc.doc.Find("#allow-edits-from-maintainers").Attr("data-url")
+ assert.True(t, exists)
+ req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/set_allow_maintainer_edit", dataURL), map[string]string{
+ "_csrf": htmlDoc.GetCSRF(),
+ "allow_maintainer_edit": "true",
+ })
+ user4Session.MakeRequest(t, req, http.StatusOK)
+
+ // user2 (admin of repo3) goes to the PR files page again
+ resp = user2Session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("%s/files", prURL)), http.StatusOK)
+ htmlDoc = NewHTMLParser(t, resp.Body)
+ nodes = htmlDoc.doc.Find(".diff-file-box[data-new-filename=\"README.md\"] .diff-file-header-actions .dropdown .menu a")
+ if assert.Equal(t, 2, nodes.Length()) {
+ // there are "View File" button and "Edit File" button
+ assert.Equal(t, "View File", nodes.First().Text())
+ viewFileLink, exists := nodes.First().Attr("href")
+ if assert.True(t, exists) {
+ user2Session.MakeRequest(t, NewRequest(t, "GET", viewFileLink), http.StatusOK)
+ }
+
+ assert.Equal(t, "Edit File", nodes.Last().Text())
+ editFileLink, exists := nodes.Last().Attr("href")
+ if assert.True(t, exists) {
+ // edit the file
+ resp := user2Session.MakeRequest(t, NewRequest(t, "GET", editFileLink), http.StatusOK)
+ htmlDoc := NewHTMLParser(t, resp.Body)
+ lastCommit := htmlDoc.GetInputValueByName("last_commit")
+ assert.NotEmpty(t, lastCommit)
+ req := NewRequestWithValues(t, "POST", editFileLink, map[string]string{
+ "_csrf": htmlDoc.GetCSRF(),
+ "last_commit": lastCommit,
+ "tree_path": "README.md",
+ "content": "File is edited by the maintainer user2",
+ "commit_summary": "user2 updated the file",
+ "commit_choice": "direct",
+ })
+ user2Session.MakeRequest(t, req, http.StatusSeeOther)
+ }
+ }
+ })
+}
diff --git a/web_src/css/base.css b/web_src/css/base.css
index 223d9fbad65b6..8d9f810ef8fae 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -1364,6 +1364,10 @@ table th[data-sortt-desc] .svg {
min-width: 0; /* make ellipsis work */
}
+.ui.multiple.selection.dropdown {
+ flex-wrap: wrap;
+}
+
.ui.ui.dropdown.selection {
min-width: 14em; /* match the default min width */
}
diff --git a/web_src/js/features/repo-diff-filetree.ts b/web_src/js/features/repo-diff-filetree.ts
index 6d9533d0669f0..bc275a90f6ab3 100644
--- a/web_src/js/features/repo-diff-filetree.ts
+++ b/web_src/js/features/repo-diff-filetree.ts
@@ -8,7 +8,9 @@ export function initDiffFileTree() {
const fileTreeView = createApp(DiffFileTree);
fileTreeView.mount(el);
+}
+export function initDiffFileList() {
const fileListElement = document.querySelector('#diff-file-list');
if (!fileListElement) return;
diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts
index 5d6388a43e1d8..a0bd9955fe468 100644
--- a/web_src/js/features/repo-diff.ts
+++ b/web_src/js/features/repo-diff.ts
@@ -1,7 +1,7 @@
import $ from 'jquery';
import {initCompReactionSelector} from './comp/ReactionSelector.ts';
import {initRepoIssueContentHistory} from './repo-issue-content.ts';
-import {initDiffFileTree} from './repo-diff-filetree.ts';
+import {initDiffFileTree, initDiffFileList} from './repo-diff-filetree.ts';
import {initDiffCommitSelect} from './repo-diff-commitselect.ts';
import {validateTextareaNonEmpty} from './comp/ComboMarkdownEditor.ts';
import {initViewedCheckboxListenerFor, countAndUpdateViewedFiles, initExpandAndCollapseFilesButton} from './pull-view-file.ts';
@@ -216,6 +216,7 @@ export function initRepoDiffView() {
initRepoDiffConversationForm();
if (!$('#diff-file-list').length) return;
initDiffFileTree();
+ initDiffFileList();
initDiffCommitSelect();
initRepoDiffShowMore();
initRepoDiffReviewButton();