+66
-1
knotserver/git/merge.go
+66
-1
knotserver/git/merge.go
···
2
2
3
3
import (
4
4
"bytes"
5
+
"crypto/sha256"
5
6
"fmt"
6
7
"os"
7
8
"os/exec"
8
9
"regexp"
9
10
"strings"
10
11
12
+
"github.com/dgraph-io/ristretto"
11
13
"github.com/go-git/go-git/v5"
12
14
"github.com/go-git/go-git/v5/plumbing"
13
15
"tangled.sh/tangled.sh/core/patchutil"
14
16
)
17
+
18
+
type MergeCheckCache struct {
19
+
cache *ristretto.Cache
20
+
}
21
+
22
+
var (
23
+
mergeCheckCache MergeCheckCache
24
+
)
25
+
26
+
func init() {
27
+
cache, _ := ristretto.NewCache(&ristretto.Config{
28
+
NumCounters: 1e7,
29
+
MaxCost: 1 << 30,
30
+
BufferItems: 64,
31
+
TtlTickerDurationInSec: 60 * 60 * 24 * 2, // 2 days
32
+
})
33
+
mergeCheckCache = MergeCheckCache{cache}
34
+
}
35
+
36
+
func (m *MergeCheckCache) cacheKey(g *GitRepo, patch []byte, targetBranch string) string {
37
+
sep := byte(':')
38
+
hash := sha256.Sum256(fmt.Append([]byte{}, g.path, sep, g.h.String(), sep, patch, sep, targetBranch))
39
+
return fmt.Sprintf("%x", hash)
40
+
}
41
+
42
+
// we can't cache "mergeable" in risetto, nil is not cacheable
43
+
//
44
+
// we use the sentinel value instead
45
+
func (m *MergeCheckCache) cacheVal(check error) any {
46
+
if check == nil {
47
+
return struct{}{}
48
+
} else {
49
+
return check
50
+
}
51
+
}
52
+
53
+
func (m *MergeCheckCache) Set(g *GitRepo, patch []byte, targetBranch string, mergeCheck error) {
54
+
key := m.cacheKey(g, patch, targetBranch)
55
+
val := m.cacheVal(mergeCheck)
56
+
m.cache.Set(key, val, 0)
57
+
}
58
+
59
+
func (m *MergeCheckCache) Get(g *GitRepo, patch []byte, targetBranch string) (error, bool) {
60
+
key := m.cacheKey(g, patch, targetBranch)
61
+
if val, ok := m.cache.Get(key); ok {
62
+
if val == struct{}{} {
63
+
// cache hit for mergeable
64
+
return nil, true
65
+
} else if e, ok := val.(error); ok {
66
+
// cache hit for merge conflict
67
+
return e, true
68
+
}
69
+
}
70
+
71
+
// cache miss
72
+
return nil, false
73
+
}
15
74
16
75
type ErrMerge struct {
17
76
Message string
···
165
224
}
166
225
167
226
func (g *GitRepo) MergeCheck(patchData []byte, targetBranch string) error {
227
+
if val, ok := mergeCheckCache.Get(g, patchData, targetBranch); ok {
228
+
return val
229
+
}
230
+
168
231
var opts MergeOptions
169
232
opts.FormatPatch = patchutil.IsFormatPatch(string(patchData))
170
233
···
186
249
}
187
250
defer os.RemoveAll(tmpDir)
188
251
189
-
return g.applyPatch(tmpDir, patchFile, true, &opts)
252
+
result := g.applyPatch(tmpDir, patchFile, true, &opts)
253
+
mergeCheckCache.Set(g, patchData, targetBranch, result)
254
+
return result
190
255
}
191
256
192
257
func (g *GitRepo) Merge(patchData []byte, targetBranch string) error {