Skip to content

Commit 70d2872

Browse files
committed
agit flow add refs/for-review/<pull index> support
Signed-off-by: a1012112796 <[email protected]>
1 parent 9000811 commit 70d2872

File tree

10 files changed

+265
-66
lines changed

10 files changed

+265
-66
lines changed

cmd/serv.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ func runServ(c *cli.Context) error {
181181
if git.DefaultFeatures().SupportProcReceive {
182182
// for AGit Flow
183183
if cmd == "ssh_info" {
184-
fmt.Print(`{"type":"gitea","version":1}`)
184+
data := private.GetSshInfo(ctx)
185+
fmt.Print(data)
185186
return nil
186187
}
187188
}

modules/git/git.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,16 @@ func syncGitConfig() (err error) {
259259
if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
260260
return err
261261
}
262+
if err := configAddNonExist("receive.procReceiveRefs", "refs/for-review"); err != nil {
263+
return err
264+
}
262265
} else {
263266
if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
264267
return err
265268
}
269+
if err := configUnsetAll("receive.procReceiveRefs", "refs/for-review"); err != nil {
270+
return err
271+
}
266272
}
267273

268274
// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user.

modules/git/ref.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ func (ref *Reference) RefGroup() string {
6767
// or refs/for/<targe-branch> -o topic='<topic-branch>'
6868
const ForPrefix = "refs/for/"
6969

70-
// TODO: /refs/for-review for suggest change interface
70+
// ForReviewPrefix special ref to update a pull request: refs/for-review/<pull index>
71+
const ForReviewPrefix = "refs/for-review/"
7172

7273
// RefName represents a full git reference name
7374
type RefName string
@@ -104,6 +105,12 @@ func (ref RefName) IsFor() bool {
104105
return strings.HasPrefix(string(ref), ForPrefix)
105106
}
106107

108+
var forReviewPattern = regexp.MustCompile(ForReviewPrefix + `[1-9][0-9]*`)
109+
110+
func (ref RefName) IsForReview() bool {
111+
return forReviewPattern.MatchString(string(ref))
112+
}
113+
107114
func (ref RefName) nameWithoutPrefix(prefix string) string {
108115
if strings.HasPrefix(string(ref), prefix) {
109116
return strings.TrimPrefix(string(ref), prefix)

modules/private/manager.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,29 @@ func ReloadTemplates(ctx context.Context) ResponseExtra {
3636
return requestJSONClientMsg(req, "Reloaded")
3737
}
3838

39+
// Shutdown calls the internal shutdown function
40+
func GetSshInfo(ctx context.Context) string {
41+
reqURL := setting.LocalURL + "ssh_info"
42+
req := newInternalRequest(ctx, reqURL, "GET")
43+
44+
resp, err := req.Response()
45+
if err != nil {
46+
return ""
47+
}
48+
defer resp.Body.Close()
49+
50+
if resp.StatusCode != http.StatusOK {
51+
return ""
52+
}
53+
54+
content, err := io.ReadAll(resp.Body)
55+
if err != nil {
56+
return ""
57+
}
58+
59+
return string(content)
60+
}
61+
3962
// FlushOptions represents the options for the flush call
4063
type FlushOptions struct {
4164
Timeout time.Duration

routers/private/hook_pre_receive.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"fmt"
88
"net/http"
99
"os"
10+
"strconv"
11+
"strings"
1012

1113
"code.gitea.io/gitea/models"
1214
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -124,6 +126,8 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) {
124126
preReceiveTag(ourCtx, refFullName)
125127
case git.DefaultFeatures().SupportProcReceive && refFullName.IsFor():
126128
preReceiveFor(ourCtx, refFullName)
129+
case git.DefaultFeatures().SupportProcReceive && refFullName.IsForReview():
130+
preReceiveForReview(ourCtx, refFullName)
127131
default:
128132
ourCtx.AssertCanWriteCode()
129133
}
@@ -447,6 +451,77 @@ func preReceiveFor(ctx *preReceiveContext, refFullName git.RefName) {
447451
}
448452
}
449453

454+
func canUpdateAgitPull(ctx *preReceiveContext, pull *issues_model.PullRequest) bool {
455+
if pull.Flow != issues_model.PullRequestFlowAGit {
456+
return false
457+
}
458+
459+
if ctx.opts.UserID == pull.Issue.PosterID {
460+
return true
461+
}
462+
463+
if !pull.AllowMaintainerEdit {
464+
return false
465+
}
466+
467+
if !ctx.loadPusherAndPermission() {
468+
return false
469+
}
470+
471+
return ctx.userPerm.CanWrite(unit.TypeCode)
472+
}
473+
474+
func preReceiveForReview(ctx *preReceiveContext, refFullName git.RefName) {
475+
if !ctx.AssertCreatePullRequest() {
476+
return
477+
}
478+
479+
if ctx.Repo.Repository.IsEmpty {
480+
ctx.JSON(http.StatusForbidden, private.Response{
481+
UserMsg: "Can't create pull request for an empty repository.",
482+
})
483+
return
484+
}
485+
486+
if ctx.opts.IsWiki {
487+
ctx.JSON(http.StatusForbidden, private.Response{
488+
UserMsg: "Pull requests are not supported on the wiki.",
489+
})
490+
return
491+
}
492+
493+
pullIndex, err := strconv.ParseInt(strings.TrimPrefix(string(refFullName), git.ForReviewPrefix), 10, 64)
494+
if err != nil {
495+
ctx.JSON(http.StatusForbidden, private.Response{
496+
UserMsg: "Unknow pull request index.",
497+
})
498+
return
499+
}
500+
pull, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, pullIndex)
501+
if err != nil {
502+
log.Error("preReceiveForReview: GetPullRequestByIndex: err: %v", err)
503+
ctx.JSON(http.StatusForbidden, private.Response{
504+
UserMsg: "Unknow pull request index.",
505+
})
506+
return
507+
}
508+
err = pull.LoadIssue(ctx)
509+
if err != nil {
510+
log.Error("preReceiveForReview: pull.LoadIssue: err: %v", err)
511+
ctx.JSON(http.StatusForbidden, private.Response{
512+
UserMsg: "Unknow pull request.",
513+
})
514+
return
515+
}
516+
517+
if !canUpdateAgitPull(ctx, pull) {
518+
ctx.JSON(http.StatusForbidden, private.Response{
519+
UserMsg: "Unknow pull request.",
520+
})
521+
return
522+
}
523+
}
524+
450525
func generateGitEnv(opts *private.HookOptions) (env []string) {
451526
env = os.Environ()
452527
if opts.GitAlternativeObjectDirectories != "" {

routers/web/misc/misc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func SSHInfo(rw http.ResponseWriter, req *http.Request) {
2020
return
2121
}
2222
rw.Header().Set("content-type", "text/json;charset=UTF-8")
23-
_, err := rw.Write([]byte(`{"type":"gitea","version":1}`))
23+
_, err := rw.Write([]byte(`{"type":"gitea","version":2}`))
2424
if err != nil {
2525
log.Error("fail to write result: err: %v", err)
2626
rw.WriteHeader(http.StatusInternalServerError)

services/agit/agit.go

Lines changed: 102 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,75 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
3636
return nil, fmt.Errorf("failed to get user. Error: %w", err)
3737
}
3838

39+
updateExistPull := func(pr *issues_model.PullRequest, i int) error {
40+
// update exist pull request
41+
if err := pr.LoadBaseRepo(ctx); err != nil {
42+
return fmt.Errorf("unable to load base repository for PR[%d] Error: %w", pr.ID, err)
43+
}
44+
45+
oldCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
46+
if err != nil {
47+
return fmt.Errorf("unable to get ref commit id in base repository for PR[%d] Error: %w", pr.ID, err)
48+
}
49+
50+
if oldCommitID == opts.NewCommitIDs[i] {
51+
results = append(results, private.HookProcReceiveRefResult{
52+
OriginalRef: opts.RefFullNames[i],
53+
OldOID: opts.OldCommitIDs[i],
54+
NewOID: opts.NewCommitIDs[i],
55+
Err: "new commit is same with old commit",
56+
})
57+
return nil
58+
}
59+
60+
if !forcePush {
61+
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").
62+
AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).
63+
RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()})
64+
if err != nil {
65+
return fmt.Errorf("failed to detect force push: %w", err)
66+
} else if len(output) > 0 {
67+
results = append(results, private.HookProcReceiveRefResult{
68+
OriginalRef: opts.RefFullNames[i],
69+
OldOID: opts.OldCommitIDs[i],
70+
NewOID: opts.NewCommitIDs[i],
71+
Err: "request `force-push` push option",
72+
})
73+
return nil
74+
}
75+
}
76+
77+
pr.HeadCommitID = opts.NewCommitIDs[i]
78+
if err = pull_service.UpdateRef(ctx, pr); err != nil {
79+
return fmt.Errorf("failed to update pull ref. Error: %w", err)
80+
}
81+
82+
pull_service.AddToTaskQueue(ctx, pr)
83+
err = pr.LoadIssue(ctx)
84+
if err != nil {
85+
return fmt.Errorf("failed to load pull issue. Error: %w", err)
86+
}
87+
comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i])
88+
if err == nil && comment != nil {
89+
notify_service.PullRequestPushCommits(ctx, pusher, pr, comment)
90+
}
91+
notify_service.PullRequestSynchronized(ctx, pusher, pr)
92+
isForcePush := comment != nil && comment.IsForcePush
93+
94+
results = append(results, private.HookProcReceiveRefResult{
95+
OldOID: oldCommitID,
96+
NewOID: opts.NewCommitIDs[i],
97+
Ref: pr.GetGitRefName(),
98+
OriginalRef: opts.RefFullNames[i],
99+
IsForcePush: isForcePush,
100+
IsCreatePR: false,
101+
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
102+
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
103+
})
104+
105+
return nil
106+
}
107+
39108
for i := range opts.OldCommitIDs {
40109
if opts.NewCommitIDs[i] == objectFormat.EmptyObjectID().String() {
41110
results = append(results, private.HookProcReceiveRefResult{
@@ -47,6 +116,37 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
47116
continue
48117
}
49118

119+
if opts.RefFullNames[i].IsForReview() {
120+
// try match refs/for-review/<pull index>
121+
pullIndex, err := strconv.ParseInt(strings.TrimPrefix(string(opts.RefFullNames[i]), git.ForReviewPrefix), 10, 64)
122+
if err != nil {
123+
results = append(results, private.HookProcReceiveRefResult{
124+
OriginalRef: opts.RefFullNames[i],
125+
OldOID: opts.OldCommitIDs[i],
126+
NewOID: opts.NewCommitIDs[i],
127+
Err: "Unknow pull request index",
128+
})
129+
continue
130+
}
131+
pull, err := issues_model.GetPullRequestByIndex(ctx, repo.ID, pullIndex)
132+
if err != nil {
133+
results = append(results, private.HookProcReceiveRefResult{
134+
OriginalRef: opts.RefFullNames[i],
135+
OldOID: opts.OldCommitIDs[i],
136+
NewOID: opts.NewCommitIDs[i],
137+
Err: "Unknow pull request index",
138+
})
139+
continue
140+
}
141+
142+
err = updateExistPull(pull, i)
143+
if err != nil {
144+
return nil, err
145+
}
146+
147+
continue
148+
}
149+
50150
if !opts.RefFullNames[i].IsFor() {
51151
results = append(results, private.HookProcReceiveRefResult{
52152
IsNotMatched: true,
@@ -158,70 +258,10 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
158258
continue
159259
}
160260

161-
// update exist pull request
162-
if err := pr.LoadBaseRepo(ctx); err != nil {
163-
return nil, fmt.Errorf("unable to load base repository for PR[%d] Error: %w", pr.ID, err)
164-
}
165-
166-
oldCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
167-
if err != nil {
168-
return nil, fmt.Errorf("unable to get ref commit id in base repository for PR[%d] Error: %w", pr.ID, err)
169-
}
170-
171-
if oldCommitID == opts.NewCommitIDs[i] {
172-
results = append(results, private.HookProcReceiveRefResult{
173-
OriginalRef: opts.RefFullNames[i],
174-
OldOID: opts.OldCommitIDs[i],
175-
NewOID: opts.NewCommitIDs[i],
176-
Err: "new commit is same with old commit",
177-
})
178-
continue
179-
}
180-
181-
if !forcePush {
182-
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").
183-
AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]).
184-
RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()})
185-
if err != nil {
186-
return nil, fmt.Errorf("failed to detect force push: %w", err)
187-
} else if len(output) > 0 {
188-
results = append(results, private.HookProcReceiveRefResult{
189-
OriginalRef: opts.RefFullNames[i],
190-
OldOID: opts.OldCommitIDs[i],
191-
NewOID: opts.NewCommitIDs[i],
192-
Err: "request `force-push` push option",
193-
})
194-
continue
195-
}
196-
}
197-
198-
pr.HeadCommitID = opts.NewCommitIDs[i]
199-
if err = pull_service.UpdateRef(ctx, pr); err != nil {
200-
return nil, fmt.Errorf("failed to update pull ref. Error: %w", err)
201-
}
202-
203-
pull_service.AddToTaskQueue(ctx, pr)
204-
err = pr.LoadIssue(ctx)
261+
err = updateExistPull(pr, i)
205262
if err != nil {
206-
return nil, fmt.Errorf("failed to load pull issue. Error: %w", err)
263+
return nil, err
207264
}
208-
comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i])
209-
if err == nil && comment != nil {
210-
notify_service.PullRequestPushCommits(ctx, pusher, pr, comment)
211-
}
212-
notify_service.PullRequestSynchronized(ctx, pusher, pr)
213-
isForcePush := comment != nil && comment.IsForcePush
214-
215-
results = append(results, private.HookProcReceiveRefResult{
216-
OldOID: oldCommitID,
217-
NewOID: opts.NewCommitIDs[i],
218-
Ref: pr.GetGitRefName(),
219-
OriginalRef: opts.RefFullNames[i],
220-
IsForcePush: isForcePush,
221-
IsCreatePR: false,
222-
URL: fmt.Sprintf("%s/pulls/%d", repo.HTMLURL(), pr.Index),
223-
ShouldShowMessage: setting.Git.PullRequestPushMessage && repo.AllowsPulls(ctx),
224-
})
225265
}
226266

227267
return results, nil

services/pull/edits.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ func SetAllowEdits(ctx context.Context, doer *user_model.User, pr *issues_model.
2222
return ErrUserHasNoPermissionForAction
2323
}
2424

25+
if doer.ID == pr.Issue.PosterID {
26+
pr.AllowMaintainerEdit = allow
27+
return issues_model.UpdateAllowEdits(ctx, pr)
28+
}
29+
2530
if err := pr.LoadHeadRepo(ctx); err != nil {
2631
return err
2732
}

templates/repo/issue/view_content/sidebar.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@
674674
{{end}}
675675

676676
{{if and .Issue.IsPull .IsIssuePoster (not .Issue.IsClosed) .Issue.PullRequest.HeadRepo}}
677-
{{if and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo}}
677+
{{if or (eq .SignedUserID .Issue.PosterID) (and (not (eq .Issue.PullRequest.HeadRepo.FullName .Issue.PullRequest.BaseRepo.FullName)) .CanWriteToHeadRepo)}}
678678
<div class="divider"></div>
679679
<div class="inline field">
680680
<div class="ui checkbox loading-icon-2px" id="allow-edits-from-maintainers"

0 commit comments

Comments
 (0)