refactor(automerge): fix error handling, populate recent automerge tasks on restart (#39001)

* Refactor "automerge" related code, clarify many details (including "unique queue item", start check by pull head or commit)
* Fix automerge queue handler's error handling, clarify error messages
* Populate recent automerge tasks on restart to restore the previous aborted automerge tasks

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Willem Kokke
2026-08-28 22:17:01 +01:00
committed by GitHub
parent 1652dfe62a
commit 60326ca03d
15 changed files with 246 additions and 176 deletions
+20 -7
View File
@@ -6,6 +6,7 @@ package git
import (
"context"
"regexp"
"strconv"
"strings"
"gitea.dev/modules/git/gitcmd"
@@ -18,6 +19,7 @@ const (
RemotePrefix = "refs/remotes/"
// PullPrefix is the base directory of the pull information of git.
PullPrefix = "refs/pull/"
pullSuffix = "/head"
)
// refNamePatternInvalid is regular expression with unallowed characters in git reference name
@@ -93,6 +95,10 @@ func RefNameFromCommit(shortName string) RefName {
return RefName(shortName)
}
func RefNameFromPullIndex(prIndex int64) RefName {
return RefName(PullPrefix + strconv.FormatInt(prIndex, 10) + pullSuffix)
}
func (ref RefName) String() string {
return string(ref)
}
@@ -134,14 +140,21 @@ func (ref RefName) BranchName() string {
return ref.nameWithoutPrefix(BranchPrefix)
}
// PullName returns the pull request name part of refs like refs/pull/<pull_name>/head
func (ref RefName) PullName() string {
func (ref RefName) PullIndex() (int64, bool) {
refName := string(ref)
lastIdx := strings.LastIndexByte(refName[len(PullPrefix):], '/')
if strings.HasPrefix(refName, PullPrefix) && lastIdx > -1 {
return refName[len(PullPrefix) : lastIdx+len(PullPrefix)]
s, ok := strings.CutPrefix(refName, PullPrefix)
if !ok {
return 0, false
}
return ""
pullStr, last, ok := strings.CutLast(s, "/")
if !ok || last != "head" {
return 0, false
}
pullIndex, err := strconv.ParseInt(pullStr, 10, 64)
if err != nil {
return 0, false
}
return pullIndex, true
}
// ForBranchName returns the branch name part of refs like refs/for/<branch_name>
@@ -165,7 +178,7 @@ func (ref RefName) ShortName() string {
return ref.RemoteName()
}
if ref.IsPull() {
return ref.PullName()
return strings.TrimSuffix(ref.nameWithoutPrefix(PullPrefix), pullSuffix)
}
if ref.IsFor() {
return ref.ForBranchName()
+4 -2
View File
@@ -19,10 +19,12 @@ func TestRefName(t *testing.T) {
assert.Equal(t, "release/foo", RefName("refs/tags/release/foo").TagName())
// Test pull names
assert.Equal(t, "1", RefName("refs/pull/1/head").PullName())
pullIndex, ok := RefName("refs/pull/1/head").PullIndex()
assert.True(t, ok)
assert.EqualValues(t, 1, pullIndex)
assert.True(t, RefName("refs/pull/1/head").IsPull())
assert.True(t, RefName("refs/pull/1/merge").IsPull())
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName())
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").ShortName())
// Test for branch names
assert.Equal(t, "main", RefName("refs/for/main").ForBranchName())