~gitpocket/gitpocket

1

feat(basin): merge confirmation with editable commit message

commit
8cc766074b6d287b3e9d1be8622fbe6f3ebf8660 browse tree
parent
099159809d1f25a9cbe4a40199da509a4d23c317
author
james <j@cpl0.zip>
signature
none
date
2026-09-30 21:55 UTC

3 files changed, +28 −2

pull.go modified

@@ -417,7 +417,15 @@ func (s *Server) pull(w http.ResponseWriter, r *http.Request, repo Repo, rest st
417417 s.fail(w, r, 403, "Only the repository owner can merge")
418418 return
419419 }
420- hash, err := g.Merge(pull.TargetBranch, pull.Ref(), fmt.Sprintf("Merge pull request #%d from %s\n\n%s", pull.Number, pull.SourceRepo.Path()+":"+pull.SourceBranch, pull.Title), user.Name, user.Name+"@"+s.host)
420+ title := strings.TrimSpace(r.FormValue("title"))
421+ if title == "" {
422+ title = mergeTitle(pull)
423+ }
424+ message := title
425+ if body := strings.TrimSpace(r.FormValue("body")); body != "" {
426+ message += "\n\n" + body
427+ }
428+ hash, err := g.Merge(pull.TargetBranch, pull.Ref(), message, user.Name, user.Name+"@"+s.host)
421429 if err != nil {
422430 http.Redirect(w, r, back+"?error="+template.URLQueryEscaper(err.Error()), http.StatusSeeOther)
423431 return
@@ -458,6 +466,10 @@ func (s *Server) pull(w http.ResponseWriter, r *http.Request, repo Repo, rest st
458466 if cur, err := g.Resolve("refs/heads/" + pull.TargetBranch); err == nil && cur == head {
459467 data["Mergeable"] = false
460468 }
469+ if isOwner && data["Mergeable"] == true && r.URL.Query().Get("merge") == "1" {
470+ data["Confirm"] = true
471+ data["MergeTitle"] = mergeTitle(pull)
472+ }
461473 }
462474 } else {
463475 data["Unrelated"] = true
@@ -471,6 +483,10 @@ func (s *Server) pull(w http.ResponseWriter, r *http.Request, repo Repo, rest st
471483 s.render(w, r, "pull", page{Title: fmt.Sprintf("#%d %s", pull.Number, pull.Title), Error: r.URL.Query().Get("error"), Math: hasMath(data["Body"]), Data: data})
472484 }
473485
486+func mergeTitle(pull Pull) string {
487+ return fmt.Sprintf("Merge pull request #%d from %s", pull.Number, pull.SourceRepo.Path()+":"+pull.SourceBranch)
488+}
489+
474490 func (s *Server) fork(w http.ResponseWriter, r *http.Request, repo Repo) {
475491 user := s.require(w, r)
476492 if user == nil {

static/style.css modified

@@ -204,3 +204,7 @@ nav.filters a.active{color:var(--fg);font-weight:600}
204204 .btn.block+form{margin-top:.4rem}
205205 .btn:disabled{opacity:.5;cursor:default}
206206 select{font:inherit;padding:.35rem .5rem;border:1px solid #ced4da;background:#fff}
207+.mergeform{max-width:none;margin:.4rem 0 .8rem}
208+.mergeform input,.mergeform textarea{width:100%}
209+.mergeform .actions{gap:.4rem}
210+.panel a.btn.block{margin-top:.4rem}

templates/pull.html modified

@@ -19,7 +19,13 @@
1919 {{if eq $pr.State "open"}}
2020 {{if .Data.Unrelated}}<p>The branches have no common history.</p>
2121 {{else if .Data.Mergeable}}<p>This branch can be merged automatically.</p>{{else}}<p>This branch has conflicts or nothing new to merge. Resolve locally, push, then update.</p>{{end}}
22-{{if .Data.Owner}}<form method="post" action="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}/merge"><button class="btn primary block"{{if not .Data.Mergeable}} disabled{{end}}>Merge pull request ▸</button></form>{{end}}
22+{{if .Data.Owner}}{{if .Data.Confirm}}
23+<form method="post" action="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}/merge" class="stack mergeform">
24+<label>Commit message <input name="title" value="{{.Data.MergeTitle}}" maxlength="200" required></label>
25+<label>Extended description <textarea name="body" rows="4">{{$pr.Title}}</textarea></label>
26+<div class="actions left"><button class="btn primary">Confirm merge ▸</button><a class="btn" href="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}">Cancel</a></div>
27+</form>
28+{{else}}{{if .Data.Mergeable}}<a class="btn primary block" href="?merge=1">Merge pull request ▸</a>{{else}}<button class="btn primary block" disabled>Merge pull request ▸</button>{{end}}{{end}}{{end}}
2329 {{if or .Data.Owner .Data.Author}}<form method="post" action="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}/update"><button class="btn block">Update from source ▸</button></form>
2430 <form method="post" action="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}/close"><button class="btn danger block">Close ▸</button></form>{{end}}
2531 {{else if eq $pr.State "closed"}}<p>Closed without merging.</p>{{if or .Data.Owner .Data.Author}}<form method="post" action="/{{$p.Repo.Path}}/pulls/{{$pr.Number}}/reopen"><button class="btn block">Reopen ▸</button></form>{{end}}