aboutsummaryrefslogtreecommitdiffstats
path: root/cache
diff options
context:
space:
mode:
Diffstat (limited to 'cache')
-rw-r--r--cache/bug_cache.go4
-rw-r--r--cache/bug_excerpt.go2
-rw-r--r--cache/filter.go3
-rw-r--r--cache/identity_cache.go8
-rw-r--r--cache/repo_cache.go5
-rw-r--r--cache/repo_cache_bug.go75
-rw-r--r--cache/repo_cache_common.go15
-rw-r--r--cache/repo_cache_identity.go13
-rw-r--r--cache/repo_cache_test.go33
-rw-r--r--cache/resolvers.go29
10 files changed, 157 insertions, 30 deletions
diff --git a/cache/bug_cache.go b/cache/bug_cache.go
index ca526f7b..bbe9830f 100644
--- a/cache/bug_cache.go
+++ b/cache/bug_cache.go
@@ -51,9 +51,7 @@ func (c *BugCache) ResolveOperationWithMetadata(key string, value string) (entit
// preallocate but empty
matching := make([]entity.Id, 0, 5)
- it := bug.NewOperationIterator(c.bug)
- for it.Next() {
- op := it.Value()
+ for _, op := range c.bug.Operations() {
opValue, ok := op.GetMetadata(key)
if ok && value == opValue {
matching = append(matching, op.Id())
diff --git a/cache/bug_excerpt.go b/cache/bug_excerpt.go
index 6a9e7f75..152bdacf 100644
--- a/cache/bug_excerpt.go
+++ b/cache/bug_excerpt.go
@@ -87,7 +87,7 @@ func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
}
switch snap.Author.(type) {
- case *identity.Identity, *IdentityCache:
+ case *identity.Identity, *identity.IdentityStub, *IdentityCache:
e.AuthorId = snap.Author.Id()
default:
panic("unhandled identity type")
diff --git a/cache/filter.go b/cache/filter.go
index c167fe71..7ea40673 100644
--- a/cache/filter.go
+++ b/cache/filter.go
@@ -153,6 +153,9 @@ func compileMatcher(filters query.Filters) *Matcher {
for _, value := range filters.Title {
result.Title = append(result.Title, TitleFilter(value))
}
+ if filters.NoLabel {
+ result.NoFilters = append(result.NoFilters, NoLabelFilter())
+ }
return result
}
diff --git a/cache/identity_cache.go b/cache/identity_cache.go
index 25e273b9..e419387f 100644
--- a/cache/identity_cache.go
+++ b/cache/identity_cache.go
@@ -2,6 +2,7 @@ package cache
import (
"github.com/MichaelMure/git-bug/identity"
+ "github.com/MichaelMure/git-bug/repository"
)
var _ identity.Interface = &IdentityCache{}
@@ -23,8 +24,11 @@ func (i *IdentityCache) notifyUpdated() error {
return i.repoCache.identityUpdated(i.Identity.Id())
}
-func (i *IdentityCache) Mutate(f func(identity.Mutator) identity.Mutator) error {
- i.Identity.Mutate(f)
+func (i *IdentityCache) Mutate(repo repository.RepoClock, f func(*identity.Mutator)) error {
+ err := i.Identity.Mutate(repo, f)
+ if err != nil {
+ return err
+ }
return i.notifyUpdated()
}
diff --git a/cache/repo_cache.go b/cache/repo_cache.go
index b5b9ee54..14d5f3db 100644
--- a/cache/repo_cache.go
+++ b/cache/repo_cache.go
@@ -18,7 +18,8 @@ import (
// 1: original format
// 2: added cache for identities with a reference in the bug cache
// 3: no more legacy identity
-const formatVersion = 3
+// 4: entities make their IDs from data, not git commit
+const formatVersion = 4
// The maximum number of bugs loaded in memory. After that, eviction will be done.
const defaultMaxLoadedBugs = 1000
@@ -194,7 +195,7 @@ func (c *RepoCache) buildCache() error {
c.bugExcerpts = make(map[entity.Id]*BugExcerpt)
- allBugs := bug.ReadAllLocal(c.repo)
+ allBugs := bug.ReadAllWithResolver(c.repo, newIdentityCacheResolverNoLock(c))
// wipe the index just to be sure
err := c.repo.ClearBleveIndex("bug")
diff --git a/cache/repo_cache_bug.go b/cache/repo_cache_bug.go
index 1701f66d..c019da68 100644
--- a/cache/repo_cache_bug.go
+++ b/cache/repo_cache_bug.go
@@ -8,18 +8,17 @@ import (
"sort"
"strings"
"time"
+ "unicode/utf8"
+
+ "github.com/blevesearch/bleve"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/query"
"github.com/MichaelMure/git-bug/repository"
- "github.com/blevesearch/bleve"
)
-const (
- bugCacheFile = "bug-cache"
- searchCacheDir = "search-cache"
-)
+const bugCacheFile = "bug-cache"
var errBugNotInCache = errors.New("bug missing from cache")
@@ -154,7 +153,7 @@ func (c *RepoCache) ResolveBug(id entity.Id) (*BugCache, error) {
}
c.muBug.RUnlock()
- b, err := bug.ReadLocalWithResolver(c.repo, newIdentityCacheResolver(c), id)
+ b, err := bug.ReadWithResolver(c.repo, newIdentityCacheResolver(c), id)
if err != nil {
return nil, err
}
@@ -261,6 +260,53 @@ func (c *RepoCache) resolveBugMatcher(f func(*BugExcerpt) bool) (entity.Id, erro
return matching[0], nil
}
+// ResolveComment search for a Bug/Comment combination matching the merged
+// bug/comment Id prefix. Returns the Bug containing the Comment and the Comment's
+// Id.
+func (c *RepoCache) ResolveComment(prefix string) (*BugCache, entity.Id, error) {
+ bugPrefix, _ := entity.SeparateIds(prefix)
+ bugCandidate := make([]entity.Id, 0, 5)
+
+ // build a list of possible matching bugs
+ c.muBug.RLock()
+ for _, excerpt := range c.bugExcerpts {
+ if excerpt.Id.HasPrefix(bugPrefix) {
+ bugCandidate = append(bugCandidate, excerpt.Id)
+ }
+ }
+ c.muBug.RUnlock()
+
+ matchingBugIds := make([]entity.Id, 0, 5)
+ matchingCommentId := entity.UnsetId
+ var matchingBug *BugCache
+
+ // search for matching comments
+ // searching every bug candidate allow for some collision with the bug prefix only,
+ // before being refined with the full comment prefix
+ for _, bugId := range bugCandidate {
+ b, err := c.ResolveBug(bugId)
+ if err != nil {
+ return nil, entity.UnsetId, err
+ }
+
+ for _, comment := range b.Snapshot().Comments {
+ if comment.Id().HasPrefix(prefix) {
+ matchingBugIds = append(matchingBugIds, bugId)
+ matchingBug = b
+ matchingCommentId = comment.Id()
+ }
+ }
+ }
+
+ if len(matchingBugIds) > 1 {
+ return nil, entity.UnsetId, entity.NewErrMultipleMatch("bug/comment", matchingBugIds)
+ } else if len(matchingBugIds) == 0 {
+ return nil, entity.UnsetId, errors.New("comment doesn't exist")
+ }
+
+ return matchingBug, matchingCommentId, nil
+}
+
// QueryBugs return the id of all Bug matching the given Query
func (c *RepoCache) QueryBugs(q *query.Query) ([]entity.Id, error) {
c.muBug.RLock()
@@ -479,11 +525,24 @@ func (c *RepoCache) addBugToSearchIndex(snap *bug.Snapshot) error {
Text []string
}{}
+ // See https://github.com/blevesearch/bleve/issues/1576
+ var sb strings.Builder
+ normalize := func(text string) string {
+ sb.Reset()
+ for _, field := range strings.Fields(text) {
+ if utf8.RuneCountInString(field) < 100 {
+ sb.WriteString(field)
+ sb.WriteRune(' ')
+ }
+ }
+ return sb.String()
+ }
+
for _, comment := range snap.Comments {
- searchableBug.Text = append(searchableBug.Text, comment.Message)
+ searchableBug.Text = append(searchableBug.Text, normalize(comment.Message))
}
- searchableBug.Text = append(searchableBug.Text, snap.Title)
+ searchableBug.Text = append(searchableBug.Text, normalize(snap.Title))
index, err := c.repo.GetBleveIndex("bug")
if err != nil {
diff --git a/cache/repo_cache_common.go b/cache/repo_cache_common.go
index 5dc19d22..e23315f9 100644
--- a/cache/repo_cache_common.go
+++ b/cache/repo_cache_common.go
@@ -95,6 +95,12 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
go func() {
defer close(out)
+ author, err := c.GetUserIdentity()
+ if err != nil {
+ out <- entity.NewMergeError(err, "")
+ return
+ }
+
results := identity.MergeAll(c.repo, remote)
for result := range results {
out <- result
@@ -112,7 +118,7 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
}
}
- results = bug.MergeAll(c.repo, remote)
+ results = bug.MergeAll(c.repo, remote, author)
for result := range results {
out <- result
@@ -130,11 +136,10 @@ func (c *RepoCache) MergeAll(remote string) <-chan entity.MergeResult {
}
}
- err := c.write()
-
- // No easy way out here ..
+ err = c.write()
if err != nil {
- panic(err)
+ out <- entity.NewMergeError(err, "")
+ return
}
}()
diff --git a/cache/repo_cache_identity.go b/cache/repo_cache_identity.go
index 8df5b810..75453cb8 100644
--- a/cache/repo_cache_identity.go
+++ b/cache/repo_cache_identity.go
@@ -225,17 +225,20 @@ func (c *RepoCache) NewIdentityFromGitUserRaw(metadata map[string]string) (*Iden
// NewIdentity create a new identity
// The new identity is written in the repository (commit)
func (c *RepoCache) NewIdentity(name string, email string) (*IdentityCache, error) {
- return c.NewIdentityRaw(name, email, "", "", nil)
+ return c.NewIdentityRaw(name, email, "", "", nil, nil)
}
// NewIdentityFull create a new identity
// The new identity is written in the repository (commit)
-func (c *RepoCache) NewIdentityFull(name string, email string, login string, avatarUrl string) (*IdentityCache, error) {
- return c.NewIdentityRaw(name, email, login, avatarUrl, nil)
+func (c *RepoCache) NewIdentityFull(name string, email string, login string, avatarUrl string, keys []*identity.Key) (*IdentityCache, error) {
+ return c.NewIdentityRaw(name, email, login, avatarUrl, keys, nil)
}
-func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, metadata map[string]string) (*IdentityCache, error) {
- i := identity.NewIdentityFull(name, email, login, avatarUrl)
+func (c *RepoCache) NewIdentityRaw(name string, email string, login string, avatarUrl string, keys []*identity.Key, metadata map[string]string) (*IdentityCache, error) {
+ i, err := identity.NewIdentityFull(c.repo, name, email, login, avatarUrl, keys)
+ if err != nil {
+ return nil, err
+ }
return c.finishIdentity(i, metadata)
}
diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go
index bd06e84d..d13fa026 100644
--- a/cache/repo_cache_test.go
+++ b/cache/repo_cache_test.go
@@ -1,7 +1,9 @@
package cache
import (
+ "strings"
"testing"
+ "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -84,11 +86,12 @@ func TestCache(t *testing.T) {
require.Empty(t, cache.identities)
require.Empty(t, cache.identitiesExcerpts)
- // Reload, only excerpt are loaded
+ // Reload, only excerpt are loaded, but as we need to load the identities used in the bugs
+ // to check the signatures, we also load the identity used above
cache, err = NewRepoCache(repo)
require.NoError(t, err)
require.Empty(t, cache.bugs)
- require.Empty(t, cache.identities)
+ require.Len(t, cache.identities, 1)
require.Len(t, cache.bugExcerpts, 2)
require.Len(t, cache.identitiesExcerpts, 2)
@@ -108,8 +111,8 @@ func TestCache(t *testing.T) {
require.NoError(t, err)
}
-func TestPushPull(t *testing.T) {
- repoA, repoB, remote := repository.SetupReposAndRemote()
+func TestCachePushPull(t *testing.T) {
+ repoA, repoB, remote := repository.SetupGoGitReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
cacheA, err := NewRepoCache(repoA)
@@ -123,6 +126,10 @@ func TestPushPull(t *testing.T) {
require.NoError(t, err)
err = cacheA.SetUserIdentity(reneA)
require.NoError(t, err)
+ isaacB, err := cacheB.NewIdentity("Isaac Newton", "isaac@newton.uk")
+ require.NoError(t, err)
+ err = cacheB.SetUserIdentity(isaacB)
+ require.NoError(t, err)
// distribute the identity
_, err = cacheA.Push("origin")
@@ -274,3 +281,21 @@ func checkBugPresence(t *testing.T, cache *RepoCache, bug *BugCache, presence bo
require.Equal(t, bug, b)
}
}
+
+func TestLongDescription(t *testing.T) {
+ // See https://github.com/MichaelMure/git-bug/issues/606
+
+ text := strings.Repeat("x", 65536)
+
+ repo := repository.CreateGoGitTestRepo(false)
+ defer repository.CleanupTestRepos(repo)
+
+ backend, err := NewRepoCache(repo)
+ require.NoError(t, err)
+
+ i, err := backend.NewIdentity("René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+
+ _, _, err = backend.NewBugRaw(i, time.Now().Unix(), text, text, nil, nil)
+ require.NoError(t, err)
+}
diff --git a/cache/resolvers.go b/cache/resolvers.go
index 36b70d3b..e53c3660 100644
--- a/cache/resolvers.go
+++ b/cache/resolvers.go
@@ -20,3 +20,32 @@ func newIdentityCacheResolver(cache *RepoCache) *identityCacheResolver {
func (i *identityCacheResolver) ResolveIdentity(id entity.Id) (identity.Interface, error) {
return i.cache.ResolveIdentity(id)
}
+
+var _ identity.Resolver = &identityCacheResolverNoLock{}
+
+// identityCacheResolverNoLock is an identity Resolver that retrieve identities from
+// the cache, without locking it.
+type identityCacheResolverNoLock struct {
+ cache *RepoCache
+}
+
+func newIdentityCacheResolverNoLock(cache *RepoCache) *identityCacheResolverNoLock {
+ return &identityCacheResolverNoLock{cache: cache}
+}
+
+func (ir *identityCacheResolverNoLock) ResolveIdentity(id entity.Id) (identity.Interface, error) {
+ cached, ok := ir.cache.identities[id]
+ if ok {
+ return cached, nil
+ }
+
+ i, err := identity.ReadLocal(ir.cache.repo, id)
+ if err != nil {
+ return nil, err
+ }
+
+ cached = NewIdentityCache(ir.cache, i)
+ ir.cache.identities[id] = cached
+
+ return cached, nil
+}