Backport #37657 by @bircni Fixes an issue where users could not commit changes on a file which is unprotected. Fixes #37655 Co-authored-by: Nicolas <bircni@icloud.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -173,6 +173,16 @@ func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *r
|
|||||||
protectedBranch.Repo = targetRepo
|
protectedBranch.Repo = targetRepo
|
||||||
canPushWithProtection = protectedBranch.CanUserPush(ctx, doer)
|
canPushWithProtection = protectedBranch.CanUserPush(ctx, doer)
|
||||||
protectionRequireSigned = protectedBranch.RequireSignedCommits
|
protectionRequireSigned = protectedBranch.RequireSignedCommits
|
||||||
|
// If branch-wide push is restricted, allow direct commit when the
|
||||||
|
// URL-derived tree path matches an unprotected file pattern. The
|
||||||
|
// pre-receive hook re-checks every path the commit actually touches
|
||||||
|
// (e.g. rename source and destination).
|
||||||
|
if !canPushWithProtection && ctx.Repo.TreePath != "" && protectedBranch.UnprotectedFilePatterns != "" {
|
||||||
|
globs := protectedBranch.GetUnprotectedFilePatterns()
|
||||||
|
if protectedBranch.IsUnprotectedFile(globs, ctx.Repo.TreePath) {
|
||||||
|
canPushWithProtection = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targetGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, targetRepo)
|
targetGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, targetRepo)
|
||||||
|
|||||||
@@ -98,6 +98,42 @@ func testEditorProtectedBranch(t *testing.T) {
|
|||||||
resp := testEditorActionPostRequest(t, session, "/user2/repo1/_new/master/", map[string]string{"tree_path": "test-protected-branch.txt", "commit_choice": "direct"})
|
resp := testEditorActionPostRequest(t, session, "/user2/repo1/_new/master/", map[string]string{"tree_path": "test-protected-branch.txt", "commit_choice": "direct"})
|
||||||
assert.Equal(t, http.StatusBadRequest, resp.Code)
|
assert.Equal(t, http.StatusBadRequest, resp.Code)
|
||||||
assert.Equal(t, `Cannot commit to protected branch "master".`, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
assert.Equal(t, `Cannot commit to protected branch "master".`, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||||
|
|
||||||
|
// Change "master" branch to mark files under "docs/" as unprotected
|
||||||
|
req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/branches/edit", map[string]string{
|
||||||
|
"rule_name": "master",
|
||||||
|
"protected_file_patterns": "",
|
||||||
|
"unprotected_file_patterns": "docs/*.md",
|
||||||
|
"enable_push": "true",
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
flashMsg = session.GetCookieFlashMessage()
|
||||||
|
assert.Equal(t, `Branch protection for rule "master" has been updated.`, flashMsg.SuccessMsg)
|
||||||
|
|
||||||
|
resp = testEditorActionPostRequest(t, session, "/user2/repo1/_new/master/docs/new.md", map[string]string{"tree_path": "docs/new.md", "commit_choice": "direct"})
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
assert.Contains(t, resp.Body.String(), `"redirect":"/user2/repo1/src/branch/master/docs/new.md"`)
|
||||||
|
|
||||||
|
// Form's destination (renamed.md) is decided by the pre-receive hook, not the controller.
|
||||||
|
resp = testEditorActionPostRequest(t, session, "/user2/repo1/_edit/master/docs/new.md", map[string]string{
|
||||||
|
"content": "renamed via editor",
|
||||||
|
"commit_choice": "direct",
|
||||||
|
"tree_path": "docs/renamed.md",
|
||||||
|
})
|
||||||
|
assert.Equal(t, http.StatusOK, resp.Code)
|
||||||
|
assert.Contains(t, resp.Body.String(), `"redirect":"/user2/repo1/src/branch/master/docs/renamed.md"`)
|
||||||
|
|
||||||
|
// Protected source path: controller rejects up-front regardless of unprotected destination.
|
||||||
|
resp = testEditorActionPostRequest(t, session, "/user2/repo1/_edit/master/README.md", map[string]string{
|
||||||
|
"commit_choice": "direct",
|
||||||
|
"tree_path": "docs/from-readme.md",
|
||||||
|
})
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.Code)
|
||||||
|
assert.Equal(t, `Cannot commit to protected branch "master".`, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||||
|
|
||||||
|
resp = testEditorActionPostRequest(t, session, "/user2/repo1/_delete/master/README.md", map[string]string{"commit_choice": "direct"})
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.Code)
|
||||||
|
assert.Equal(t, `Cannot commit to protected branch "master".`, test.ParseJSONError(resp.Body.Bytes()).ErrorMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEditorActionPostRequest(t *testing.T, session *TestSession, requestPath string, params map[string]string) *httptest.ResponseRecorder {
|
func testEditorActionPostRequest(t *testing.T, session *TestSession, requestPath string, params map[string]string) *httptest.ResponseRecorder {
|
||||||
|
|||||||
Reference in New Issue
Block a user